* 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:
parent
51a802f096
commit
7a053b9fa0
@ -17,20 +17,35 @@
|
||||
<body>
|
||||
<script src="app.js"></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({
|
||||
client: JSON.parse(localStorage.getItem("tooty.client")),
|
||||
clients: clients,
|
||||
registration: JSON.parse(localStorage.getItem("tooty.registration"))
|
||||
});
|
||||
|
||||
app.ports.saveClient.subscribe(json => {
|
||||
localStorage.setItem("tooty.client", json);
|
||||
app.ports.saveClients.subscribe(json => {
|
||||
localStorage.setItem("tooty.clients", json);
|
||||
});
|
||||
|
||||
app.ports.saveRegistration.subscribe(json => {
|
||||
localStorage.setItem("tooty.registration", json);
|
||||
});
|
||||
|
||||
app.ports.deleteRegistration.subscribe(json => {
|
||||
localStorage.removeItem("tooty.registration");
|
||||
});
|
||||
|
||||
app.ports.setStatus.subscribe(function(data) {
|
||||
var element = document.getElementById(data.id);
|
||||
if (element) {
|
||||
|
120
public/style.css
120
public/style.css
@ -157,6 +157,10 @@ li.load-more {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.current-user .username > span {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.follow-entry {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@ -467,6 +471,10 @@ li.load-more {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.account-infos.row {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.account-infos a {
|
||||
color: #c8c8c8;
|
||||
}
|
||||
@ -561,71 +569,71 @@ li.load-more {
|
||||
/* Scrollbars */
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
height: 8px
|
||||
width: 10px;
|
||||
height: 8px
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #444;
|
||||
border: 0px none #ffffff;
|
||||
border-radius: 50px;
|
||||
background: #444;
|
||||
border: 0px none #ffffff;
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #777;
|
||||
background: #777;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:active {
|
||||
background: #777;
|
||||
background: #777;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
border: 0px none #ffffff;
|
||||
border-radius: 0;
|
||||
background: rgba(0,0,0,0.1);
|
||||
border: 0px none #ffffff;
|
||||
border-radius: 0;
|
||||
background: rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track:hover {
|
||||
background: #2a2e31;
|
||||
background: #2a2e31;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track:active {
|
||||
background: #2a2e31;
|
||||
background: #2a2e31;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-corner {
|
||||
background: transparent;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* Autocomplete */
|
||||
|
||||
.autocomplete-menu {
|
||||
position: relative;
|
||||
margin-top: -10px;
|
||||
min-width: 120px;
|
||||
position: relative;
|
||||
margin-top: -10px;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.autocomplete-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: auto;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: auto;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.autocomplete-item {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
align-content: stretch;
|
||||
align-items: flex-start;
|
||||
padding: 8px;
|
||||
cursor: pointer;
|
||||
background: #fff;
|
||||
color: #777;
|
||||
border-color: #bbb;
|
||||
border-left-color: #272b30;
|
||||
border-right-color: #272b30;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
align-content: stretch;
|
||||
align-items: flex-start;
|
||||
padding: 8px;
|
||||
cursor: pointer;
|
||||
background: #fff;
|
||||
color: #777;
|
||||
border-color: #bbb;
|
||||
border-left-color: #272b30;
|
||||
border-right-color: #272b30;
|
||||
}
|
||||
|
||||
.autocomplete-item:hover,
|
||||
@ -644,11 +652,11 @@ li.load-more {
|
||||
}
|
||||
|
||||
.autocomplete-item > img {
|
||||
flex: 1 1 auto;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
margin-right: 10px;
|
||||
border-radius: 2px;
|
||||
flex: 1 1 auto;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
margin-right: 10px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.autocomplete-item > strong {
|
||||
@ -667,3 +675,39 @@ li.load-more {
|
||||
overflow: hidden;
|
||||
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;
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ module Command
|
||||
( initCommands
|
||||
, navigateToAuthUrl
|
||||
, registerApp
|
||||
, saveClient
|
||||
, saveClients
|
||||
, saveRegistration
|
||||
, loadNotifications
|
||||
, loadUserAccount
|
||||
@ -57,7 +57,9 @@ initCommands registration client authCode =
|
||||
Just authCode ->
|
||||
case registration of
|
||||
Just registration ->
|
||||
[ getAccessToken registration authCode ]
|
||||
[ getAccessToken registration authCode
|
||||
, Ports.deleteRegistration ""
|
||||
]
|
||||
|
||||
Nothing ->
|
||||
[]
|
||||
@ -107,11 +109,13 @@ registerApp { server, location } =
|
||||
|> send (MastodonEvent << AppRegistered)
|
||||
|
||||
|
||||
saveClient : Client -> Cmd Msg
|
||||
saveClient client =
|
||||
clientEncoder client
|
||||
saveClients : List Client -> Cmd Msg
|
||||
saveClients clients =
|
||||
clients
|
||||
|> List.map clientEncoder
|
||||
|> Encode.list
|
||||
|> Encode.encode 0
|
||||
|> Ports.saveClient
|
||||
|> Ports.saveClients
|
||||
|
||||
|
||||
saveRegistration : AppRegistration -> Cmd Msg
|
||||
|
@ -9,11 +9,11 @@ import Util
|
||||
|
||||
|
||||
init : Flags -> Navigation.Location -> ( Model, Cmd Msg )
|
||||
init { registration, client } location =
|
||||
init { registration, clients } location =
|
||||
{ server = ""
|
||||
, currentTime = 0
|
||||
, registration = registration
|
||||
, client = client
|
||||
, clients = clients
|
||||
, homeTimeline = Update.Timeline.empty "home-timeline"
|
||||
, localTimeline = Update.Timeline.empty "local-timeline"
|
||||
, globalTimeline = Update.Timeline.empty "global-timeline"
|
||||
@ -32,4 +32,4 @@ init { registration, client } location =
|
||||
, currentUser = Nothing
|
||||
, notificationFilter = NotificationAll
|
||||
}
|
||||
! [ Command.initCommands registration client (Util.extractAuthCode location) ]
|
||||
! [ Command.initCommands registration (List.head clients) (Util.extractAuthCode location) ]
|
||||
|
@ -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.object
|
||||
[ ( "server", Encode.string client.server )
|
||||
, ( "token", Encode.string client.token )
|
||||
, ( "account", encodeMaybe accountEncoder client.account )
|
||||
]
|
||||
|
||||
|
||||
|
@ -104,6 +104,7 @@ type alias Attachment =
|
||||
type alias Client =
|
||||
{ server : Server
|
||||
, token : Token
|
||||
, account : Maybe Account
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
port module Ports
|
||||
exposing
|
||||
( saveRegistration
|
||||
, deleteRegistration
|
||||
, scrollIntoView
|
||||
, saveClient
|
||||
, saveClients
|
||||
, setStatus
|
||||
)
|
||||
|
||||
@ -10,7 +11,10 @@ port module Ports
|
||||
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
|
||||
|
@ -7,14 +7,14 @@ import Types exposing (..)
|
||||
|
||||
|
||||
subscriptions : Model -> Sub Msg
|
||||
subscriptions { client, currentView } =
|
||||
subscriptions { clients, currentView } =
|
||||
let
|
||||
timeSub =
|
||||
Time.every Time.millisecond Tick
|
||||
|
||||
userWsSub =
|
||||
Mastodon.WebSocket.subscribeToWebSockets
|
||||
client
|
||||
(List.head clients)
|
||||
Mastodon.WebSocket.UserStream
|
||||
NewWebsocketUserMessage
|
||||
|> Sub.map WebSocketEvent
|
||||
@ -22,13 +22,13 @@ subscriptions { client, currentView } =
|
||||
otherWsSub =
|
||||
if currentView == GlobalTimelineView then
|
||||
Mastodon.WebSocket.subscribeToWebSockets
|
||||
client
|
||||
(List.head clients)
|
||||
Mastodon.WebSocket.GlobalPublicStream
|
||||
NewWebsocketGlobalMessage
|
||||
|> Sub.map WebSocketEvent
|
||||
else if currentView == LocalTimelineView then
|
||||
Mastodon.WebSocket.subscribeToWebSockets
|
||||
client
|
||||
(List.head clients)
|
||||
Mastodon.WebSocket.LocalPublicStream
|
||||
NewWebsocketLocalMessage
|
||||
|> Sub.map WebSocketEvent
|
||||
|
@ -8,7 +8,7 @@ import Time exposing (Time)
|
||||
|
||||
|
||||
type alias Flags =
|
||||
{ client : Maybe Client
|
||||
{ clients : List Client
|
||||
, registration : Maybe AppRegistration
|
||||
}
|
||||
|
||||
@ -72,6 +72,7 @@ type Msg
|
||||
= AddFavorite Int
|
||||
| ClearError Int
|
||||
| CloseAccount
|
||||
| CloseAccountSelector
|
||||
| CloseThread
|
||||
| DeleteStatus Int
|
||||
| DraftEvent DraftMsg
|
||||
@ -82,12 +83,14 @@ type Msg
|
||||
| MastodonEvent MastodonMsg
|
||||
| NoOp
|
||||
| OpenThread Status
|
||||
| OpenAccountSelector
|
||||
| ReblogStatus Int
|
||||
| Register
|
||||
| RemoveFavorite Int
|
||||
| ScrollColumn ScrollDirection String
|
||||
| ServerChange String
|
||||
| SubmitDraft
|
||||
| SwitchClient Client
|
||||
| Tick Time
|
||||
| UnfollowAccount Int
|
||||
| UrlChange Navigation.Location
|
||||
@ -105,6 +108,7 @@ type CurrentView
|
||||
AccountFollowersView Account (Timeline Account)
|
||||
| AccountFollowingView Account (Timeline Account)
|
||||
| AccountView Account
|
||||
| AccountSelectorView
|
||||
| GlobalTimelineView
|
||||
| LocalTimelineView
|
||||
| ThreadView Thread
|
||||
@ -172,7 +176,7 @@ type alias Model =
|
||||
{ server : String
|
||||
, currentTime : Time
|
||||
, registration : Maybe AppRegistration
|
||||
, client : Maybe Client
|
||||
, clients : List Client
|
||||
, homeTimeline : Timeline Status
|
||||
, localTimeline : Timeline Status
|
||||
, globalTimeline : Timeline Status
|
||||
|
@ -165,7 +165,7 @@ update draftMsg currentUser model =
|
||||
in
|
||||
{ model | draft = newDraft }
|
||||
! 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
|
||||
[]
|
||||
|
||||
|
@ -43,6 +43,19 @@ update msg model =
|
||||
ClearError index ->
|
||||
{ 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 ->
|
||||
let
|
||||
( newModel, commands ) =
|
||||
@ -67,35 +80,38 @@ update msg model =
|
||||
model ! [ Command.registerApp model ]
|
||||
|
||||
OpenThread status ->
|
||||
model ! [ Command.loadThread model.client status ]
|
||||
model ! [ Command.loadThread (List.head model.clients) status ]
|
||||
|
||||
OpenAccountSelector ->
|
||||
{ model | currentView = AccountSelectorView, server = "" } ! []
|
||||
|
||||
CloseThread ->
|
||||
{ model | currentView = Update.Timeline.preferred model } ! []
|
||||
|
||||
FollowAccount id ->
|
||||
model ! [ Command.follow model.client id ]
|
||||
model ! [ Command.follow (List.head model.clients) id ]
|
||||
|
||||
UnfollowAccount id ->
|
||||
model ! [ Command.unfollow model.client id ]
|
||||
model ! [ Command.unfollow (List.head model.clients) id ]
|
||||
|
||||
DeleteStatus id ->
|
||||
model ! [ Command.deleteStatus model.client id ]
|
||||
model ! [ Command.deleteStatus (List.head model.clients) id ]
|
||||
|
||||
ReblogStatus id ->
|
||||
Update.Timeline.processReblog id True model
|
||||
! [ Command.reblogStatus model.client id ]
|
||||
! [ Command.reblogStatus (List.head model.clients) id ]
|
||||
|
||||
UnreblogStatus id ->
|
||||
Update.Timeline.processReblog id False model
|
||||
! [ Command.unreblogStatus model.client id ]
|
||||
! [ Command.unreblogStatus (List.head model.clients) id ]
|
||||
|
||||
AddFavorite id ->
|
||||
Update.Timeline.processFavourite id True model
|
||||
! [ Command.favouriteStatus model.client id ]
|
||||
! [ Command.favouriteStatus (List.head model.clients) id ]
|
||||
|
||||
RemoveFavorite id ->
|
||||
Update.Timeline.processFavourite id False model
|
||||
! [ Command.unfavouriteStatus model.client id ]
|
||||
! [ Command.unfavouriteStatus (List.head model.clients) id ]
|
||||
|
||||
DraftEvent draftMsg ->
|
||||
case model.currentUser of
|
||||
@ -113,7 +129,7 @@ update msg model =
|
||||
{ model | viewer = viewer } ! [ commands ]
|
||||
|
||||
SubmitDraft ->
|
||||
model ! [ Command.postStatus model.client <| toStatusRequestBody model.draft ]
|
||||
model ! [ Command.postStatus (List.head model.clients) <| toStatusRequestBody model.draft ]
|
||||
|
||||
LoadAccount accountId ->
|
||||
{ model
|
||||
@ -123,25 +139,25 @@ update msg model =
|
||||
, accountRelationships = []
|
||||
, accountRelationship = Nothing
|
||||
}
|
||||
! [ Command.loadAccount model.client accountId ]
|
||||
! [ Command.loadAccount (List.head model.clients) accountId ]
|
||||
|
||||
TimelineLoadNext id next ->
|
||||
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 ->
|
||||
{ model
|
||||
| currentView = AccountFollowersView account model.accountFollowers
|
||||
, accountRelationships = []
|
||||
}
|
||||
! [ Command.loadAccountFollowers model.client account.id Nothing ]
|
||||
! [ Command.loadAccountFollowers (List.head model.clients) account.id Nothing ]
|
||||
|
||||
ViewAccountFollowing account ->
|
||||
{ model
|
||||
| currentView = AccountFollowingView account model.accountFollowing
|
||||
, accountRelationships = []
|
||||
}
|
||||
! [ Command.loadAccountFollowing model.client account.id Nothing ]
|
||||
! [ Command.loadAccountFollowing (List.head model.clients) account.id Nothing ]
|
||||
|
||||
ViewAccountStatuses account ->
|
||||
{ model | currentView = AccountView account } ! []
|
||||
@ -162,6 +178,9 @@ update msg model =
|
||||
}
|
||||
! []
|
||||
|
||||
CloseAccountSelector ->
|
||||
{ model | currentView = Update.Timeline.preferred model } ! []
|
||||
|
||||
FilterNotifications filter ->
|
||||
{ model | notificationFilter = filter } ! []
|
||||
|
||||
|
@ -35,11 +35,11 @@ update msg model =
|
||||
Ok { decoded } ->
|
||||
let
|
||||
client =
|
||||
Client decoded.server decoded.accessToken
|
||||
Client decoded.server decoded.accessToken Nothing
|
||||
in
|
||||
{ model | client = Just client }
|
||||
{ model | clients = client :: model.clients }
|
||||
! [ Command.loadTimelines <| Just client
|
||||
, Command.saveClient client
|
||||
, Command.saveClients <| client :: model.clients
|
||||
, Navigation.modifyUrl model.location.pathname
|
||||
, Navigation.reload
|
||||
]
|
||||
@ -90,7 +90,17 @@ update msg model =
|
||||
CurrentUser result ->
|
||||
case result of
|
||||
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 ->
|
||||
{ model | errors = addErrorNotification (errorText error) model } ! []
|
||||
@ -182,7 +192,11 @@ update msg model =
|
||||
| currentView = AccountView decoded
|
||||
, 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 ->
|
||||
{ model
|
||||
@ -203,7 +217,7 @@ update msg model =
|
||||
case result of
|
||||
Ok { decoded, links } ->
|
||||
{ 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 ->
|
||||
{ model | errors = addErrorNotification (errorText error) model } ! []
|
||||
@ -212,7 +226,7 @@ update msg model =
|
||||
case result of
|
||||
Ok { decoded, links } ->
|
||||
{ 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 ->
|
||||
{ model | errors = addErrorNotification (errorText error) model } ! []
|
||||
|
76
src/View/AccountSelector.elm
Normal file
76
src/View/AccountSelector.elm
Normal 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
|
||||
]
|
||||
]
|
||||
]
|
@ -7,6 +7,7 @@ import Html.Attributes exposing (..)
|
||||
import Mastodon.Model exposing (..)
|
||||
import Types exposing (..)
|
||||
import View.Account exposing (accountFollowView, accountTimelineView)
|
||||
import View.AccountSelector exposing (accountSelectorView)
|
||||
import View.Auth exposing (authView)
|
||||
import View.Common as Common
|
||||
import View.Draft exposing (draftView)
|
||||
@ -114,6 +115,9 @@ homepageView model =
|
||||
model.accountRelationship
|
||||
account
|
||||
|
||||
AccountSelectorView ->
|
||||
accountSelectorView model
|
||||
|
||||
AccountFollowersView account followers ->
|
||||
accountFollowView
|
||||
currentUser
|
||||
@ -139,7 +143,7 @@ view : Model -> Html Msg
|
||||
view model =
|
||||
div [ class "container-fluid" ]
|
||||
[ errorsListView model
|
||||
, case model.client of
|
||||
, case (List.head model.clients) of
|
||||
Just client ->
|
||||
homepageView model
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
module View.Auth exposing (authView)
|
||||
module View.Auth exposing (authForm, authView)
|
||||
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
@ -6,6 +6,33 @@ import Html.Events 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 =
|
||||
div [ class "col-md-4 col-md-offset-4" ]
|
||||
@ -26,29 +53,6 @@ authView model =
|
||||
, div [ class "panel panel-default" ]
|
||||
[ div [ class "panel-heading" ] [ text "Authenticate" ]
|
||||
, div [ class "panel-body" ]
|
||||
[ 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" ]
|
||||
]
|
||||
]
|
||||
[ authForm model ]
|
||||
]
|
||||
]
|
||||
|
@ -1,6 +1,7 @@
|
||||
module View.Common
|
||||
exposing
|
||||
( accountAvatarLink
|
||||
( accountAvatar
|
||||
, accountAvatarLink
|
||||
, accountLink
|
||||
, closeablePanelheading
|
||||
, icon
|
||||
@ -16,6 +17,11 @@ import Types 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 external account =
|
||||
let
|
||||
@ -52,7 +58,7 @@ accountAvatarLink external account =
|
||||
, accountHref
|
||||
, title <| "@" ++ account.username
|
||||
]
|
||||
[ img [ class avatarClass, src account.avatar ] [] ]
|
||||
[ accountAvatar avatarClass account ]
|
||||
|
||||
|
||||
closeablePanelheading : String -> String -> String -> Msg -> Html Msg
|
||||
|
@ -77,7 +77,15 @@ currentUserView currentUser =
|
||||
Just currentUser ->
|
||||
div [ class "current-user" ]
|
||||
[ 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 []
|
||||
]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user