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:
Danila Fedorin 2019-02-25 16:44:47 -08:00
parent 1703c091a7
commit ce1580926c
4 changed files with 79 additions and 44 deletions

View File

@ -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
View 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

View File

@ -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)

View File

@ -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;