diff --git a/public/style.css b/public/style.css index af4a60b..29b93ca 100644 --- a/public/style.css +++ b/public/style.css @@ -62,19 +62,30 @@ body { .timeline { overflow-y: auto; overflow-x: hidden; - max-height: calc(100vh - 45px); + max-height: calc(100vh - 44px); overflow: hidden; overflow-y: scroll; } -.timeline > .list-group-item { +.timeline .list-group-item { padding: 8px 12px; } +.empty-timeline-text { + font-size: 30px; + font-weight: bold; + text-align: center; + line-height: 35px; + opacity: .5; + padding: 100px 20px; +} + +#blocks-timeline, #favorite-timeline, +#global-timeline, #local-timeline, -#global-timeline { - max-height: calc(100vh - 80px); +#mutes-timeline { + max-height: calc(100vh - 81px); } @@ -83,7 +94,7 @@ li.load-more { } .notifications-panel .timeline { - max-height: calc(100vh - 80px); + max-height: calc(100vh - 81px); } .panel a { @@ -223,8 +234,9 @@ span.applink { order: 0; flex: 0 1 auto; align-self: auto; - width: 40px; - height: 40px; + width: 38px; + height: 38px; + margin-left: 2px; } .acct { @@ -618,7 +630,7 @@ input.form-control[type=file] { text-align: center; } -.account-detail .opacity-layer{ +.account-detail .opacity-layer { background: rgba(49,53,67,0.9); } @@ -631,13 +643,21 @@ input.form-control[type=file] { .account-detail .btn { position: absolute; - top: 1em; - left: 1em; - width: 50px; - height: 50px; + top: 10px; + right: 10px; + width: 42px; + height: 42px; opacity: .8; } +.account-detail .btn-mute { + top: 54px; +} + +.account-detail .btn-block { + top: 98px; +} + .account-detail .account-display-name { display: block; font-size: 130%; diff --git a/src/Command.elm b/src/Command.elm index 532471d..cd0c525 100644 --- a/src/Command.elm +++ b/src/Command.elm @@ -15,6 +15,8 @@ module Command , loadGlobalTimeline , loadAccountTimeline , loadFavoriteTimeline + , loadMutes + , loadBlocks , loadNextTimeline , loadRelationships , loadThread @@ -28,6 +30,10 @@ module Command , unfavouriteStatus , follow , unfollow + , mute + , unmute + , block + , unblock , uploadMedia , focusId , scrollColumnToTop @@ -332,6 +338,34 @@ loadFavoriteTimeline client url = Cmd.none +loadMutes : Maybe Client -> Maybe String -> Cmd Msg +loadMutes client url = + case client of + Just client -> + HttpBuilder.get (Maybe.withDefault ApiUrl.mutes url) + |> withClient client + |> withBodyDecoder (Decode.list accountDecoder) + |> withQueryParams [ ( "limit", "60" ) ] + |> send (MastodonEvent << Mutes (url /= Nothing)) + + Nothing -> + Cmd.none + + +loadBlocks : Maybe Client -> Maybe String -> Cmd Msg +loadBlocks client url = + case client of + Just client -> + HttpBuilder.get (Maybe.withDefault ApiUrl.blocks url) + |> withClient client + |> withBodyDecoder (Decode.list accountDecoder) + |> withQueryParams [ ( "limit", "60" ) ] + |> send (MastodonEvent << Blocks (url /= Nothing)) + + Nothing -> + Cmd.none + + loadTimelines : Maybe Client -> Cmd Msg loadTimelines client = Cmd.batch @@ -498,6 +532,58 @@ unfollow client account = Cmd.none +mute : Maybe Client -> Account -> Cmd Msg +mute client account = + case client of + Just client -> + HttpBuilder.post (ApiUrl.mute account.id) + |> withClient client + |> withBodyDecoder relationshipDecoder + |> send (MastodonEvent << (AccountMuted account)) + + Nothing -> + Cmd.none + + +unmute : Maybe Client -> Account -> Cmd Msg +unmute client account = + case client of + Just client -> + HttpBuilder.post (ApiUrl.unmute account.id) + |> withClient client + |> withBodyDecoder relationshipDecoder + |> send (MastodonEvent << (AccountUnmuted account)) + + Nothing -> + Cmd.none + + +block : Maybe Client -> Account -> Cmd Msg +block client account = + case client of + Just client -> + HttpBuilder.post (ApiUrl.block account.id) + |> withClient client + |> withBodyDecoder relationshipDecoder + |> send (MastodonEvent << (AccountBlocked account)) + + Nothing -> + Cmd.none + + +unblock : Maybe Client -> Account -> Cmd Msg +unblock client account = + case client of + Just client -> + HttpBuilder.post (ApiUrl.unblock account.id) + |> withClient client + |> withBodyDecoder relationshipDecoder + |> send (MastodonEvent << (AccountUnblocked account)) + + Nothing -> + Cmd.none + + uploadMedia : Maybe Client -> String -> Cmd Msg uploadMedia client fileInputId = case client of diff --git a/src/Init.elm b/src/Init.elm index 68533de..eedead1 100644 --- a/src/Init.elm +++ b/src/Init.elm @@ -18,6 +18,8 @@ init { registration, clients } location = , localTimeline = Update.Timeline.empty "local-timeline" , globalTimeline = Update.Timeline.empty "global-timeline" , favoriteTimeline = Update.Timeline.empty "favorite-timeline" + , mutes = Update.Timeline.empty "mutes-timeline" + , blocks = Update.Timeline.empty "blocks-timeline" , accountTimeline = Update.Timeline.empty "account-timeline" , accountFollowers = Update.Timeline.empty "account-followers" , accountFollowing = Update.Timeline.empty "account-following" diff --git a/src/Mastodon/ApiUrl.elm b/src/Mastodon/ApiUrl.elm index fc9e635..9c9edbe 100644 --- a/src/Mastodon/ApiUrl.elm +++ b/src/Mastodon/ApiUrl.elm @@ -12,6 +12,8 @@ module Mastodon.ApiUrl , homeTimeline , publicTimeline , favouriteTimeline + , mutes + , blocks , notifications , relationships , statuses @@ -22,6 +24,10 @@ module Mastodon.ApiUrl , unfavourite , follow , unfollow + , mute + , unmute + , block + , unblock , uploadMedia , streaming , searchAccount @@ -68,6 +74,26 @@ unfollow id = accounts ++ (toString id) ++ "/unfollow" +mute : Int -> String +mute id = + accounts ++ (toString id) ++ "/mute" + + +unmute : Int -> String +unmute id = + accounts ++ (toString id) ++ "/unmute" + + +block : Int -> String +block id = + accounts ++ (toString id) ++ "/block" + + +unblock : Int -> String +unblock id = + accounts ++ (toString id) ++ "/unblock" + + userAccount : String userAccount = accounts ++ "verify_credentials" @@ -113,6 +139,16 @@ favouriteTimeline = apiPrefix ++ "/favourites" +mutes : String +mutes = + apiPrefix ++ "/mutes" + + +blocks : String +blocks = + apiPrefix ++ "/blocks" + + notifications : String notifications = apiPrefix ++ "/notifications" diff --git a/src/Types.elm b/src/Types.elm index 2af2142..339108c 100644 --- a/src/Types.elm +++ b/src/Types.elm @@ -45,13 +45,18 @@ type MastodonMsg | AccountFollowed Account (MastodonResult Relationship) | AccountFollowers Bool (MastodonResult (List Account)) | AccountFollowing Bool (MastodonResult (List Account)) + | AccountBlocked Account (MastodonResult Relationship) + | AccountMuted Account (MastodonResult Relationship) | AccountReceived (MastodonResult Account) | AccountRelationship (MastodonResult (List Relationship)) | AccountRelationships (MastodonResult (List Relationship)) | AccountTimeline Bool (MastodonResult (List Status)) | AccountUnfollowed Account (MastodonResult Relationship) + | AccountUnblocked Account (MastodonResult Relationship) + | AccountUnmuted Account (MastodonResult Relationship) | AppRegistered (MastodonResult AppRegistration) | AutoSearch (MastodonResult (List Account)) + | Blocks Bool (MastodonResult (List Account)) | ContextLoaded Status (MastodonResult Context) | CurrentUser (MastodonResult Account) | FavoriteAdded (MastodonResult Status) @@ -60,6 +65,7 @@ type MastodonMsg | GlobalTimeline Bool (MastodonResult (List Status)) | HomeTimeline Bool (MastodonResult (List Status)) | LocalTimeline Bool (MastodonResult (List Status)) + | Mutes Bool (MastodonResult (List Account)) | Notifications Bool (MastodonResult (List Notification)) | Reblogged (MastodonResult Status) | StatusDeleted (MastodonResult Int) @@ -76,9 +82,9 @@ type WebSocketMsg type Msg = AddFavorite Status | AskConfirm String Msg Msg + | Block Account | ClearError Int | CloseAccount - | CloseAccountSelector | CloseThread | ConfirmCancelled Msg | Confirmed Msg @@ -90,6 +96,7 @@ type Msg | LogoutClient Client | TimelineLoadNext String String | MastodonEvent MastodonMsg + | Mute Account | NoOp | OpenThread Status | ReblogStatus Status @@ -102,8 +109,10 @@ type Msg | SwitchClient Client | Tick Time | UnfollowAccount Account - | UrlChange Navigation.Location + | Unblock Account + | Unmute Account | UnreblogStatus Status + | UrlChange Navigation.Location | ViewAccountFollowing Account | ViewAccountFollowers Account | ViewAccountStatuses Account @@ -124,9 +133,11 @@ type CurrentView | AccountFollowingView Account (Timeline Account) | AccountView Account | AccountSelectorView + | BlocksView | FavoriteTimelineView | GlobalTimelineView | LocalTimelineView + | MutesView | ThreadView Thread @@ -200,6 +211,8 @@ type alias Model = , localTimeline : Timeline Status , globalTimeline : Timeline Status , favoriteTimeline : Timeline Status + , mutes : Timeline Account + , blocks : Timeline Account , accountTimeline : Timeline Status , accountFollowers : Timeline Account , accountFollowing : Timeline Account diff --git a/src/Update/Main.elm b/src/Update/Main.elm index 6a6fc64..82e0151 100644 --- a/src/Update/Main.elm +++ b/src/Update/Main.elm @@ -60,9 +60,26 @@ update msg model = { model | currentView = view, server = "" } ! [] FavoriteTimelineView -> - { model | currentView = view } + { model + | currentView = view + , favoriteTimeline = Update.Timeline.setLoading True model.favoriteTimeline + } ! [ Command.loadFavoriteTimeline (List.head model.clients) Nothing ] + BlocksView -> + { model + | currentView = view + , blocks = Update.Timeline.setLoading True model.blocks + } + ! [ Command.loadBlocks (List.head model.clients) Nothing ] + + MutesView -> + { model + | currentView = view + , mutes = Update.Timeline.setLoading True model.mutes + } + ! [ Command.loadMutes (List.head model.clients) Nothing ] + _ -> { model | currentView = view } ! [] @@ -141,6 +158,18 @@ update msg model = UnfollowAccount account -> model ! [ Command.unfollow (List.head model.clients) account ] + Mute account -> + model ! [ Command.mute (List.head model.clients) account ] + + Unmute account -> + model ! [ Command.unmute (List.head model.clients) account ] + + Block account -> + model ! [ Command.block (List.head model.clients) account ] + + Unblock account -> + model ! [ Command.unblock (List.head model.clients) account ] + DeleteStatus id -> model ! [ Command.deleteStatus (List.head model.clients) id ] @@ -221,9 +250,6 @@ update msg model = } ! [] - CloseAccountSelector -> - { model | currentView = LocalTimelineView } ! [] - FilterNotifications filter -> { model | notificationFilter = filter } ! [] diff --git a/src/Update/Mastodon.elm b/src/Update/Mastodon.elm index 67d39fc..b4dcce6 100644 --- a/src/Update/Mastodon.elm +++ b/src/Update/Mastodon.elm @@ -50,7 +50,7 @@ update msg model = AccountFollowed _ result -> case result of Ok { decoded } -> - processFollowEvent decoded True model ! [] + processFollowEvent decoded model ! [] Err error -> { model | errors = addErrorNotification (errorText error) model } ! [] @@ -63,6 +63,38 @@ update msg model = Err error -> { model | errors = addErrorNotification (errorText error) model } ! [] + AccountMuted account result -> + case result of + Ok { decoded } -> + processMuteEvent account decoded model ! [] + + Err error -> + { model | errors = addErrorNotification (errorText error) model } ! [] + + AccountUnmuted account result -> + case result of + Ok { decoded } -> + processMuteEvent account decoded model ! [] + + Err error -> + { model | errors = addErrorNotification (errorText error) model } ! [] + + AccountBlocked account result -> + case result of + Ok { decoded } -> + processBlockEvent account decoded model ! [] + + Err error -> + { model | errors = addErrorNotification (errorText error) model } ! [] + + AccountUnblocked account result -> + case result of + Ok { decoded } -> + processBlockEvent account decoded model ! [] + + Err error -> + { model | errors = addErrorNotification (errorText error) model } ! [] + AppRegistered result -> case result of Ok { decoded } -> @@ -157,6 +189,22 @@ update msg model = Err error -> { model | errors = addErrorNotification (errorText error) model } ! [] + Mutes append result -> + case result of + Ok { decoded, links } -> + { model | mutes = Update.Timeline.update append decoded links model.mutes } ! [] + + Err error -> + { model | errors = addErrorNotification (errorText error) model } ! [] + + Blocks append result -> + case result of + Ok { decoded, links } -> + { model | blocks = Update.Timeline.update append decoded links model.blocks } ! [] + + Err error -> + { model | errors = addErrorNotification (errorText error) model } ! [] + Reblogged result -> case result of Ok _ -> @@ -301,12 +349,12 @@ update msg model = {-| Update viewed account relationships as well as the relationship with the current connected user, both according to the "following" status provided. -} -processFollowEvent : Relationship -> Bool -> Model -> Model -processFollowEvent relationship flag model = +processFollowEvent : Relationship -> Model -> Model +processFollowEvent relationship model = let updateRelationship r = if r.id == relationship.id then - { r | following = flag } + { r | following = relationship.following } else r @@ -317,7 +365,7 @@ processFollowEvent relationship flag model = case model.accountRelationship of Just accountRelationship -> if accountRelationship.id == relationship.id then - Just { relationship | following = flag } + Just { relationship | following = relationship.following } else model.accountRelationship @@ -334,7 +382,7 @@ processUnfollowEvent : Account -> Relationship -> Model -> Model processUnfollowEvent account relationship model = let newModel = - processFollowEvent relationship False model + processFollowEvent relationship model in case model.currentUser of Just currentUser -> @@ -344,3 +392,76 @@ processUnfollowEvent account relationship model = Nothing -> newModel + + +{-| Update viewed account relationships as well as the relationship with the +current connected user, both according to the "muting" status provided. +-} +processMuteEvent : Account -> Relationship -> Model -> Model +processMuteEvent account relationship model = + let + updateRelationship r = + if r.id == relationship.id then + { r | muting = relationship.muting } + else + r + + accountRelationships = + model.accountRelationships |> List.map updateRelationship + + accountRelationship = + case model.accountRelationship of + Just accountRelationship -> + if accountRelationship.id == relationship.id then + Just { relationship | muting = relationship.muting } + else + model.accountRelationship + + Nothing -> + Nothing + in + { model + | accountRelationships = accountRelationships + , accountRelationship = accountRelationship + , homeTimeline = Update.Timeline.dropAccountStatuses account model.homeTimeline + , localTimeline = Update.Timeline.dropAccountStatuses account model.localTimeline + , globalTimeline = Update.Timeline.dropAccountStatuses account model.globalTimeline + , mutes = Update.Timeline.removeMute account model.mutes + } + + +{-| Update viewed account relationships as well as the relationship with the +current connected user, both according to the "blocking" status provided. +-} +processBlockEvent : Account -> Relationship -> Model -> Model +processBlockEvent account relationship model = + let + updateRelationship r = + if r.id == relationship.id then + { r | blocking = relationship.blocking } + else + r + + accountRelationships = + model.accountRelationships |> List.map updateRelationship + + accountRelationship = + case model.accountRelationship of + Just accountRelationship -> + if accountRelationship.id == relationship.id then + Just { relationship | blocking = relationship.blocking } + else + model.accountRelationship + + Nothing -> + Nothing + in + { model + | accountRelationships = accountRelationships + , accountRelationship = accountRelationship + , homeTimeline = Update.Timeline.dropAccountStatuses account model.homeTimeline + , localTimeline = Update.Timeline.dropAccountStatuses account model.localTimeline + , globalTimeline = Update.Timeline.dropAccountStatuses account model.globalTimeline + , blocks = Update.Timeline.removeBlock account model.blocks + , notifications = Update.Timeline.dropNotificationsFromAccount account model.notifications + } diff --git a/src/Update/Timeline.elm b/src/Update/Timeline.elm index d2ecd40..b6e631c 100644 --- a/src/Update/Timeline.elm +++ b/src/Update/Timeline.elm @@ -3,11 +3,16 @@ module Update.Timeline ( cleanUnfollow , deleteStatusFromAllTimelines , deleteStatus + , dropAccountStatuses + , dropNotificationsFromAccount , empty , markAsLoading , prepend , processReblog , processFavourite + , removeBlock + , removeMute + , setLoading , update , updateWithBoolFlag ) @@ -101,6 +106,29 @@ deleteStatus statusId ({ entries } as timeline) = } +dropAccountStatuses : Account -> Timeline Status -> Timeline Status +dropAccountStatuses account timeline = + let + keep status = + not <| Mastodon.Helper.sameAccount account status.account + in + { timeline | entries = List.filter keep timeline.entries } + + +dropNotificationsFromAccount : Account -> Timeline NotificationAggregate -> Timeline NotificationAggregate +dropNotificationsFromAccount account timeline = + let + keepNotification notification = + case notification.status of + Just status -> + status.account /= account + + Nothing -> + True + in + { timeline | entries = List.filter keepNotification timeline.entries } + + empty : String -> Timeline a empty id = { id = id @@ -197,6 +225,29 @@ processReblog status added model = model +removeBlock : Account -> Timeline Account -> Timeline Account +removeBlock account timeline = + let + keep blockedAccount = + not <| Mastodon.Helper.sameAccount account blockedAccount + in + { timeline | entries = List.filter keep timeline.entries } + + +removeMute : Account -> Timeline Account -> Timeline Account +removeMute account timeline = + let + keep mutedAccount = + not <| Mastodon.Helper.sameAccount account mutedAccount + in + { timeline | entries = List.filter keep timeline.entries } + + +setLoading : Bool -> Timeline a -> Timeline a +setLoading flag timeline = + { timeline | loading = flag } + + update : Bool -> List a -> Links -> Timeline a -> Timeline a update append entries links timeline = let diff --git a/src/View/Account.elm b/src/View/Account.elm index f6c6776..7dbef15 100644 --- a/src/View/Account.elm +++ b/src/View/Account.elm @@ -51,7 +51,7 @@ followButton currentUser relationship account = case relationship of Nothing -> ( NoOp - , "btn btn-default btn-disabled" + , "btn btn-default btn-follow btn-disabled" , "question-sign" , "Unknown relationship" ) @@ -59,13 +59,13 @@ followButton currentUser relationship account = Just relationship -> if relationship.following then ( UnfollowAccount account - , "btn btn-default btn-primary" + , "btn btn-default btn-follow btn-primary" , "eye-close" , "Unfollow" ) else ( FollowAccount account - , "btn btn-default" + , "btn btn-default btn-follow" , "eye-open" , "Follow" ) @@ -94,10 +94,77 @@ followView currentUser relationship account = , br [] [] , text <| "@" ++ account.acct ] + , muteButton currentUser relationship account , followButton currentUser relationship account ] +muteButton : CurrentUser -> CurrentUserRelation -> Account -> Html Msg +muteButton currentUser relationship account = + if Mastodon.Helper.sameAccount account currentUser then + text "" + else + let + ( muteEvent, btnClasses, iconName, tooltip ) = + case relationship of + Nothing -> + ( NoOp + , "btn btn-default btn-mute btn-disabled" + , "question-sign" + , "Unknown relationship" + ) + + Just relationship -> + if relationship.muting then + ( Unmute account + , "btn btn-default btn-mute btn-primary" + , "volume-up" + , "Unmute" + ) + else + ( Mute account + , "btn btn-default btn-mute" + , "volume-off" + , "Mute" + ) + in + button [ class btnClasses, title tooltip, onClick muteEvent ] + [ Common.icon iconName ] + + +blockButton : CurrentUser -> CurrentUserRelation -> Account -> Html Msg +blockButton currentUser relationship account = + if Mastodon.Helper.sameAccount account currentUser then + text "" + else + let + ( blockEvent, btnClasses, iconName, tooltip ) = + case relationship of + Nothing -> + ( NoOp + , "btn btn-default btn-block btn-disabled" + , "question-sign" + , "Unknown relationship" + ) + + Just relationship -> + if relationship.blocking then + ( Unblock account + , "btn btn-default btn-block btn-primary" + , "ok-circle" + , "Unblock" + ) + else + ( Block account + , "btn btn-default btn-block" + , "ban-circle" + , "Block" + ) + in + button [ class btnClasses, title tooltip, onClick blockEvent ] + [ Common.icon iconName ] + + accountFollowView : CurrentUser -> Timeline Account @@ -157,16 +224,30 @@ accountView currentUser account relationship panelContent = ] [ div [ class "opacity-layer" ] [ followButton currentUser relationship account + , muteButton currentUser relationship account + , blockButton currentUser relationship account , Common.accountAvatarLink True account , span [ class "account-display-name" ] [ text account.display_name ] , span [ class "account-username" ] [ Common.accountLink True account , case relationship of Just relationship -> - if relationship.followed_by then - span [ class "badge followed-by" ] [ text "Follows you" ] - else - text "" + span [] + [ if relationship.followed_by then + span [ class "badge followed-by" ] [ text "Follows you" ] + else + text "" + , text " " + , if relationship.muting then + span [ class "badge muting" ] [ text "Muted" ] + else + text "" + , text " " + , if relationship.blocking then + span [ class "badge blocking" ] [ text "Blocked" ] + else + text "" + ] Nothing -> text "" diff --git a/src/View/AccountSelector.elm b/src/View/AccountSelector.elm index 4b3caa5..e039c41 100644 --- a/src/View/AccountSelector.elm +++ b/src/View/AccountSelector.elm @@ -80,7 +80,7 @@ accountSelectorView : Model -> Html Msg accountSelectorView model = div [ class "col-md-3 column" ] [ div [ class "panel panel-default" ] - [ closeablePanelheading "account-selector" "user" "Account selector" CloseAccountSelector + [ closeablePanelheading "account-selector" "user" "Account selector" (SetView LocalTimelineView) , contextualTimelineMenu model.currentView , ul [ class "list-group " ] <| List.map (accountIdentityView model.currentUser) model.clients diff --git a/src/View/App.elm b/src/View/App.elm index 0af6732..e557e7d 100644 --- a/src/View/App.elm +++ b/src/View/App.elm @@ -8,9 +8,11 @@ import Types exposing (..) import View.Account exposing (accountFollowView, accountTimelineView) import View.AccountSelector exposing (accountSelectorView) import View.Auth exposing (authView) +import View.Blocks exposing (blocksView) import View.Common as Common import View.Draft exposing (draftView) import View.Error exposing (errorsListView) +import View.Mutes exposing (mutesView) import View.Notification exposing (notificationListView) import View.Thread exposing (threadView) import View.Timeline exposing (contextualTimelineView, homeTimelineView) @@ -58,6 +60,12 @@ homepageView model = AccountSelectorView -> accountSelectorView model + MutesView -> + mutesView model + + BlocksView -> + blocksView model + AccountFollowersView account followers -> accountFollowView currentUser diff --git a/src/View/Blocks.elm b/src/View/Blocks.elm new file mode 100644 index 0000000..396e1c1 --- /dev/null +++ b/src/View/Blocks.elm @@ -0,0 +1,83 @@ +module View.Blocks exposing (blocksView) + +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) +import Html.Keyed as Keyed +import Mastodon.Helper exposing (..) +import Mastodon.Model exposing (..) +import Types exposing (..) +import View.Common as Common +import View.Events exposing (..) +import View.Timeline exposing (contextualTimelineMenu) + + +type alias CurrentUser = + Maybe Account + + +blockView : CurrentUser -> Account -> Html Msg +blockView currentUser account = + let + ( isCurrentUser, entryClass ) = + case currentUser of + Just currentUser -> + if sameAccount account currentUser then + ( True, "active" ) + else + ( False, "" ) + + Nothing -> + ( False, "" ) + in + li [ class <| "list-group-item status " ++ entryClass ] + [ div [ class "follow-entry" ] + [ Common.accountAvatarLink False account + , div [ class "userinfo" ] + [ strong [] + [ a + [ href account.url + , onClickWithPreventAndStop <| LoadAccount account.id + ] + [ text <| + if account.display_name /= "" then + account.display_name + else + account.username + ] + ] + , br [] [] + , text <| "@" ++ account.acct + ] + , button + [ class "btn btn-default btn-block btn-primary" + , title "Unblock" + , onClick <| Unblock account + ] + [ Common.icon "ok-circle" ] + ] + ] + + +blocksView : Model -> Html Msg +blocksView model = + let + keyedEntry account = + ( toString account.id + , blockView model.currentUser account + ) + + entries = + List.map keyedEntry model.blocks.entries + in + div [ class "col-md-3 column" ] + [ div [ class "panel panel-default" ] + [ Common.closeablePanelheading "blocks-timeline" "ban-circle" "Blocked accounts" (SetView LocalTimelineView) + , contextualTimelineMenu model.currentView + , if List.length model.blocks.entries == 0 then + p [ class "empty-timeline-text" ] [ text "Nobody's muted here." ] + else + Keyed.ul [ id "blocks-timeline", class "list-group timeline" ] <| + (entries ++ [ ( "load-more", Common.loadMoreBtn model.blocks ) ]) + ] + ] diff --git a/src/View/Mutes.elm b/src/View/Mutes.elm new file mode 100644 index 0000000..8ebecc3 --- /dev/null +++ b/src/View/Mutes.elm @@ -0,0 +1,83 @@ +module View.Mutes exposing (mutesView) + +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) +import Html.Keyed as Keyed +import Mastodon.Helper exposing (..) +import Mastodon.Model exposing (..) +import Types exposing (..) +import View.Common as Common +import View.Events exposing (..) +import View.Timeline exposing (contextualTimelineMenu) + + +type alias CurrentUser = + Maybe Account + + +muteView : CurrentUser -> Account -> Html Msg +muteView currentUser account = + let + ( isCurrentUser, entryClass ) = + case currentUser of + Just currentUser -> + if sameAccount account currentUser then + ( True, "active" ) + else + ( False, "" ) + + Nothing -> + ( False, "" ) + in + li [ class <| "list-group-item status " ++ entryClass ] + [ div [ class "follow-entry" ] + [ Common.accountAvatarLink False account + , div [ class "userinfo" ] + [ strong [] + [ a + [ href account.url + , onClickWithPreventAndStop <| LoadAccount account.id + ] + [ text <| + if account.display_name /= "" then + account.display_name + else + account.username + ] + ] + , br [] [] + , text <| "@" ++ account.acct + ] + , button + [ class "btn btn-default btn-mute btn-primary" + , title "Unmute" + , onClick <| Unmute account + ] + [ Common.icon "volume-up" ] + ] + ] + + +mutesView : Model -> Html Msg +mutesView model = + let + keyedEntry account = + ( toString account.id + , muteView model.currentUser account + ) + + entries = + List.map keyedEntry model.mutes.entries + in + div [ class "col-md-3 column" ] + [ div [ class "panel panel-default" ] + [ Common.closeablePanelheading "mutes-timeline" "volume-off" "Muted accounts" (SetView LocalTimelineView) + , contextualTimelineMenu model.currentView + , if (not model.mutes.loading && List.length model.mutes.entries == 0) then + p [ class "empty-timeline-text" ] [ text "Nobody's blocked here." ] + else + Keyed.ul [ id "mutes-timeline", class "list-group timeline" ] <| + (entries ++ [ ( "load-more", Common.loadMoreBtn model.mutes ) ]) + ] + ] diff --git a/src/View/Timeline.elm b/src/View/Timeline.elm index 22a45eb..316687d 100644 --- a/src/View/Timeline.elm +++ b/src/View/Timeline.elm @@ -81,6 +81,8 @@ contextualTimelineMenu currentView = [ btnView "Local timeline" "th-large" LocalTimelineView , btnView "Global timeline" "globe" GlobalTimelineView , btnView "Favorites" "star" FavoriteTimelineView + , btnView "Blocks" "ban-circle" BlocksView + , btnView "Mutes" "volume-off" MutesView , btnView "Accounts" "user" AccountSelectorView ]