From 4d8757ad7c0a72ada1ec764a51feb0e6cb4088a6 Mon Sep 17 00:00:00 2001 From: Nicolas Perriault Date: Sat, 13 May 2017 15:55:46 +0200 Subject: [PATCH] Fix #168: List user favorites. - Favorite timeline model and commands. - 4th column navigation bar. - Use of SetView for account selector. - Move timeline views to their own view module. - Handle favorite timeline updates. - Integrate contextual menu in account selector view. --- public/style.css | 13 ++++- src/Command.elm | 18 +++++++ src/Init.elm | 2 +- src/Mastodon/ApiUrl.elm | 6 +++ src/Types.elm | 17 ++++--- src/Update/Main.elm | 57 +++++++++++----------- src/Update/Mastodon.elm | 12 ++++- src/Update/Timeline.elm | 72 ++++++++++++++------------- src/View/AccountSelector.elm | 2 + src/View/App.elm | 88 ++++++++++----------------------- src/View/Draft.elm | 2 +- src/View/Settings.elm | 22 --------- src/View/Status.elm | 8 +-- src/View/Timeline.elm | 94 ++++++++++++++++++++++++++++++++++++ 14 files changed, 252 insertions(+), 161 deletions(-) delete mode 100644 src/View/Settings.elm create mode 100644 src/View/Timeline.elm diff --git a/public/style.css b/public/style.css index b96c2a3..733a387 100644 --- a/public/style.css +++ b/public/style.css @@ -46,6 +46,10 @@ body { min-height: 0; } +.panel-default .panel-heading { + background-color: inherit; +} + .timeline { overflow-y: auto; overflow-x: hidden; @@ -54,6 +58,13 @@ body { overflow-y: scroll; } +#favorite-timeline, +#local-timeline, +#global-timeline { + max-height: calc(100vh - 102px); +} + + li.load-more { cursor: wait; } @@ -119,7 +130,7 @@ li.load-more { padding-top: 6px; } -.notifications-panel .btn-group-justified .btn { +.panel .btn-group-justified .btn { border-radius: 0; border-bottom: 0; outline: 0; diff --git a/src/Command.elm b/src/Command.elm index 3859962..f512716 100644 --- a/src/Command.elm +++ b/src/Command.elm @@ -14,6 +14,7 @@ module Command , loadLocalTimeline , loadGlobalTimeline , loadAccountTimeline + , loadFavoriteTimeline , loadNextTimeline , loadRelationships , loadThread @@ -317,6 +318,20 @@ loadAccountTimeline client accountId url = Cmd.none +loadFavoriteTimeline : Maybe Client -> Maybe String -> Cmd Msg +loadFavoriteTimeline client url = + case client of + Just client -> + HttpBuilder.get (Maybe.withDefault ApiUrl.favouriteTimeline url) + |> withClient client + |> withBodyDecoder (Decode.list statusDecoder) + |> withQueryParams [ ( "limit", "60" ) ] + |> send (MastodonEvent << FavoriteTimeline (url /= Nothing)) + + Nothing -> + Cmd.none + + loadTimelines : Maybe Client -> Cmd Msg loadTimelines client = Cmd.batch @@ -342,6 +357,9 @@ loadNextTimeline client currentView id next = "global-timeline" -> loadGlobalTimeline client (Just next) + "favorite-timeline" -> + loadFavoriteTimeline client (Just next) + "account-timeline" -> case currentView of AccountView account -> diff --git a/src/Init.elm b/src/Init.elm index cf3fa2c..68533de 100644 --- a/src/Init.elm +++ b/src/Init.elm @@ -17,6 +17,7 @@ init { registration, clients } location = , homeTimeline = Update.Timeline.empty "home-timeline" , localTimeline = Update.Timeline.empty "local-timeline" , globalTimeline = Update.Timeline.empty "global-timeline" + , favoriteTimeline = Update.Timeline.empty "favorite-timeline" , accountTimeline = Update.Timeline.empty "account-timeline" , accountFollowers = Update.Timeline.empty "account-followers" , accountFollowing = Update.Timeline.empty "account-following" @@ -26,7 +27,6 @@ init { registration, clients } location = , draft = Update.Draft.empty , errors = [] , location = location - , useGlobalTimeline = False , viewer = Nothing , currentView = LocalTimelineView , currentUser = Nothing diff --git a/src/Mastodon/ApiUrl.elm b/src/Mastodon/ApiUrl.elm index af4f13f..fc9e635 100644 --- a/src/Mastodon/ApiUrl.elm +++ b/src/Mastodon/ApiUrl.elm @@ -11,6 +11,7 @@ module Mastodon.ApiUrl , status , homeTimeline , publicTimeline + , favouriteTimeline , notifications , relationships , statuses @@ -107,6 +108,11 @@ accountTimeline id = (account id) ++ "/statuses" +favouriteTimeline : String +favouriteTimeline = + apiPrefix ++ "/favourites" + + notifications : String notifications = apiPrefix ++ "/notifications" diff --git a/src/Types.elm b/src/Types.elm index 225e357..5d3224f 100644 --- a/src/Types.elm +++ b/src/Types.elm @@ -56,14 +56,15 @@ type MastodonMsg | CurrentUser (MastodonResult Account) | FavoriteAdded (MastodonResult Status) | FavoriteRemoved (MastodonResult Status) + | FavoriteTimeline Bool (MastodonResult (List Status)) | GlobalTimeline Bool (MastodonResult (List Status)) + | HomeTimeline Bool (MastodonResult (List Status)) | LocalTimeline Bool (MastodonResult (List Status)) | Notifications Bool (MastodonResult (List Notification)) | Reblogged (MastodonResult Status) | StatusDeleted (MastodonResult Int) | StatusPosted (MastodonResult Status) | Unreblogged (MastodonResult Status) - | HomeTimeline Bool (MastodonResult (List Status)) type WebSocketMsg @@ -73,7 +74,7 @@ type WebSocketMsg type Msg - = AddFavorite Int + = AddFavorite Status | AskConfirm String Msg Msg | ClearError Int | CloseAccount @@ -91,19 +92,18 @@ type Msg | MastodonEvent MastodonMsg | NoOp | OpenThread Status - | OpenAccountSelector - | ReblogStatus Int + | ReblogStatus Status | Register - | RemoveFavorite Int + | RemoveFavorite Status | ScrollColumn ScrollDirection String | ServerChange String + | SetView CurrentView | SubmitDraft | SwitchClient Client | Tick Time | UnfollowAccount Int | UrlChange Navigation.Location - | UseGlobalTimeline Bool - | UnreblogStatus Int + | UnreblogStatus Status | ViewAccountFollowing Account | ViewAccountFollowers Account | ViewAccountStatuses Account @@ -124,6 +124,7 @@ type CurrentView | AccountFollowingView Account (Timeline Account) | AccountView Account | AccountSelectorView + | FavoriteTimelineView | GlobalTimelineView | LocalTimelineView | ThreadView Thread @@ -198,6 +199,7 @@ type alias Model = , homeTimeline : Timeline Status , localTimeline : Timeline Status , globalTimeline : Timeline Status + , favoriteTimeline : Timeline Status , accountTimeline : Timeline Status , accountFollowers : Timeline Account , accountFollowing : Timeline Account @@ -207,7 +209,6 @@ type alias Model = , draft : Draft , errors : List ErrorNotification , location : Navigation.Location - , useGlobalTimeline : Bool , viewer : Maybe Viewer , currentUser : Maybe Account , currentView : CurrentView diff --git a/src/Update/Main.elm b/src/Update/Main.elm index 00afad2..56c8e8c 100644 --- a/src/Update/Main.elm +++ b/src/Update/Main.elm @@ -54,6 +54,18 @@ update msg model = Confirmed onConfirm -> update onConfirm { model | confirm = Nothing } + SetView view -> + case view of + AccountSelectorView -> + { model | currentView = view, server = "" } ! [] + + FavoriteTimelineView -> + { model | currentView = view } + ! [ Command.loadFavoriteTimeline (List.head model.clients) Nothing ] + + _ -> + { model | currentView = view } ! [] + SwitchClient client -> let newClients = @@ -64,13 +76,14 @@ update msg model = , homeTimeline = Update.Timeline.empty "home-timeline" , localTimeline = Update.Timeline.empty "local-timeline" , globalTimeline = Update.Timeline.empty "global-timeline" + , favoriteTimeline = Update.Timeline.empty "favorite-timeline" , accountTimeline = Update.Timeline.empty "account-timeline" , accountFollowers = Update.Timeline.empty "account-followers" , accountFollowing = Update.Timeline.empty "account-following" , notifications = Update.Timeline.empty "notifications" , accountRelationships = [] , accountRelationship = Nothing - , currentView = Update.Timeline.preferred model + , currentView = LocalTimelineView } ! [ Command.loadUserAccount <| Just client , Command.loadTimelines <| Just client @@ -86,7 +99,7 @@ update msg model = in { model | clients = newClients - , currentView = Update.Timeline.preferred model + , currentView = LocalTimelineView } ! [ Command.saveClients newClients , Command.loadUserAccount newClient @@ -119,11 +132,8 @@ update msg model = OpenThread status -> model ! [ Command.loadThread (List.head model.clients) status ] - OpenAccountSelector -> - { model | currentView = AccountSelectorView, server = "" } ! [] - CloseThread -> - { model | currentView = Update.Timeline.preferred model } ! [] + { model | currentView = LocalTimelineView } ! [] FollowAccount id -> model ! [ Command.follow (List.head model.clients) id ] @@ -134,21 +144,21 @@ update msg model = DeleteStatus id -> model ! [ Command.deleteStatus (List.head model.clients) id ] - ReblogStatus id -> - Update.Timeline.processReblog id True model - ! [ Command.reblogStatus (List.head model.clients) id ] + ReblogStatus status -> + Update.Timeline.processReblog status True model + ! [ Command.reblogStatus (List.head model.clients) status.id ] - UnreblogStatus id -> - Update.Timeline.processReblog id False model - ! [ Command.unreblogStatus (List.head model.clients) id ] + UnreblogStatus status -> + Update.Timeline.processReblog status False model + ! [ Command.unreblogStatus (List.head model.clients) status.id ] - AddFavorite id -> - Update.Timeline.processFavourite id True model - ! [ Command.favouriteStatus (List.head model.clients) id ] + AddFavorite status -> + Update.Timeline.processFavourite status True model + ! [ Command.favouriteStatus (List.head model.clients) status.id ] - RemoveFavorite id -> - Update.Timeline.processFavourite id False model - ! [ Command.unfavouriteStatus (List.head model.clients) id ] + RemoveFavorite status -> + Update.Timeline.processFavourite status False model + ! [ Command.unfavouriteStatus (List.head model.clients) status.id ] DraftEvent draftMsg -> case model.currentUser of @@ -199,16 +209,9 @@ update msg model = ViewAccountStatuses account -> { model | currentView = AccountView account } ! [] - UseGlobalTimeline flag -> - let - newModel = - { model | useGlobalTimeline = flag } - in - { newModel | currentView = Update.Timeline.preferred newModel } ! [] - CloseAccount -> { model - | currentView = Update.Timeline.preferred model + | currentView = LocalTimelineView , accountTimeline = Update.Timeline.empty "account-timeline" , accountFollowing = Update.Timeline.empty "account-following" , accountFollowers = Update.Timeline.empty "account-followers" @@ -216,7 +219,7 @@ update msg model = ! [] CloseAccountSelector -> - { model | currentView = Update.Timeline.preferred model } ! [] + { model | currentView = LocalTimelineView } ! [] FilterNotifications filter -> { model | notificationFilter = filter } ! [] diff --git a/src/Update/Mastodon.elm b/src/Update/Mastodon.elm index 86e21d6..be9dba5 100644 --- a/src/Update/Mastodon.elm +++ b/src/Update/Mastodon.elm @@ -82,7 +82,7 @@ update msg model = Err error -> { model - | currentView = Update.Timeline.preferred model + | currentView = LocalTimelineView , errors = addErrorNotification (errorText error) model } ! [] @@ -149,6 +149,14 @@ update msg model = Err error -> { model | errors = addErrorNotification (errorText error) model } ! [] + FavoriteTimeline append result -> + case result of + Ok { decoded, links } -> + { model | favoriteTimeline = Update.Timeline.update append decoded links model.favoriteTimeline } ! [] + + Err error -> + { model | errors = addErrorNotification (errorText error) model } ! [] + Reblogged result -> case result of Ok _ -> @@ -200,7 +208,7 @@ update msg model = Err error -> { model - | currentView = Update.Timeline.preferred model + | currentView = LocalTimelineView , errors = addErrorNotification (errorText error) model } ! [] diff --git a/src/Update/Timeline.elm b/src/Update/Timeline.elm index 3a75445..5b0573a 100644 --- a/src/Update/Timeline.elm +++ b/src/Update/Timeline.elm @@ -4,7 +4,6 @@ module Update.Timeline , deleteStatus , empty , markAsLoading - , preferred , prepend , processReblog , processFavourite @@ -25,7 +24,7 @@ deleteStatusFromCurrentView id model = ThreadView thread -> if thread.status.id == id then -- the current thread status as been deleted, close it - preferred model + LocalTimelineView else let update statuses = @@ -49,6 +48,7 @@ deleteStatusFromAllTimelines id model = | homeTimeline = deleteStatus id model.homeTimeline , localTimeline = deleteStatus id model.localTimeline , globalTimeline = deleteStatus id model.globalTimeline + , favoriteTimeline = deleteStatus id model.favoriteTimeline , accountTimeline = deleteStatus id model.accountTimeline , notifications = deleteStatusFromNotifications id model.notifications , currentView = deleteStatusFromCurrentView id model @@ -104,6 +104,9 @@ markAsLoading loading id model = "global-timeline" -> { model | globalTimeline = mark model.globalTimeline } + "favorite-timeline" -> + { model | favoriteTimeline = mark model.favoriteTimeline } + "account-timeline" -> case model.currentView of AccountView account -> @@ -116,47 +119,49 @@ markAsLoading loading id model = model -preferred : Model -> CurrentView -preferred model = - if model.useGlobalTimeline then - GlobalTimelineView - else - LocalTimelineView - - prepend : a -> Timeline a -> Timeline a prepend entry timeline = { timeline | entries = entry :: timeline.entries } -processFavourite : Int -> Bool -> Model -> Model -processFavourite statusId flag model = - updateWithBoolFlag statusId - flag - (\s -> - { s - | favourited = Just flag - , favourites_count = - if flag then - s.favourites_count + 1 - else if s.favourites_count > 0 then - s.favourites_count - 1 - else - 0 - } - ) - model +processFavourite : Status -> Bool -> Model -> Model +processFavourite status added model = + let + favoriteTimeline = + if added then + prepend status model.favoriteTimeline + else + deleteStatus status.id model.favoriteTimeline + + newModel = + { model | favoriteTimeline = favoriteTimeline } + in + updateWithBoolFlag status.id + added + (\s -> + { s + | favourited = Just added + , favourites_count = + if added then + s.favourites_count + 1 + else if s.favourites_count > 0 then + s.favourites_count - 1 + else + 0 + } + ) + newModel -processReblog : Int -> Bool -> Model -> Model -processReblog statusId flag model = - updateWithBoolFlag statusId - flag +processReblog : Status -> Bool -> Model -> Model +processReblog status added model = + updateWithBoolFlag status.id + added (\s -> { s - | reblogged = Just flag + | reblogged = Just added , reblogs_count = - if flag then + if added then s.reblogs_count + 1 else if s.reblogs_count > 0 then s.reblogs_count - 1 @@ -208,6 +213,7 @@ updateWithBoolFlag statusId flag statusUpdater model = , accountTimeline = updateTimeline updateStatus model.accountTimeline , localTimeline = updateTimeline updateStatus model.localTimeline , globalTimeline = updateTimeline updateStatus model.globalTimeline + , favoriteTimeline = updateTimeline updateStatus model.favoriteTimeline , notifications = updateTimeline updateNotification model.notifications , currentView = case model.currentView of diff --git a/src/View/AccountSelector.elm b/src/View/AccountSelector.elm index 6ea1edb..4b3caa5 100644 --- a/src/View/AccountSelector.elm +++ b/src/View/AccountSelector.elm @@ -9,6 +9,7 @@ import String.Extra exposing (replace) import Types exposing (..) import View.Auth exposing (authForm) import View.Common exposing (..) +import View.Timeline exposing (contextualTimelineMenu) type alias CurrentUser = @@ -80,6 +81,7 @@ accountSelectorView model = div [ class "col-md-3 column" ] [ div [ class "panel panel-default" ] [ closeablePanelheading "account-selector" "user" "Account selector" CloseAccountSelector + , contextualTimelineMenu model.currentView , ul [ class "list-group " ] <| List.map (accountIdentityView model.currentUser) model.clients , div [ class "panel-body" ] diff --git a/src/View/App.elm b/src/View/App.elm index ddcbbb4..0af6732 100644 --- a/src/View/App.elm +++ b/src/View/App.elm @@ -1,9 +1,8 @@ module View.App exposing (view) import Html exposing (..) -import Html.Keyed as Keyed -import Html.Lazy as Lazy import Html.Attributes exposing (..) +import Html.Lazy as Lazy import Mastodon.Model exposing (..) import Types exposing (..) import View.Account exposing (accountFollowView, accountTimelineView) @@ -12,11 +11,9 @@ import View.Auth exposing (authView) import View.Common as Common import View.Draft exposing (draftView) import View.Error exposing (errorsListView) -import View.Events exposing (..) import View.Notification exposing (notificationListView) -import View.Settings exposing (settingsView) -import View.Status exposing (statusView, statusActionsView, statusEntryView) import View.Thread exposing (threadView) +import View.Timeline exposing (contextualTimelineView, homeTimelineView) import View.Viewer exposing (viewerView) @@ -28,61 +25,10 @@ type alias CurrentUserRelation = Maybe Relationship -timelineView : ( String, String, CurrentUser, Timeline Status ) -> Html Msg -timelineView ( label, iconName, currentUser, timeline ) = - let - keyedEntry status = - ( toString id, statusEntryView timeline.id "" currentUser status ) - - entries = - List.map keyedEntry timeline.entries - in - div [ class "col-md-3 column" ] - [ div [ class "panel panel-default" ] - [ a - [ href "", onClickWithPreventAndStop <| ScrollColumn ScrollTop timeline.id ] - [ div [ class "panel-heading" ] [ Common.icon iconName, text label ] ] - , Keyed.ul [ id timeline.id, class "list-group timeline" ] <| - (entries ++ [ ( "load-more", Common.loadMoreBtn timeline ) ]) - ] - ] - - -homeTimelineView : CurrentUser -> Timeline Status -> Html Msg -homeTimelineView currentUser timeline = - Lazy.lazy timelineView - ( "Home timeline" - , "home" - , currentUser - , timeline - ) - - -localTimelineView : CurrentUser -> Timeline Status -> Html Msg -localTimelineView currentUser timeline = - Lazy.lazy timelineView - ( "Local timeline" - , "th-large" - , currentUser - , timeline - ) - - -globalTimelineView : CurrentUser -> Timeline Status -> Html Msg -globalTimelineView currentUser timeline = - Lazy.lazy timelineView - ( "Global timeline" - , "globe" - , currentUser - , timeline - ) - - sidebarView : Model -> Html Msg sidebarView model = div [ class "col-md-3 column" ] [ Lazy.lazy draftView model - , Lazy.lazy settingsView model ] @@ -102,12 +48,6 @@ homepageView model = model.notificationFilter model.notifications , case model.currentView of - LocalTimelineView -> - localTimelineView currentUser model.localTimeline - - GlobalTimelineView -> - globalTimelineView currentUser model.globalTimeline - AccountView account -> accountTimelineView currentUser @@ -136,6 +76,30 @@ homepageView model = ThreadView thread -> threadView currentUser thread + + LocalTimelineView -> + contextualTimelineView + LocalTimelineView + "Local timeline" + "th-large" + currentUser + model.localTimeline + + GlobalTimelineView -> + contextualTimelineView + GlobalTimelineView + "Global timeline" + "globe" + currentUser + model.globalTimeline + + FavoriteTimelineView -> + contextualTimelineView + FavoriteTimelineView + "Favorites" + "star" + currentUser + model.favoriteTimeline ] diff --git a/src/View/Draft.elm b/src/View/Draft.elm index a32e205..59ce011 100644 --- a/src/View/Draft.elm +++ b/src/View/Draft.elm @@ -81,7 +81,7 @@ currentUserView currentUser = [ Common.accountLink False currentUser , span [] [ text " (" - , a [ href "", onClickWithPreventAndStop <| OpenAccountSelector ] + , a [ href "", onClickWithPreventAndStop <| SetView AccountSelectorView ] [ text "switch account" ] , text ")" ] diff --git a/src/View/Settings.elm b/src/View/Settings.elm deleted file mode 100644 index 12d5417..0000000 --- a/src/View/Settings.elm +++ /dev/null @@ -1,22 +0,0 @@ -module View.Settings exposing (settingsView) - -import Html exposing (..) -import Html.Attributes exposing (..) -import Html.Events exposing (..) -import Types exposing (..) -import View.Common as Common - - -settingsView : Model -> Html Msg -settingsView model = - div [ class "panel panel-default options" ] - [ div [ class "panel-heading" ] [ Common.icon "cog", text "options" ] - , div [ class "panel-body" ] - [ div [ class "checkbox" ] - [ label [] - [ input [ type_ "checkbox", onCheck UseGlobalTimeline ] [] - , text " 4th column renders the global timeline" - ] - ] - ] - ] diff --git a/src/View/Status.elm b/src/View/Status.elm index 0b410ec..7117538 100644 --- a/src/View/Status.elm +++ b/src/View/Status.elm @@ -96,18 +96,18 @@ statusActionsView status currentUser = ( reblogClasses, reblogEvent ) = case status.reblogged of Just True -> - ( baseBtnClasses ++ " reblogged", UnreblogStatus sourceStatus.id ) + ( baseBtnClasses ++ " reblogged", UnreblogStatus sourceStatus ) _ -> - ( baseBtnClasses, ReblogStatus sourceStatus.id ) + ( baseBtnClasses, ReblogStatus sourceStatus ) ( favClasses, favEvent ) = case status.favourited of Just True -> - ( baseBtnClasses ++ " favourited", RemoveFavorite sourceStatus.id ) + ( baseBtnClasses ++ " favourited", RemoveFavorite sourceStatus ) _ -> - ( baseBtnClasses, AddFavorite sourceStatus.id ) + ( baseBtnClasses, AddFavorite sourceStatus ) statusDate = Date.fromString status.created_at diff --git a/src/View/Timeline.elm b/src/View/Timeline.elm new file mode 100644 index 0000000..401870d --- /dev/null +++ b/src/View/Timeline.elm @@ -0,0 +1,94 @@ +module View.Timeline + exposing + ( contextualTimelineView + , contextualTimelineMenu + , homeTimelineView + ) + +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) +import Html.Keyed as Keyed +import Html.Lazy as Lazy +import Mastodon.Model exposing (..) +import Types exposing (..) +import View.Common as Common +import View.Events exposing (..) +import View.Status exposing (statusView, statusActionsView, statusEntryView) + + +type alias CurrentUser = + Account + + +type alias CurrentUserRelation = + Maybe Relationship + + +topScrollableColumn : ( String, String, String ) -> Html Msg -> Html Msg +topScrollableColumn ( label, iconName, timelineId ) content = + div [ class "col-md-3 column" ] + [ div [ class "panel panel-default" ] + [ a + [ href "", onClickWithPreventAndStop <| ScrollColumn ScrollTop timelineId ] + [ div [ class "panel-heading" ] [ Common.icon iconName, text label ] ] + , content + ] + ] + + +timelineView : CurrentUser -> Timeline Status -> Html Msg +timelineView currentUser timeline = + let + keyedEntry status = + ( toString id, statusEntryView timeline.id "" currentUser status ) + + entries = + List.map keyedEntry timeline.entries + in + Keyed.ul [ id timeline.id, class "list-group timeline" ] <| + (entries ++ [ ( "load-more", Common.loadMoreBtn timeline ) ]) + + +homeTimelineView : CurrentUser -> Timeline Status -> Html Msg +homeTimelineView currentUser timeline = + Lazy.lazy2 topScrollableColumn + ( "Home timeline" + , "home" + , timeline.id + ) + (timelineView currentUser timeline) + + +contextualTimelineMenu : CurrentView -> Html Msg +contextualTimelineMenu currentView = + let + btnView tooltip iconName view = + button + [ class <| + "btn " + ++ (if currentView == view then + "btn-primary active" + else + "btn-default" + ) + , onClick <| SetView view + , Html.Attributes.title tooltip + ] + [ Common.icon iconName ] + in + Common.justifiedButtonGroup "" + [ btnView "Local timeline" "th-large" LocalTimelineView + , btnView "Global timeline" "globe" GlobalTimelineView + , btnView "Favorites" "star" FavoriteTimelineView + , btnView "Accounts" "user" AccountSelectorView + ] + + +contextualTimelineView : CurrentView -> String -> String -> CurrentUser -> Timeline Status -> Html Msg +contextualTimelineView currentView title iconName currentUser timeline = + div [] + [ contextualTimelineMenu currentView + , timelineView currentUser timeline + ] + |> Lazy.lazy2 topScrollableColumn ( title, iconName, timeline.id )