2017-04-20 03:46:18 -04:00
|
|
|
|
module View exposing (view)
|
|
|
|
|
|
2017-04-21 08:03:39 -04:00
|
|
|
|
import Dict
|
2017-04-20 03:46:18 -04:00
|
|
|
|
import Html exposing (..)
|
|
|
|
|
import Html.Attributes exposing (..)
|
|
|
|
|
import Html.Events exposing (..)
|
2017-04-24 15:21:43 -04:00
|
|
|
|
import List.Extra exposing (elemIndex, getAt)
|
2017-04-27 10:34:27 -04:00
|
|
|
|
import Mastodon.Helper
|
|
|
|
|
import Mastodon.Model
|
2017-04-27 12:39:14 -04:00
|
|
|
|
import Model exposing (..)
|
2017-04-28 03:41:11 -04:00
|
|
|
|
import ViewHelper exposing (..)
|
2017-04-25 06:15:45 -04:00
|
|
|
|
import Date
|
2017-04-25 06:18:24 -04:00
|
|
|
|
import Date.Extra.Config.Config_en_au as DateEn
|
|
|
|
|
import Date.Extra.Format as DateFormat
|
2017-04-20 03:46:18 -04:00
|
|
|
|
|
|
|
|
|
|
2017-04-21 08:03:39 -04: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-27 12:39:14 -04:00
|
|
|
|
closeablePanelheading : String -> String -> Msg -> Html Msg
|
|
|
|
|
closeablePanelheading iconName label onClose =
|
|
|
|
|
div [ class "panel-heading" ]
|
|
|
|
|
[ div [ class "row" ]
|
|
|
|
|
[ div [ class "col-xs-9 heading" ] [ icon iconName, text label ]
|
|
|
|
|
, div [ class "col-xs-3 text-right" ]
|
|
|
|
|
[ a
|
|
|
|
|
[ href ""
|
2017-04-28 03:41:11 -04:00
|
|
|
|
, onClickWithPreventAndStop onClose
|
2017-04-27 12:39:14 -04:00
|
|
|
|
]
|
|
|
|
|
[ icon "remove" ]
|
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
2017-04-20 03:46:18 -04: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
|
|
|
|
|
|
|
|
|
|
|
2017-04-23 04:18:47 -04:00
|
|
|
|
justifiedButtonGroup : List (Html Msg) -> Html Msg
|
|
|
|
|
justifiedButtonGroup buttons =
|
|
|
|
|
div [ class "btn-group btn-group-justified" ] <|
|
|
|
|
|
List.map (\b -> div [ class "btn-group" ] [ b ]) buttons
|
|
|
|
|
|
|
|
|
|
|
2017-04-21 04:41:46 -04:00
|
|
|
|
icon : String -> Html Msg
|
|
|
|
|
icon name =
|
|
|
|
|
i [ class <| "glyphicon glyphicon-" ++ name ] []
|
|
|
|
|
|
|
|
|
|
|
2017-04-27 10:34:27 -04:00
|
|
|
|
accountLink : Mastodon.Model.Account -> Html Msg
|
2017-04-22 10:39:19 -04:00
|
|
|
|
accountLink account =
|
|
|
|
|
a
|
|
|
|
|
[ href account.url
|
2017-04-28 03:41:11 -04:00
|
|
|
|
, onClickWithPreventAndStop (LoadAccount account.id)
|
2017-04-22 10:39:19 -04:00
|
|
|
|
]
|
|
|
|
|
[ text <| "@" ++ account.username ]
|
|
|
|
|
|
|
|
|
|
|
2017-04-27 10:34:27 -04:00
|
|
|
|
accountAvatarLink : Mastodon.Model.Account -> Html Msg
|
2017-04-23 15:49:04 -04:00
|
|
|
|
accountAvatarLink account =
|
|
|
|
|
a
|
|
|
|
|
[ href account.url
|
2017-04-28 03:41:11 -04:00
|
|
|
|
, onClickWithPreventAndStop (LoadAccount account.id)
|
2017-04-23 15:49:04 -04:00
|
|
|
|
, title <| "@" ++ account.username
|
|
|
|
|
]
|
2017-04-24 05:46:14 -04:00
|
|
|
|
[ img [ class "avatar", src account.avatar ] [] ]
|
2017-04-23 15:49:04 -04:00
|
|
|
|
|
|
|
|
|
|
2017-04-27 10:34:27 -04:00
|
|
|
|
attachmentPreview : String -> Maybe Bool -> List Mastodon.Model.Attachment -> Mastodon.Model.Attachment -> Html Msg
|
2017-04-25 11:07:48 -04:00
|
|
|
|
attachmentPreview context sensitive attachments ({ url, preview_url } as attachment) =
|
2017-04-22 07:50:02 -04:00
|
|
|
|
let
|
|
|
|
|
nsfw =
|
|
|
|
|
case sensitive of
|
|
|
|
|
Just sensitive ->
|
|
|
|
|
sensitive
|
|
|
|
|
|
|
|
|
|
Nothing ->
|
|
|
|
|
False
|
|
|
|
|
|
|
|
|
|
attId =
|
2017-04-25 11:07:48 -04:00
|
|
|
|
"att" ++ (toString attachment.id) ++ context
|
2017-04-22 07:50:02 -04:00
|
|
|
|
|
|
|
|
|
media =
|
|
|
|
|
a
|
|
|
|
|
[ class "attachment-image"
|
|
|
|
|
, href url
|
2017-04-28 03:41:11 -04:00
|
|
|
|
, onClickWithPreventAndStop <|
|
2017-04-24 15:21:43 -04:00
|
|
|
|
ViewerEvent (OpenViewer attachments attachment)
|
2017-04-22 07:50:02 -04:00
|
|
|
|
, 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 ]
|
|
|
|
|
|
|
|
|
|
|
2017-04-27 10:34:27 -04:00
|
|
|
|
attachmentListView : String -> Mastodon.Model.Status -> Html Msg
|
2017-04-25 11:07:48 -04:00
|
|
|
|
attachmentListView context { media_attachments, sensitive } =
|
2017-04-22 07:50:02 -04:00
|
|
|
|
case media_attachments of
|
|
|
|
|
[] ->
|
|
|
|
|
text ""
|
|
|
|
|
|
|
|
|
|
attachments ->
|
2017-04-24 15:21:43 -04:00
|
|
|
|
ul [ class "attachments" ] <|
|
2017-04-25 11:07:48 -04:00
|
|
|
|
List.map (attachmentPreview context sensitive attachments) attachments
|
2017-04-22 07:50:02 -04:00
|
|
|
|
|
|
|
|
|
|
2017-04-27 10:34:27 -04:00
|
|
|
|
statusContentView : String -> Mastodon.Model.Status -> Html Msg
|
2017-04-25 11:07:48 -04:00
|
|
|
|
statusContentView context status =
|
2017-04-21 08:07:55 -04:00
|
|
|
|
case status.spoiler_text of
|
|
|
|
|
"" ->
|
2017-04-28 03:41:11 -04:00
|
|
|
|
div [ class "status-text", onClickWithStop <| OpenThread status ]
|
|
|
|
|
[ div [] <| formatContent status.content status.mentions
|
2017-04-25 11:07:48 -04:00
|
|
|
|
, attachmentListView context status
|
2017-04-22 07:50:02 -04:00
|
|
|
|
]
|
2017-04-21 08:07:55 -04:00
|
|
|
|
|
|
|
|
|
spoiler ->
|
|
|
|
|
-- Note: Spoilers are dealt with using pure CSS.
|
|
|
|
|
let
|
|
|
|
|
statusId =
|
2017-04-25 11:07:48 -04:00
|
|
|
|
"spoiler" ++ (toString status.id) ++ context
|
2017-04-21 08:07:55 -04:00
|
|
|
|
in
|
|
|
|
|
div [ class "status-text spoiled" ]
|
2017-04-22 04:16:14 -04:00
|
|
|
|
[ div [ class "spoiler" ] [ text status.spoiler_text ]
|
2017-04-21 08:07:55 -04:00
|
|
|
|
, input [ type_ "checkbox", id statusId, class "spoiler-toggler" ] []
|
|
|
|
|
, label [ for statusId ] [ text "Reveal content" ]
|
2017-04-22 07:50:02 -04:00
|
|
|
|
, div [ class "spoiled-content" ]
|
2017-04-28 03:41:11 -04:00
|
|
|
|
[ div [] <| formatContent status.content status.mentions
|
2017-04-25 11:07:48 -04:00
|
|
|
|
, attachmentListView context status
|
2017-04-22 07:50:02 -04:00
|
|
|
|
]
|
2017-04-21 08:07:55 -04:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
2017-04-27 10:34:27 -04:00
|
|
|
|
statusView : String -> Mastodon.Model.Status -> Html Msg
|
2017-04-25 11:07:48 -04:00
|
|
|
|
statusView context ({ account, content, media_attachments, reblog, mentions } as status) =
|
2017-04-22 04:16:14 -04:00
|
|
|
|
let
|
|
|
|
|
accountLinkAttributes =
|
|
|
|
|
[ href account.url
|
2017-04-27 10:34:27 -04: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
|
2017-04-28 03:41:11 -04:00
|
|
|
|
, onClickWithPreventAndStop (LoadAccount account.id)
|
2017-04-22 04:16:14 -04:00
|
|
|
|
]
|
|
|
|
|
in
|
|
|
|
|
case reblog of
|
2017-04-27 10:34:27 -04:00
|
|
|
|
Just (Mastodon.Model.Reblog reblog) ->
|
2017-04-22 04:16:14 -04:00
|
|
|
|
div [ class "reblog" ]
|
2017-04-23 15:49:04 -04:00
|
|
|
|
[ p [ class "status-info" ]
|
2017-04-22 04:16:14 -04:00
|
|
|
|
[ icon "fire"
|
|
|
|
|
, a (accountLinkAttributes ++ [ class "reblogger" ])
|
2017-04-23 15:49:04 -04:00
|
|
|
|
[ text <| " @" ++ account.username ]
|
2017-04-22 04:16:14 -04:00
|
|
|
|
, text " boosted"
|
|
|
|
|
]
|
2017-04-25 11:07:48 -04:00
|
|
|
|
, statusView context reblog
|
2017-04-20 03:46:18 -04:00
|
|
|
|
]
|
|
|
|
|
|
2017-04-22 04:16:14 -04:00
|
|
|
|
Nothing ->
|
|
|
|
|
div [ class "status" ]
|
2017-04-24 05:46:14 -04:00
|
|
|
|
[ accountAvatarLink account
|
2017-04-22 04:16:14 -04:00
|
|
|
|
, div [ class "username" ]
|
|
|
|
|
[ a accountLinkAttributes
|
|
|
|
|
[ text account.display_name
|
|
|
|
|
, span [ class "acct" ] [ text <| " @" ++ account.username ]
|
|
|
|
|
]
|
2017-04-21 04:41:46 -04:00
|
|
|
|
]
|
2017-04-25 11:07:48 -04:00
|
|
|
|
, statusContentView context status
|
2017-04-20 09:43:15 -04:00
|
|
|
|
]
|
2017-04-22 04:16:14 -04:00
|
|
|
|
|
|
|
|
|
|
2017-04-27 10:34:27 -04:00
|
|
|
|
accountTimelineView : Mastodon.Model.Account -> List Mastodon.Model.Status -> String -> String -> Html Msg
|
2017-04-22 04:16:14 -04:00
|
|
|
|
accountTimelineView account statuses label iconName =
|
2017-04-28 13:33:38 -04:00
|
|
|
|
div [ class "col-md-3 column" ]
|
2017-04-22 04:16:14 -04:00
|
|
|
|
[ div [ class "panel panel-default" ]
|
2017-04-27 13:44:19 -04:00
|
|
|
|
[ closeablePanelheading iconName label ClearOpenedAccount
|
2017-04-28 13:33:38 -04:00
|
|
|
|
, div [ class "timeline" ]
|
|
|
|
|
[ 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" ] (formatContent account.note [])
|
|
|
|
|
]
|
2017-04-22 04:16:14 -04:00
|
|
|
|
]
|
2017-04-28 13:33:38 -04:00
|
|
|
|
, 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
|
|
|
|
|
]
|
2017-04-22 04:16:14 -04:00
|
|
|
|
]
|
2017-04-28 13:33:38 -04:00
|
|
|
|
, ul [ class "list-group" ] <|
|
|
|
|
|
List.map
|
|
|
|
|
(\s ->
|
|
|
|
|
li [ class "list-group-item status" ]
|
|
|
|
|
[ statusView "account" s ]
|
|
|
|
|
)
|
|
|
|
|
statuses
|
2017-04-22 04:16:14 -04:00
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
]
|
2017-04-20 03:46:18 -04:00
|
|
|
|
|
|
|
|
|
|
2017-04-27 10:34:27 -04:00
|
|
|
|
statusActionsView : Mastodon.Model.Status -> Html Msg
|
2017-04-23 04:18:47 -04:00
|
|
|
|
statusActionsView status =
|
|
|
|
|
let
|
2017-04-25 09:29:33 -04:00
|
|
|
|
targetStatus =
|
2017-04-27 10:34:27 -04:00
|
|
|
|
Mastodon.Helper.extractReblog status
|
2017-04-23 04:18:47 -04:00
|
|
|
|
|
|
|
|
|
baseBtnClasses =
|
|
|
|
|
"btn btn-sm btn-default"
|
|
|
|
|
|
|
|
|
|
( reblogClasses, reblogEvent ) =
|
2017-04-24 05:46:14 -04:00
|
|
|
|
case status.reblogged of
|
2017-04-23 04:18:47 -04:00
|
|
|
|
Just True ->
|
2017-04-25 09:29:33 -04:00
|
|
|
|
( baseBtnClasses ++ " reblogged", Unreblog targetStatus.id )
|
2017-04-23 04:18:47 -04:00
|
|
|
|
|
|
|
|
|
_ ->
|
2017-04-25 09:29:33 -04:00
|
|
|
|
( baseBtnClasses, Reblog targetStatus.id )
|
2017-04-23 04:18:47 -04:00
|
|
|
|
|
|
|
|
|
( favClasses, favEvent ) =
|
|
|
|
|
case status.favourited of
|
|
|
|
|
Just True ->
|
2017-04-25 09:29:33 -04:00
|
|
|
|
( baseBtnClasses ++ " favourited", RemoveFavorite targetStatus.id )
|
2017-04-23 04:18:47 -04:00
|
|
|
|
|
|
|
|
|
_ ->
|
2017-04-25 09:29:33 -04:00
|
|
|
|
( baseBtnClasses, AddFavorite targetStatus.id )
|
2017-04-25 06:15:45 -04:00
|
|
|
|
|
2017-04-25 09:29:33 -04:00
|
|
|
|
statusDate =
|
2017-04-25 06:15:45 -04:00
|
|
|
|
Date.fromString status.created_at
|
|
|
|
|
|> Result.withDefault (Date.fromTime 0)
|
|
|
|
|
|
|
|
|
|
formatDate =
|
2017-04-25 09:29:33 -04:00
|
|
|
|
text <| DateFormat.format DateEn.config "%m/%d/%Y %H:%M" statusDate
|
2017-04-23 04:18:47 -04:00
|
|
|
|
in
|
|
|
|
|
div [ class "btn-group actions" ]
|
|
|
|
|
[ a
|
|
|
|
|
[ class baseBtnClasses
|
2017-04-28 03:41:11 -04:00
|
|
|
|
, onClickWithPreventAndStop <|
|
2017-04-25 09:29:33 -04:00
|
|
|
|
DraftEvent (UpdateReplyTo targetStatus)
|
2017-04-23 04:18:47 -04:00
|
|
|
|
]
|
|
|
|
|
[ icon "share-alt" ]
|
|
|
|
|
, a
|
|
|
|
|
[ class reblogClasses
|
2017-04-28 03:41:11 -04:00
|
|
|
|
, onClickWithPreventAndStop reblogEvent
|
2017-04-23 04:18:47 -04:00
|
|
|
|
]
|
|
|
|
|
[ icon "fire", text (toString status.reblogs_count) ]
|
|
|
|
|
, a
|
|
|
|
|
[ class favClasses
|
2017-04-28 03:41:11 -04:00
|
|
|
|
, onClickWithPreventAndStop favEvent
|
2017-04-23 04:18:47 -04:00
|
|
|
|
]
|
|
|
|
|
[ icon "star", text (toString status.favourites_count) ]
|
2017-04-25 09:29:33 -04:00
|
|
|
|
, a
|
|
|
|
|
[ class baseBtnClasses
|
|
|
|
|
, href status.url
|
2017-04-28 03:41:11 -04:00
|
|
|
|
, onClickWithPreventAndStop <| OpenThread status
|
2017-04-25 09:29:33 -04:00
|
|
|
|
]
|
|
|
|
|
[ icon "time", formatDate ]
|
2017-04-23 04:18:47 -04:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
2017-04-27 12:39:14 -04:00
|
|
|
|
statusEntryView : String -> String -> Mastodon.Model.Status -> Html Msg
|
|
|
|
|
statusEntryView context className status =
|
2017-04-22 07:50:02 -04:00
|
|
|
|
let
|
|
|
|
|
nsfwClass =
|
|
|
|
|
case status.sensitive of
|
|
|
|
|
Just True ->
|
|
|
|
|
"nsfw"
|
|
|
|
|
|
|
|
|
|
_ ->
|
|
|
|
|
""
|
|
|
|
|
in
|
2017-04-27 12:39:14 -04:00
|
|
|
|
li [ class <| "list-group-item " ++ className ++ " " ++ nsfwClass ]
|
2017-04-25 11:07:48 -04:00
|
|
|
|
[ statusView context status
|
2017-04-23 04:18:47 -04:00
|
|
|
|
, statusActionsView status
|
|
|
|
|
]
|
2017-04-22 07:50:02 -04:00
|
|
|
|
|
|
|
|
|
|
2017-04-27 10:34:27 -04:00
|
|
|
|
timelineView : String -> String -> String -> List Mastodon.Model.Status -> Html Msg
|
2017-04-25 11:07:48 -04:00
|
|
|
|
timelineView label iconName context statuses =
|
2017-04-28 13:33:38 -04:00
|
|
|
|
div [ class "col-md-3 column" ]
|
2017-04-20 03:46:18 -04:00
|
|
|
|
[ div [ class "panel panel-default" ]
|
2017-04-28 08:06:00 -04:00
|
|
|
|
[ a
|
|
|
|
|
[ href "", onClickWithPreventAndStop <| ScrollColumn context ]
|
|
|
|
|
[ div [ class "panel-heading" ] [ icon iconName, text label ] ]
|
|
|
|
|
, ul [ id context, class "list-group timeline" ] <|
|
2017-04-27 12:39:14 -04:00
|
|
|
|
List.map (statusEntryView context "") statuses
|
2017-04-20 03:46:18 -04:00
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
2017-04-27 10:34:27 -04:00
|
|
|
|
notificationHeading : List Mastodon.Model.Account -> String -> String -> Html Msg
|
2017-04-23 15:49:04 -04:00
|
|
|
|
notificationHeading accounts str iconType =
|
|
|
|
|
div [ class "status-info" ]
|
|
|
|
|
[ div [ class "avatars" ] <| List.map accountAvatarLink accounts
|
2017-04-24 05:46:14 -04:00
|
|
|
|
, p [ class "status-info-text" ] <|
|
2017-04-23 15:49:04 -04:00
|
|
|
|
List.intersperse (text " ")
|
|
|
|
|
[ icon iconType
|
|
|
|
|
, span [] <| List.intersperse (text ", ") (List.map accountLink accounts)
|
|
|
|
|
, text str
|
|
|
|
|
]
|
|
|
|
|
]
|
2017-04-22 10:39:19 -04:00
|
|
|
|
|
|
|
|
|
|
2017-04-27 10:34:27 -04:00
|
|
|
|
notificationStatusView : String -> Mastodon.Model.Status -> Mastodon.Model.NotificationAggregate -> Html Msg
|
2017-04-25 11:07:48 -04:00
|
|
|
|
notificationStatusView context status { type_, accounts } =
|
2017-04-23 15:49:04 -04:00
|
|
|
|
div [ class <| "notification " ++ type_ ]
|
2017-04-22 10:39:19 -04:00
|
|
|
|
[ case type_ of
|
|
|
|
|
"reblog" ->
|
2017-04-23 15:49:04 -04:00
|
|
|
|
notificationHeading accounts "boosted your toot" "fire"
|
2017-04-22 10:39:19 -04:00
|
|
|
|
|
|
|
|
|
"favourite" ->
|
2017-04-23 15:49:04 -04:00
|
|
|
|
notificationHeading accounts "favourited your toot" "star"
|
2017-04-22 10:39:19 -04:00
|
|
|
|
|
|
|
|
|
_ ->
|
|
|
|
|
text ""
|
2017-04-25 11:07:48 -04:00
|
|
|
|
, statusView context status
|
2017-04-23 04:18:47 -04:00
|
|
|
|
, statusActionsView status
|
2017-04-22 10:39:19 -04:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
2017-04-27 10:34:27 -04:00
|
|
|
|
notificationFollowView : Mastodon.Model.NotificationAggregate -> Html Msg
|
2017-04-23 15:49:04 -04:00
|
|
|
|
notificationFollowView { accounts } =
|
2017-04-24 05:46:14 -04:00
|
|
|
|
let
|
|
|
|
|
profileView account =
|
|
|
|
|
div [ class "status follow-profile" ]
|
|
|
|
|
[ accountAvatarLink account
|
|
|
|
|
, div [ class "username" ] [ accountLink account ]
|
2017-04-28 03:24:14 -04:00
|
|
|
|
, p
|
|
|
|
|
[ class "status-text"
|
|
|
|
|
, onClick <| LoadAccount account.id
|
|
|
|
|
]
|
|
|
|
|
<|
|
2017-04-28 03:41:11 -04:00
|
|
|
|
formatContent account.note []
|
2017-04-24 05:46:14 -04:00
|
|
|
|
]
|
|
|
|
|
in
|
|
|
|
|
div [ class "notification follow" ]
|
|
|
|
|
[ notificationHeading accounts "started following you" "user"
|
|
|
|
|
, div [ class "" ] <| List.map profileView accounts
|
|
|
|
|
]
|
2017-04-22 10:39:19 -04:00
|
|
|
|
|
|
|
|
|
|
2017-04-27 10:34:27 -04:00
|
|
|
|
notificationEntryView : Mastodon.Model.NotificationAggregate -> Html Msg
|
2017-04-22 10:39:19 -04:00
|
|
|
|
notificationEntryView notification =
|
|
|
|
|
li [ class "list-group-item" ]
|
|
|
|
|
[ case notification.status of
|
|
|
|
|
Just status ->
|
2017-04-25 11:07:48 -04:00
|
|
|
|
notificationStatusView "notification" status notification
|
2017-04-22 10:39:19 -04:00
|
|
|
|
|
|
|
|
|
Nothing ->
|
|
|
|
|
notificationFollowView notification
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
2017-04-27 10:34:27 -04:00
|
|
|
|
notificationListView : List Mastodon.Model.NotificationAggregate -> Html Msg
|
2017-04-22 10:39:19 -04:00
|
|
|
|
notificationListView notifications =
|
2017-04-28 13:33:38 -04:00
|
|
|
|
div [ class "col-md-3 column" ]
|
2017-04-22 10:39:19 -04:00
|
|
|
|
[ div [ class "panel panel-default" ]
|
2017-04-28 08:06:00 -04:00
|
|
|
|
[ a
|
|
|
|
|
[ href "", onClickWithPreventAndStop <| ScrollColumn "notifications" ]
|
|
|
|
|
[ div [ class "panel-heading" ] [ icon "bell", text "Notifications" ] ]
|
|
|
|
|
, ul [ id "notifications", class "list-group timeline" ] <|
|
2017-04-22 10:39:19 -04:00
|
|
|
|
List.map notificationEntryView notifications
|
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
2017-04-23 04:18:47 -04:00
|
|
|
|
draftReplyToView : Draft -> Html Msg
|
|
|
|
|
draftReplyToView draft =
|
|
|
|
|
case draft.in_reply_to of
|
|
|
|
|
Just status ->
|
|
|
|
|
div [ class "in-reply-to" ]
|
|
|
|
|
[ p []
|
|
|
|
|
[ strong []
|
|
|
|
|
[ text "In reply to this toot ("
|
|
|
|
|
, a
|
|
|
|
|
[ href ""
|
2017-04-28 05:21:54 -04:00
|
|
|
|
, onClickWithPreventAndStop <| DraftEvent ClearDraft
|
2017-04-23 04:18:47 -04:00
|
|
|
|
]
|
|
|
|
|
[ icon "remove" ]
|
|
|
|
|
, text ")"
|
|
|
|
|
]
|
|
|
|
|
]
|
2017-04-25 11:07:48 -04:00
|
|
|
|
, div [ class "well" ] [ statusView "draft" status ]
|
2017-04-23 04:18:47 -04:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
Nothing ->
|
|
|
|
|
text ""
|
|
|
|
|
|
|
|
|
|
|
2017-04-28 05:21:54 -04:00
|
|
|
|
currentUserView : Maybe Mastodon.Model.Account -> Html Msg
|
|
|
|
|
currentUserView currentUser =
|
|
|
|
|
case currentUser of
|
|
|
|
|
Just currentUser ->
|
|
|
|
|
div [ class "current-user" ]
|
|
|
|
|
[ accountAvatarLink currentUser
|
|
|
|
|
, div [ class "username" ] [ accountLink currentUser ]
|
|
|
|
|
, p [ class "status-text" ] <| formatContent currentUser.note []
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
Nothing ->
|
|
|
|
|
text ""
|
|
|
|
|
|
|
|
|
|
|
2017-04-20 14:30:19 -04:00
|
|
|
|
draftView : Model -> Html Msg
|
2017-04-28 05:21:54 -04:00
|
|
|
|
draftView { draft, currentUser } =
|
2017-04-20 14:30:19 -04:00
|
|
|
|
let
|
|
|
|
|
hasSpoiler =
|
2017-04-22 07:50:02 -04:00
|
|
|
|
draft.spoiler_text /= Nothing
|
2017-04-21 08:03:39 -04:00
|
|
|
|
|
|
|
|
|
visibilityOptionView ( visibility, description ) =
|
|
|
|
|
option [ value visibility ]
|
|
|
|
|
[ text <| visibility ++ ": " ++ description ]
|
2017-04-20 14:30:19 -04:00
|
|
|
|
in
|
2017-04-22 10:39:19 -04:00
|
|
|
|
div [ class "panel panel-default" ]
|
2017-04-23 04:18:47 -04:00
|
|
|
|
[ div [ class "panel-heading" ]
|
|
|
|
|
[ icon "envelope"
|
|
|
|
|
, text <|
|
|
|
|
|
if draft.in_reply_to /= Nothing then
|
|
|
|
|
"Post a reply"
|
|
|
|
|
else
|
|
|
|
|
"Post a message"
|
|
|
|
|
]
|
2017-04-22 10:39:19 -04:00
|
|
|
|
, div [ class "panel-body" ]
|
2017-04-28 05:21:54 -04:00
|
|
|
|
[ currentUserView currentUser
|
|
|
|
|
, draftReplyToView draft
|
2017-04-23 04:18:47 -04:00
|
|
|
|
, Html.form [ class "form", onSubmit SubmitDraft ]
|
2017-04-22 10:39:19 -04:00
|
|
|
|
[ div [ class "form-group checkbox" ]
|
|
|
|
|
[ label []
|
|
|
|
|
[ input
|
|
|
|
|
[ type_ "checkbox"
|
|
|
|
|
, onCheck <| DraftEvent << ToggleSpoiler
|
|
|
|
|
, checked hasSpoiler
|
2017-04-20 14:30:19 -04:00
|
|
|
|
]
|
2017-04-22 10:39:19 -04:00
|
|
|
|
[]
|
|
|
|
|
, text " Add a spoiler"
|
2017-04-20 14:30:19 -04:00
|
|
|
|
]
|
2017-04-22 10:39:19 -04:00
|
|
|
|
]
|
|
|
|
|
, if hasSpoiler then
|
|
|
|
|
div [ class "form-group" ]
|
|
|
|
|
[ label [ for "spoiler" ] [ text "Visible part" ]
|
2017-04-20 14:30:19 -04:00
|
|
|
|
, textarea
|
2017-04-22 10:39:19 -04:00
|
|
|
|
[ id "spoiler"
|
2017-04-20 14:30:19 -04:00
|
|
|
|
, class "form-control"
|
2017-04-22 10:39:19 -04:00
|
|
|
|
, rows 5
|
|
|
|
|
, placeholder "This text will always be visible."
|
|
|
|
|
, onInput <| DraftEvent << UpdateSpoiler
|
2017-04-20 14:30:19 -04:00
|
|
|
|
, required True
|
2017-04-22 10:39:19 -04:00
|
|
|
|
, value <| Maybe.withDefault "" draft.spoiler_text
|
2017-04-20 14:30:19 -04:00
|
|
|
|
]
|
|
|
|
|
[]
|
|
|
|
|
]
|
2017-04-22 10:39:19 -04:00
|
|
|
|
else
|
|
|
|
|
text ""
|
|
|
|
|
, div [ class "form-group" ]
|
|
|
|
|
[ label [ for "status" ]
|
|
|
|
|
[ text <|
|
|
|
|
|
if hasSpoiler then
|
|
|
|
|
"Hidden part"
|
|
|
|
|
else
|
|
|
|
|
"Status"
|
2017-04-21 08:03:39 -04:00
|
|
|
|
]
|
2017-04-22 10:39:19 -04: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 14:30:19 -04:00
|
|
|
|
]
|
2017-04-22 10:39:19 -04: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 14:30:19 -04:00
|
|
|
|
]
|
|
|
|
|
]
|
2017-04-23 04:18:47 -04:00
|
|
|
|
, justifiedButtonGroup
|
|
|
|
|
[ button
|
|
|
|
|
[ type_ "button"
|
|
|
|
|
, class "btn btn-default"
|
|
|
|
|
, onClick (DraftEvent ClearDraft)
|
|
|
|
|
]
|
|
|
|
|
[ text "Clear" ]
|
|
|
|
|
, button
|
|
|
|
|
[ type_ "submit"
|
|
|
|
|
, class "btn btn-primary"
|
|
|
|
|
]
|
2017-04-22 10:39:19 -04:00
|
|
|
|
[ text "Toot!" ]
|
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
2017-04-27 12:39:14 -04:00
|
|
|
|
threadView : Thread -> Html Msg
|
|
|
|
|
threadView thread =
|
|
|
|
|
let
|
|
|
|
|
statuses =
|
|
|
|
|
List.concat
|
|
|
|
|
[ thread.context.ancestors
|
|
|
|
|
, [ thread.status ]
|
|
|
|
|
, thread.context.descendants
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
threadEntry status =
|
|
|
|
|
statusEntryView "thread"
|
|
|
|
|
(if status == thread.status then
|
|
|
|
|
"thread-target"
|
|
|
|
|
else
|
|
|
|
|
""
|
|
|
|
|
)
|
|
|
|
|
status
|
|
|
|
|
in
|
2017-04-28 13:33:38 -04:00
|
|
|
|
div [ class "col-md-3 column" ]
|
2017-04-27 12:39:14 -04:00
|
|
|
|
[ div [ class "panel panel-default" ]
|
|
|
|
|
[ closeablePanelheading "list" "Thread" CloseThread
|
2017-04-27 13:40:45 -04:00
|
|
|
|
, ul [ class "list-group timeline" ] <|
|
|
|
|
|
List.map threadEntry statuses
|
2017-04-27 12:39:14 -04:00
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
2017-04-22 10:39:19 -04:00
|
|
|
|
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 []
|
2017-04-28 08:06:00 -04:00
|
|
|
|
[ input [ type_ "checkbox", onCheck UseGlobalTimeline ] []
|
2017-04-22 10:39:19 -04:00
|
|
|
|
, text " 4th column renders the global timeline"
|
2017-04-20 14:30:19 -04:00
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
]
|
2017-04-22 10:39:19 -04:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sidebarView : Model -> Html Msg
|
|
|
|
|
sidebarView model =
|
2017-04-28 13:33:38 -04:00
|
|
|
|
div [ class "col-md-3 column" ]
|
2017-04-22 10:39:19 -04:00
|
|
|
|
[ draftView model
|
|
|
|
|
, optionsView model
|
|
|
|
|
]
|
2017-04-20 14:30:19 -04:00
|
|
|
|
|
|
|
|
|
|
2017-04-20 03:46:18 -04:00
|
|
|
|
homepageView : Model -> Html Msg
|
|
|
|
|
homepageView model =
|
|
|
|
|
div [ class "row" ]
|
2017-04-22 10:39:19 -04:00
|
|
|
|
[ sidebarView model
|
2017-04-25 11:07:48 -04:00
|
|
|
|
, timelineView "Home timeline" "home" "home" model.userTimeline
|
2017-04-22 10:39:19 -04:00
|
|
|
|
, notificationListView model.notifications
|
2017-04-27 12:39:14 -04:00
|
|
|
|
, case model.currentView of
|
|
|
|
|
Model.LocalTimelineView ->
|
|
|
|
|
timelineView "Local timeline" "th-large" "local" model.localTimeline
|
|
|
|
|
|
|
|
|
|
Model.GlobalTimelineView ->
|
|
|
|
|
timelineView "Global timeline" "globe" "global" model.globalTimeline
|
|
|
|
|
|
|
|
|
|
Model.AccountView account ->
|
2017-04-22 04:16:14 -04:00
|
|
|
|
-- Todo: Load the user timeline
|
2017-04-27 16:01:51 -04:00
|
|
|
|
accountTimelineView account model.accountTimeline "Account" "user"
|
2017-04-22 04:16:14 -04:00
|
|
|
|
|
2017-04-27 12:39:14 -04:00
|
|
|
|
Model.ThreadView thread ->
|
|
|
|
|
threadView thread
|
2017-04-20 03:46:18 -04:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
authView : Model -> Html Msg
|
|
|
|
|
authView model =
|
|
|
|
|
div [ class "col-md-4 col-md-offset-4" ]
|
2017-04-25 04:13:00 -04:00
|
|
|
|
[ div [ class "page-header" ]
|
|
|
|
|
[ h1 []
|
|
|
|
|
[ text "tooty"
|
|
|
|
|
, small []
|
|
|
|
|
[ text " is a Web client for the "
|
|
|
|
|
, a
|
|
|
|
|
[ href "https://github.com/tootsuite/mastodon"
|
|
|
|
|
, target "_blank"
|
|
|
|
|
]
|
|
|
|
|
[ text "Mastodon" ]
|
|
|
|
|
, text " API."
|
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
, div [ class "panel panel-default" ]
|
2017-04-20 03:46:18 -04:00
|
|
|
|
[ 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 08:03:39 -04:00
|
|
|
|
[ text <|
|
|
|
|
|
"You'll be redirected to that server to authenticate yourself. "
|
|
|
|
|
++ "We don't have access to your password."
|
|
|
|
|
]
|
2017-04-20 03:46:18 -04:00
|
|
|
|
]
|
|
|
|
|
, button [ class "btn btn-primary", type_ "submit" ]
|
|
|
|
|
[ text "Sign into Tooty" ]
|
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
2017-04-24 15:21:43 -04:00
|
|
|
|
viewerView : Viewer -> Html Msg
|
|
|
|
|
viewerView { attachments, attachment } =
|
|
|
|
|
let
|
|
|
|
|
index =
|
|
|
|
|
Maybe.withDefault -1 <| elemIndex attachment attachments
|
|
|
|
|
|
|
|
|
|
( prev, next ) =
|
|
|
|
|
( getAt (index - 1) attachments, getAt (index + 1) attachments )
|
|
|
|
|
|
|
|
|
|
navLink label className target =
|
|
|
|
|
case target of
|
|
|
|
|
Nothing ->
|
|
|
|
|
text ""
|
|
|
|
|
|
|
|
|
|
Just target ->
|
|
|
|
|
a
|
|
|
|
|
[ href ""
|
|
|
|
|
, class className
|
2017-04-28 03:41:11 -04:00
|
|
|
|
, onClickWithPreventAndStop <|
|
2017-04-24 15:21:43 -04:00
|
|
|
|
ViewerEvent (OpenViewer attachments target)
|
|
|
|
|
]
|
|
|
|
|
[ text label ]
|
|
|
|
|
in
|
|
|
|
|
div
|
|
|
|
|
[ class "viewer"
|
|
|
|
|
, tabindex -1
|
2017-04-28 03:41:11 -04:00
|
|
|
|
, onClickWithPreventAndStop <| ViewerEvent CloseViewer
|
2017-04-24 15:21:43 -04:00
|
|
|
|
]
|
|
|
|
|
[ span [ class "close" ] [ text "×" ]
|
|
|
|
|
, navLink "❮" "prev" prev
|
|
|
|
|
, case attachment.type_ of
|
|
|
|
|
"image" ->
|
|
|
|
|
img [ class "viewer-content", src attachment.url ] []
|
|
|
|
|
|
|
|
|
|
_ ->
|
|
|
|
|
video
|
|
|
|
|
[ class "viewer-content"
|
|
|
|
|
, preload "auto"
|
|
|
|
|
, autoplay True
|
|
|
|
|
, loop True
|
|
|
|
|
]
|
|
|
|
|
[ source [ src attachment.url ] [] ]
|
|
|
|
|
, navLink "❯" "next" next
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
2017-04-20 03:46:18 -04:00
|
|
|
|
view : Model -> Html Msg
|
|
|
|
|
view model =
|
|
|
|
|
div [ class "container-fluid" ]
|
2017-04-22 10:43:45 -04:00
|
|
|
|
[ errorsListView model
|
2017-04-20 03:46:18 -04:00
|
|
|
|
, case model.client of
|
|
|
|
|
Just client ->
|
|
|
|
|
homepageView model
|
|
|
|
|
|
|
|
|
|
Nothing ->
|
|
|
|
|
authView model
|
2017-04-24 15:21:43 -04:00
|
|
|
|
, case model.viewer of
|
|
|
|
|
Just viewer ->
|
|
|
|
|
viewerView viewer
|
|
|
|
|
|
|
|
|
|
Nothing ->
|
|
|
|
|
text ""
|
2017-04-20 03:46:18 -04:00
|
|
|
|
]
|