Compare commits

..

7 Commits

4 changed files with 69 additions and 24 deletions

View File

@ -53,6 +53,7 @@ init _ url key =
, transactionId = 0 , transactionId = 0
, userData = Dict.empty , userData = Dict.empty
, connected = True , connected = True
, searchText = ""
} }
cmd = getStoreValuePort "scylla.loginInfo" cmd = getStoreValuePort "scylla.loginInfo"
in in
@ -105,6 +106,7 @@ update msg model = case msg of
ReceiveMarkdown md -> updateMarkdown model md ReceiveMarkdown md -> updateMarkdown model md
DismissError i -> updateDismissError model i DismissError i -> updateDismissError model i
AttemptReconnect -> ({ model | connected = True }, firstSync model.apiUrl (Maybe.withDefault "" model.token)) AttemptReconnect -> ({ model | connected = True }, firstSync model.apiUrl (Maybe.withDefault "" model.token))
UpdateSearchText s -> ({ model | searchText = s }, Cmd.none)
requestScrollCmd : Cmd Msg requestScrollCmd : Cmd Msg
requestScrollCmd = Task.attempt ViewportAfterMessage (Browser.Dom.getViewportOf "messages-wrapper") requestScrollCmd = Task.attempt ViewportAfterMessage (Browser.Dom.getViewportOf "messages-wrapper")

View File

@ -32,6 +32,7 @@ type alias Model =
, transactionId : Int , transactionId : Int
, userData : Dict Username UserData , userData : Dict Username UserData
, connected : Bool , connected : Bool
, searchText : String
} }
type Msg = type Msg =
@ -68,6 +69,7 @@ type Msg =
| ReceiveMarkdown MarkdownResponse | ReceiveMarkdown MarkdownResponse
| DismissError Int | DismissError Int
| AttemptReconnect | AttemptReconnect
| UpdateSearchText String
displayName : Model -> Username -> String displayName : Model -> Username -> String
displayName m s = Maybe.withDefault (senderName s) <| Maybe.andThen .displayName <| Dict.get s m.userData displayName m s = Maybe.withDefault (senderName s) <| Maybe.andThen .displayName <| Dict.get s m.userData

View File

@ -15,7 +15,7 @@ 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, p) 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_, placeholder, value, href, class, style, src, id, rows, controls, src) import Html.Attributes exposing (type_, placeholder, value, href, class, style, src, id, rows, controls, src, classList)
import Html.Events exposing (onInput, onClick, preventDefaultOn) import Html.Events exposing (onInput, onClick, preventDefaultOn)
import Dict exposing (Dict) import Dict exposing (Dict)
import Tuple import Tuple
@ -91,6 +91,13 @@ roomListView m =
in in
div [ class "rooms-wrapper" ] div [ class "rooms-wrapper" ]
[ h2 [] [ text "Rooms" ] [ h2 [] [ text "Rooms" ]
, input
[ class "room-search"
, type_ "text"
, placeholder "Search chats..."
, onInput UpdateSearchText
, value m.searchText
] []
, homeserverList , homeserverList
] ]
@ -110,8 +117,17 @@ roomListElementView : Model -> String -> JoinedRoom -> Html Msg
roomListElementView m s jr = roomListElementView m s jr =
let let
name = roomDisplayName m jr name = roomDisplayName m jr
isVisible = m.searchText == "" || (String.contains (String.toLower m.searchText) <| String.toLower name)
isCurrentRoom = case currentRoomId m of
Nothing -> False
Just cr -> cr == s
in in
div [ class "room-link-wrapper" ] div [ classList
[ ("room-link-wrapper", True)
, ("active", isCurrentRoom)
, ("hidden", not isVisible)
]
]
[ a [ href <| roomUrl s ] [ text name ] [ a [ href <| roomUrl s ] [ text name ]
, roomNotificationCountView jr.unreadNotifications , roomNotificationCountView jr.unreadNotifications
] ]
@ -156,6 +172,7 @@ joinedRoomView m roomId rd =
[ rows 1 [ rows 1
, onInput <| ChangeRoomText roomId , onInput <| ChangeRoomText roomId
, onEnterKey <| SendRoomText roomId , onEnterKey <| SendRoomText roomId
, placeholder "Type your message here..."
, value <| Maybe.withDefault "" <| Dict.get roomId m.roomText , value <| Maybe.withDefault "" <| Dict.get roomId m.roomText
] [] ] []
, button [ onClick <| SendFiles roomId ] [ iconView "file" ] , button [ onClick <| SendFiles roomId ] [ iconView "file" ]
@ -166,8 +183,8 @@ joinedRoomView m roomId rd =
div [ class "room-wrapper" ] div [ class "room-wrapper" ]
[ h2 [] [ text <| roomDisplayName m rd.joinedRoom ] [ h2 [] [ text <| roomDisplayName m rd.joinedRoom ]
, messagesWrapper , messagesWrapper
, typingWrapper
, messageInput , messageInput
, typingWrapper
] ]
onEnterKey : Msg -> Attribute Msg onEnterKey : Msg -> Attribute Msg

View File

@ -2,23 +2,20 @@
$primary-color: #53C0FA; $primary-color: #53C0FA;
$primary-color-highlight: #4298C7; $primary-color-highlight: #4298C7;
$primary-color-light: #9FDBFB; $primary-color-light: #9FDBFB;
$background-color: #1b1e21; $background-color: #1b1e21;
$background-color-light: lighten($background-color, 4%); $background-color-light: lighten($background-color, 4%);
$background-color-dark: darken($background-color, 4%); $background-color-dark: darken($background-color, 4%);
$error-color: #f01d43; $error-color: #f01d43;
$error-color-dark: darken(#f01d43, 10%); $error-color-dark: darken(#f01d43, 10%);
$inactive-input-color: lighten($background-color-light, 5%);
$active-input-color: lighten($inactive-input-color, 5%);
$active-input-color: lighten($background-color-light, 2%); $transition-duration: .250s;
$active-input-border-color: $primary-color;
$inactive-input-color: $background-color-light;
$inactive-input-border-color: darken($inactive-input-color, 10%);
$transition-duration: .125s;
$inset-shadow: inset 0px 0px 5px rgba(0, 0, 0, .25); $inset-shadow: inset 0px 0px 5px rgba(0, 0, 0, .25);
$border-radius: 3px;
html, body { html, body {
height: 100vh; height: 100vh;
} }
@ -33,23 +30,27 @@ body {
@mixin input-common { @mixin input-common {
padding: 5px; padding: 5px;
border-radius: 3px; border-radius: $border-radius;
outline: none; outline: none;
transition: background-color $transition-duration;
font-family: inherit; font-family: inherit;
font-size: inherit; font-size: inherit;
&:focus {
transition: background-color $transition-duration;
}
} }
input, textarea { input, textarea {
@include input-common(); @include input-common();
overflow-x: hidden;
background-color: $inactive-input-color; background-color: $inactive-input-color;
color: white; color: white;
border: .5px solid $inactive-input-border-color; border: none;
padding: 10px;
&:focus { &:focus {
background-color: $active-input-color; background-color: $active-input-color;
border-color: $active-input-border-color;
} }
} }
@ -105,7 +106,6 @@ div.errors-wrapper {
div.error-wrapper { div.error-wrapper {
pointer-events: auto; pointer-events: auto;
width: 400px; width: 400px;
box-shadow: 0px 0px 5px rgba(0, 0, 0, .25);
padding: 5px; padding: 5px;
background-color: $error-color; background-color: $error-color;
border: 1px solid $error-color-dark; border: 1px solid $error-color-dark;
@ -155,16 +155,42 @@ div.base-wrapper {
*/ */
div.rooms-wrapper { div.rooms-wrapper {
flex-shrink: 0; flex-shrink: 0;
width: 15%;
min-width: 200px;
background-color: $background-color-light; background-color: $background-color-light;
.room-search {
padding: 5px;
width: 100%;
box-sizing: border-box;
}
} }
div.room-link-wrapper { div.room-link-wrapper {
whitespace: nowrap; whitespace: nowrap;
display: flex; display: flex;
padding: 0px; padding: 0px;
border-left: solid 2px $background-color-light;
padding-left: 5px;
margin: 3px;
align-items: center;
span, a { span, a {
margin-right: 5px; margin-right: 5px;
color: lightgrey;
}
&:hover a {
color: $primary-color;
transition: color $transition-duration;
}
&.active {
border-left: solid 2px $primary-color;
}
&.hidden {
display: none;
} }
} }
@ -175,8 +201,7 @@ div.reconnect-wrapper {
display: flex; display: flex;
align-items: center; align-items: center;
background-color: $inactive-input-color; background-color: $inactive-input-color;
border: 1px solid $inactive-input-border-color; border-radius: $border-radius;
border-radius: 3px;
.feather-icon { .feather-icon {
margin-right: 10px; margin-right: 10px;
@ -191,13 +216,12 @@ div.room-wrapper {
display: flex; display: flex;
height: 100%; height: 100%;
flex-direction: column; flex-direction: column;
box-shadow: $inset-shadow;
padding: 5px; padding: 5px;
} }
div.typing-wrapper { div.typing-wrapper {
padding: 5px; padding: 5px;
height: 12px; height: 1em;
flex-shrink: 0; flex-shrink: 0;
} }
@ -217,7 +241,7 @@ div.message-wrapper {
margin: 3px; margin: 3px;
height: 40px; height: 40px;
width: 40px; width: 40px;
border-radius: 50px; transition: background-color $transition-duration;
} }
} }
@ -292,13 +316,13 @@ div.message {
box-sizing: border-box; box-sizing: border-box;
padding: 10px; padding: 10px;
background-color: $background-color-dark; background-color: $background-color-dark;
border-radius: 3px; border-radius: $border-radius;
box-shadow: $inset-shadow; box-shadow: $inset-shadow;
} }
} }
span.sender-wrapper { span.sender-wrapper {
border-radius: 3px; border-radius: $border-radius;
padding-left: 5px; padding-left: 5px;
padding-right: 5px; padding-right: 5px;
display: inline-block; display: inline-block;