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 ]