elm-matrix-sdk-beta/src/Internal/Values/Room.elm

256 lines
6.9 KiB
Elm

module Internal.Values.Room exposing
( Room, init
, RoomUpdate, update
, Batch, addBatch, addSync, addEvents, mostRecentEvents
, getAccountData, setAccountData
, coder, encode, decode
)
{-|
# Room
What is usually called a chat, a channel, a conversation or a group chat on
other platforms, the term used in Matrix is a "room". A room is a conversation
where a group of users talk to each other.
This module is the internal module of a room. Its functions serve the update
the local room state. Its changes do **NOT** reflect the actual room state on
the homeserver: as a matter of fact, these functions are meant to help the local
room state reflect the homeserver state of the room.
## Room
@docs Room, init
## Update
@docs RoomUpdate, update
## Timeline
@docs Batch, addBatch, addSync, addEvents, mostRecentEvents
## Account data
@docs getAccountData, setAccountData
## JSON coding
@docs coder, encode, decode
-}
import FastDict as Dict exposing (Dict)
import Internal.Config.Log exposing (log)
import Internal.Config.Text as Text
import Internal.Filter.Timeline as Filter exposing (Filter)
import Internal.Tools.Hashdict as Hashdict exposing (Hashdict)
import Internal.Tools.Json as Json
import Internal.Values.Event as Event exposing (Event)
import Internal.Values.StateManager as StateManager exposing (StateManager)
import Internal.Values.Timeline as Timeline exposing (Timeline)
import Json.Encode as E
{-| The Batch is a group of new events from somewhere in the timeline.
-}
type alias Batch =
{ events : List Event, filter : Filter, start : Maybe String, end : String }
{-| The Matrix Room is a representation of a Matrix Room as portrayed by the
homeserver.
-}
type alias Room =
{ accountData : Dict String Json.Value
, events : Hashdict Event
, roomId : String
, state : StateManager
, timeline : Timeline
}
{-| The RoomUpdate type explains how to update a room based on new information
from the Matrix API.
-}
type RoomUpdate
= AddSync Batch
| More (List RoomUpdate)
| SetAccountData String Json.Value
{-| Add new events to the Room's event directory + Room's timeline.
-}
addEventsToTimeline : (Timeline.Batch -> Timeline -> Timeline) -> Batch -> Room -> Room
addEventsToTimeline f { events, filter, start, end } room =
let
batch : Timeline.Batch
batch =
{ events = List.map .eventId events
, filter = filter
, start = start
, end = end
}
in
{ room
| events = List.foldl Hashdict.insert room.events events
, timeline = f batch room.timeline
}
{-| Add a batch of events to the Room.
-}
addBatch : Batch -> Room -> Room
addBatch =
addEventsToTimeline Timeline.insert
{-| Add events to the room, with no particular information about their location
on the timeline. This is especially helpful for events that offer information
like the room's state, given that it is essential to know them but they have
often been sent a long time ago.
-}
addEvents : List Event -> Room -> Room
addEvents events room =
{ room
| events = List.foldl Hashdict.insert room.events events
}
{-| Add a new sync to the Room. The difference with the
[addBatch](Internal-Values-Room#addBatch) function is that this function
explicitly tells the Timeline that it is at the front of the timeline.
-}
addSync : Batch -> Room -> Room
addSync =
addEventsToTimeline Timeline.addSync
{-| Define how a Room can be encoded and decoded to and from a JavaScript value.
-}
coder : Json.Coder Room
coder =
Json.object5
{ name = Text.docs.room.name
, description = Text.docs.room.description
, init = Room
}
(Json.field.optional.withDefault
{ fieldName = "accountData"
, toField = .accountData
, description = Text.fields.room.accountData
, coder = Json.fastDict Json.value
, default = ( Dict.empty, [] )
, defaultToString = Json.encode (Json.fastDict Json.value) >> E.encode 0
}
)
(Json.field.optional.withDefault
{ fieldName = "events"
, toField = .events
, description = Text.fields.room.events
, coder = Hashdict.coder .eventId Event.coder
, default = ( Hashdict.empty .eventId, [ log.warn "Found a room with no known events! Is it empty?" ] )
, defaultToString = Json.encode (Hashdict.coder .eventId Event.coder) >> E.encode 0
}
)
(Json.field.required
{ fieldName = "roomId"
, toField = .roomId
, description = Text.fields.room.roomId
, coder = Json.string
}
)
(Json.field.optional.withDefault
{ fieldName = "state"
, toField = .state
, description = Text.fields.room.state
, coder = StateManager.coder
, default = ( StateManager.empty, [] )
, defaultToString = Json.encode StateManager.coder >> E.encode 0
}
)
(Json.field.optional.withDefault
{ fieldName = "timeline"
, toField = .timeline
, description = Text.fields.room.timeline
, coder = Timeline.coder
, default = ( Timeline.empty, [] )
, defaultToString = Json.encode Timeline.coder >> E.encode 0
}
)
{-| Decode a Room from JSON format.
-}
decode : Json.Decoder Room
decode =
Json.decode coder
{-| Encode a Room into JSON format.
-}
encode : Json.Encoder Room
encode =
Json.encode coder
{-| Get a piece of account data as information from the room.
-}
getAccountData : String -> Room -> Maybe Json.Value
getAccountData key room =
Dict.get key room.accountData
{-| Create an empty room for which nothing is known.
-}
init : String -> Room
init roomId =
{ accountData = Dict.empty
, events = Hashdict.empty .eventId
, roomId = roomId
, state = StateManager.empty
, timeline = Timeline.empty
}
{-| Get the most recent events from the timeline.
-}
mostRecentEvents : Room -> List Event
mostRecentEvents room =
room.timeline
|> Timeline.mostRecentEvents Filter.pass
|> List.map (List.filterMap (\e -> Hashdict.get e room.events))
|> List.sortBy List.length
-- Get the largest list of events
|> List.head
|> Maybe.withDefault []
{-| Set a piece of account data as information about the room.
-}
setAccountData : String -> Json.Value -> Room -> Room
setAccountData key value room =
{ room | accountData = Dict.insert key value room.accountData }
{-| Update the Room based on given instructions.
-}
update : RoomUpdate -> Room -> Room
update ru room =
case ru of
AddSync batch ->
addSync batch room
More items ->
List.foldl update room items
SetAccountData key value ->
setAccountData key value room