Add event to get older events

pull/1/head
Bram van den Heuvel 2023-03-23 22:45:26 +01:00
parent c32a62c242
commit 098b38170a
7 changed files with 250 additions and 52 deletions

View File

@ -7,6 +7,7 @@ import Hash
import Internal.Api.Chain as Chain
import Internal.Api.Credentials as Cred exposing (Credentials)
import Internal.Api.GetEvent.Main exposing (EventInput)
import Internal.Api.GetMessages.Main exposing (GetMessagesInput)
import Internal.Api.Invite.Main exposing (InviteInput)
import Internal.Api.JoinRoomById.Main exposing (JoinRoomByIdInput)
import Internal.Api.JoinedMembers.Main exposing (JoinedMembersInput)
@ -35,6 +36,13 @@ getEvent { eventId, roomId } cred =
|> C.toTask
getMessages : GetMessagesInput -> Credentials -> FutureTask
getMessages data cred =
C.makeVBA cred
|> Chain.andThen (C.getMessages data)
|> C.toTask
invite : InviteInput -> Credentials -> FutureTask
invite data cred =
C.makeVBA cred

View File

@ -10,6 +10,7 @@ resend other events or forward them elsewhere.
import Internal.Api.Credentials exposing (Credentials)
import Internal.Api.GetEvent.Main as GetEvent
import Internal.Api.GetEvent.V1.SpecObjects as GetEventSO
import Internal.Api.GetMessages.V4.SpecObjects as GetMessagesSO
import Internal.Api.Sync.V2.SpecObjects as SyncSO
import Internal.Tools.Timestamp exposing (Timestamp)
import Internal.Values.Event as Internal
@ -62,6 +63,32 @@ initFromGetEvent output =
}
{-| Create an internal event type from an API endpoint event object.
This function is placed in this file to respect file hierarchy and avoid circular imports.
-}
initFromGetMessages : GetMessagesSO.ClientEvent -> Internal.IEvent
initFromGetMessages output =
Internal.init
{ content = output.content
, eventId = output.eventId
, originServerTs = output.originServerTs
, roomId = output.roomId
, sender = output.sender
, stateKey = output.stateKey
, contentType = output.contentType
, unsigned =
output.unsigned
|> Maybe.map
(\(GetMessagesSO.UnsignedData data) ->
{ age = data.age
, prevContent = data.prevContent
, redactedBecause = Maybe.map initFromGetMessages data.redactedBecause
, transactionId = data.transactionId
}
)
}
{-| Create an internal event type from an API endpoint event object.
This function is placed in this file to respect file hierarchy and avoid circular imports.
-}

View File

@ -7,10 +7,11 @@ import Dict
import Internal.Api.Credentials exposing (Credentials)
import Internal.Api.Sync.V2.SpecObjects as Sync
import Internal.Api.Task as Api
import Internal.Api.VaultUpdate exposing (VaultUpdate)
import Internal.Api.VaultUpdate exposing (VaultUpdate(..))
import Internal.Event as Event exposing (Event)
import Internal.Tools.Exceptions as X
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
@ -118,6 +119,26 @@ withoutCredentials (Room { room }) =
room
{-| Get older events from the Matrix API.
-}
getOlderEvents : { limit : Maybe Int } -> Room -> Task X.Error VaultUpdate
getOlderEvents { limit } (Room { context, room }) =
case Internal.latestGap room of
Nothing ->
Task.succeed (MultipleUpdates [])
Just { from, to } ->
Api.getMessages
{ direction = Enums.ReverseChronological
, filter = Nothing
, from = Just to
, limit = limit
, roomId = Internal.roomId room
, to = from
}
context
{-| Get the most recent events.
-}
mostRecentEvents : Room -> List Event

View File

@ -59,11 +59,28 @@ getStateEvent data (IRoom room) =
|> StateManager.getStateEvent data
{-| Get the room's id.
{-| Insert a chunk of events into a room.
-}
roomId : IRoom -> String
roomId (IRoom room) =
room.roomId
insertEvents :
{ events : List IEvent
, nextBatch : String
, prevBatch : Maybe String
, stateDelta : Maybe StateManager
}
-> IRoom
-> IRoom
insertEvents data (IRoom ({ timeline } as room)) =
IRoom
{ room | timeline = Timeline.insertEvents data timeline }
|> List.foldl addEvent
|> (|>) data.events
{-| Get the latest gap.
-}
latestGap : IRoom -> Maybe { from : Maybe String, to : String }
latestGap (IRoom room) =
Timeline.latestGap room.timeline
{-| Get the most recent events.
@ -71,3 +88,10 @@ roomId (IRoom room) =
mostRecentEvents : IRoom -> List IEvent
mostRecentEvents (IRoom room) =
Timeline.mostRecentEvents room.timeline
{-| Get the room's id.
-}
roomId : IRoom -> String
roomId (IRoom room) =
room.roomId

View File

@ -92,63 +92,119 @@ newFromEvents { events, nextBatch, prevBatch, stateDelta } =
insertEvents :
{ events : List IEvent
, nextBatch : String
, prevBatch : String
, prevBatch : Maybe String
, stateDelta : Maybe StateManager
}
-> Timeline
-> Timeline
insertEvents ({ events, nextBatch, prevBatch, stateDelta } as data) (Timeline t) =
Timeline
(if t.nextBatch == prevBatch then
{ t
| events = t.events ++ events
, nextBatch = nextBatch
}
else if nextBatch == t.prevBatch then
case t.previous of
Gap (Timeline prevT) ->
if prevT.nextBatch == prevBatch then
{ events = prevT.events ++ events ++ t.events
, nextBatch = t.nextBatch
, prevBatch = prevT.prevBatch
, stateAtStart = prevT.stateAtStart
, previous = prevT.previous
}
else
(case prevBatch of
-- No prevbatch suggests the start of the timeline.
-- This means that we must recurse until we've hit the bottom,
-- and then mark the bottom of the timeline.
Nothing ->
case t.previous of
Gap prevT ->
{ t
| events = events ++ t.events
, prevBatch = prevBatch
, stateAtStart =
stateDelta
|> Maybe.withDefault StateManager.empty
| previous =
prevT
|> insertEvents data
|> Gap
}
_ ->
{ t
| events = events ++ t.events
, prevBatch = prevBatch
, stateAtStart =
stateDelta
|> Maybe.withDefault StateManager.empty
}
_ ->
if nextBatch == t.prevBatch then
{ t | previous = StartOfTimeline, events = events ++ t.events, stateAtStart = StateManager.empty }
else
case t.previous of
Gap prevT ->
{ t
| previous =
prevT
|> insertEvents data
|> Gap
}
else
{ t | previous = Gap <| newFromEvents data }
_ ->
t
-- If there is a prevbatch, it is not the start of the timeline
-- and could be located anywhere.
-- Starting at the front, look for a way to match it with the existing timeline.
Just p ->
-- Piece connects to the front of the timeline.
if t.nextBatch == p then
{ t
| events = t.events ++ events
, nextBatch = nextBatch
}
-- Piece connects to the back of the timeline.
else if nextBatch == t.prevBatch then
case t.previous of
Gap (Timeline prevT) ->
-- Piece also connects to the timeline in the back,
-- allowing the two timelines to merge.
if prevT.nextBatch == p then
{ events = prevT.events ++ events ++ t.events
, nextBatch = t.nextBatch
, prevBatch = prevT.prevBatch
, stateAtStart = prevT.stateAtStart
, previous = prevT.previous
}
else
{ t
| events = events ++ t.events
, prevBatch = p
, stateAtStart =
stateDelta
|> Maybe.withDefault StateManager.empty
}
Endless _ ->
{ t
| events = events ++ t.events
, prevBatch = p
, stateAtStart =
stateDelta
|> Maybe.withDefault StateManager.empty
, previous = Endless p
}
_ ->
{ t
| events = events ++ t.events
, prevBatch = p
, stateAtStart =
stateDelta
|> Maybe.withDefault StateManager.empty
}
-- Piece doesn't connect to this piece of the timeline.
-- Consequently, look for previous parts of the timeline to see if it connects.
else
case t.previous of
Gap prevT ->
{ t
| previous =
prevT
|> insertEvents data
|> Gap
}
_ ->
t
)
{-| Get the width of the latest gap. This data is usually accessed when trying to get more messages.
-}
latestGap : Timeline -> Maybe { from : Maybe String, to : String }
latestGap (Timeline t) =
case t.previous of
StartOfTimeline ->
Nothing
Endless prevBatch ->
Just { from = Nothing, to = prevBatch }
Gap (Timeline pt) ->
Just { from = Just pt.nextBatch, to = t.prevBatch }
{-| Get the longest uninterrupted length of most recent events.
-}
localSize : Timeline -> Int

View File

@ -16,6 +16,7 @@ import Internal.Event as Event
import Internal.Invite as Invite
import Internal.Room as Room
import Internal.Tools.Exceptions as X
import Internal.Tools.SpecEnums as Enums
import Internal.Values.Room as IRoom
import Internal.Values.RoomInvite exposing (IRoomInvite)
import Internal.Values.StateManager as StateManager
@ -125,8 +126,62 @@ updateWith vaultUpdate ((Vault ({ cred, context } as data)) as vault) =
vault
-- TODO
GetMessages _ _ ->
vault
GetMessages input output ->
let
prevBatch : Maybe String
prevBatch =
case input.direction of
Enums.Chronological ->
Just output.start
Enums.ReverseChronological ->
case output.end of
Just end ->
Just end
Nothing ->
input.to
nextBatch : Maybe String
nextBatch =
case input.direction of
Enums.Chronological ->
case output.end of
Just end ->
Just end
Nothing ->
input.to
Enums.ReverseChronological ->
Just output.start
in
case ( getRoomById input.roomId vault, nextBatch ) of
( Just room, Just nb ) ->
room
|> Room.withoutCredentials
|> IRoom.insertEvents
{ events =
output.chunk
|> List.map Event.initFromGetMessages
|> (\x ->
case input.direction of
Enums.Chronological ->
x
Enums.ReverseChronological ->
List.reverse x
)
, prevBatch = prevBatch
, nextBatch = nb
, stateDelta = Just <| StateManager.fromEventList (List.map Event.initFromGetMessages output.state)
}
|> Internal.insertRoom
|> (|>) cred
|> (\v -> Vault { cred = v, context = context })
_ ->
vault
-- TODO
InviteSent _ _ ->

View File

@ -1,5 +1,5 @@
module Matrix.Room exposing
( Room, roomId, mostRecentEvents
( Room, roomId, mostRecentEvents, findOlderEvents
, sendMessage, sendMessages, sendOneEvent, sendMultipleEvents
)
@ -10,7 +10,7 @@ module Matrix.Room exposing
A room represents a channel of communication within a Matrix home server.
@docs Room, roomId, mostRecentEvents
@docs Room, roomId, mostRecentEvents, findOlderEvents
# Sending events
@ -35,6 +35,13 @@ type alias Room =
Internal.Room
{-| If you want more events as part of the most recent events, you can run this task to get more.
-}
findOlderEvents : { limit : Maybe Int, room : Room } -> Task X.Error VaultUpdate
findOlderEvents { limit, room } =
Internal.getOlderEvents { limit = limit } room
{-| Get the most recent events from this room.
-}
mostRecentEvents : Room -> List Event.Event