Add part5

This commit is contained in:
Richard Feldman
2018-08-11 14:42:13 -04:00
parent 032194c6be
commit 42f4182e1b
55 changed files with 5997 additions and 0 deletions

View File

@@ -0,0 +1,346 @@
module Page.Profile exposing (Model, Msg, init, subscriptions, toSession, update, view)
{-| An Author's profile.
-}
import Article.Feed as Feed
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 Route
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
| LoadingSlowly 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
, Task.perform (\_ -> PassedSlowLoadThreshold) Loading.slowThreshold
]
)
-- 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 ->
titleForMe (Session.cred model.session) username
LoadingSlowly username ->
titleForMe (Session.cred model.session) username
Failed username ->
titleForMe (Session.cred model.session) username
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 _ ->
text ""
LoadingSlowly _ ->
Loading.icon
Failed _ ->
Loading.error "feed"
]
Loading _ ->
text ""
LoadingSlowly _ ->
Loading.icon
Failed _ ->
Loading.error "profile"
}
-- PAGE TITLE
titleForOther : Username -> String
titleForOther otherUsername =
"Profile " ++ Username.toString otherUsername
titleForMe : Maybe Cred -> Username -> String
titleForMe maybeCred username =
case maybeCred of
Just cred ->
if username == Cred.username cred then
myProfileTitle
else
defaultTitle
Nothing ->
defaultTitle
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
| PassedSlowLoadThreshold
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 )
LoadingSlowly _ ->
( model, Log.error )
Failed _ ->
( model, Log.error )
GotTimeZone tz ->
( { model | timeZone = tz }, Cmd.none )
GotSession session ->
( { model | session = session }
, Route.replaceUrl (Session.navKey session) Route.Home
)
PassedSlowLoadThreshold ->
let
-- If any data is still Loading, change it to LoadingSlowly
-- so `view` knows to render a spinner.
feed =
case model.feed of
Loading username ->
LoadingSlowly username
other ->
other
in
( { model | feed = feed }, 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 ]