Scylla/src/Main.elm

184 lines
7.0 KiB
Elm
Raw Normal View History

2018-12-08 20:02:29 -08:00
import Browser exposing (application, UrlRequest(..))
import Browser.Navigation as Nav
import Browser.Dom exposing (Viewport, setViewportOf)
2018-12-08 13:49:30 -08:00
import Scylla.Sync exposing (..)
2018-12-08 15:06:14 -08:00
import Scylla.Login exposing (..)
2018-12-08 13:49:30 -08:00
import Scylla.Model exposing (..)
import Scylla.Http exposing (..)
2018-12-08 15:06:14 -08:00
import Scylla.Views exposing (viewFull)
2018-12-08 19:09:20 -08:00
import Scylla.Route exposing (Route(..))
import Scylla.UserData exposing (..)
2018-12-13 13:42:23 -08:00
import Scylla.Notification exposing (..)
import Url exposing (Url)
2018-12-08 19:09:20 -08:00
import Url.Parser exposing (parse)
import Url.Builder
2018-12-08 13:49:30 -08:00
import Html exposing (div, text)
2018-12-08 15:06:14 -08:00
import Http
2018-12-09 23:38:43 -08:00
import Dict
import Task
type alias Flags =
{ token : Maybe String
}
init : Flags -> Url -> Nav.Key -> (Model, Cmd Msg)
init flags url key =
let
model =
{ key = key
2018-12-08 19:09:20 -08:00
, route = Maybe.withDefault Unknown <| parse Scylla.Route.route url
, token = flags.token
2018-12-08 15:06:14 -08:00
, loginUsername = ""
, loginPassword = ""
2018-12-08 13:49:30 -08:00
, apiUrl = "https://matrix.org"
2018-12-08 17:15:35 -08:00
, sync =
{ nextBatch = ""
, rooms = Nothing
, presence = Nothing
, accountData = Nothing
}
, errors = []
2018-12-09 23:38:43 -08:00
, roomText = Dict.empty
, transactionId = 0
, userData = Dict.empty
}
cmd = case flags.token of
Just _ -> Cmd.none
2018-12-08 19:09:20 -08:00
Nothing -> Nav.pushUrl key <| Url.Builder.absolute [ "login" ] []
in
(model, cmd)
view : Model -> Browser.Document Msg
view m =
{ title = "Scylla"
2018-12-08 17:15:35 -08:00
, body = viewFull m
}
update : Msg -> Model -> (Model, Cmd Msg)
2018-12-08 15:06:14 -08:00
update msg model = case msg of
ChangeApiUrl u -> ({ model | apiUrl = u }, Cmd.none)
ChangeLoginUsername u -> ({ model | loginUsername = u }, Cmd.none)
ChangeLoginPassword p -> ({ model | loginPassword = p }, Cmd.none)
AttemptLogin -> (model, Scylla.Http.login model.apiUrl model.loginUsername model.loginPassword) -- TODO
2018-12-08 20:02:29 -08:00
TryUrl urlRequest -> updateTryUrl model urlRequest
2018-12-13 14:06:15 -08:00
OpenRoom s -> (model, Nav.pushUrl model.key <| roomUrl s)
2018-12-08 19:09:20 -08:00
ChangeRoute r -> ({ model | route = r }, Cmd.none)
ViewportAfterMessage v -> updateViewportAfterMessage model v
ViewportChangeComplete _ -> (model, Cmd.none)
2018-12-08 15:06:14 -08:00
ReceiveLoginResponse r -> updateLoginResponse model r
ReceiveFirstSyncResponse r -> updateSyncResponse model r False
ReceiveSyncResponse r -> updateSyncResponse model r True
ReceiveUserData s r -> updateUserData model s r
2018-12-09 23:38:43 -08:00
ChangeRoomText r t -> ({ model | roomText = Dict.insert r t model.roomText}, Cmd.none)
SendRoomText r -> updateSendRoomText model r
SendRoomTextResponse r -> (model, Cmd.none)
ReceiveCompletedReadMarker r -> (model, Cmd.none)
2018-12-09 23:38:43 -08:00
updateViewportAfterMessage : Model -> Result Browser.Dom.Error Viewport -> (Model, Cmd Msg)
updateViewportAfterMessage m vr =
let
cmd vp = if vp.scene.height - (vp.viewport.y + vp.viewport.height ) < 100
then Task.attempt ViewportChangeComplete <| setViewportOf "events-wrapper" vp.viewport.x vp.scene.height
else Cmd.none
in
(m, Result.withDefault Cmd.none <| Result.map cmd vr)
updateUserData : Model -> String -> Result Http.Error UserData -> (Model, Cmd Msg)
updateUserData m s r = case r of
Ok ud -> ({ m | userData = Dict.insert s ud m.userData }, Cmd.none)
Err e -> (m, userData m.apiUrl (Maybe.withDefault "" m.token) s)
2018-12-09 23:38:43 -08:00
updateSendRoomText : Model -> String -> (Model, Cmd Msg)
updateSendRoomText m r =
let
token = Maybe.withDefault "" m.token
message = Maybe.andThen (\s -> if s == "" then Nothing else Just s)
<| Dict.get r m.roomText
command = Maybe.withDefault Cmd.none
<| Maybe.map (sendTextMessage m.apiUrl token m.transactionId r) message
in
({ m | roomText = Dict.insert r "" m.roomText, transactionId = m.transactionId + 1 }, command)
2018-12-08 20:02:29 -08:00
updateTryUrl : Model -> Browser.UrlRequest -> (Model, Cmd Msg)
updateTryUrl m ur = case ur of
Internal u -> (m, Nav.pushUrl m.key (Url.toString u))
_ -> (m, Cmd.none)
2018-12-08 15:06:14 -08:00
updateLoginResponse : Model -> Result Http.Error LoginResponse -> (Model, Cmd Msg)
updateLoginResponse model r = case r of
2018-12-13 13:42:23 -08:00
Ok lr -> ( { model | token = Just lr.accessToken, loginUsername = lr.userId } , Cmd.batch
2018-12-08 19:09:20 -08:00
[ firstSync model.apiUrl lr.accessToken
, Nav.pushUrl model.key <| Url.Builder.absolute [] []
] )
2018-12-08 15:06:14 -08:00
Err e -> (model, Cmd.none)
updateSyncResponse : Model -> Result Http.Error SyncResponse -> Bool -> (Model, Cmd Msg)
updateSyncResponse model r notify =
2018-12-09 00:35:07 -08:00
let
token = Maybe.withDefault "" model.token
nextBatch = Result.withDefault model.sync.nextBatch
<| Result.map .nextBatch r
syncCmd = sync nextBatch model.apiUrl token
newUsers sr = List.filter (\s -> not <| Dict.member s model.userData) <| roomsUsers sr
2018-12-14 00:04:41 -08:00
newUserCmd sr = Cmd.batch
2018-12-13 13:42:23 -08:00
<| List.map (userData model.apiUrl
<| Maybe.withDefault "" model.token)
<| newUsers sr
2018-12-13 16:01:54 -08:00
notification sr = findFirstBy
(\(s, e) -> e.originServerTs)
(\(s, e) -> e.sender /= model.loginUsername)
2018-12-13 13:42:23 -08:00
<| notificationEvents sr
2018-12-14 00:04:41 -08:00
notificationCmd sr = if notify
then Maybe.withDefault Cmd.none
<| Maybe.map (\(s, e) -> sendNotificationPort
{ name = displayName model e.sender
, text = notificationText e
, room = s
}) <| notification sr
else Cmd.none
room = currentRoomId model
roomMessages sr = case room of
Just rid -> List.filter (((==) "m.room.message") << .type_)
<| Maybe.withDefault []
<| Maybe.andThen .events
<| Maybe.andThen .timeline
<| Maybe.andThen (Dict.get rid)
<| Maybe.andThen .join
<| sr.rooms
Nothing -> []
2018-12-14 00:04:41 -08:00
setScrollCmd sr = if List.isEmpty
<| roomMessages sr
then Cmd.none
else Task.attempt ViewportAfterMessage (Browser.Dom.getViewportOf "events-wrapper")
setReadReceiptCmd sr = case (room, List.head <| List.reverse <| roomMessages sr) of
(Just rid, Just re) -> setReadMarkers model.apiUrl token rid re.eventId <| Just re.eventId
_ -> Cmd.none
2018-12-09 00:35:07 -08:00
in
case r of
2018-12-13 13:42:23 -08:00
Ok sr -> ({ model | sync = mergeSyncResponse model.sync sr }, Cmd.batch
[ syncCmd
2018-12-14 00:04:41 -08:00
, newUserCmd sr
, notificationCmd sr
, setScrollCmd sr
, setReadReceiptCmd sr
2018-12-13 13:42:23 -08:00
])
_ -> (model, syncCmd)
subscriptions : Model -> Sub Msg
2018-12-13 14:06:15 -08:00
subscriptions m = onNotificationClickPort OpenRoom
onUrlRequest : Browser.UrlRequest -> Msg
onUrlRequest = TryUrl
onUrlChange : Url -> Msg
2018-12-08 19:09:20 -08:00
onUrlChange = ChangeRoute << Maybe.withDefault Unknown << parse Scylla.Route.route
main = application
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
, onUrlRequest = onUrlRequest
, onUrlChange = onUrlChange
}