1
0
Fork 0

Fix #196: Fix incompatibilities with the Mastodon API v2.0.0. (#197)

This commit is contained in:
Nicolas Perriault 2017-11-29 14:06:08 +01:00 committed by GitHub
parent 8fca68438f
commit 2e89ad0b0f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 240 additions and 227 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@
/elm-stuff /elm-stuff
/node_modules /node_modules
app.js app.js
package-lock.json

View File

@ -17,20 +17,8 @@
<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({
clients: clients, clients: localStorage.getItem("tooty.clients") || "[]",
registration: JSON.parse(localStorage.getItem("tooty.registration")) registration: JSON.parse(localStorage.getItem("tooty.registration"))
}); });

View File

@ -54,6 +54,7 @@ import HttpBuilder
import Mastodon.ApiUrl as ApiUrl import Mastodon.ApiUrl as ApiUrl
import Mastodon.Decoder exposing (..) import Mastodon.Decoder exposing (..)
import Mastodon.Encoder exposing (..) import Mastodon.Encoder exposing (..)
import Mastodon.Helper exposing (extractStatusId)
import Mastodon.Http exposing (..) import Mastodon.Http exposing (..)
import Mastodon.Model exposing (..) import Mastodon.Model exposing (..)
import Navigation import Navigation
@ -166,7 +167,7 @@ loadUserAccount client =
Cmd.none Cmd.none
loadAccount : Maybe Client -> Int -> Cmd Msg loadAccount : Maybe Client -> String -> Cmd Msg
loadAccount client accountId = loadAccount client accountId =
case client of case client of
Just client -> Just client ->
@ -183,7 +184,7 @@ loadAccount client accountId =
Cmd.none Cmd.none
loadAccountFollowers : Maybe Client -> Int -> Maybe String -> Cmd Msg loadAccountFollowers : Maybe Client -> String -> Maybe String -> Cmd Msg
loadAccountFollowers client accountId url = loadAccountFollowers client accountId url =
case client of case client of
Just client -> Just client ->
@ -196,7 +197,7 @@ loadAccountFollowers client accountId url =
Cmd.none Cmd.none
loadAccountFollowing : Maybe Client -> Int -> Maybe String -> Cmd Msg loadAccountFollowing : Maybe Client -> String -> Maybe String -> Cmd Msg
loadAccountFollowing client accountId url = loadAccountFollowing client accountId url =
case client of case client of
Just client -> Just client ->
@ -256,16 +257,16 @@ searchAccounts client query limit resolve =
Cmd.none Cmd.none
requestRelationships : Client -> List Int -> Request (List Relationship) requestRelationships : Client -> List String -> Request (List Relationship)
requestRelationships client ids = requestRelationships client ids =
HttpBuilder.get ApiUrl.relationships HttpBuilder.get ApiUrl.relationships
|> withClient client |> withClient client
|> withBodyDecoder (Decode.list relationshipDecoder) |> withBodyDecoder (Decode.list relationshipDecoder)
|> withQueryParams |> withQueryParams
(List.map (\id -> ( "id[]", toString id )) ids) (List.map (\id -> ( "id[]", id )) ids)
loadRelationships : Maybe Client -> List Int -> Cmd Msg loadRelationships : Maybe Client -> List String -> Cmd Msg
loadRelationships client ids = loadRelationships client ids =
if List.length ids > 0 then if List.length ids > 0 then
case client of case client of
@ -279,7 +280,7 @@ loadRelationships client ids =
Cmd.none Cmd.none
loadThread : Maybe Client -> Int -> Cmd Msg loadThread : Maybe Client -> StatusId -> Cmd Msg
loadThread client id = loadThread client id =
case client of case client of
Just client -> Just client ->
@ -340,7 +341,7 @@ loadGlobalTimeline client url =
Cmd.none Cmd.none
loadAccountTimeline : Maybe Client -> Int -> Maybe String -> Cmd Msg loadAccountTimeline : Maybe Client -> String -> Maybe String -> Cmd Msg
loadAccountTimeline client accountId url = loadAccountTimeline client accountId url =
case client of case client of
Just client -> Just client ->
@ -497,7 +498,7 @@ updateDomStatus statusText =
Ports.setStatus { id = "status", status = statusText } Ports.setStatus { id = "status", status = statusText }
deleteStatus : Maybe Client -> Int -> Cmd Msg deleteStatus : Maybe Client -> StatusId -> Cmd Msg
deleteStatus client id = deleteStatus client id =
case client of case client of
Just client -> Just client ->
@ -510,7 +511,7 @@ deleteStatus client id =
Cmd.none Cmd.none
reblogStatus : Maybe Client -> Int -> Cmd Msg reblogStatus : Maybe Client -> StatusId -> Cmd Msg
reblogStatus client statusId = reblogStatus client statusId =
case client of case client of
Just client -> Just client ->
@ -523,7 +524,7 @@ reblogStatus client statusId =
Cmd.none Cmd.none
unreblogStatus : Maybe Client -> Int -> Cmd Msg unreblogStatus : Maybe Client -> StatusId -> Cmd Msg
unreblogStatus client statusId = unreblogStatus client statusId =
case client of case client of
Just client -> Just client ->
@ -536,7 +537,7 @@ unreblogStatus client statusId =
Cmd.none Cmd.none
favouriteStatus : Maybe Client -> Int -> Cmd Msg favouriteStatus : Maybe Client -> StatusId -> Cmd Msg
favouriteStatus client statusId = favouriteStatus client statusId =
case client of case client of
Just client -> Just client ->
@ -549,7 +550,7 @@ favouriteStatus client statusId =
Cmd.none Cmd.none
unfavouriteStatus : Maybe Client -> Int -> Cmd Msg unfavouriteStatus : Maybe Client -> StatusId -> Cmd Msg
unfavouriteStatus client statusId = unfavouriteStatus client statusId =
case client of case client of
Just client -> Just client ->
@ -680,7 +681,7 @@ notifyStatus status =
{ title = status.account.acct { title = status.account.acct
, icon = status.account.avatar , icon = status.account.avatar
, body = status.content |> textContent , body = status.content |> textContent
, clickUrl = "#thread/" ++ (toString status.id) , clickUrl = "#thread/" ++ extractStatusId status.id
} }
@ -694,7 +695,7 @@ notifyNotification notification =
{ title = notification.account.acct ++ " reboosted" { title = notification.account.acct ++ " reboosted"
, icon = notification.account.avatar , icon = notification.account.avatar
, body = status.content |> textContent , body = status.content |> textContent
, clickUrl = "#thread/" ++ (toString status.id) , clickUrl = "#thread/" ++ extractStatusId status.id
} }
"favourite" -> "favourite" ->
@ -702,7 +703,7 @@ notifyNotification notification =
{ title = notification.account.acct ++ " favorited" { title = notification.account.acct ++ " favorited"
, icon = notification.account.avatar , icon = notification.account.avatar
, body = status.content |> textContent , body = status.content |> textContent
, clickUrl = "#thread/" ++ (toString status.id) , clickUrl = "#thread/" ++ extractStatusId status.id
} }
"mention" -> "mention" ->
@ -710,7 +711,7 @@ notifyNotification notification =
{ title = notification.account.acct ++ " mentioned you" { title = notification.account.acct ++ " mentioned you"
, icon = notification.account.avatar , icon = notification.account.avatar
, body = status.content |> textContent , body = status.content |> textContent
, clickUrl = "#thread/" ++ (toString status.id) , clickUrl = "#thread/" ++ extractStatusId status.id
} }
_ -> _ ->
@ -723,7 +724,7 @@ notifyNotification notification =
{ title = notification.account.acct ++ " follows you" { title = notification.account.acct ++ " follows you"
, icon = notification.account.avatar , icon = notification.account.avatar
, body = notification.account.note , body = notification.account.note
, clickUrl = "#account/" ++ (toString notification.account.id) , clickUrl = "#account/" ++ notification.account.id
} }
_ -> _ ->

View File

@ -1,6 +1,7 @@
module Init exposing (init) module Init exposing (init)
import Command import Command
import Mastodon.Decoder exposing (decodeClients)
import Navigation import Navigation
import Types exposing (..) import Types exposing (..)
import Update.AccountInfo import Update.AccountInfo
@ -13,12 +14,15 @@ import Util
init : Flags -> Navigation.Location -> ( Model, Cmd Msg ) init : Flags -> Navigation.Location -> ( Model, Cmd Msg )
init { registration, clients } location = init { registration, clients } location =
let let
decodedClients =
Result.withDefault [] <| decodeClients clients
( model, commands ) = ( model, commands ) =
Update.Route.update Update.Route.update
{ server = "" { server = ""
, currentTime = 0 , currentTime = 0
, registration = registration , registration = registration
, clients = clients , clients = decodedClients
, 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"
@ -41,4 +45,4 @@ init { registration, clients } location =
} }
in in
model model
! [ commands, Command.initCommands registration (List.head clients) (Util.extractAuthCode location) ] ! [ commands, Command.initCommands registration (List.head decodedClients) (Util.extractAuthCode location) ]

View File

@ -35,6 +35,8 @@ module Mastodon.ApiUrl
, search , search
) )
import Mastodon.Model exposing (StatusId(..))
apiPrefix : String apiPrefix : String
apiPrefix = apiPrefix =
@ -61,39 +63,39 @@ accounts =
apiPrefix ++ "/accounts/" apiPrefix ++ "/accounts/"
account : Int -> String account : String -> String
account id = account id =
accounts ++ (toString id) accounts ++ id
follow : Int -> String follow : String -> String
follow id = follow id =
accounts ++ (toString id) ++ "/follow" accounts ++ id ++ "/follow"
unfollow : Int -> String unfollow : String -> String
unfollow id = unfollow id =
accounts ++ (toString id) ++ "/unfollow" accounts ++ id ++ "/unfollow"
mute : Int -> String mute : String -> String
mute id = mute id =
accounts ++ (toString id) ++ "/mute" accounts ++ id ++ "/mute"
unmute : Int -> String unmute : String -> String
unmute id = unmute id =
accounts ++ (toString id) ++ "/unmute" accounts ++ id ++ "/unmute"
block : Int -> String block : String -> String
block id = block id =
accounts ++ (toString id) ++ "/block" accounts ++ id ++ "/block"
unblock : Int -> String unblock : String -> String
unblock id = unblock id =
accounts ++ (toString id) ++ "/unblock" accounts ++ id ++ "/unblock"
userAccount : String userAccount : String
@ -116,14 +118,14 @@ relationships =
accounts ++ "relationships" accounts ++ "relationships"
followers : Int -> String followers : String -> String
followers id = followers id =
(account id) ++ "/followers" account id ++ "/followers"
following : Int -> String following : String -> String
following id = following id =
(account id) ++ "/following" account id ++ "/following"
homeTimeline : String homeTimeline : String
@ -136,9 +138,9 @@ publicTimeline =
apiPrefix ++ "/timelines/public" apiPrefix ++ "/timelines/public"
accountTimeline : Int -> String accountTimeline : String -> String
accountTimeline id = accountTimeline id =
(account id) ++ "/statuses" account id ++ "/statuses"
favouriteTimeline : String favouriteTimeline : String
@ -171,34 +173,34 @@ statuses =
apiPrefix ++ "/statuses" apiPrefix ++ "/statuses"
context : Int -> String context : StatusId -> String
context id = context (StatusId id) =
statuses ++ "/" ++ (toString id) ++ "/context" statuses ++ "/" ++ id ++ "/context"
reblog : Int -> String reblog : StatusId -> String
reblog id = reblog (StatusId id) =
statuses ++ "/" ++ (toString id) ++ "/reblog" statuses ++ "/" ++ id ++ "/reblog"
status : Int -> String status : StatusId -> String
status id = status (StatusId id) =
statuses ++ "/" ++ (toString id) statuses ++ "/" ++ id
unreblog : Int -> String unreblog : StatusId -> String
unreblog id = unreblog (StatusId id) =
statuses ++ "/" ++ (toString id) ++ "/unreblog" statuses ++ "/" ++ id ++ "/unreblog"
favourite : Int -> String favourite : StatusId -> String
favourite id = favourite (StatusId id) =
statuses ++ "/" ++ (toString id) ++ "/favourite" statuses ++ "/" ++ id ++ "/favourite"
unfavourite : Int -> String unfavourite : StatusId -> String
unfavourite id = unfavourite (StatusId id) =
statuses ++ "/" ++ (toString id) ++ "/unfavourite" statuses ++ "/" ++ id ++ "/unfavourite"
streaming : String streaming : String

View File

@ -6,6 +6,7 @@ module Mastodon.Decoder
, attachmentDecoder , attachmentDecoder
, contextDecoder , contextDecoder
, decodeWebSocketMessage , decodeWebSocketMessage
, decodeClients
, mastodonErrorDecoder , mastodonErrorDecoder
, mentionDecoder , mentionDecoder
, notificationDecoder , notificationDecoder
@ -14,7 +15,6 @@ module Mastodon.Decoder
, relationshipDecoder , relationshipDecoder
, searchResultsDecoder , searchResultsDecoder
, statusDecoder , statusDecoder
, webSocketPayloadDecoder
, webSocketEventDecoder , webSocketEventDecoder
) )
@ -31,7 +31,7 @@ appRegistrationDecoder server scope =
|> Pipe.hardcoded scope |> Pipe.hardcoded scope
|> Pipe.required "client_id" Decode.string |> Pipe.required "client_id" Decode.string
|> Pipe.required "client_secret" Decode.string |> Pipe.required "client_secret" Decode.string
|> Pipe.required "id" Decode.int |> Pipe.required "id" idDecoder
|> Pipe.required "redirect_uri" Decode.string |> Pipe.required "redirect_uri" Decode.string
@ -52,7 +52,7 @@ accountDecoder =
|> Pipe.required "followers_count" Decode.int |> Pipe.required "followers_count" Decode.int
|> Pipe.required "following_count" Decode.int |> Pipe.required "following_count" Decode.int
|> Pipe.required "header" Decode.string |> Pipe.required "header" Decode.string
|> Pipe.required "id" Decode.int |> Pipe.required "id" idDecoder
|> Pipe.required "locked" Decode.bool |> Pipe.required "locked" Decode.bool
|> Pipe.required "note" Decode.string |> Pipe.required "note" Decode.string
|> Pipe.required "statuses_count" Decode.int |> Pipe.required "statuses_count" Decode.int
@ -70,7 +70,7 @@ applicationDecoder =
attachmentDecoder : Decode.Decoder Attachment attachmentDecoder : Decode.Decoder Attachment
attachmentDecoder = attachmentDecoder =
Pipe.decode Attachment Pipe.decode Attachment
|> Pipe.required "id" Decode.int |> Pipe.required "id" idDecoder
|> Pipe.required "type" Decode.string |> Pipe.required "type" Decode.string
|> Pipe.required "url" Decode.string |> Pipe.required "url" Decode.string
|> Pipe.optional "remote_url" Decode.string "" |> Pipe.optional "remote_url" Decode.string ""
@ -85,6 +85,19 @@ contextDecoder =
|> Pipe.required "descendants" (Decode.list statusDecoder) |> Pipe.required "descendants" (Decode.list statusDecoder)
clientDecoder : Decode.Decoder Client
clientDecoder =
Pipe.decode Client
|> Pipe.required "server" Decode.string
|> Pipe.required "token" Decode.string
|> Pipe.required "account" (Decode.maybe accountDecoder)
decodeClients : String -> Result String (List Client)
decodeClients json =
Decode.decodeString (Decode.list clientDecoder) json
mastodonErrorDecoder : Decode.Decoder String mastodonErrorDecoder : Decode.Decoder String
mastodonErrorDecoder = mastodonErrorDecoder =
Decode.field "error" Decode.string Decode.field "error" Decode.string
@ -93,7 +106,7 @@ mastodonErrorDecoder =
mentionDecoder : Decode.Decoder Mention mentionDecoder : Decode.Decoder Mention
mentionDecoder = mentionDecoder =
Pipe.decode Mention Pipe.decode Mention
|> Pipe.required "id" Decode.int |> Pipe.required "id" idDecoder
|> Pipe.required "url" Decode.string |> Pipe.required "url" Decode.string
|> Pipe.required "username" Decode.string |> Pipe.required "username" Decode.string
|> Pipe.required "acct" Decode.string |> Pipe.required "acct" Decode.string
@ -102,7 +115,7 @@ mentionDecoder =
notificationDecoder : Decode.Decoder Notification notificationDecoder : Decode.Decoder Notification
notificationDecoder = notificationDecoder =
Pipe.decode Notification Pipe.decode Notification
|> Pipe.required "id" Decode.int |> Pipe.required "id" idDecoder
|> Pipe.required "type" Decode.string |> Pipe.required "type" Decode.string
|> Pipe.required "created_at" Decode.string |> Pipe.required "created_at" Decode.string
|> Pipe.required "account" accountDecoder |> Pipe.required "account" accountDecoder
@ -112,7 +125,7 @@ notificationDecoder =
relationshipDecoder : Decode.Decoder Relationship relationshipDecoder : Decode.Decoder Relationship
relationshipDecoder = relationshipDecoder =
Pipe.decode Relationship Pipe.decode Relationship
|> Pipe.required "id" Decode.int |> Pipe.required "id" idDecoder
|> Pipe.required "blocking" Decode.bool |> Pipe.required "blocking" Decode.bool
|> Pipe.required "followed_by" Decode.bool |> Pipe.required "followed_by" Decode.bool
|> Pipe.required "following" Decode.bool |> Pipe.required "following" Decode.bool
@ -140,6 +153,21 @@ searchResultsDecoder =
|> Pipe.required "hashtags" (Decode.list Decode.string) |> Pipe.required "hashtags" (Decode.list Decode.string)
idDecoder : Decode.Decoder String
idDecoder =
-- Note: since v2.0.0 of the Mastodon API, ids are treated as strings, so we
-- treat all ids as strings.
Decode.oneOf
[ Decode.string
, Decode.int |> Decode.map toString
]
statusIdDecoder : Decode.Decoder StatusId
statusIdDecoder =
idDecoder |> Decode.map StatusId
statusDecoder : Decode.Decoder Status statusDecoder : Decode.Decoder Status
statusDecoder = statusDecoder =
Pipe.decode Status Pipe.decode Status
@ -149,9 +177,9 @@ statusDecoder =
|> Pipe.required "created_at" Decode.string |> Pipe.required "created_at" Decode.string
|> Pipe.optional "favourited" (Decode.nullable Decode.bool) Nothing |> Pipe.optional "favourited" (Decode.nullable Decode.bool) Nothing
|> Pipe.required "favourites_count" Decode.int |> Pipe.required "favourites_count" Decode.int
|> Pipe.required "id" Decode.int |> Pipe.required "id" statusIdDecoder
|> Pipe.required "in_reply_to_account_id" (Decode.nullable Decode.int) |> Pipe.required "in_reply_to_account_id" (Decode.nullable idDecoder)
|> Pipe.required "in_reply_to_id" (Decode.nullable Decode.int) |> Pipe.required "in_reply_to_id" (Decode.nullable statusIdDecoder)
|> Pipe.required "media_attachments" (Decode.list attachmentDecoder) |> Pipe.required "media_attachments" (Decode.list attachmentDecoder)
|> Pipe.required "mentions" (Decode.list mentionDecoder) |> Pipe.required "mentions" (Decode.list mentionDecoder)
|> Pipe.optional "reblog" (Decode.lazy (\_ -> Decode.nullable reblogDecoder)) Nothing |> Pipe.optional "reblog" (Decode.lazy (\_ -> Decode.nullable reblogDecoder)) Nothing
@ -165,49 +193,34 @@ statusDecoder =
|> Pipe.required "visibility" Decode.string |> Pipe.required "visibility" Decode.string
webSocketPayloadDecoder : Decode.Decoder WebSocketPayload
webSocketPayloadDecoder =
Decode.oneOf
[ Decode.map StringPayload Decode.string
, Decode.map IntPayload Decode.int
]
webSocketEventDecoder : Decode.Decoder WebSocketMessage webSocketEventDecoder : Decode.Decoder WebSocketMessage
webSocketEventDecoder = webSocketEventDecoder =
Pipe.decode WebSocketMessage Pipe.decode WebSocketMessage
|> Pipe.required "event" Decode.string |> Pipe.required "event" Decode.string
|> Pipe.required "payload" webSocketPayloadDecoder |> Pipe.required "payload"
-- NOTE: as of the Mastodon API v2.0.0, ids may be either ints or
-- strings. If we receive an int (most likely for the delete event),
-- we cast it to a string.
(Decode.oneOf
[ Decode.string
, Decode.int |> Decode.map toString
]
)
decodeWebSocketMessage : String -> WebSocketEvent decodeWebSocketMessage : String -> WebSocketEvent
decodeWebSocketMessage message = decodeWebSocketMessage message =
case (Decode.decodeString webSocketEventDecoder message) of case (Decode.decodeString webSocketEventDecoder message) of
Ok message -> Ok { event, payload } ->
case message.event of case event of
"update" -> "update" ->
case message.payload of StatusUpdateEvent (Decode.decodeString statusDecoder payload)
StringPayload payload ->
StatusUpdateEvent (Decode.decodeString statusDecoder payload)
_ ->
ErrorEvent "WS status update event payload must be a string"
"delete" -> "delete" ->
case message.payload of StatusDeleteEvent (StatusId payload)
IntPayload payload ->
StatusDeleteEvent <| Ok payload
_ ->
ErrorEvent "WS status delete event payload must be an int"
"notification" -> "notification" ->
case message.payload of NotificationEvent (Decode.decodeString notificationDecoder payload)
StringPayload payload ->
NotificationEvent (Decode.decodeString notificationDecoder payload)
_ ->
ErrorEvent "WS notification event payload must be an string"
event -> event ->
ErrorEvent <| "Unknown WS event " ++ event ErrorEvent <| "Unknown WS event " ++ event

View File

@ -61,7 +61,7 @@ accountEncoder account =
, ( "followers_count", Encode.int account.followers_count ) , ( "followers_count", Encode.int account.followers_count )
, ( "following_count", Encode.int account.following_count ) , ( "following_count", Encode.int account.following_count )
, ( "header", Encode.string account.header ) , ( "header", Encode.string account.header )
, ( "id", Encode.int account.id ) , ( "id", Encode.string account.id )
, ( "locked", Encode.bool account.locked ) , ( "locked", Encode.bool account.locked )
, ( "note", Encode.string account.note ) , ( "note", Encode.string account.note )
, ( "statuses_count", Encode.int account.statuses_count ) , ( "statuses_count", Encode.int account.statuses_count )
@ -86,18 +86,23 @@ registrationEncoder registration =
, ( "scope", Encode.string registration.scope ) , ( "scope", Encode.string registration.scope )
, ( "client_id", Encode.string registration.client_id ) , ( "client_id", Encode.string registration.client_id )
, ( "client_secret", Encode.string registration.client_secret ) , ( "client_secret", Encode.string registration.client_secret )
, ( "id", Encode.int registration.id ) , ( "id", Encode.string registration.id )
, ( "redirect_uri", Encode.string registration.redirect_uri ) , ( "redirect_uri", Encode.string registration.redirect_uri )
] ]
encodeStatusId : StatusId -> Encode.Value
encodeStatusId (StatusId id) =
Encode.string id
statusRequestBodyEncoder : StatusRequestBody -> Encode.Value statusRequestBodyEncoder : StatusRequestBody -> Encode.Value
statusRequestBodyEncoder statusData = statusRequestBodyEncoder statusData =
Encode.object Encode.object
[ ( "status", Encode.string statusData.status ) [ ( "status", Encode.string statusData.status )
, ( "in_reply_to_id", encodeMaybe Encode.int statusData.in_reply_to_id ) , ( "in_reply_to_id", encodeMaybe encodeStatusId statusData.in_reply_to_id )
, ( "spoiler_text", encodeMaybe Encode.string statusData.spoiler_text ) , ( "spoiler_text", encodeMaybe Encode.string statusData.spoiler_text )
, ( "sensitive", Encode.bool statusData.sensitive ) , ( "sensitive", Encode.bool statusData.sensitive )
, ( "visibility", Encode.string statusData.visibility ) , ( "visibility", Encode.string statusData.visibility )
, ( "media_ids", Encode.list (List.map Encode.int statusData.media_ids) ) , ( "media_ids", Encode.list (List.map Encode.string statusData.media_ids) )
] ]

View File

@ -3,6 +3,7 @@ module Mastodon.Helper
( extractReblog ( extractReblog
, aggregateNotifications , aggregateNotifications
, addNotificationToAggregates , addNotificationToAggregates
, extractStatusId
, getReplyPrefix , getReplyPrefix
, notificationToAggregate , notificationToAggregate
, sameAccount , sameAccount
@ -187,6 +188,11 @@ sameAccount { id, acct, username } account =
id == account.id && acct == account.acct && username == account.username id == account.id && acct == account.acct && username == account.username
statusReferenced : Int -> Status -> Bool statusReferenced : StatusId -> Status -> Bool
statusReferenced id status = statusReferenced id status =
status.id == id || (extractReblog status).id == id status.id == id || (extractReblog status).id == id
extractStatusId : StatusId -> String
extractStatusId (StatusId id) =
id

View File

@ -17,12 +17,13 @@ module Mastodon.Model
, Tag , Tag
, SearchResults , SearchResults
, Status , Status
, StatusId(..)
, StatusRequestBody , StatusRequestBody
) )
type alias AccountId = type alias AccountId =
Int String
type alias AuthCode = type alias AuthCode =
@ -41,6 +42,10 @@ type alias Server =
String String
type StatusId
= StatusId String
type alias StatusCode = type alias StatusCode =
Int Int
@ -71,7 +76,7 @@ type alias AppRegistration =
, scope : String , scope : String
, client_id : ClientId , client_id : ClientId
, client_secret : ClientSecret , client_secret : ClientSecret
, id : Int , id : String
, redirect_uri : String , redirect_uri : String
} }
@ -101,7 +106,7 @@ type alias Application =
type alias Attachment = type alias Attachment =
-- type_: -- "image", "video", "gifv" -- type_: -- "image", "video", "gifv"
{ id : Int { id : String
, type_ : String , type_ : String
, url : String , url : String
, remote_url : String , remote_url : String
@ -139,7 +144,7 @@ type alias Notification =
- account: The Account sending the notification to the user - account: The Account sending the notification to the user
- status: The Status associated with the notification, if applicable - status: The Status associated with the notification, if applicable
-} -}
{ id : Int { id : String
, type_ : String , type_ : String
, created_at : String , created_at : String
, account : Account , account : Account
@ -154,7 +159,7 @@ type alias AccountNotificationDate =
type alias NotificationAggregate = type alias NotificationAggregate =
{ id : Int { id : String
, type_ : String , type_ : String
, status : Maybe Status , status : Maybe Status
, accounts : List AccountNotificationDate , accounts : List AccountNotificationDate
@ -167,7 +172,7 @@ type Reblog
type alias Relationship = type alias Relationship =
{ id : Int { id : String
, blocking : Bool , blocking : Bool
, followed_by : Bool , followed_by : Bool
, following : Bool , following : Bool
@ -190,9 +195,9 @@ type alias Status =
, created_at : String , created_at : String
, favourited : Maybe Bool , favourited : Maybe Bool
, favourites_count : Int , favourites_count : Int
, id : Int , id : StatusId
, in_reply_to_account_id : Maybe Int , in_reply_to_account_id : Maybe String
, in_reply_to_id : Maybe Int , in_reply_to_id : Maybe StatusId
, media_attachments : List Attachment , media_attachments : List Attachment
, mentions : List Mention , mentions : List Mention
, reblog : Maybe Reblog , reblog : Maybe Reblog
@ -214,11 +219,11 @@ type alias StatusRequestBody =
-- spoiler_text: text to be shown as a warning before the actual content -- spoiler_text: text to be shown as a warning before the actual content
-- visibility: either "direct", "private", "unlisted" or "public" -- visibility: either "direct", "private", "unlisted" or "public"
{ status : String { status : String
, in_reply_to_id : Maybe Int , in_reply_to_id : Maybe StatusId
, spoiler_text : Maybe String , spoiler_text : Maybe String
, sensitive : Bool , sensitive : Bool
, visibility : String , visibility : String
, media_ids : List Int , media_ids : List String
} }

View File

@ -3,7 +3,6 @@ module Mastodon.WebSocket
( StreamType(..) ( StreamType(..)
, WebSocketEvent(..) , WebSocketEvent(..)
, WebSocketMessage , WebSocketMessage
, WebSocketPayload(..)
, subscribeToWebSockets , subscribeToWebSockets
) )
@ -23,18 +22,13 @@ type StreamType
type WebSocketEvent type WebSocketEvent
= StatusUpdateEvent (Result String Status) = StatusUpdateEvent (Result String Status)
| NotificationEvent (Result String Notification) | NotificationEvent (Result String Notification)
| StatusDeleteEvent (Result String Int) | StatusDeleteEvent StatusId
| ErrorEvent String | ErrorEvent String
type WebSocketPayload
= StringPayload String
| IntPayload Int
type alias WebSocketMessage = type alias WebSocketMessage =
{ event : String { event : String
, payload : WebSocketPayload , payload : String
} }

View File

@ -9,7 +9,7 @@ import Time exposing (Time)
type alias Flags = type alias Flags =
{ clients : List Client { clients : String
, registration : Maybe AppRegistration , registration : Maybe AppRegistration
} }
@ -17,7 +17,7 @@ type alias Flags =
type DraftMsg type DraftMsg
= ClearDraft = ClearDraft
| CloseAutocomplete | CloseAutocomplete
| RemoveMedia Int | RemoveMedia String
| ResetAutocomplete Bool | ResetAutocomplete Bool
| SelectAccount String | SelectAccount String
| SetAutoState Autocomplete.Msg | SetAutoState Autocomplete.Msg
@ -72,10 +72,10 @@ type MastodonMsg
| Notifications Bool (MastodonResult (List Notification)) | Notifications Bool (MastodonResult (List Notification))
| Reblogged (MastodonResult Status) | Reblogged (MastodonResult Status)
| SearchResultsReceived (MastodonResult SearchResults) | SearchResultsReceived (MastodonResult SearchResults)
| StatusDeleted (MastodonResult Int) | StatusDeleted (MastodonResult StatusId)
| StatusPosted (MastodonResult Status) | StatusPosted (MastodonResult Status)
| ThreadStatusLoaded Int (MastodonResult Status) | ThreadStatusLoaded StatusId (MastodonResult Status)
| ThreadContextLoaded Int (MastodonResult Context) | ThreadContextLoaded StatusId (MastodonResult Context)
| Unreblogged (MastodonResult Status) | Unreblogged (MastodonResult Status)
@ -103,7 +103,7 @@ type Msg
| ClearError Int | ClearError Int
| ConfirmCancelled Msg | ConfirmCancelled Msg
| Confirmed Msg | Confirmed Msg
| DeleteStatus Int | DeleteStatus StatusId
| DraftEvent DraftMsg | DraftEvent DraftMsg
| FilterNotifications NotificationFilter | FilterNotifications NotificationFilter
| FollowAccount Account | FollowAccount Account

View File

@ -20,7 +20,7 @@ import Util
autocompleteUpdateConfig : Autocomplete.UpdateConfig Msg Account autocompleteUpdateConfig : Autocomplete.UpdateConfig Msg Account
autocompleteUpdateConfig = autocompleteUpdateConfig =
Autocomplete.updateConfig Autocomplete.updateConfig
{ toId = .id >> toString { toId = .id
, onKeyDown = , onKeyDown =
\code maybeId -> \code maybeId ->
if code == 38 || code == 40 then if code == 38 || code == 40 then
@ -173,7 +173,7 @@ update draftMsg currentUser ({ draft } as model) =
SelectAccount id -> SelectAccount id ->
let let
account = account =
List.filter (\account -> toString account.id == id) draft.autoAccounts List.filter (\account -> account.id == id) draft.autoAccounts
|> List.head |> List.head
stringToAtPos = stringToAtPos =

View File

@ -2,6 +2,7 @@ module Update.Main exposing (update)
import Command import Command
import List.Extra exposing (removeAt) import List.Extra exposing (removeAt)
import Mastodon.Helper exposing (extractStatusId)
import Mastodon.Model exposing (..) import Mastodon.Model exposing (..)
import Navigation import Navigation
import Types exposing (..) import Types exposing (..)
@ -155,7 +156,7 @@ update msg model =
OpenThread status -> OpenThread status ->
{ model | currentView = ThreadView (Thread Nothing Nothing) } { model | currentView = ThreadView (Thread Nothing Nothing) }
! [ Navigation.newUrl <| "#thread/" ++ (toString status.id) ] ! [ Navigation.newUrl <| "#thread/" ++ extractStatusId status.id ]
FollowAccount account -> FollowAccount account ->
model ! [ Command.follow (List.head model.clients) account ] model ! [ Command.follow (List.head model.clients) account ]

View File

@ -2,7 +2,7 @@ module Update.Mastodon exposing (update)
import Command import Command
import Navigation import Navigation
import Mastodon.Helper import Mastodon.Helper exposing (extractStatusId)
import Mastodon.Model exposing (..) import Mastodon.Model exposing (..)
import Task import Task
import Types exposing (..) import Types exposing (..)
@ -116,7 +116,7 @@ update msg ({ accountInfo, search } as model) =
_ -> _ ->
model.currentView model.currentView
} }
! [ Command.scrollToThreadStatus <| toString id ] ! [ Command.scrollToThreadStatus <| extractStatusId id ]
Err error -> Err error ->
{ model { model
@ -137,7 +137,7 @@ update msg ({ accountInfo, search } as model) =
_ -> _ ->
model.currentView model.currentView
} }
! [ Command.scrollToThreadStatus <| toString id ] ! [ Command.scrollToThreadStatus <| extractStatusId id ]
Err error -> Err error ->
{ model { model

View File

@ -1,6 +1,7 @@
module Update.Route exposing (update) module Update.Route exposing (update)
import Command import Command
import Mastodon.Model exposing (StatusId(..))
import Types exposing (..) import Types exposing (..)
import Update.AccountInfo import Update.AccountInfo
import Update.Timeline import Update.Timeline
@ -8,9 +9,9 @@ import UrlParser exposing (..)
type Route type Route
= AccountFollowersRoute Int = AccountFollowersRoute String
| AccountFollowingRoute Int | AccountFollowingRoute String
| AccountRoute Int | AccountRoute String
| AccountSelectorRoute | AccountSelectorRoute
| BlocksRoute | BlocksRoute
| FavoriteTimelineRoute | FavoriteTimelineRoute
@ -19,7 +20,12 @@ type Route
| LocalTimelineRoute | LocalTimelineRoute
| MutesRoute | MutesRoute
| SearchRoute | SearchRoute
| ThreadRoute Int | ThreadRoute StatusId
statusIdParser : Parser (StatusId -> a) a
statusIdParser =
custom "id" (Ok << StatusId)
route : Parser (Route -> a) a route : Parser (Route -> a) a
@ -29,12 +35,12 @@ route =
, map GlobalTimelineRoute (s "global" </> top) , map GlobalTimelineRoute (s "global" </> top)
, map FavoriteTimelineRoute (s "favorites" </> top) , map FavoriteTimelineRoute (s "favorites" </> top)
, map HashtagRoute (s "hashtag" </> string) , map HashtagRoute (s "hashtag" </> string)
, map ThreadRoute (s "thread" </> int) , map ThreadRoute (s "thread" </> statusIdParser)
, map BlocksRoute (s "blocks" </> top) , map BlocksRoute (s "blocks" </> top)
, map MutesRoute (s "mutes" </> top) , map MutesRoute (s "mutes" </> top)
, map AccountFollowersRoute (s "account" </> int </> s "followers") , map AccountFollowersRoute (s "account" </> string </> s "followers")
, map AccountFollowingRoute (s "account" </> int </> s "following") , map AccountFollowingRoute (s "account" </> string </> s "following")
, map AccountRoute (s "account" </> int) , map AccountRoute (s "account" </> string)
, map AccountSelectorRoute (s "accounts") , map AccountSelectorRoute (s "accounts")
, map SearchRoute (s "search" </> top) , map SearchRoute (s "search" </> top)
] ]

View File

@ -47,7 +47,7 @@ cleanUnfollow account currentUser timeline =
{ timeline | entries = List.filter keep timeline.entries } { timeline | entries = List.filter keep timeline.entries }
deleteStatusFromCurrentView : Int -> Model -> CurrentView deleteStatusFromCurrentView : StatusId -> Model -> CurrentView
deleteStatusFromCurrentView id model = deleteStatusFromCurrentView id model =
-- Note: account timeline is already cleaned in deleteStatusFromAllTimelines -- Note: account timeline is already cleaned in deleteStatusFromAllTimelines
case model.currentView of case model.currentView of
@ -78,7 +78,7 @@ deleteStatusFromCurrentView id model =
currentView currentView
deleteStatusFromAllTimelines : Int -> Model -> Model deleteStatusFromAllTimelines : StatusId -> Model -> Model
deleteStatusFromAllTimelines id ({ accountInfo } as model) = deleteStatusFromAllTimelines id ({ accountInfo } as model) =
let let
accountTimeline = accountTimeline =
@ -95,7 +95,7 @@ deleteStatusFromAllTimelines id ({ accountInfo } as model) =
} }
deleteStatusFromNotifications : Int -> Timeline NotificationAggregate -> Timeline NotificationAggregate deleteStatusFromNotifications : StatusId -> Timeline NotificationAggregate -> Timeline NotificationAggregate
deleteStatusFromNotifications statusId notifications = deleteStatusFromNotifications statusId notifications =
let let
update notification = update notification =
@ -109,7 +109,7 @@ deleteStatusFromNotifications statusId notifications =
{ notifications | entries = List.filter update notifications.entries } { notifications | entries = List.filter update notifications.entries }
deleteStatus : Int -> Timeline Status -> Timeline Status deleteStatus : StatusId -> Timeline Status -> Timeline Status
deleteStatus statusId ({ entries } as timeline) = deleteStatus statusId ({ entries } as timeline) =
{ timeline { timeline
| entries = List.filter (not << Mastodon.Helper.statusReferenced statusId) entries | entries = List.filter (not << Mastodon.Helper.statusReferenced statusId) entries
@ -283,7 +283,7 @@ update append entries links timeline =
} }
updateWithBoolFlag : Int -> Bool -> (Status -> Status) -> Model -> Model updateWithBoolFlag : StatusId -> Bool -> (Status -> Status) -> Model -> Model
updateWithBoolFlag statusId flag statusUpdater ({ accountInfo } as model) = updateWithBoolFlag statusId flag statusUpdater ({ accountInfo } as model) =
let let
updateStatus status = updateStatus status =

View File

@ -30,13 +30,8 @@ update msg model =
Err error -> Err error ->
{ model | errors = addErrorNotification error model } ! [] { model | errors = addErrorNotification error model } ! []
Mastodon.WebSocket.StatusDeleteEvent result -> Mastodon.WebSocket.StatusDeleteEvent id ->
case result of Update.Timeline.deleteStatusFromAllTimelines id model ! []
Ok id ->
Update.Timeline.deleteStatusFromAllTimelines id model ! []
Err error ->
{ model | errors = addErrorNotification error model } ! []
Mastodon.WebSocket.NotificationEvent result -> Mastodon.WebSocket.NotificationEvent result ->
case result of case result of
@ -76,13 +71,8 @@ update msg model =
Err error -> Err error ->
{ model | errors = addErrorNotification error model } ! [] { model | errors = addErrorNotification error model } ! []
Mastodon.WebSocket.StatusDeleteEvent result -> Mastodon.WebSocket.StatusDeleteEvent id ->
case result of Update.Timeline.deleteStatusFromAllTimelines id model ! []
Ok id ->
Update.Timeline.deleteStatusFromAllTimelines id model ! []
Err error ->
{ model | errors = addErrorNotification error model } ! []
_ -> _ ->
model ! [] model ! []
@ -104,13 +94,8 @@ update msg model =
Err error -> Err error ->
{ model | errors = addErrorNotification error model } ! [] { model | errors = addErrorNotification error model } ! []
Mastodon.WebSocket.StatusDeleteEvent result -> Mastodon.WebSocket.StatusDeleteEvent id ->
case result of Update.Timeline.deleteStatusFromAllTimelines id model ! []
Ok id ->
Update.Timeline.deleteStatusFromAllTimelines id model ! []
Err error ->
{ model | errors = addErrorNotification error model } ! []
_ -> _ ->
model ! [] model ! []

View File

@ -6,7 +6,7 @@ import Html.Events exposing (..)
import Html.Keyed as Keyed import Html.Keyed as Keyed
import Html.Lazy as Lazy import Html.Lazy as Lazy
import List.Extra exposing (find) import List.Extra exposing (find)
import Mastodon.Helper import Mastodon.Helper exposing (extractStatusId)
import Mastodon.Model exposing (..) import Mastodon.Model exposing (..)
import Types exposing (..) import Types exposing (..)
import View.Common as Common import View.Common as Common
@ -62,7 +62,7 @@ followView currentUser relationship account =
, div [ class "userinfo" ] , div [ class "userinfo" ]
[ strong [] [ strong []
[ a [ a
[ href <| "#account/" ++ (toString account.id) ] [ href <| "#account/" ++ account.id ]
[ text <| [ text <|
if account.display_name /= "" then if account.display_name /= "" then
account.display_name account.display_name
@ -148,7 +148,7 @@ accountFollowView : CurrentAccountView -> CurrentUser -> AccountInfo -> Html Msg
accountFollowView view currentUser accountInfo = accountFollowView view currentUser accountInfo =
let let
keyedEntry account = keyedEntry account =
( toString account.id ( account.id
, li [ class "list-group-item status" ] , li [ class "list-group-item status" ]
[ followView [ followView
currentUser currentUser
@ -179,7 +179,7 @@ accountTimelineView : CurrentUser -> AccountInfo -> Html Msg
accountTimelineView currentUser accountInfo = accountTimelineView currentUser accountInfo =
let let
keyedEntry status = keyedEntry status =
( toString status.id ( extractStatusId status.id
, Lazy.lazy (statusEntryView "account" "status" currentUser) status , Lazy.lazy (statusEntryView "account" "status" currentUser) status
) )
@ -221,17 +221,17 @@ counterLinks subView account =
in in
div [ class "row account-infos" ] div [ class "row account-infos" ]
[ counterLink [ counterLink
("#account/" ++ (toString account.id)) ("#account/" ++ account.id)
"Statuses" "Statuses"
statuses_count statuses_count
(subView == AccountStatusesView) (subView == AccountStatusesView)
, counterLink , counterLink
("#account/" ++ (toString account.id) ++ "/following") ("#account/" ++ account.id ++ "/following")
"Following" "Following"
following_count following_count
(subView == AccountFollowingView) (subView == AccountFollowingView)
, counterLink , counterLink
("#account/" ++ (toString account.id) ++ "/followers") ("#account/" ++ account.id ++ "/followers")
"Followers" "Followers"
followers_count followers_count
(subView == AccountFollowersView) (subView == AccountFollowersView)

View File

@ -35,7 +35,7 @@ blockView currentUser account =
, div [ class "userinfo" ] , div [ class "userinfo" ]
[ strong [] [ strong []
[ a [ a
[ href <| "#account/" ++ (toString account.id) ] [ href <| "#account/" ++ account.id ]
[ text <| [ text <|
if account.display_name /= "" then if account.display_name /= "" then
account.display_name account.display_name
@ -60,7 +60,7 @@ blocksView : Model -> Html Msg
blocksView { currentUser, currentView, blocks, location } = blocksView { currentUser, currentView, blocks, location } =
let let
keyedEntry account = keyedEntry account =
( toString account.id ( account.id
, blockView currentUser account , blockView currentUser account
) )

View File

@ -36,7 +36,7 @@ accountLink external account =
if external then if external then
target "_blank" target "_blank"
else else
href <| "#account/" ++ (toString account.id) href <| "#account/" ++ account.id
in in
a a
[ href account.url [ href account.url
@ -52,7 +52,7 @@ accountAvatarLink external account =
if external then if external then
target "_blank" target "_blank"
else else
href <| "#account/" ++ (toString account.id) href <| "#account/" ++ account.id
avatarClass = avatarClass =
if external then if external then

View File

@ -65,7 +65,7 @@ viewConfig =
} }
in in
Autocomplete.viewConfig Autocomplete.viewConfig
{ toId = .id >> toString { toId = .id
, ul = [ class "list-group autocomplete-list" ] , ul = [ class "list-group autocomplete-list" ]
, li = customizedLi , li = customizedLi
} }

View File

@ -47,7 +47,7 @@ createLinkNode attrs children mentions =
case (getMentionForLink attrs mentions) of case (getMentionForLink attrs mentions) of
Just mention -> Just mention ->
Html.node "a" Html.node "a"
(replaceHref ("#account/" ++ (toString mention.id)) attrs) (replaceHref ("#account/" ++ mention.id) attrs)
(toVirtualDom mentions children) (toVirtualDom mentions children)
Nothing -> Nothing ->

View File

@ -35,7 +35,7 @@ muteView currentUser account =
, div [ class "userinfo" ] , div [ class "userinfo" ]
[ strong [] [ strong []
[ a [ a
[ href <| "#account/" ++ (toString account.id) ] [ href <| "#account/" ++ account.id ]
[ text <| [ text <|
if account.display_name /= "" then if account.display_name /= "" then
account.display_name account.display_name
@ -60,7 +60,7 @@ mutesView : Model -> Html Msg
mutesView { currentUser, currentView, mutes, location } = mutesView { currentUser, currentView, mutes, location } =
let let
keyedEntry account = keyedEntry account =
( toString account.id ( account.id
, muteView currentUser account , muteView currentUser account
) )

View File

@ -119,7 +119,7 @@ notificationFollowView currentUser { accounts } =
, formatContent account.note [] , formatContent account.note []
|> div |> div
[ class "status-text" [ class "status-text"
, onClick <| Navigate ("#account/" ++ (toString account.id)) , onClick <| Navigate ("#account/" ++ account.id)
] ]
] ]
in in
@ -170,7 +170,7 @@ notificationListView : CurrentUser -> NotificationFilter -> Timeline Notificatio
notificationListView currentUser filter notifications = notificationListView currentUser filter notifications =
let let
keyedEntry notification = keyedEntry notification =
( toString notification.id ( notification.id
, Lazy.lazy2 notificationEntryView currentUser notification , Lazy.lazy2 notificationEntryView currentUser notification
) )

View File

@ -20,7 +20,7 @@ accountListView accounts =
, formatContent account.note [] , formatContent account.note []
|> div |> div
[ class "status-text" [ class "status-text"
, onClick <| Navigate ("#account/" ++ (toString account.id)) , onClick <| Navigate ("#account/" ++ account.id)
] ]
] ]
in in

View File

@ -9,7 +9,7 @@ import Html exposing (..)
import Html.Attributes exposing (..) import Html.Attributes exposing (..)
import Html.Keyed as Keyed import Html.Keyed as Keyed
import Html.Lazy as Lazy import Html.Lazy as Lazy
import Mastodon.Helper import Mastodon.Helper exposing (extractStatusId)
import Mastodon.Model exposing (..) import Mastodon.Model exposing (..)
import Types exposing (..) import Types exposing (..)
import View.Common as Common import View.Common as Common
@ -33,7 +33,7 @@ attachmentPreview context sensitive attachments ({ url, preview_url } as attachm
False False
attId = attId =
"att" ++ (toString attachment.id) ++ context "att" ++ attachment.id ++ context
media = media =
a a
@ -68,7 +68,7 @@ attachmentListView : String -> Status -> Html Msg
attachmentListView context { media_attachments, sensitive } = attachmentListView context { media_attachments, sensitive } =
let let
keyedEntry attachments attachment = keyedEntry attachments attachment =
( toString attachment.id ( attachment.id
, attachmentPreview context sensitive attachments attachment , attachmentPreview context sensitive attachments attachment
) )
in in
@ -158,7 +158,7 @@ statusContentView context status =
-- Note: Spoilers are dealt with using pure CSS. -- Note: Spoilers are dealt with using pure CSS.
let let
statusId = statusId =
"spoiler" ++ (toString status.id) ++ context "spoiler" ++ extractStatusId status.id ++ context
in in
div [ class "status-text spoiled" ] div [ class "status-text spoiled" ]
[ div [ div
@ -189,7 +189,7 @@ statusEntryView context className currentUser status =
liAttributes = liAttributes =
[ class <| "list-group-item " ++ className ++ " " ++ nsfwClass ] [ class <| "list-group-item " ++ className ++ " " ++ nsfwClass ]
++ if context == "thread" then ++ if context == "thread" then
[ id <| "thread-status-" ++ (toString status.id) ] [ id <| "thread-status-" ++ extractStatusId status.id ]
else else
[] []
in in
@ -203,7 +203,7 @@ statusView : String -> Status -> Html Msg
statusView context ({ account, content, media_attachments, reblog, mentions } as status) = statusView context ({ account, content, media_attachments, reblog, mentions } as status) =
let let
accountLinkAttributes = accountLinkAttributes =
[ href <| "#account/" ++ (toString account.id) ] [ href <| "#account/" ++ account.id ]
in in
case reblog of case reblog of
Just (Reblog reblog) -> Just (Reblog reblog) ->

View File

@ -4,6 +4,7 @@ import View.Common as Common
import Html exposing (..) import Html exposing (..)
import Html.Attributes exposing (..) import Html.Attributes exposing (..)
import Html.Keyed as Keyed import Html.Keyed as Keyed
import Mastodon.Helper exposing (extractStatusId)
import Mastodon.Model exposing (..) import Mastodon.Model exposing (..)
import Types exposing (..) import Types exposing (..)
import View.Status exposing (statusEntryView) import View.Status exposing (statusEntryView)
@ -36,7 +37,7 @@ threadStatuses currentUser thread =
status status
keyedEntry status = keyedEntry status =
( toString status.id, threadEntry status ) ( extractStatusId status.id, threadEntry status )
in in
Keyed.ul [ id "thread", class "list-group timeline" ] <| Keyed.ul [ id "thread", class "list-group timeline" ] <|
List.map keyedEntry statuses List.map keyedEntry statuses

View File

@ -11,6 +11,7 @@ import Html exposing (..)
import Html.Attributes exposing (..) import Html.Attributes exposing (..)
import Html.Keyed as Keyed import Html.Keyed as Keyed
import Html.Lazy as Lazy import Html.Lazy as Lazy
import Mastodon.Helper exposing (extractStatusId)
import Mastodon.Model exposing (..) import Mastodon.Model exposing (..)
import Types exposing (..) import Types exposing (..)
import View.Common as Common import View.Common as Common
@ -52,7 +53,7 @@ timelineView : CurrentUser -> Timeline Status -> Html Msg
timelineView currentUser timeline = timelineView currentUser timeline =
let let
keyedEntry status = keyedEntry status =
( toString id, statusEntryView timeline.id "" currentUser status ) ( extractStatusId status.id, statusEntryView timeline.id "" currentUser status )
entries = entries =
List.map keyedEntry timeline.entries List.map keyedEntry timeline.entries

View File

@ -12,7 +12,7 @@ accountSkro =
, followers_count = 77 , followers_count = 77
, following_count = 80 , following_count = 80
, header = "" , header = ""
, id = 1391 , id = "1391"
, locked = False , locked = False
, note = "Skro note" , note = "Skro note"
, statuses_count = 161 , statuses_count = 161
@ -30,7 +30,7 @@ accountVjousse =
, followers_count = 68 , followers_count = 68
, following_count = 31 , following_count = 31
, header = "" , header = ""
, id = 26303 , id = "26303"
, locked = False , locked = False
, note = "Vjousse note" , note = "Vjousse note"
, statuses_count = 88 , statuses_count = 88
@ -48,7 +48,7 @@ accountNico =
, followers_count = 162 , followers_count = 162
, following_count = 79 , following_count = 79
, header = "" , header = ""
, id = 17784 , id = "17784"
, locked = False , locked = False
, note = "Niko note" , note = "Niko note"
, statuses_count = 358 , statuses_count = 358
@ -66,7 +66,7 @@ accountPloum =
, followers_count = 1129 , followers_count = 1129
, following_count = 91 , following_count = 91
, header = "" , header = ""
, id = 6840 , id = "6840"
, locked = False , locked = False
, note = "Ploum note" , note = "Ploum note"
, statuses_count = 601 , statuses_count = 601
@ -83,7 +83,7 @@ statusNico =
, created_at = "2017-04-24T20:12:20.922Z" , created_at = "2017-04-24T20:12:20.922Z"
, favourited = Nothing , favourited = Nothing
, favourites_count = 0 , favourites_count = 0
, id = 737931 , id = StatusId "737931"
, in_reply_to_account_id = Nothing , in_reply_to_account_id = Nothing
, in_reply_to_id = Nothing , in_reply_to_id = Nothing
, media_attachments = [] , media_attachments = []
@ -108,12 +108,12 @@ statusNicoToVjousse =
, created_at = "2017-04-24T20:16:20.922Z" , created_at = "2017-04-24T20:16:20.922Z"
, favourited = Nothing , favourited = Nothing
, favourites_count = 0 , favourites_count = 0
, id = 737932 , id = StatusId "737932"
, in_reply_to_account_id = Just 26303 , in_reply_to_account_id = Just "26303"
, in_reply_to_id = Just 737425 , in_reply_to_id = Just <| StatusId "737425"
, media_attachments = [] , media_attachments = []
, mentions = , mentions =
[ { id = 26303 [ { id = "26303"
, url = "https://mamot.fr/@vjousse" , url = "https://mamot.fr/@vjousse"
, username = "vjousse" , username = "vjousse"
, acct = "vjousse" , acct = "vjousse"
@ -139,12 +139,12 @@ statusNicoToVjousseAgain =
, created_at = "2017-04-25T07:41:23.492Z" , created_at = "2017-04-25T07:41:23.492Z"
, favourited = Nothing , favourited = Nothing
, favourites_count = 0 , favourites_count = 0
, id = 752169 , id = StatusId "752169"
, in_reply_to_account_id = Just 26303 , in_reply_to_account_id = Just "26303"
, in_reply_to_id = Just 752153 , in_reply_to_id = Just <| StatusId "752153"
, media_attachments = [] , media_attachments = []
, mentions = , mentions =
[ { id = 26303 [ { id = "26303"
, url = "https://mamot.fr/@vjousse" , url = "https://mamot.fr/@vjousse"
, username = "vjousse" , username = "vjousse"
, acct = "vjousse" , acct = "vjousse"
@ -170,12 +170,12 @@ statusPloumToVjousse =
, created_at = "2017-04-25T07:41:23.492Z" , created_at = "2017-04-25T07:41:23.492Z"
, favourited = Nothing , favourited = Nothing
, favourites_count = 0 , favourites_count = 0
, id = 752169 , id = StatusId "752169"
, in_reply_to_account_id = Nothing , in_reply_to_account_id = Nothing
, in_reply_to_id = Nothing , in_reply_to_id = Nothing
, media_attachments = [] , media_attachments = []
, mentions = , mentions =
[ { id = 26303 [ { id = "26303"
, url = "https://mamot.fr/@vjousse" , url = "https://mamot.fr/@vjousse"
, username = "vjousse" , username = "vjousse"
, acct = "vjousse" , acct = "vjousse"
@ -201,7 +201,7 @@ statusReblogged =
, created_at = "2017-04-24T20:16:20.922Z" , created_at = "2017-04-24T20:16:20.922Z"
, favourited = Nothing , favourited = Nothing
, favourites_count = 0 , favourites_count = 0
, id = 737932 , id = StatusId "737932"
, in_reply_to_account_id = Nothing , in_reply_to_account_id = Nothing
, in_reply_to_id = Nothing , in_reply_to_id = Nothing
, media_attachments = [] , media_attachments = []
@ -220,7 +220,7 @@ statusReblogged =
notificationNicoMentionVjousse : Notification notificationNicoMentionVjousse : Notification
notificationNicoMentionVjousse = notificationNicoMentionVjousse =
{ id = 224284 { id = "224284"
, type_ = "mention" , type_ = "mention"
, created_at = "2017-04-24T20:16:20.973Z" , created_at = "2017-04-24T20:16:20.973Z"
, account = accountNico , account = accountNico
@ -230,7 +230,7 @@ notificationNicoMentionVjousse =
notificationNicoMentionVjousseAgain : Notification notificationNicoMentionVjousseAgain : Notification
notificationNicoMentionVjousseAgain = notificationNicoMentionVjousseAgain =
{ id = 226516 { id = "226516"
, type_ = "mention" , type_ = "mention"
, created_at = "2017-04-25T07:41:23.546Z" , created_at = "2017-04-25T07:41:23.546Z"
, account = accountNico , account = accountNico
@ -240,7 +240,7 @@ notificationNicoMentionVjousseAgain =
notificationNicoFollowsVjousse : Notification notificationNicoFollowsVjousse : Notification
notificationNicoFollowsVjousse = notificationNicoFollowsVjousse =
{ id = 224257 { id = "224257"
, type_ = "follow" , type_ = "follow"
, created_at = "2017-04-24T20:13:47.431Z" , created_at = "2017-04-24T20:13:47.431Z"
, account = accountNico , account = accountNico
@ -250,7 +250,7 @@ notificationNicoFollowsVjousse =
notificationSkroFollowsVjousse : Notification notificationSkroFollowsVjousse : Notification
notificationSkroFollowsVjousse = notificationSkroFollowsVjousse =
{ id = 224 { id = "224"
, type_ = "follow" , type_ = "follow"
, created_at = "2017-04-24T19:12:47.431Z" , created_at = "2017-04-24T19:12:47.431Z"
, account = accountSkro , account = accountSkro
@ -260,7 +260,7 @@ notificationSkroFollowsVjousse =
notificationPloumFollowsVjousse : Notification notificationPloumFollowsVjousse : Notification
notificationPloumFollowsVjousse = notificationPloumFollowsVjousse =
{ id = 220 { id = "220"
, type_ = "follow" , type_ = "follow"
, created_at = "2017-04-24T18:12:47.431Z" , created_at = "2017-04-24T18:12:47.431Z"
, account = accountPloum , account = accountPloum