module Internal.Room exposing (..) {-| The `Room` type represents a Matrix Room. In here, you will find utilities to ask information about a room. -} import Dict import Internal.Api.Snackbar as Snackbar import Internal.Api.Sync.V2.SpecObjects as Sync import Internal.Api.Task as Api import Internal.Api.VaultUpdate exposing (VaultUpdate(..), Vnackbar) import Internal.Event as Event exposing (Event) import Internal.Tools.Hashdict as Hashdict import Internal.Tools.SpecEnums as Enums import Internal.Values.Event as IEvent import Internal.Values.Room as Internal import Internal.Values.StateManager as StateManager import Internal.Values.Timeline as Timeline import Json.Encode as E import Task exposing (Task) {-| The `Room` type represents a Matrix Room. It contains context information such as the `accessToken` that allows the retrieval of new information from the Matrix API if necessary. The `Room` type contains utilities to inquire about the room and send messages to it. -} type alias Room = Vnackbar Internal.IRoom {-| Create a new object from a joined room. -} initFromJoinedRoom : { roomId : String, nextBatch : String } -> Sync.JoinedRoom -> Internal.IRoom initFromJoinedRoom data jroom = Internal.IRoom { accountData = jroom.accountData |> Maybe.map .events |> Maybe.withDefault [] |> List.map (\{ eventType, content } -> ( eventType, content )) |> Dict.fromList , ephemeral = jroom.ephemeral |> Maybe.map .events |> Maybe.withDefault [] |> List.map IEvent.BlindEvent , events = jroom.timeline |> Maybe.map .events |> Maybe.withDefault [] |> List.map (Event.initFromClientEventWithoutRoomId data.roomId) |> Hashdict.fromList IEvent.eventId , roomId = data.roomId , tempEvents = [] , timeline = jroom.timeline |> Maybe.map (\timeline -> Timeline.newFromEvents { events = List.map (Event.initFromClientEventWithoutRoomId data.roomId) timeline.events , nextBatch = data.nextBatch , prevBatch = timeline.prevBatch , stateDelta = jroom.state |> Maybe.map (.events >> List.map (Event.initFromClientEventWithoutRoomId data.roomId) >> StateManager.fromEventList ) } ) |> Maybe.withDefault (Timeline.newFromEvents { events = [] , nextBatch = data.nextBatch , prevBatch = Nothing , stateDelta = Nothing } ) } accountData : String -> Room -> Maybe E.Value accountData key = Snackbar.withoutCandy >> Internal.accountData key {-| Add account data to the room. -} addAccountData : String -> E.Value -> Room -> Room addAccountData eventType content = Snackbar.map (Internal.addAccountData eventType content) {-| Adds an internal event to the `Room`. An internal event is a custom event that has been generated by the client. -} addInternalEvent : IEvent.IEvent -> Room -> Room addInternalEvent ievent = Snackbar.map (Internal.addEvent ievent) {-| Adds an `Event` object to the `Room`. An `Event` is a value from the `Internal.Event` module that is used to represent an event in a Matrix room. -} addEvent : Event -> Room -> Room addEvent = Snackbar.withoutCandy >> addInternalEvent {-| Get a given state event. -} getStateEvent : { eventType : String, stateKey : String } -> Room -> Maybe Event getStateEvent data = Snackbar.mapMaybe (Internal.getStateEvent data) {-| Get older events from the Matrix API. -} findOlderEvents : { limit : Maybe Int, onResponse : VaultUpdate -> msg } -> Room -> Cmd msg findOlderEvents { limit, onResponse } room = case Internal.latestGap (Snackbar.withoutCandy room) of Nothing -> Task.succeed (MultipleUpdates []) |> Task.perform onResponse Just { from, to } -> Api.getMessages { direction = Enums.ReverseChronological , filter = Nothing , from = Just to , limit = limit , roomId = roomId room , to = from } room |> Task.perform onResponse {-| Get the most recent events. -} mostRecentEvents : Room -> List Event mostRecentEvents = Snackbar.mapList Internal.mostRecentEvents {-| Retrieves the ID of the Matrix room associated with the given `Room`. -} roomId : Room -> String roomId = Snackbar.withoutCandy >> Internal.roomId {-| Sends a new event to the Matrix room associated with the given `Room`. -} sendEvent : { content : E.Value, eventType : String, stateKey : Maybe String, onResponse : VaultUpdate -> msg, room : Room } -> Cmd msg sendEvent { eventType, content, stateKey, onResponse, room } = case stateKey of Nothing -> Api.sendMessageEvent { content = content , eventType = eventType , extraTransactionNoise = "send-one-message" , roomId = roomId room } room |> Task.perform onResponse Just s -> Api.sendStateEvent { content = content , eventType = eventType , stateKey = s , roomId = roomId room } room |> Task.perform onResponse sendEvents : List { content : E.Value, eventType : String, stateKey : Maybe String, onResponse : VaultUpdate -> msg } -> Room -> Cmd msg sendEvents events room = List.indexedMap Tuple.pair events |> List.map (\( i, { eventType, content, stateKey, onResponse } ) -> case stateKey of Nothing -> Api.sendMessageEvent { content = content , eventType = eventType , extraTransactionNoise = "send-message-" ++ String.fromInt i , roomId = roomId room } room |> Task.perform onResponse Just s -> Api.sendStateEvent { content = content , eventType = eventType , stateKey = s , roomId = roomId room } room |> Task.perform onResponse ) |> Cmd.batch {-| Sends a new text message to the Matrix room associated with the given `Room`. -} sendMessage : { text : String, onResponse : VaultUpdate -> msg } -> Room -> Cmd msg sendMessage { text, onResponse } room = Api.sendMessageEvent { content = E.object [ ( "msgtype", E.string "m.text" ) , ( "body", E.string text ) ] , eventType = "m.room.message" , extraTransactionNoise = "literal-message:" ++ text , roomId = roomId room } room |> Task.perform onResponse sendMessages : { textPieces : List String, onResponse : VaultUpdate -> msg } -> Room -> Cmd msg sendMessages { textPieces, onResponse } room = textPieces |> List.indexedMap Tuple.pair |> List.map (\( i, piece ) -> Api.sendMessageEvent { content = E.object [ ( "msgtype", E.string "m.text" ) , ( "body", E.string piece ) ] , eventType = "m.room.message" , extraTransactionNoise = "literal-message-" ++ String.fromInt i ++ ":" ++ piece , roomId = roomId room } room ) |> List.map (Task.perform onResponse) |> Cmd.batch {-| Leave this room. -} leave : (VaultUpdate -> msg) -> Room -> Cmd msg leave onResponse room = Api.leave { roomId = roomId room, reason = Nothing } room |> Task.perform onResponse {-| Set account data. -} setAccountData : { key : String, value : E.Value, onResponse : VaultUpdate -> msg, room : Room } -> Cmd msg setAccountData { key, value, onResponse, room } = Api.setAccountData { content = value, eventType = key, roomId = Just (roomId room) } room |> Task.perform onResponse