Add part2, clean up part1 some
This commit is contained in:
309
advanced/part2/src/Page/Profile.elm
Normal file
309
advanced/part2/src/Page/Profile.elm
Normal file
@@ -0,0 +1,309 @@
|
||||
module Page.Profile exposing (Model, Msg, init, subscriptions, toSession, update, view)
|
||||
|
||||
{-| An Author's profile.
|
||||
-}
|
||||
|
||||
import Article.Feed as Feed exposing (ListConfig)
|
||||
import Article.FeedSources as FeedSources exposing (FeedSources, Source(..))
|
||||
import Author exposing (Author(..), FollowedAuthor, UnfollowedAuthor)
|
||||
import Avatar exposing (Avatar)
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
import Http
|
||||
import Loading
|
||||
import Log
|
||||
import Page
|
||||
import Profile exposing (Profile)
|
||||
import Session exposing (Session)
|
||||
import Task exposing (Task)
|
||||
import Time
|
||||
import Username exposing (Username)
|
||||
import Viewer exposing (Viewer)
|
||||
import Viewer.Cred as Cred exposing (Cred)
|
||||
|
||||
|
||||
|
||||
-- MODEL
|
||||
|
||||
|
||||
type alias Model =
|
||||
{ session : Session
|
||||
, timeZone : Time.Zone
|
||||
, errors : List String
|
||||
|
||||
-- Loaded independently from server
|
||||
, author : Status Author
|
||||
, feed : Status Feed.Model
|
||||
}
|
||||
|
||||
|
||||
type Status a
|
||||
= Loading Username
|
||||
| Loaded a
|
||||
| Failed Username
|
||||
|
||||
|
||||
init : Session -> Username -> ( Model, Cmd Msg )
|
||||
init session username =
|
||||
let
|
||||
maybeCred =
|
||||
Session.cred session
|
||||
in
|
||||
( { session = session
|
||||
, timeZone = Time.utc
|
||||
, errors = []
|
||||
, author = Loading username
|
||||
, feed = Loading username
|
||||
}
|
||||
, Cmd.batch
|
||||
[ Author.fetch username maybeCred
|
||||
|> Http.toTask
|
||||
|> Task.mapError (Tuple.pair username)
|
||||
|> Task.attempt CompletedAuthorLoad
|
||||
, defaultFeedSources username
|
||||
|> Feed.init session
|
||||
|> Task.mapError (Tuple.pair username)
|
||||
|> Task.attempt CompletedFeedLoad
|
||||
, Task.perform GotTimeZone Time.here
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
|
||||
-- VIEW
|
||||
|
||||
|
||||
view : Model -> { title : String, content : Html Msg }
|
||||
view model =
|
||||
let
|
||||
title =
|
||||
case model.author of
|
||||
Loaded (IsViewer _ _) ->
|
||||
myProfileTitle
|
||||
|
||||
Loaded ((IsFollowing followedAuthor) as author) ->
|
||||
titleForOther (Author.username author)
|
||||
|
||||
Loaded ((IsNotFollowing unfollowedAuthor) as author) ->
|
||||
titleForOther (Author.username author)
|
||||
|
||||
Loading username ->
|
||||
if Just username == Maybe.map .username (Session.cred model.session) then
|
||||
myProfileTitle
|
||||
|
||||
else
|
||||
defaultTitle
|
||||
|
||||
Failed username ->
|
||||
-- We can't follow if it hasn't finished loading yet
|
||||
if Just username == Maybe.map .username (Session.cred model.session) then
|
||||
myProfileTitle
|
||||
|
||||
else
|
||||
defaultTitle
|
||||
in
|
||||
{ title = title
|
||||
, content =
|
||||
case model.author of
|
||||
Loaded author ->
|
||||
let
|
||||
profile =
|
||||
Author.profile author
|
||||
|
||||
username =
|
||||
Author.username author
|
||||
|
||||
followButton =
|
||||
case Session.cred model.session of
|
||||
Just cred ->
|
||||
case author of
|
||||
IsViewer _ _ ->
|
||||
-- We can't follow ourselves!
|
||||
text ""
|
||||
|
||||
IsFollowing followedAuthor ->
|
||||
Author.unfollowButton (ClickedUnfollow cred) followedAuthor
|
||||
|
||||
IsNotFollowing unfollowedAuthor ->
|
||||
Author.followButton (ClickedFollow cred) unfollowedAuthor
|
||||
|
||||
Nothing ->
|
||||
-- We can't follow if we're logged out
|
||||
text ""
|
||||
in
|
||||
div [ class "profile-page" ]
|
||||
[ Page.viewErrors ClickedDismissErrors model.errors
|
||||
, div [ class "user-info" ]
|
||||
[ div [ class "container" ]
|
||||
[ div [ class "row" ]
|
||||
[ div [ class "col-xs-12 col-md-10 offset-md-1" ]
|
||||
[ img [ class "user-img", Avatar.src (Profile.avatar profile) ] []
|
||||
, h4 [] [ Username.toHtml username ]
|
||||
, p [] [ text (Maybe.withDefault "" (Profile.bio profile)) ]
|
||||
, followButton
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
, case model.feed of
|
||||
Loaded feed ->
|
||||
div [ class "container" ]
|
||||
[ div [ class "row" ] [ viewFeed model.timeZone feed ] ]
|
||||
|
||||
Loading _ ->
|
||||
Loading.icon
|
||||
|
||||
Failed _ ->
|
||||
Loading.error "feed"
|
||||
]
|
||||
|
||||
Loading _ ->
|
||||
Loading.icon
|
||||
|
||||
Failed _ ->
|
||||
Loading.error "profile"
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- PAGE TITLE
|
||||
|
||||
|
||||
titleForOther : Username -> String
|
||||
titleForOther otherUsername =
|
||||
"Profile — " ++ Username.toString otherUsername
|
||||
|
||||
|
||||
myProfileTitle : String
|
||||
myProfileTitle =
|
||||
"My Profile"
|
||||
|
||||
|
||||
defaultTitle : String
|
||||
defaultTitle =
|
||||
"Profile"
|
||||
|
||||
|
||||
|
||||
-- FEED
|
||||
|
||||
|
||||
viewFeed : Time.Zone -> Feed.Model -> Html Msg
|
||||
viewFeed timeZone feed =
|
||||
div [ class "col-xs-12 col-md-10 offset-md-1" ] <|
|
||||
div [ class "articles-toggle" ]
|
||||
[ Feed.viewFeedSources feed |> Html.map GotFeedMsg ]
|
||||
:: (Feed.viewArticles timeZone feed |> List.map (Html.map GotFeedMsg))
|
||||
|
||||
|
||||
|
||||
-- UPDATE
|
||||
|
||||
|
||||
type Msg
|
||||
= ClickedDismissErrors
|
||||
| ClickedFollow Cred UnfollowedAuthor
|
||||
| ClickedUnfollow Cred FollowedAuthor
|
||||
| CompletedFollowChange (Result Http.Error Author)
|
||||
| CompletedAuthorLoad (Result ( Username, Http.Error ) Author)
|
||||
| CompletedFeedLoad (Result ( Username, Http.Error ) Feed.Model)
|
||||
| GotTimeZone Time.Zone
|
||||
| GotFeedMsg Feed.Msg
|
||||
| GotSession Session
|
||||
|
||||
|
||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||
update msg model =
|
||||
case msg of
|
||||
ClickedDismissErrors ->
|
||||
( { model | errors = [] }, Cmd.none )
|
||||
|
||||
ClickedUnfollow cred followedAuthor ->
|
||||
( model
|
||||
, Author.requestUnfollow followedAuthor cred
|
||||
|> Http.send CompletedFollowChange
|
||||
)
|
||||
|
||||
ClickedFollow cred unfollowedAuthor ->
|
||||
( model
|
||||
, Author.requestFollow unfollowedAuthor cred
|
||||
|> Http.send CompletedFollowChange
|
||||
)
|
||||
|
||||
CompletedFollowChange (Ok newAuthor) ->
|
||||
( { model | author = Loaded newAuthor }
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
CompletedFollowChange (Err error) ->
|
||||
( model
|
||||
, Log.error
|
||||
)
|
||||
|
||||
CompletedAuthorLoad (Ok author) ->
|
||||
( { model | author = Loaded author }, Cmd.none )
|
||||
|
||||
CompletedAuthorLoad (Err ( username, err )) ->
|
||||
( { model | author = Failed username }
|
||||
, Log.error
|
||||
)
|
||||
|
||||
CompletedFeedLoad (Ok feed) ->
|
||||
( { model | feed = Loaded feed }
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
CompletedFeedLoad (Err ( username, err )) ->
|
||||
( { model | feed = Failed username }
|
||||
, Log.error
|
||||
)
|
||||
|
||||
GotFeedMsg subMsg ->
|
||||
case model.feed of
|
||||
Loaded feed ->
|
||||
let
|
||||
( newFeed, subCmd ) =
|
||||
Feed.update (Session.cred model.session) subMsg feed
|
||||
in
|
||||
( { model | feed = Loaded newFeed }
|
||||
, Cmd.map GotFeedMsg subCmd
|
||||
)
|
||||
|
||||
Loading _ ->
|
||||
( model, Log.error )
|
||||
|
||||
Failed _ ->
|
||||
( model, Log.error )
|
||||
|
||||
GotTimeZone tz ->
|
||||
( { model | timeZone = tz }, Cmd.none )
|
||||
|
||||
GotSession session ->
|
||||
( { model | session = session }, Cmd.none )
|
||||
|
||||
|
||||
|
||||
-- SUBSCRIPTIONS
|
||||
|
||||
|
||||
subscriptions : Model -> Sub Msg
|
||||
subscriptions model =
|
||||
Session.changes GotSession (Session.navKey model.session)
|
||||
|
||||
|
||||
|
||||
-- EXPORT
|
||||
|
||||
|
||||
toSession : Model -> Session
|
||||
toSession model =
|
||||
model.session
|
||||
|
||||
|
||||
|
||||
-- INTERNAL
|
||||
|
||||
|
||||
defaultFeedSources : Username -> FeedSources
|
||||
defaultFeedSources username =
|
||||
FeedSources.fromLists (AuthorFeed username) [ FavoritedFeed username ]
|
||||
Reference in New Issue
Block a user