tooty/src/View.elm

482 lines
16 KiB
Elm
Raw Normal View History

2017-04-20 07:46:18 +00:00
module View exposing (view)
2017-04-21 12:03:39 +00:00
import Dict
2017-04-20 07:46:18 +00:00
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Mastodon
2017-04-20 18:30:19 +00:00
import Model exposing (Model, DraftMsg(..), Msg(..))
import ViewHelper
2017-04-20 07:46:18 +00:00
2017-04-21 12:03:39 +00:00
visibilities : Dict.Dict String String
visibilities =
Dict.fromList
[ ( "public", "post to public timelines" )
, ( "unlisted", "do not show in public timelines" )
, ( "private", "post to followers only" )
, ( "direct", "post to mentioned users only" )
]
2017-04-20 07:46:18 +00:00
errorView : String -> Html Msg
errorView error =
div [ class "alert alert-danger" ] [ text error ]
errorsListView : Model -> Html Msg
errorsListView model =
case model.errors of
[] ->
text ""
errors ->
div [] <| List.map errorView model.errors
icon : String -> Html Msg
icon name =
i [ class <| "glyphicon glyphicon-" ++ name ] []
accountLink : Mastodon.Account -> Html Msg
accountLink account =
a
[ href account.url
, ViewHelper.onClickWithPreventAndStop (OnLoadUserAccount account.id)
]
[ text <| "@" ++ account.username ]
attachmentPreview : Maybe Bool -> Mastodon.Attachment -> Html Msg
attachmentPreview sensitive ({ url, preview_url } as attachment) =
let
nsfw =
case sensitive of
Just sensitive ->
sensitive
Nothing ->
False
attId =
"att" ++ (toString attachment.id)
media =
a
[ class "attachment-image"
, href url
, target "_blank"
, style
[ ( "background"
, "url(" ++ preview_url ++ ") center center / cover no-repeat"
)
]
]
[]
in
li [ class "attachment-entry" ] <|
if nsfw then
[ input [ type_ "radio", id attId ] []
, label [ for attId ]
[ text "Sensitive content"
, br [] []
, br [] []
, text "click to show image"
]
, media
]
else
[ media ]
attachmentListView : Mastodon.Status -> Html Msg
attachmentListView { media_attachments, sensitive } =
case media_attachments of
[] ->
text ""
attachments ->
ul [ class "attachments" ] <| List.map (attachmentPreview sensitive) attachments
statusContentView : Mastodon.Status -> Html Msg
statusContentView status =
case status.spoiler_text of
"" ->
div [ class "status-text" ]
[ div [] <| ViewHelper.formatContent status.content status.mentions
, attachmentListView status
]
spoiler ->
-- Note: Spoilers are dealt with using pure CSS.
let
statusId =
"spoiler" ++ (toString status.id)
in
div [ class "status-text spoiled" ]
[ div [ class "spoiler" ] [ text status.spoiler_text ]
, input [ type_ "checkbox", id statusId, class "spoiler-toggler" ] []
, label [ for statusId ] [ text "Reveal content" ]
, div [ class "spoiled-content" ]
[ div [] <| ViewHelper.formatContent status.content status.mentions
, attachmentListView status
]
]
2017-04-20 07:46:18 +00:00
statusView : Mastodon.Status -> Html Msg
statusView ({ account, content, media_attachments, reblog, mentions } as status) =
let
accountLinkAttributes =
[ href account.url
2017-04-22 12:49:05 +00:00
-- When clicking on a status, we should not let the browser
-- redirect to a new page. That's why we're preventing the default
-- behavior here
, ViewHelper.onClickWithPreventAndStop (OnLoadUserAccount account.id)
]
in
case reblog of
Just (Mastodon.Reblog reblog) ->
div [ class "reblog" ]
[ p []
[ icon "fire"
, a (accountLinkAttributes ++ [ class "reblogger" ])
[ text <| " " ++ account.username ]
, text " boosted"
]
, statusView reblog
2017-04-20 07:46:18 +00:00
]
Nothing ->
div [ class "status" ]
2017-04-22 12:48:03 +00:00
[ a accountLinkAttributes
2017-04-22 12:49:05 +00:00
[ img [ class "avatar", src account.avatar ] [] ]
, div [ class "username" ]
[ a accountLinkAttributes
[ text account.display_name
, span [ class "acct" ] [ text <| " @" ++ account.username ]
]
]
, statusContentView status
2017-04-20 13:43:15 +00:00
]
accountTimelineView : Mastodon.Account -> List Mastodon.Status -> String -> String -> Html Msg
accountTimelineView account statuses label iconName =
div [ class "col-md-3" ]
[ div [ class "panel panel-default" ]
[ div [ class "panel-heading" ]
[ icon iconName
, text label
2017-04-20 07:46:18 +00:00
]
, div [ class "account-detail", style [ ( "background-image", "url('" ++ account.header ++ "')" ) ] ]
[ div [ class "opacity-layer" ]
[ img [ src account.avatar ] []
, span [ class "account-display-name" ] [ text account.display_name ]
, span [ class "account-username" ] [ text ("@" ++ account.username) ]
, span [ class "account-note" ] (ViewHelper.formatContent account.note [])
]
]
, div [ class "row account-infos" ]
[ div [ class "col-md-4" ]
[ text "Statuses"
, br [] []
, text <| toString account.statuses_count
]
, div [ class "col-md-4" ]
[ text "Following"
, br [] []
, text <| toString account.following_count
]
, div [ class "col-md-4" ]
[ text "Followers"
, br [] []
, text <| toString account.followers_count
]
]
, ul [ class "list-group" ] <|
List.map
(\s ->
li [ class "list-group-item status" ]
[ statusView s ]
)
statuses
]
]
2017-04-20 07:46:18 +00:00
statusEntryView : Mastodon.Status -> Html Msg
statusEntryView status =
let
nsfwClass =
case status.sensitive of
Just True ->
"nsfw"
_ ->
""
in
li [ class <| "list-group-item " ++ nsfwClass ]
[ statusView status ]
timelineView : List Mastodon.Status -> String -> String -> Html Msg
timelineView statuses label iconName =
2017-04-21 11:04:28 +00:00
div [ class "col-md-3" ]
2017-04-20 07:46:18 +00:00
[ div [ class "panel panel-default" ]
[ div [ class "panel-heading" ]
[ icon iconName
, text label
]
2017-04-20 07:46:18 +00:00
, ul [ class "list-group" ] <|
List.map statusEntryView statuses
2017-04-20 07:46:18 +00:00
]
]
notificationHeading : Mastodon.Account -> String -> String -> Html Msg
notificationHeading account str iconType =
p [] <|
List.intersperse (text " ")
[ icon iconType, accountLink account, text str ]
notificationStatusView : Mastodon.Status -> Mastodon.Notification -> Html Msg
notificationStatusView status { type_, account } =
div [ class "notification mention" ]
[ case type_ of
"reblog" ->
notificationHeading account "boosted your toot" "fire"
"favourite" ->
notificationHeading account "favourited your toot" "star"
_ ->
text ""
, statusView status
]
notificationFollowView : Mastodon.Notification -> Html Msg
notificationFollowView { account } =
div [ class "notification follow" ]
[ notificationHeading account "started following you" "user" ]
notificationEntryView : Mastodon.Notification -> Html Msg
notificationEntryView notification =
li [ class "list-group-item" ]
[ case notification.status of
Just status ->
notificationStatusView status notification
Nothing ->
notificationFollowView notification
]
notificationListView : List Mastodon.Notification -> Html Msg
notificationListView notifications =
div [ class "col-md-3" ]
[ div [ class "panel panel-default" ]
[ div [ class "panel-heading" ]
[ icon "bell"
, text "Notifications"
]
, ul [ class "list-group" ] <|
List.map notificationEntryView notifications
]
]
2017-04-20 18:30:19 +00:00
draftView : Model -> Html Msg
draftView { draft } =
let
hasSpoiler =
draft.spoiler_text /= Nothing
2017-04-21 12:03:39 +00:00
visibilityOptionView ( visibility, description ) =
option [ value visibility ]
[ text <| visibility ++ ": " ++ description ]
2017-04-20 18:30:19 +00:00
in
div [ class "panel panel-default" ]
[ div [ class "panel-heading" ] [ icon "envelope", text "Post a message" ]
, div [ class "panel-body" ]
[ Html.form [ class "form", onSubmit SubmitDraft ]
[ div [ class "form-group checkbox" ]
[ label []
[ input
[ type_ "checkbox"
, onCheck <| DraftEvent << ToggleSpoiler
, checked hasSpoiler
2017-04-20 18:30:19 +00:00
]
[]
, text " Add a spoiler"
2017-04-20 18:30:19 +00:00
]
]
, if hasSpoiler then
div [ class "form-group" ]
[ label [ for "spoiler" ] [ text "Visible part" ]
2017-04-20 18:30:19 +00:00
, textarea
[ id "spoiler"
2017-04-20 18:30:19 +00:00
, class "form-control"
, rows 5
, placeholder "This text will always be visible."
, onInput <| DraftEvent << UpdateSpoiler
2017-04-20 18:30:19 +00:00
, required True
, value <| Maybe.withDefault "" draft.spoiler_text
2017-04-20 18:30:19 +00:00
]
[]
]
else
text ""
, div [ class "form-group" ]
[ label [ for "status" ]
[ text <|
if hasSpoiler then
"Hidden part"
else
"Status"
2017-04-21 12:03:39 +00:00
]
, textarea
[ id "status"
, class "form-control"
, rows 8
, placeholder <|
if hasSpoiler then
"This text will be hidden by default, as you have enabled a spoiler."
else
"Once upon a time..."
, onInput <| DraftEvent << UpdateStatus
, required True
, value draft.status
2017-04-20 18:30:19 +00:00
]
[]
]
, div [ class "form-group" ]
[ label [ for "visibility" ] [ text "Visibility" ]
, select
[ id "visibility"
, class "form-control"
, onInput <| DraftEvent << UpdateVisibility
, required True
, value draft.visibility
]
<|
List.map visibilityOptionView <|
Dict.toList visibilities
]
, div [ class "form-group checkbox" ]
[ label []
[ input
[ type_ "checkbox"
, onCheck <| DraftEvent << UpdateSensitive
, checked draft.sensitive
]
[]
, text " This post is NSFW"
2017-04-20 18:30:19 +00:00
]
]
, p [ class "text-right" ]
[ button [ class "btn btn-primary" ]
[ text "Toot!" ]
]
]
]
]
optionsView : Model -> Html Msg
optionsView model =
div [ class "panel panel-default" ]
[ div [ class "panel-heading" ] [ icon "cog", text "options" ]
, div [ class "panel-body" ]
[ div [ class "checkbox" ]
[ label []
[ input
[ type_ "checkbox"
, onCheck UseGlobalTimeline
]
[]
, text " 4th column renders the global timeline"
2017-04-20 18:30:19 +00:00
]
]
]
]
sidebarView : Model -> Html Msg
sidebarView model =
div [ class "col-md-3" ]
[ draftView model
, optionsView model
]
2017-04-20 18:30:19 +00:00
2017-04-20 07:46:18 +00:00
homepageView : Model -> Html Msg
homepageView model =
div [ class "row" ]
[ sidebarView model
, timelineView model.userTimeline "Home timeline" "home"
, notificationListView model.notifications
, case model.account of
Just account ->
-- Todo: Load the user timeline
accountTimelineView account [] "Account" "user"
Nothing ->
if model.useGlobalTimeline then
timelineView model.publicTimeline "Global timeline" "globe"
else
timelineView model.localTimeline "Local timeline" "th-large"
2017-04-20 07:46:18 +00:00
]
authView : Model -> Html Msg
authView model =
div [ class "col-md-4 col-md-offset-4" ]
[ div [ class "panel panel-default" ]
[ div [ class "panel-heading" ] [ text "Authenticate" ]
, div [ class "panel-body" ]
[ Html.form [ class "form", onSubmit Register ]
[ div [ class "form-group" ]
[ label [ for "server" ] [ text "Mastodon server root URL" ]
, input
[ type_ "url"
, class "form-control"
, id "server"
, required True
, placeholder "https://mastodon.social"
, value model.server
, pattern "https://.+"
, onInput ServerChange
]
[]
, p [ class "help-block" ]
2017-04-21 12:03:39 +00:00
[ text <|
"You'll be redirected to that server to authenticate yourself. "
++ "We don't have access to your password."
]
2017-04-20 07:46:18 +00:00
]
, button [ class "btn btn-primary", type_ "submit" ]
[ text "Sign into Tooty" ]
]
]
]
]
view : Model -> Html Msg
view model =
div [ class "container-fluid" ]
2017-04-22 14:43:45 +00:00
[ errorsListView model
2017-04-20 07:46:18 +00:00
, case model.client of
Just client ->
homepageView model
Nothing ->
authView model
]