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

324 lines
11 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 "#" status.url), target "_blank" ]
[ Common.icon "time", text <| Common.formatDate status.created_at ]
, if showApp then
Common.appLink (baseBtnClasses ++ " applink") status.application
else
text ""
]
statusContentView : String -> Status -> Html Msg
statusContentView context status =
case status.spoiler_text of
"" ->
div [ class "status-text" ]
[ div [] <| formatContent status.content status.mentions
, pollView status
, attachmentListView context 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" ]
[ text status.spoiler_text ]
, input [ onClickWithStop NoOp, type_ "checkbox", id statusId, class "spoiler-toggler" ] []
, label [ onClickWithStop NoOp, for statusId ] [ text "Reveal content" ]
, div [ class "spoiled-content" ]
[ div [] <| formatContent status.content status.mentions
, pollView status
, attachmentListView context 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")
]
statusAcctView : Account -> Html Msg
statusAcctView account =
let
acctText = text <| " @" ++ account.acct
instUrl = String.join "/" ( List.take 3 ( String.split "/" account.url ) )
instIcon = img [ class "acct-instance-icon", src ( instUrl ++ "/favicon.png" ), alt "" ] []
instIcon2 = img [ class "acct-instance-icon", src ( instUrl ++ "/favicon.ico" ), alt "" ] []
in
span [ class "acct" ] [ acctText, instIcon, instIcon2 ]
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" ])
[ text <| " " ++ if account.display_name == "" then
account.username
else
account.display_name]
, text " boosted"
]
, Lazy.lazy3 statusView context reblog clickOpen
]
Nothing ->
div statusAttribs
[ pin
, Common.accountAvatarLink False account
, div [ class "username" ]
[ a accountLinkAttributes
[ text (if account.display_name=="" then account.username else account.display_name)
, statusAcctView <| account
]
]
, Lazy.lazy2 statusContentView context status
]