1
0
Fork 0
tooty/src/View/Status.elm

350 lines
12 KiB
Elm

module View.Status
exposing
( statusView
, statusActionsView
, statusEntryView
)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Keyed as Keyed
import Html.Lazy as Lazy
import Mastodon.Helper exposing (extractStatusId)
import Mastodon.Model exposing (..)
import Types exposing (..)
import View.Common as Common
import View.Events exposing (..)
import View.Formatter exposing (formatContent)
type alias CurrentUser =
Account
attachmentPreview : String -> Maybe Bool -> List Attachment -> Attachment -> Html Msg
attachmentPreview context sensitive attachments ({ url, preview_url } as attachment) =
let
nsfw =
case sensitive of
Just sensitive ->
sensitive
Nothing ->
False
attId =
"att" ++ attachment.id ++ context
previewUrl =
if preview_url == "" then
url
else
preview_url
media =
case attachment.type_ of
"audio" ->
a
[ if nsfw then
class "attachment-image nsfw"
else
class "attachment-image"
]
[ audio
[ controls True
, style [("width", "100%")]
]
[ source [ src url ] []
]
]
_ ->
a
[ if nsfw then
class "attachment-image nsfw"
else
class "attachment-image"
, href url
, onClickWithPreventAndStop <|
ViewerEvent (OpenViewer attachments attachment)
]
[ img [ src previewUrl ] [] ]
in
li [ class "attachment-entry" ] <|
if nsfw then
[ input [ onClickWithStop NoOp, type_ "radio", id attId ] []
, label [ onClickWithStop NoOp, for attId ]
[ text "Sensitive content"
, br [] []
, br [] []
, text "click to show attachment"
]
, media
]
else
[ media ]
attachmentListView : String -> Status -> Html Msg
attachmentListView context { media_attachments, sensitive } =
let
keyedEntry attachments attachment =
( attachment.id
, attachmentPreview context sensitive attachments attachment
)
in
case media_attachments of
[] ->
text ""
attachments ->
Keyed.ul [ class "attachments" ] <|
List.map (keyedEntry attachments) attachments
pollView : Status -> Html Msg
pollView status =
let
pollStatus poll =
if poll.voted then
text "Voted"
else if poll.expired then
text "Expired"
else
text "Not voted"
optionPercentage option =
100*option.votes_count//status.poll.votes_count
optionPercentageText option =
span [ class "poll-percentage" ]
[ text <| (toString (optionPercentage option))++"%"
]
optionBar option =
span
[ class "poll-bar"
, style [("width", toString (optionPercentage option)++"%")]
] []
optionEntry option =
li []
[ optionPercentageText option
, text option.title
, optionBar option
]
in
if status.poll.id == PollId("") then
text ""
else
div [ class "poll" ]
[ ul [] <|
List.map optionEntry status.poll.options
, span [ class "poll-footer" ]
[ text <| (toString status.poll.votes_count)++" votes"
, text " "
, pollStatus status.poll ]
]
statusActionsView : Status -> CurrentUser -> Bool -> Html Msg
statusActionsView status currentUser showApp =
let
sourceStatus =
Mastodon.Helper.extractReblog status
baseBtnClasses =
"btn btn-sm btn-default"
( reblogClasses, reblogEvent ) =
case status.reblogged of
Just True ->
( baseBtnClasses ++ " reblogged", UnreblogStatus sourceStatus )
_ ->
( baseBtnClasses, ReblogStatus sourceStatus )
( favClasses, favEvent ) =
case status.favourited of
Just True ->
( baseBtnClasses ++ " favourited", RemoveFavorite sourceStatus )
_ ->
( baseBtnClasses, AddFavorite sourceStatus )
in
div [ class "btn-group actions" ]
[ a
[ class baseBtnClasses
, onClickWithPreventAndStop <| DraftEvent (UpdateReplyTo status)
]
[ Common.icon "share-alt" ]
, if status.visibility == "private" then
span [ class <| reblogClasses ++ " disabled" ]
[ span [ title "Private" ] [ Common.icon "lock" ] ]
else if status.visibility == "direct" then
span [ class <| reblogClasses ++ " disabled" ]
[ span [ title "Direct" ] [ Common.icon "envelope" ] ]
else
a
[ class reblogClasses, onClickWithPreventAndStop reblogEvent ]
[ Common.icon "fire", text (toString sourceStatus.reblogs_count) ]
, a
[ class favClasses, onClickWithPreventAndStop favEvent ]
[ Common.icon "star", text (toString sourceStatus.favourites_count) ]
, if Mastodon.Helper.sameAccount sourceStatus.account currentUser then
a
[ class <| baseBtnClasses ++ " btn-delete"
, href ""
, onClickWithPreventAndStop <|
AskConfirm "Are you sure you want to delete this toot?" (DeleteStatus sourceStatus.id) NoOp
]
[ Common.icon "trash" ]
else
text ""
, a
[ class baseBtnClasses, href (Maybe.withDefault "#" sourceStatus.url), target "_blank" ]
[ Common.icon "time", text <| Common.formatDate sourceStatus.created_at ]
, if showApp then
Common.appLink (baseBtnClasses ++ " applink") sourceStatus.application
else
text ""
]
mentionView : Mention -> Html Msg
mentionView mention =
a
[ href ("#account/"++mention.id)
, class "mention" ]
[ text <| "@" ++ mention.username
]
quoteView : Status -> Html Msg
quoteView status =
let
quote =
Mastodon.Helper.extractQuote status
in
case quote of
Just (Quote quote) ->
div [ class "status-quote" ]
[ statusView "quote" quote True ]
Nothing ->
text ""
mentionsView : List Mention -> Html Msg
mentionsView mentions =
let
mentionLinks =
List.map mentionView mentions
in
if (List.isEmpty mentions) then
text ""
else
div [ class "status-mentions" ]
( List.append [ text "Mentioning" ] mentionLinks )
statusContentView : String -> Status -> Html Msg
statusContentView context status =
case status.spoiler_text of
"" ->
div [ class "status-text" ]
[ mentionsView status.mentions
, div [] <| formatContent status.content status.mentions status.emojis
, pollView status
, attachmentListView context status
, quoteView status
]
spoiler ->
-- Note: Spoilers are dealt with using pure CSS.
let
statusId =
"spoiler" ++ extractStatusId status.id ++ context
in
div [ class "status-text spoiled" ]
[ div [ class "spoiler" ] <| formatContent status.spoiler_text status.mentions status.emojis
, input [ onClickWithStop NoOp, type_ "checkbox", id statusId, class "spoiler-toggler" ] []
, label [ onClickWithStop NoOp, for statusId ] [ text "Reveal content" ]
, div [ class "spoiled-content" ]
[ mentionsView status.mentions
, div [] <| formatContent status.content status.mentions status.emojis
, pollView status
, attachmentListView context status
, quoteView status
]
]
statusEntryView : String -> String -> CurrentUser -> Status -> Html Msg
statusEntryView context className currentUser status =
let
nsfwClass =
case status.sensitive of
Just True ->
"nsfw"
_ ->
""
liAttributes =
[ class <| "list-group-item " ++ className ++ " " ++ nsfwClass ]
++ if context == "thread" then
[ id <| "thread-status-" ++ extractStatusId status.id ]
else
[]
in
li liAttributes
[ Lazy.lazy3 statusView context status (className /= "thread-target")
, Lazy.lazy3 statusActionsView status currentUser (className == "thread-target")
]
statusView : String -> Status -> Bool -> Html Msg
statusView context ({ account, content, media_attachments, reblog, mentions, pinned } as status) clickOpen =
let
statusAttribs =
if clickOpen then
[ class "status"
, onClickWithStop <| OpenThread status
]
else
[ class "status" ]
accountLinkAttributes =
[ href <| "#account/" ++ account.id ]
pin =
if pinned then
p [ class "status-info" ]
[ Common.icon "pushpin"
, text " Pinned status"
]
else
text ""
in
case reblog of
Just (Reblog reblog) ->
div [ class "reblog" ]
[ p [ class "status-info" ]
[ Common.icon "fire"
, a (accountLinkAttributes ++ [ class "reblogger" ])
[ span [ class "display-name" ] <| Common.accountDisplayNameRich account ]
, text "boosted"
]
, Lazy.lazy3 statusView context reblog clickOpen
]
Nothing ->
div statusAttribs
[ pin
, Common.accountAvatarLink False account
, div [ class "username" ]
[ a accountLinkAttributes
[ span [ class "display-name" ] <| Common.accountDisplayNameRich account
, Common.accountAcctView True account
]
]
, Lazy.lazy2 statusContentView context status
]