Refactor to allow "messages".
This will allow us to group non-event things as messages, which will then allow us to display messages that are still being sent.
This commit is contained in:
parent
1703c091a7
commit
ce1580926c
|
@ -230,7 +230,7 @@ updateViewportAfterMessage : Model -> Result Browser.Dom.Error Viewport -> (Mode
|
||||||
updateViewportAfterMessage m vr =
|
updateViewportAfterMessage m vr =
|
||||||
let
|
let
|
||||||
cmd vp = if vp.scene.height - (vp.viewport.y + vp.viewport.height ) < 100
|
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
|
then Task.attempt ViewportChangeComplete <| setViewportOf "messages-wrapper" vp.viewport.x vp.scene.height
|
||||||
else Cmd.none
|
else Cmd.none
|
||||||
in
|
in
|
||||||
(m, Result.withDefault Cmd.none <| Result.map cmd vr)
|
(m, Result.withDefault Cmd.none <| Result.map cmd vr)
|
||||||
|
@ -308,7 +308,7 @@ updateSyncResponse model r notify =
|
||||||
setScrollCmd sr = if List.isEmpty
|
setScrollCmd sr = if List.isEmpty
|
||||||
<| roomMessages sr
|
<| roomMessages sr
|
||||||
then Cmd.none
|
then Cmd.none
|
||||||
else Task.attempt ViewportAfterMessage (Browser.Dom.getViewportOf "events-wrapper")
|
else Task.attempt ViewportAfterMessage (Browser.Dom.getViewportOf "messages-wrapper")
|
||||||
setReadReceiptCmd sr = case (room, List.head <| List.reverse <| roomMessages sr) of
|
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
|
(Just rid, Just re) -> setReadMarkers model.apiUrl token rid re.eventId <| Just re.eventId
|
||||||
_ -> Cmd.none
|
_ -> Cmd.none
|
||||||
|
|
33
src/Scylla/Messages.elm
Normal file
33
src/Scylla/Messages.elm
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
module Scylla.Messages exposing (..)
|
||||||
|
import Scylla.Model exposing (Model)
|
||||||
|
import Scylla.Sync exposing (RoomEvent)
|
||||||
|
import Scylla.Login exposing (Username)
|
||||||
|
|
||||||
|
type Message =
|
||||||
|
SendingTextMessage String Int
|
||||||
|
| SyncMessage RoomEvent
|
||||||
|
|
||||||
|
extractMessageEvents : List RoomEvent -> List Message
|
||||||
|
extractMessageEvents es = List.map SyncMessage
|
||||||
|
<| List.filter (\e -> e.type_ == "m.room.message") es
|
||||||
|
|
||||||
|
messageUsername : Model -> Message -> Username
|
||||||
|
messageUsername m msg = case msg of
|
||||||
|
SendingTextMessage _ _ -> m.loginUsername
|
||||||
|
SyncMessage re -> re.sender
|
||||||
|
|
||||||
|
mergeMessages : Model -> List Message -> List (Username, List Message)
|
||||||
|
mergeMessages m xs =
|
||||||
|
let
|
||||||
|
initialState = (Nothing, [], [])
|
||||||
|
appendNamed mu ms msl = case mu of
|
||||||
|
Just u -> msl ++ [(u, ms)]
|
||||||
|
Nothing -> msl
|
||||||
|
foldFunction msg (pu, ms, msl) =
|
||||||
|
let
|
||||||
|
nu = Just <| messageUsername m msg
|
||||||
|
in
|
||||||
|
if pu == nu then (pu, ms ++ [msg], msl) else (nu, [msg], appendNamed pu ms msl)
|
||||||
|
(fmu, fms, fmsl) = List.foldl foldFunction initialState xs
|
||||||
|
in
|
||||||
|
appendNamed fmu fms fmsl
|
|
@ -3,6 +3,7 @@ import Scylla.Model exposing (..)
|
||||||
import Scylla.Sync exposing (..)
|
import Scylla.Sync exposing (..)
|
||||||
import Scylla.Route exposing (..)
|
import Scylla.Route exposing (..)
|
||||||
import Scylla.Fnv as Fnv
|
import Scylla.Fnv as Fnv
|
||||||
|
import Scylla.Messages exposing (..)
|
||||||
import Scylla.Login exposing (Username)
|
import Scylla.Login exposing (Username)
|
||||||
import Scylla.Http exposing (fullMediaUrl)
|
import Scylla.Http exposing (fullMediaUrl)
|
||||||
import Scylla.Api exposing (ApiUrl)
|
import Scylla.Api exposing (ApiUrl)
|
||||||
|
@ -12,7 +13,7 @@ import Svg
|
||||||
import Svg.Attributes
|
import Svg.Attributes
|
||||||
import Url.Builder
|
import Url.Builder
|
||||||
import Json.Decode as Decode
|
import Json.Decode as Decode
|
||||||
import Html exposing (Html, Attribute, div, input, text, button, div, span, a, h2, h3, table, td, tr, img, textarea, video, source)
|
import Html exposing (Html, Attribute, div, input, text, button, div, span, a, h2, h3, table, td, tr, img, textarea, video, source, p)
|
||||||
import Html.Attributes exposing (type_, value, href, class, style, src, id, rows, controls, src)
|
import Html.Attributes exposing (type_, value, href, class, style, src, id, rows, controls, src)
|
||||||
import Html.Events exposing (onInput, onClick, preventDefaultOn)
|
import Html.Events exposing (onInput, onClick, preventDefaultOn)
|
||||||
import Dict exposing (Dict)
|
import Dict exposing (Dict)
|
||||||
|
@ -140,8 +141,8 @@ joinedRoomView : Model -> RoomId -> JoinedRoom -> Html Msg
|
||||||
joinedRoomView m roomId jr =
|
joinedRoomView m roomId jr =
|
||||||
let
|
let
|
||||||
events = Maybe.withDefault [] <| Maybe.andThen .events jr.timeline
|
events = Maybe.withDefault [] <| Maybe.andThen .events jr.timeline
|
||||||
renderedEvents = List.filterMap (eventView m) events
|
renderedMessages = List.map (userMessagesView m) <| mergeMessages m <| extractMessageEvents events
|
||||||
eventWrapper = eventWrapperView m roomId renderedEvents
|
messagesWrapper = messagesWrapperView m roomId renderedMessages
|
||||||
typing = List.map (displayName m) <| roomTypingUsers jr
|
typing = List.map (displayName m) <| roomTypingUsers jr
|
||||||
typingText = String.join ", " typing
|
typingText = String.join ", " typing
|
||||||
typingSuffix = case List.length typing of
|
typingSuffix = case List.length typing of
|
||||||
|
@ -163,7 +164,7 @@ joinedRoomView m roomId jr =
|
||||||
in
|
in
|
||||||
div [ class "room-wrapper" ]
|
div [ class "room-wrapper" ]
|
||||||
[ h2 [] [ text <| Maybe.withDefault "<No Name>" <| roomName jr ]
|
[ h2 [] [ text <| Maybe.withDefault "<No Name>" <| roomName jr ]
|
||||||
, eventWrapper
|
, messagesWrapper
|
||||||
, typingWrapper
|
, typingWrapper
|
||||||
, messageInput
|
, messageInput
|
||||||
]
|
]
|
||||||
|
@ -187,58 +188,58 @@ iconView name =
|
||||||
[ Svg.Attributes.class "feather-icon"
|
[ Svg.Attributes.class "feather-icon"
|
||||||
] [ Svg.use [ Svg.Attributes.xlinkHref (url ++ "#" ++ name) ] [] ]
|
] [ Svg.use [ Svg.Attributes.xlinkHref (url ++ "#" ++ name) ] [] ]
|
||||||
|
|
||||||
eventWrapperView : Model -> RoomId -> List (Html Msg) -> Html Msg
|
messagesWrapperView : Model -> RoomId -> List (Html Msg) -> Html Msg
|
||||||
eventWrapperView m rid es = div [ class "events-wrapper", id "events-wrapper" ]
|
messagesWrapperView m rid es = div [ class "messages-wrapper", id "messages-wrapper" ]
|
||||||
[ a [ class "history-link", onClick <| History rid ] [ text "Load older messages" ]
|
[ a [ class "history-link", onClick <| History rid ] [ text "Load older messages" ]
|
||||||
, table [ class "events-table" ] es
|
, table [ class "messages-table" ] es
|
||||||
]
|
]
|
||||||
|
|
||||||
eventView : Model -> RoomEvent -> Maybe (Html Msg)
|
senderView : Model -> Username -> Html Msg
|
||||||
eventView m re =
|
senderView m s =
|
||||||
|
span [ style "color" <| stringColor s, class "sender-wrapper" ] [ text <| displayName m s ]
|
||||||
|
|
||||||
|
userMessagesView : Model -> (Username, List Message) -> Html Msg
|
||||||
|
userMessagesView m (u, ms) =
|
||||||
let
|
let
|
||||||
viewFunction = case re.type_ of
|
wrap h = div [ class "message" ] [ h ]
|
||||||
"m.room.message" -> Just messageView
|
|
||||||
_ -> Nothing
|
|
||||||
createRow mhtml = tr []
|
|
||||||
[ td [] [ eventSenderView m re.sender ]
|
|
||||||
, td [] [ mhtml ]
|
|
||||||
]
|
|
||||||
in
|
in
|
||||||
Maybe.map createRow
|
tr []
|
||||||
<| Maybe.andThen (\f -> f m re) viewFunction
|
[ td [] [ senderView m u ]
|
||||||
|
, td [] <| List.map wrap <| List.filterMap (messageView m) ms
|
||||||
|
]
|
||||||
|
|
||||||
eventSenderView : Model -> Username -> Html Msg
|
messageView : Model -> Message -> Maybe (Html Msg)
|
||||||
eventSenderView m s =
|
messageView m msg = case msg of
|
||||||
span [ style "background-color" <| stringColor s, class "sender-wrapper" ] [ text <| displayName m s ]
|
SendingTextMessage t _ -> Just <| div [] [ text t ]
|
||||||
|
SyncMessage re -> roomEventView m re
|
||||||
|
|
||||||
messageView : Model -> RoomEvent -> Maybe (Html Msg)
|
roomEventView : Model -> RoomEvent -> Maybe (Html Msg)
|
||||||
messageView m re =
|
roomEventView m re =
|
||||||
let
|
let
|
||||||
msgtype = Decode.decodeValue (Decode.field "msgtype" Decode.string) re.content
|
msgtype = Decode.decodeValue (Decode.field "msgtype" Decode.string) re.content
|
||||||
in
|
in
|
||||||
case msgtype of
|
case msgtype of
|
||||||
Ok "m.text" -> messageTextView m re
|
Ok "m.text" -> roomEventTextView m re
|
||||||
Ok "m.image" -> messageImageView m re
|
Ok "m.image" -> roomEventImageView m re
|
||||||
Ok "m.file" -> messageFileView m re
|
Ok "m.file" -> roomEventFileView m re
|
||||||
Ok "m.video" -> messageVideoView m re
|
Ok "m.video" -> roomEventVideoView m re
|
||||||
_ -> Nothing
|
_ -> Nothing
|
||||||
|
|
||||||
messageTextView : Model -> RoomEvent -> Maybe (Html Msg)
|
roomEventTextView : Model -> RoomEvent -> Maybe (Html Msg)
|
||||||
messageTextView m re =
|
roomEventTextView m re =
|
||||||
let
|
let
|
||||||
body = Decode.decodeValue (Decode.field "body" Decode.string) re.content
|
body = Decode.decodeValue (Decode.field "body" Decode.string) re.content
|
||||||
customHtml = Maybe.map Html.Parser.Util.toVirtualDom
|
customHtml = Maybe.map Html.Parser.Util.toVirtualDom
|
||||||
<| Maybe.andThen (Result.toMaybe << Html.Parser.run )
|
<| Maybe.andThen (Result.toMaybe << Html.Parser.run )
|
||||||
<| Result.toMaybe
|
<| Result.toMaybe
|
||||||
<| Decode.decodeValue (Decode.field "formatted_body" Decode.string) re.content
|
<| Decode.decodeValue (Decode.field "formatted_body" Decode.string) re.content
|
||||||
wrap mtext = span [] [ text mtext ]
|
|
||||||
in
|
in
|
||||||
case customHtml of
|
case customHtml of
|
||||||
Just c -> Just <| div [ class "markdown-wrapper" ] c
|
Just c -> Just <| div [] c
|
||||||
Nothing -> Maybe.map wrap <| Result.toMaybe body
|
Nothing -> Maybe.map (p [] << List.singleton << text) <| Result.toMaybe body
|
||||||
|
|
||||||
messageImageView : Model -> RoomEvent -> Maybe (Html Msg)
|
roomEventImageView : Model -> RoomEvent -> Maybe (Html Msg)
|
||||||
messageImageView m re =
|
roomEventImageView m re =
|
||||||
let
|
let
|
||||||
body = Decode.decodeValue (Decode.field "url" Decode.string) re.content
|
body = Decode.decodeValue (Decode.field "url" Decode.string) re.content
|
||||||
in
|
in
|
||||||
|
@ -246,8 +247,8 @@ messageImageView m re =
|
||||||
<| Maybe.map (contentRepositoryDownloadUrl m.apiUrl)
|
<| Maybe.map (contentRepositoryDownloadUrl m.apiUrl)
|
||||||
<| Result.toMaybe body
|
<| Result.toMaybe body
|
||||||
|
|
||||||
messageFileView : Model -> RoomEvent -> Maybe (Html Msg)
|
roomEventFileView : Model -> RoomEvent -> Maybe (Html Msg)
|
||||||
messageFileView m re =
|
roomEventFileView m re =
|
||||||
let
|
let
|
||||||
decoder = Decode.map2 (\l r -> (l, r)) (Decode.field "url" Decode.string) (Decode.field "body" Decode.string)
|
decoder = Decode.map2 (\l r -> (l, r)) (Decode.field "url" Decode.string) (Decode.field "body" Decode.string)
|
||||||
fileData = Decode.decodeValue decoder re.content
|
fileData = Decode.decodeValue decoder re.content
|
||||||
|
@ -256,8 +257,8 @@ messageFileView m re =
|
||||||
<| Maybe.map (\(url, name) -> (contentRepositoryDownloadUrl m.apiUrl url, name))
|
<| Maybe.map (\(url, name) -> (contentRepositoryDownloadUrl m.apiUrl url, name))
|
||||||
<| Result.toMaybe fileData
|
<| Result.toMaybe fileData
|
||||||
|
|
||||||
messageVideoView : Model -> RoomEvent -> Maybe (Html Msg)
|
roomEventVideoView : Model -> RoomEvent -> Maybe (Html Msg)
|
||||||
messageVideoView m re =
|
roomEventVideoView m re =
|
||||||
let
|
let
|
||||||
decoder = Decode.map2 (\l r -> (l, r))
|
decoder = Decode.map2 (\l r -> (l, r))
|
||||||
(Decode.field "url" Decode.string)
|
(Decode.field "url" Decode.string)
|
||||||
|
|
|
@ -212,7 +212,7 @@ div.message-wrapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
div.events-wrapper {
|
div.messages-wrapper {
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
|
||||||
|
@ -225,7 +225,7 @@ div.events-wrapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
table.events-table {
|
table.messages-table {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
table-layout: fixed;
|
table-layout: fixed;
|
||||||
|
@ -258,7 +258,7 @@ table.events-table {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
div.markdown-wrapper {
|
div.message {
|
||||||
p {
|
p {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
}
|
}
|
||||||
|
@ -291,7 +291,8 @@ span.sender-wrapper {
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
text-align: center;
|
text-align: right;
|
||||||
|
font-weight: 800;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user