diff --git a/cmd/mi/main.go b/cmd/mi/main.go index 3b92552..f941a90 100644 --- a/cmd/mi/main.go +++ b/cmd/mi/main.go @@ -160,6 +160,12 @@ func main() { mi.RegisterPusher(mi.PushTwitter) go mi.StreamMastodonToTwitter(ctx) go mi.BlogPOSSE(ctx) + } else { + mi.RegisterPusher(func(ctx context.Context, p Post) error { + ln.Log(ctx, p) + fmt.Println(p.Format()) + return nil + }) } h := &http.Server{ diff --git a/cmd/mi/mi.go b/cmd/mi/mi.go index 17b2e38..24608cb 100644 --- a/cmd/mi/mi.go +++ b/cmd/mi/mi.go @@ -27,4 +27,5 @@ func (mi *Mi) RegisterPusher(p Pusher) { func (mi *Mi) RegisterRoutes() { mi.mux.HandleFunc("/blog/refresh", mi.RefreshBlog) mi.mux.HandleFunc("/.within/botinfo", botInfoPage) + mi.mux.HandleFunc("/posse/post", mi.PostPOSSE) } diff --git a/cmd/mi/posse.go b/cmd/mi/posse.go index 4a1653e..e333297 100644 --- a/cmd/mi/posse.go +++ b/cmd/mi/posse.go @@ -2,7 +2,9 @@ package main import ( "context" + "encoding/json" "fmt" + "net/http" "strings" "github.com/hashicorp/go-multierror" @@ -12,21 +14,32 @@ import ( type Pusher func(context.Context, Post) error type Post struct { - Title string - URL string - Tags []string + Title string `json:"title"` + URL string `json:"url"` + Body string `json:"body"` + Tags []string `json:"tags"` } func (p Post) Format() string { var sb strings.Builder - fmt.Fprintf(&sb, "New post: %s\n\n%s\n\n", p.Title, p.URL) + if p.Title != "" { + fmt.Fprintf(&sb, "%s\n\n", p.Title) + } + + if p.URL != "" { + fmt.Fprintf(&sb, "%s\n\n", p.URL) + } + + if p.Body != "" { + fmt.Fprintf(&sb, "%s\n\n", p.Body) + } for _, tg := range p.Tags { tg = strings.ReplaceAll(tg, "-", "") fmt.Fprintf(&sb, "#%s ", tg) } - return sb.String() + return strings.TrimSpace(sb.String()) } func (p Post) F() ln.F { @@ -34,6 +47,7 @@ func (p Post) F() ln.F { "post_title": p.Title, "post_url": p.URL, "post_tags": p.Tags, + "post_body": p.Body, } } @@ -49,3 +63,15 @@ func (mi *Mi) Push(ctx context.Context, p Post) error { return result } + +func (mi *Mi) PostPOSSE(w http.ResponseWriter, r *http.Request) { + var data Post + err := json.NewDecoder(r.Body).Decode(&data) + if err != nil { + http.Error(w, "bad data", http.StatusBadRequest) + ln.Error(r.Context(), err) + return + } + + mi.Push(r.Context(), data) +} diff --git a/frontend/src/Layout.elm b/frontend/src/Layout.elm index a2f3f60..d87b215 100644 --- a/frontend/src/Layout.elm +++ b/frontend/src/Layout.elm @@ -44,6 +44,7 @@ viewHeader currentRoute = ] [ viewLink currentRoute ( "Mi", routes.top ) , viewLink currentRoute ( "Switch Data", routes.switch ) + , viewLink currentRoute ( "POSSE", routes.pOSSE ) ] diff --git a/frontend/src/Mi.elm b/frontend/src/Mi.elm index 70908df..71257fa 100644 --- a/frontend/src/Mi.elm +++ b/frontend/src/Mi.elm @@ -1,6 +1,6 @@ -module Mi exposing (TokenData, expectJson, request, tokenDecoder) +module Mi exposing (TokenData, errorToString, expectJson, request, tokenDecoder) -import Http +import Http exposing (Error(..)) import Json.Decode as D @@ -55,3 +55,22 @@ expectJson toMsg decoder = Err err -> Err (Http.BadBody (D.errorToString err)) + + +errorToString : Http.Error -> String +errorToString err = + case err of + Timeout -> + "Timeout exceeded" + + NetworkError -> + "Network error" + + BadStatus st -> + "Bad status code: " ++ String.fromInt st + + BadBody text -> + "Unexpected response from api: " ++ text + + BadUrl url -> + "Malformed url: " ++ url diff --git a/frontend/src/Mi/POSSE.elm b/frontend/src/Mi/POSSE.elm new file mode 100644 index 0000000..2234770 --- /dev/null +++ b/frontend/src/Mi/POSSE.elm @@ -0,0 +1,31 @@ +module Mi.POSSE exposing (..) + +import Json.Decode as D +import Json.Encode as E + + +type alias Post = + { title : String + , body : String + , url : String + , tags : List String + } + + +encoder : Post -> E.Value +encoder post = + E.object + [ ( "title", E.string post.title ) + , ( "body", E.string post.body ) + , ( "url", E.string post.url ) + , ( "tags", E.list E.string post.tags ) + ] + + +decoder : D.Decoder Post +decoder = + D.map4 Post + (D.field "title" D.string) + (D.field "body" D.string) + (D.field "url" D.string) + (D.field "tags" (D.list D.string)) diff --git a/frontend/src/Pages/POSSE.elm b/frontend/src/Pages/POSSE.elm new file mode 100644 index 0000000..6914984 --- /dev/null +++ b/frontend/src/Pages/POSSE.elm @@ -0,0 +1,169 @@ +module Pages.POSSE exposing (Model, Msg, page) + +import Element exposing (..) +import Element.Background as Background +import Element.Font as Font +import Element.Input as Input +import Generated.Params as Params +import Global +import Http +import Mi +import Mi.POSSE as POSSE +import Spa.Page +import Utils.Spa exposing (Page, PageContext) + + +page : Page Params.POSSE Model Msg model msg appMsg +page = + Spa.Page.component + { title = always "POSSE" + , init = always init + , update = update + , subscriptions = always subscriptions + , view = always view + } + + +type alias Model = + { title : String + , url : String + , body : String + , tags : String + , status : String + } + + +init : Params.POSSE -> ( Model, Cmd Msg, Cmd Global.Msg ) +init _ = + ( { title = "" + , url = "" + , body = "" + , tags = "" + , status = "" + } + , Cmd.none + , Cmd.none + ) + + +type Msg + = UpdateTitle String + | UpdateBody String + | UpdateURL String + | UpdateTags String + | Send + | GotResponse (Result Http.Error String) + + +update : PageContext -> Msg -> Model -> ( Model, Cmd Msg, Cmd Global.Msg ) +update { global } msg model = + case msg of + UpdateBody body -> + ( { model | body = body } + , Cmd.none + , Cmd.none + ) + + UpdateURL url -> + ( { model | url = url } + , Cmd.none + , Cmd.none + ) + + UpdateTags tags -> + ( { model | tags = tags } + , Cmd.none + , Cmd.none + ) + + UpdateTitle title -> + ( { model | title = title } + , Cmd.none + , Cmd.none + ) + + Send -> + let + post = + { title = model.title + , body = model.body + , url = model.url + , tags = String.words model.tags + } + + postJson = + POSSE.encoder post + in + ( model + , Mi.request + "POST" + (Maybe.withDefault "" global.token) + "/posse/post" + (Http.jsonBody postJson) + (Http.expectString GotResponse) + , Cmd.none + ) + + GotResponse resp -> + case resp of + Ok _ -> + ( { model | status = "Posted!" } + , Cmd.none + , Cmd.none + ) + + Err data -> + ( { model | status = "Error when posting! " ++ Mi.errorToString data } + , Cmd.none + , Cmd.none + ) + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.none + + +view : Model -> Element Msg +view model = + let + statusMsg = + case model.status of + "" -> + none + + _ -> + row [] [ text "Status", text model.status ] + + makeInput : String -> (String -> Msg) -> String -> Element Msg + makeInput label msg text = + Input.spellChecked [ padding 10, Font.size 14 ] + { label = Input.labelAbove [] (Element.text label) + , onChange = msg + , text = text + , placeholder = Nothing + } + in + column [] + [ statusMsg + , row [ padding 10 ] + [ makeInput "Title" UpdateTitle model.title + , makeInput "URL" UpdateURL model.url + , makeInput "Tags" UpdateTags model.tags + ] + , Input.multiline [ Font.size 14 ] + { label = + Input.labelAbove + [ Input.focusedOnLoad ] + (text "Body") + , onChange = UpdateBody + , spellcheck = True + , placeholder = Nothing + , text = model.body + } + , Input.button + [] + { onPress = Just Send + , label = text "Send" + } + ] diff --git a/frontend/src/Pages/SignIn.elm b/frontend/src/Pages/SignIn.elm index e518bf0..6fc0462 100644 --- a/frontend/src/Pages/SignIn.elm +++ b/frontend/src/Pages/SignIn.elm @@ -129,11 +129,12 @@ view model = Nothing -> Element.column [] - [ Input.text [] + [ Input.currentPassword [ Input.focusedOnLoad ] { label = Input.labelAbove [] (text "API Token") , onChange = UpdateToken , text = model.token , placeholder = Just (Input.placeholder [] (text "")) + , show = False } , Input.button [] { onPress = Just SignIn, label = text "Login" } ] diff --git a/frontend/src/Pages/Switch.elm b/frontend/src/Pages/Switch.elm index 1337657..c4591c9 100644 --- a/frontend/src/Pages/Switch.elm +++ b/frontend/src/Pages/Switch.elm @@ -27,10 +27,6 @@ page = } - --- INIT - - type alias Model = { currentFront : Maybe Mi.SwitchData.Switch , frontData : Maybe (List Mi.SwitchData.Switch) @@ -76,10 +72,6 @@ init { global } _ = ) - --- UPDATE - - type Msg = ValidateCurrentFront (Result Http.Error Mi.SwitchData.Switch) | ValidateFrontData (Result Http.Error (List Mi.SwitchData.Switch)) @@ -126,8 +118,8 @@ update { global } msg model = , Cmd.none ) - Err _ -> - ( { model | error = Just "can't fetch current front" } + Err err -> + ( { model | error = Just ("can't fetch current front: " ++ Mi.errorToString err) } , Cmd.none , Cmd.none ) @@ -140,26 +132,18 @@ update { global } msg model = , Cmd.none ) - Err _ -> - ( { model | error = Just "can't fetch historical front data" } + Err err -> + ( { model | error = Just ("can't fetch historical front data: " ++ Mi.errorToString err) } , Cmd.none , Cmd.none ) - --- SUBSCRIPTIONS - - subscriptions : Model -> Sub Msg subscriptions model = Sub.none - --- VIEW - - view : Model -> Element Msg view model = let