Fix #152: Multiple accounts. (#153)

* Update model to store multiple clients.

* Delete tmp registration data after client creation.

* Add minimal account selector view

* Update clients so they can have an account attached.

* List clients in the account selector.

* List accounts in the account selector view.

* It works™.

* Minor CSS fix.

* Reset server value when switching account.

* Fix empty black screen on reauth with new client format.

* Fix typo.

[skip-ci]
This commit is contained in:
Nicolas Perriault 2017-05-09 18:43:12 +02:00 committed by GitHub
parent 51a802f096
commit 7a053b9fa0
17 changed files with 331 additions and 108 deletions

View File

@ -17,20 +17,35 @@
<body> <body>
<script src="app.js"></script> <script src="app.js"></script>
<script> <script>
// Note: this is a transitional upgrade to new client storage format, which
// is now now a list of clients, which all require an "account" property set.
const oldClient = JSON.parse(localStorage.getItem("tooty.client"));
const defaultClients = oldClient ? [oldClient] : [];
const clients = (JSON.parse(localStorage.getItem("tooty.clients")) || defaultClients)
.map(client => {
if (!client.hasOwnProperty("account")) {
client.account = null;
}
return client;
});
const app = Elm.Main.fullscreen({ const app = Elm.Main.fullscreen({
client: JSON.parse(localStorage.getItem("tooty.client")), clients: clients,
registration: JSON.parse(localStorage.getItem("tooty.registration")) registration: JSON.parse(localStorage.getItem("tooty.registration"))
}); });
app.ports.saveClient.subscribe(json => { app.ports.saveClients.subscribe(json => {
localStorage.setItem("tooty.client", json); localStorage.setItem("tooty.clients", json);
}); });
app.ports.saveRegistration.subscribe(json => { app.ports.saveRegistration.subscribe(json => {
localStorage.setItem("tooty.registration", json); localStorage.setItem("tooty.registration", json);
}); });
app.ports.deleteRegistration.subscribe(json => {
localStorage.removeItem("tooty.registration");
});
app.ports.setStatus.subscribe(function(data) { app.ports.setStatus.subscribe(function(data) {
var element = document.getElementById(data.id); var element = document.getElementById(data.id);
if (element) { if (element) {

View File

@ -157,6 +157,10 @@ li.load-more {
margin-bottom: 4px; margin-bottom: 4px;
} }
.current-user .username > span {
font-weight: normal;
}
.follow-entry { .follow-entry {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -467,6 +471,10 @@ li.load-more {
padding: 15px; padding: 15px;
} }
.account-infos.row {
margin-right: 0;
}
.account-infos a { .account-infos a {
color: #c8c8c8; color: #c8c8c8;
} }
@ -561,71 +569,71 @@ li.load-more {
/* Scrollbars */ /* Scrollbars */
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 10px; width: 10px;
height: 8px height: 8px
} }
::-webkit-scrollbar-thumb { ::-webkit-scrollbar-thumb {
background: #444; background: #444;
border: 0px none #ffffff; border: 0px none #ffffff;
border-radius: 50px; border-radius: 50px;
} }
::-webkit-scrollbar-thumb:hover { ::-webkit-scrollbar-thumb:hover {
background: #777; background: #777;
} }
::-webkit-scrollbar-thumb:active { ::-webkit-scrollbar-thumb:active {
background: #777; background: #777;
} }
::-webkit-scrollbar-track { ::-webkit-scrollbar-track {
border: 0px none #ffffff; border: 0px none #ffffff;
border-radius: 0; border-radius: 0;
background: rgba(0,0,0,0.1); background: rgba(0,0,0,0.1);
} }
::-webkit-scrollbar-track:hover { ::-webkit-scrollbar-track:hover {
background: #2a2e31; background: #2a2e31;
} }
::-webkit-scrollbar-track:active { ::-webkit-scrollbar-track:active {
background: #2a2e31; background: #2a2e31;
} }
::-webkit-scrollbar-corner { ::-webkit-scrollbar-corner {
background: transparent; background: transparent;
} }
/* Autocomplete */ /* Autocomplete */
.autocomplete-menu { .autocomplete-menu {
position: relative; position: relative;
margin-top: -10px; margin-top: -10px;
min-width: 120px; min-width: 120px;
} }
.autocomplete-list { .autocomplete-list {
list-style: none; list-style: none;
padding: 0; padding: 0;
margin: auto; margin: auto;
max-height: 200px; max-height: 200px;
overflow-y: auto; overflow-y: auto;
} }
.autocomplete-item { .autocomplete-item {
display: flex; display: flex;
flex-wrap: nowrap; flex-wrap: nowrap;
justify-content: space-between; justify-content: space-between;
align-content: stretch; align-content: stretch;
align-items: flex-start; align-items: flex-start;
padding: 8px; padding: 8px;
cursor: pointer; cursor: pointer;
background: #fff; background: #fff;
color: #777; color: #777;
border-color: #bbb; border-color: #bbb;
border-left-color: #272b30; border-left-color: #272b30;
border-right-color: #272b30; border-right-color: #272b30;
} }
.autocomplete-item:hover, .autocomplete-item:hover,
@ -644,11 +652,11 @@ li.load-more {
} }
.autocomplete-item > img { .autocomplete-item > img {
flex: 1 1 auto; flex: 1 1 auto;
height: 20px; height: 20px;
width: 20px; width: 20px;
margin-right: 10px; margin-right: 10px;
border-radius: 2px; border-radius: 2px;
} }
.autocomplete-item > strong { .autocomplete-item > strong {
@ -667,3 +675,39 @@ li.load-more {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
/* Account selector */
.account-selector-item {
display: flex;
flex-wrap: nowrap;
justify-content: space-between;
align-content: stretch;
align-items: flex-start;
padding: 16px;
}
.account-selector-item > img {
flex: 1 1 auto;
height: 42px;
width: 42px;
margin-right: 10px;
border-radius: 2px;
}
.account-selector-item > span {
flex: 100 1 auto;
align-self: auto;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.account-selector-item > button {
flex: 1 1 auto;
text-align: right;
align-self: auto;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

View File

@ -3,7 +3,7 @@ module Command
( initCommands ( initCommands
, navigateToAuthUrl , navigateToAuthUrl
, registerApp , registerApp
, saveClient , saveClients
, saveRegistration , saveRegistration
, loadNotifications , loadNotifications
, loadUserAccount , loadUserAccount
@ -57,7 +57,9 @@ initCommands registration client authCode =
Just authCode -> Just authCode ->
case registration of case registration of
Just registration -> Just registration ->
[ getAccessToken registration authCode ] [ getAccessToken registration authCode
, Ports.deleteRegistration ""
]
Nothing -> Nothing ->
[] []
@ -107,11 +109,13 @@ registerApp { server, location } =
|> send (MastodonEvent << AppRegistered) |> send (MastodonEvent << AppRegistered)
saveClient : Client -> Cmd Msg saveClients : List Client -> Cmd Msg
saveClient client = saveClients clients =
clientEncoder client clients
|> List.map clientEncoder
|> Encode.list
|> Encode.encode 0 |> Encode.encode 0
|> Ports.saveClient |> Ports.saveClients
saveRegistration : AppRegistration -> Cmd Msg saveRegistration : AppRegistration -> Cmd Msg

View File

@ -9,11 +9,11 @@ import Util
init : Flags -> Navigation.Location -> ( Model, Cmd Msg ) init : Flags -> Navigation.Location -> ( Model, Cmd Msg )
init { registration, client } location = init { registration, clients } location =
{ server = "" { server = ""
, currentTime = 0 , currentTime = 0
, registration = registration , registration = registration
, client = client , clients = clients
, homeTimeline = Update.Timeline.empty "home-timeline" , homeTimeline = Update.Timeline.empty "home-timeline"
, localTimeline = Update.Timeline.empty "local-timeline" , localTimeline = Update.Timeline.empty "local-timeline"
, globalTimeline = Update.Timeline.empty "global-timeline" , globalTimeline = Update.Timeline.empty "global-timeline"
@ -32,4 +32,4 @@ init { registration, client } location =
, currentUser = Nothing , currentUser = Nothing
, notificationFilter = NotificationAll , notificationFilter = NotificationAll
} }
! [ Command.initCommands registration client (Util.extractAuthCode location) ] ! [ Command.initCommands registration (List.head clients) (Util.extractAuthCode location) ]

View File

@ -51,11 +51,31 @@ authorizationCodeEncoder registration authCode =
] ]
accountEncoder : Account -> Encode.Value
accountEncoder account =
Encode.object
[ ( "acct", Encode.string account.acct )
, ( "avatar", Encode.string account.avatar )
, ( "created_at", Encode.string account.created_at )
, ( "display_name", Encode.string account.display_name )
, ( "followers_count", Encode.int account.followers_count )
, ( "following_count", Encode.int account.following_count )
, ( "header", Encode.string account.header )
, ( "id", Encode.int account.id )
, ( "locked", Encode.bool account.locked )
, ( "note", Encode.string account.note )
, ( "statuses_count", Encode.int account.statuses_count )
, ( "url", Encode.string account.url )
, ( "username", Encode.string account.username )
]
clientEncoder : Client -> Encode.Value clientEncoder : Client -> Encode.Value
clientEncoder client = clientEncoder client =
Encode.object Encode.object
[ ( "server", Encode.string client.server ) [ ( "server", Encode.string client.server )
, ( "token", Encode.string client.token ) , ( "token", Encode.string client.token )
, ( "account", encodeMaybe accountEncoder client.account )
] ]

View File

@ -104,6 +104,7 @@ type alias Attachment =
type alias Client = type alias Client =
{ server : Server { server : Server
, token : Token , token : Token
, account : Maybe Account
} }

View File

@ -1,8 +1,9 @@
port module Ports port module Ports
exposing exposing
( saveRegistration ( saveRegistration
, deleteRegistration
, scrollIntoView , scrollIntoView
, saveClient , saveClients
, setStatus , setStatus
) )
@ -10,7 +11,10 @@ port module Ports
port saveRegistration : String -> Cmd msg port saveRegistration : String -> Cmd msg
port saveClient : String -> Cmd msg port deleteRegistration : String -> Cmd msg
port saveClients : String -> Cmd msg
port setStatus : { id : String, status : String } -> Cmd msg port setStatus : { id : String, status : String } -> Cmd msg

View File

@ -7,14 +7,14 @@ import Types exposing (..)
subscriptions : Model -> Sub Msg subscriptions : Model -> Sub Msg
subscriptions { client, currentView } = subscriptions { clients, currentView } =
let let
timeSub = timeSub =
Time.every Time.millisecond Tick Time.every Time.millisecond Tick
userWsSub = userWsSub =
Mastodon.WebSocket.subscribeToWebSockets Mastodon.WebSocket.subscribeToWebSockets
client (List.head clients)
Mastodon.WebSocket.UserStream Mastodon.WebSocket.UserStream
NewWebsocketUserMessage NewWebsocketUserMessage
|> Sub.map WebSocketEvent |> Sub.map WebSocketEvent
@ -22,13 +22,13 @@ subscriptions { client, currentView } =
otherWsSub = otherWsSub =
if currentView == GlobalTimelineView then if currentView == GlobalTimelineView then
Mastodon.WebSocket.subscribeToWebSockets Mastodon.WebSocket.subscribeToWebSockets
client (List.head clients)
Mastodon.WebSocket.GlobalPublicStream Mastodon.WebSocket.GlobalPublicStream
NewWebsocketGlobalMessage NewWebsocketGlobalMessage
|> Sub.map WebSocketEvent |> Sub.map WebSocketEvent
else if currentView == LocalTimelineView then else if currentView == LocalTimelineView then
Mastodon.WebSocket.subscribeToWebSockets Mastodon.WebSocket.subscribeToWebSockets
client (List.head clients)
Mastodon.WebSocket.LocalPublicStream Mastodon.WebSocket.LocalPublicStream
NewWebsocketLocalMessage NewWebsocketLocalMessage
|> Sub.map WebSocketEvent |> Sub.map WebSocketEvent

View File

@ -8,7 +8,7 @@ import Time exposing (Time)
type alias Flags = type alias Flags =
{ client : Maybe Client { clients : List Client
, registration : Maybe AppRegistration , registration : Maybe AppRegistration
} }
@ -72,6 +72,7 @@ type Msg
= AddFavorite Int = AddFavorite Int
| ClearError Int | ClearError Int
| CloseAccount | CloseAccount
| CloseAccountSelector
| CloseThread | CloseThread
| DeleteStatus Int | DeleteStatus Int
| DraftEvent DraftMsg | DraftEvent DraftMsg
@ -82,12 +83,14 @@ type Msg
| MastodonEvent MastodonMsg | MastodonEvent MastodonMsg
| NoOp | NoOp
| OpenThread Status | OpenThread Status
| OpenAccountSelector
| ReblogStatus Int | ReblogStatus Int
| Register | Register
| RemoveFavorite Int | RemoveFavorite Int
| ScrollColumn ScrollDirection String | ScrollColumn ScrollDirection String
| ServerChange String | ServerChange String
| SubmitDraft | SubmitDraft
| SwitchClient Client
| Tick Time | Tick Time
| UnfollowAccount Int | UnfollowAccount Int
| UrlChange Navigation.Location | UrlChange Navigation.Location
@ -105,6 +108,7 @@ type CurrentView
AccountFollowersView Account (Timeline Account) AccountFollowersView Account (Timeline Account)
| AccountFollowingView Account (Timeline Account) | AccountFollowingView Account (Timeline Account)
| AccountView Account | AccountView Account
| AccountSelectorView
| GlobalTimelineView | GlobalTimelineView
| LocalTimelineView | LocalTimelineView
| ThreadView Thread | ThreadView Thread
@ -172,7 +176,7 @@ type alias Model =
{ server : String { server : String
, currentTime : Time , currentTime : Time
, registration : Maybe AppRegistration , registration : Maybe AppRegistration
, client : Maybe Client , clients : List Client
, homeTimeline : Timeline Status , homeTimeline : Timeline Status
, localTimeline : Timeline Status , localTimeline : Timeline Status
, globalTimeline : Timeline Status , globalTimeline : Timeline Status

View File

@ -165,7 +165,7 @@ update draftMsg currentUser model =
in in
{ model | draft = newDraft } { model | draft = newDraft }
! if query /= "" && atPosition /= Nothing then ! if query /= "" && atPosition /= Nothing then
[ Command.searchAccounts model.client query model.draft.autoMaxResults False ] [ Command.searchAccounts (List.head model.clients) query model.draft.autoMaxResults False ]
else else
[] []

View File

@ -43,6 +43,19 @@ update msg model =
ClearError index -> ClearError index ->
{ model | errors = removeAt index model.errors } ! [] { model | errors = removeAt index model.errors } ! []
SwitchClient client ->
let
newClients =
client :: (List.filter (\c -> c.token /= client.token) model.clients)
in
{ model
| clients = newClients
, currentView = Update.Timeline.preferred model
}
! [ Command.loadUserAccount <| Just client
, Command.loadTimelines <| Just client
]
MastodonEvent msg -> MastodonEvent msg ->
let let
( newModel, commands ) = ( newModel, commands ) =
@ -67,35 +80,38 @@ update msg model =
model ! [ Command.registerApp model ] model ! [ Command.registerApp model ]
OpenThread status -> OpenThread status ->
model ! [ Command.loadThread model.client status ] model ! [ Command.loadThread (List.head model.clients) status ]
OpenAccountSelector ->
{ model | currentView = AccountSelectorView, server = "" } ! []
CloseThread -> CloseThread ->
{ model | currentView = Update.Timeline.preferred model } ! [] { model | currentView = Update.Timeline.preferred model } ! []
FollowAccount id -> FollowAccount id ->
model ! [ Command.follow model.client id ] model ! [ Command.follow (List.head model.clients) id ]
UnfollowAccount id -> UnfollowAccount id ->
model ! [ Command.unfollow model.client id ] model ! [ Command.unfollow (List.head model.clients) id ]
DeleteStatus id -> DeleteStatus id ->
model ! [ Command.deleteStatus model.client id ] model ! [ Command.deleteStatus (List.head model.clients) id ]
ReblogStatus id -> ReblogStatus id ->
Update.Timeline.processReblog id True model Update.Timeline.processReblog id True model
! [ Command.reblogStatus model.client id ] ! [ Command.reblogStatus (List.head model.clients) id ]
UnreblogStatus id -> UnreblogStatus id ->
Update.Timeline.processReblog id False model Update.Timeline.processReblog id False model
! [ Command.unreblogStatus model.client id ] ! [ Command.unreblogStatus (List.head model.clients) id ]
AddFavorite id -> AddFavorite id ->
Update.Timeline.processFavourite id True model Update.Timeline.processFavourite id True model
! [ Command.favouriteStatus model.client id ] ! [ Command.favouriteStatus (List.head model.clients) id ]
RemoveFavorite id -> RemoveFavorite id ->
Update.Timeline.processFavourite id False model Update.Timeline.processFavourite id False model
! [ Command.unfavouriteStatus model.client id ] ! [ Command.unfavouriteStatus (List.head model.clients) id ]
DraftEvent draftMsg -> DraftEvent draftMsg ->
case model.currentUser of case model.currentUser of
@ -113,7 +129,7 @@ update msg model =
{ model | viewer = viewer } ! [ commands ] { model | viewer = viewer } ! [ commands ]
SubmitDraft -> SubmitDraft ->
model ! [ Command.postStatus model.client <| toStatusRequestBody model.draft ] model ! [ Command.postStatus (List.head model.clients) <| toStatusRequestBody model.draft ]
LoadAccount accountId -> LoadAccount accountId ->
{ model { model
@ -123,25 +139,25 @@ update msg model =
, accountRelationships = [] , accountRelationships = []
, accountRelationship = Nothing , accountRelationship = Nothing
} }
! [ Command.loadAccount model.client accountId ] ! [ Command.loadAccount (List.head model.clients) accountId ]
TimelineLoadNext id next -> TimelineLoadNext id next ->
Update.Timeline.markAsLoading True id model Update.Timeline.markAsLoading True id model
! [ Command.loadNextTimeline model.client model.currentView id next ] ! [ Command.loadNextTimeline (List.head model.clients) model.currentView id next ]
ViewAccountFollowers account -> ViewAccountFollowers account ->
{ model { model
| currentView = AccountFollowersView account model.accountFollowers | currentView = AccountFollowersView account model.accountFollowers
, accountRelationships = [] , accountRelationships = []
} }
! [ Command.loadAccountFollowers model.client account.id Nothing ] ! [ Command.loadAccountFollowers (List.head model.clients) account.id Nothing ]
ViewAccountFollowing account -> ViewAccountFollowing account ->
{ model { model
| currentView = AccountFollowingView account model.accountFollowing | currentView = AccountFollowingView account model.accountFollowing
, accountRelationships = [] , accountRelationships = []
} }
! [ Command.loadAccountFollowing model.client account.id Nothing ] ! [ Command.loadAccountFollowing (List.head model.clients) account.id Nothing ]
ViewAccountStatuses account -> ViewAccountStatuses account ->
{ model | currentView = AccountView account } ! [] { model | currentView = AccountView account } ! []
@ -162,6 +178,9 @@ update msg model =
} }
! [] ! []
CloseAccountSelector ->
{ model | currentView = Update.Timeline.preferred model } ! []
FilterNotifications filter -> FilterNotifications filter ->
{ model | notificationFilter = filter } ! [] { model | notificationFilter = filter } ! []

View File

@ -35,11 +35,11 @@ update msg model =
Ok { decoded } -> Ok { decoded } ->
let let
client = client =
Client decoded.server decoded.accessToken Client decoded.server decoded.accessToken Nothing
in in
{ model | client = Just client } { model | clients = client :: model.clients }
! [ Command.loadTimelines <| Just client ! [ Command.loadTimelines <| Just client
, Command.saveClient client , Command.saveClients <| client :: model.clients
, Navigation.modifyUrl model.location.pathname , Navigation.modifyUrl model.location.pathname
, Navigation.reload , Navigation.reload
] ]
@ -90,7 +90,17 @@ update msg model =
CurrentUser result -> CurrentUser result ->
case result of case result of
Ok { decoded } -> Ok { decoded } ->
{ model | currentUser = Just decoded } ! [] let
updatedClients =
case model.clients of
client :: xs ->
({ client | account = Just decoded }) :: xs
_ ->
model.clients
in
{ model | currentUser = Just decoded, clients = updatedClients }
! [ Command.saveClients updatedClients ]
Err error -> Err error ->
{ model | errors = addErrorNotification (errorText error) model } ! [] { model | errors = addErrorNotification (errorText error) model } ! []
@ -182,7 +192,11 @@ update msg model =
| currentView = AccountView decoded | currentView = AccountView decoded
, accountRelationships = [] , accountRelationships = []
} }
! [ Command.loadAccountTimeline model.client decoded.id model.accountTimeline.links.next ] ! [ Command.loadAccountTimeline
(List.head model.clients)
decoded.id
model.accountTimeline.links.next
]
Err error -> Err error ->
{ model { model
@ -203,7 +217,7 @@ update msg model =
case result of case result of
Ok { decoded, links } -> Ok { decoded, links } ->
{ model | accountFollowers = Update.Timeline.update append decoded links model.accountFollowers } { model | accountFollowers = Update.Timeline.update append decoded links model.accountFollowers }
! [ Command.loadRelationships model.client <| List.map .id decoded ] ! [ Command.loadRelationships (List.head model.clients) <| List.map .id decoded ]
Err error -> Err error ->
{ model | errors = addErrorNotification (errorText error) model } ! [] { model | errors = addErrorNotification (errorText error) model } ! []
@ -212,7 +226,7 @@ update msg model =
case result of case result of
Ok { decoded, links } -> Ok { decoded, links } ->
{ model | accountFollowing = Update.Timeline.update append decoded links model.accountFollowing } { model | accountFollowing = Update.Timeline.update append decoded links model.accountFollowing }
! [ Command.loadRelationships model.client <| List.map .id decoded ] ! [ Command.loadRelationships (List.head model.clients) <| List.map .id decoded ]
Err error -> Err error ->
{ model | errors = addErrorNotification (errorText error) model } ! [] { model | errors = addErrorNotification (errorText error) model } ! []

View File

@ -0,0 +1,76 @@
module View.AccountSelector exposing (accountSelectorView)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Mastodon.Helper exposing (..)
import Mastodon.Model exposing (..)
import String.Extra exposing (replace)
import Types exposing (..)
import View.Auth exposing (authForm)
import View.Common exposing (..)
type alias CurrentUser =
Maybe Account
accountIdentityView : CurrentUser -> Client -> Html Msg
accountIdentityView currentUser client =
case client.account of
Just 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 account-selector-item " ++ entryClass ]
[ accountAvatar "" account
, span []
[ strong []
[ text <|
if account.display_name /= "" then
account.display_name
else
account.username
]
, br [] []
, account.url
|> replace "https://" "@"
|> replace "/@" "@"
|> text
]
, if isCurrentUser then
text ""
else
button
[ class "btn btn-default"
, onClick <| SwitchClient client
]
[ text "Use" ]
]
Nothing ->
text ""
accountSelectorView : Model -> Html Msg
accountSelectorView model =
div [ class "col-md-3 column" ]
[ div [ class "panel panel-default" ]
[ closeablePanelheading "account-selector" "user" "Account selector" CloseAccountSelector
, ul [ class "list-group " ] <|
List.map (accountIdentityView model.currentUser) model.clients
, div [ class "panel-body" ]
[ h3 [] [ text "Add an account" ]
, authForm model
]
]
]

View File

@ -7,6 +7,7 @@ import Html.Attributes exposing (..)
import Mastodon.Model exposing (..) import Mastodon.Model exposing (..)
import Types exposing (..) import Types exposing (..)
import View.Account exposing (accountFollowView, accountTimelineView) import View.Account exposing (accountFollowView, accountTimelineView)
import View.AccountSelector exposing (accountSelectorView)
import View.Auth exposing (authView) import View.Auth exposing (authView)
import View.Common as Common import View.Common as Common
import View.Draft exposing (draftView) import View.Draft exposing (draftView)
@ -114,6 +115,9 @@ homepageView model =
model.accountRelationship model.accountRelationship
account account
AccountSelectorView ->
accountSelectorView model
AccountFollowersView account followers -> AccountFollowersView account followers ->
accountFollowView accountFollowView
currentUser currentUser
@ -139,7 +143,7 @@ view : Model -> Html Msg
view model = view model =
div [ class "container-fluid" ] div [ class "container-fluid" ]
[ errorsListView model [ errorsListView model
, case model.client of , case (List.head model.clients) of
Just client -> Just client ->
homepageView model homepageView model

View File

@ -1,4 +1,4 @@
module View.Auth exposing (authView) module View.Auth exposing (authForm, authView)
import Html exposing (..) import Html exposing (..)
import Html.Attributes exposing (..) import Html.Attributes exposing (..)
@ -6,6 +6,33 @@ import Html.Events exposing (..)
import Types exposing (..) import Types exposing (..)
authForm : Model -> Html Msg
authForm model =
Html.form [ class "form", onSubmit Register ]
[ div [ class "form-group" ]
[ label [ for "server" ] [ text "Mastodon server root URL" ]
, input
[ type_ "url"
, class "form-control"
, id "server"
, required True
, placeholder "https://mastodon.social"
, value model.server
, pattern "https://.+"
, onInput ServerChange
]
[]
, p [ class "help-block" ]
[ text <|
"You'll be redirected to that server to authenticate yourself. "
++ "We don't have access to your password."
]
]
, button [ class "btn btn-primary", type_ "submit" ]
[ text "Sign into Tooty" ]
]
authView : Model -> Html Msg authView : Model -> Html Msg
authView model = authView model =
div [ class "col-md-4 col-md-offset-4" ] div [ class "col-md-4 col-md-offset-4" ]
@ -26,29 +53,6 @@ authView model =
, div [ class "panel panel-default" ] , div [ class "panel panel-default" ]
[ div [ class "panel-heading" ] [ text "Authenticate" ] [ div [ class "panel-heading" ] [ text "Authenticate" ]
, div [ class "panel-body" ] , div [ class "panel-body" ]
[ Html.form [ class "form", onSubmit Register ] [ authForm model ]
[ div [ class "form-group" ]
[ label [ for "server" ] [ text "Mastodon server root URL" ]
, input
[ type_ "url"
, class "form-control"
, id "server"
, required True
, placeholder "https://mastodon.social"
, value model.server
, pattern "https://.+"
, onInput ServerChange
]
[]
, p [ class "help-block" ]
[ text <|
"You'll be redirected to that server to authenticate yourself. "
++ "We don't have access to your password."
]
]
, button [ class "btn btn-primary", type_ "submit" ]
[ text "Sign into Tooty" ]
]
]
] ]
] ]

View File

@ -1,6 +1,7 @@
module View.Common module View.Common
exposing exposing
( accountAvatarLink ( accountAvatar
, accountAvatarLink
, accountLink , accountLink
, closeablePanelheading , closeablePanelheading
, icon , icon
@ -16,6 +17,11 @@ import Types exposing (..)
import View.Events exposing (..) import View.Events exposing (..)
accountAvatar : String -> Account -> Html Msg
accountAvatar avatarClass account =
img [ class avatarClass, src account.avatar ] []
accountLink : Bool -> Account -> Html Msg accountLink : Bool -> Account -> Html Msg
accountLink external account = accountLink external account =
let let
@ -52,7 +58,7 @@ accountAvatarLink external account =
, accountHref , accountHref
, title <| "@" ++ account.username , title <| "@" ++ account.username
] ]
[ img [ class avatarClass, src account.avatar ] [] ] [ accountAvatar avatarClass account ]
closeablePanelheading : String -> String -> String -> Msg -> Html Msg closeablePanelheading : String -> String -> String -> Msg -> Html Msg

View File

@ -77,7 +77,15 @@ currentUserView currentUser =
Just currentUser -> Just currentUser ->
div [ class "current-user" ] div [ class "current-user" ]
[ Common.accountAvatarLink False currentUser [ Common.accountAvatarLink False currentUser
, div [ class "username" ] [ Common.accountLink False currentUser ] , div [ class "username" ]
[ Common.accountLink False currentUser
, span []
[ text " ("
, a [ href "", onClickWithPreventAndStop <| OpenAccountSelector ]
[ text "switch account" ]
, text ")"
]
]
, p [ class "status-text" ] <| formatContent currentUser.note [] , p [ class "status-text" ] <| formatContent currentUser.note []
] ]