Connect to the websocket API (#35)
This commit is contained in:
parent
ff81482572
commit
583ee2def2
@ -15,8 +15,9 @@
|
|||||||
"elm-lang/html": "2.0.0 <= v < 3.0.0",
|
"elm-lang/html": "2.0.0 <= v < 3.0.0",
|
||||||
"elm-lang/http": "1.0.0 <= v < 2.0.0",
|
"elm-lang/http": "1.0.0 <= v < 2.0.0",
|
||||||
"elm-lang/navigation": "2.1.0 <= v < 3.0.0",
|
"elm-lang/navigation": "2.1.0 <= v < 3.0.0",
|
||||||
|
"elm-lang/websocket": "1.0.2 <= v < 2.0.0",
|
||||||
"evancz/url-parser": "2.0.1 <= v < 3.0.0",
|
"evancz/url-parser": "2.0.1 <= v < 3.0.0",
|
||||||
"jinjor/elm-html-parser": "1.1.4 <= v < 2.0.0",
|
"jinjor/elm-html-parser": "1.1.5 <= v < 2.0.0",
|
||||||
"lukewestby/elm-http-builder": "5.1.0 <= v < 6.0.0",
|
"lukewestby/elm-http-builder": "5.1.0 <= v < 6.0.0",
|
||||||
"rluiten/elm-date-extra": "8.5.0 <= v < 9.0.0"
|
"rluiten/elm-date-extra": "8.5.0 <= v < 9.0.0"
|
||||||
},
|
},
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
"debug": "node_modules/.bin/elm-live src/Main.elm --dir=public/ --output=public/app.js --debug",
|
"debug": "node_modules/.bin/elm-live src/Main.elm --dir=public/ --output=public/app.js --debug",
|
||||||
"deploy": "npm run build && node_modules/.bin/gh-pages --dist build/",
|
"deploy": "npm run build && node_modules/.bin/gh-pages --dist build/",
|
||||||
"start": "node_modules/.bin/elm-live src/Main.elm --dir=public/ --output=public/app.js",
|
"start": "node_modules/.bin/elm-live src/Main.elm --dir=public/ --output=public/app.js",
|
||||||
"test": "node_modules/.bin/elm-make src/Main.elm --warn --output app.js"
|
"test": "node_modules/.bin/elm-test"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -26,6 +26,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"elm": "^0.18.0",
|
"elm": "^0.18.0",
|
||||||
"elm-live": "^2.7.4",
|
"elm-live": "^2.7.4",
|
||||||
|
"elm-test": "^0.18.2",
|
||||||
"gh-pages": "^0.12.0"
|
"gh-pages": "^0.12.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ module Main exposing (..)
|
|||||||
|
|
||||||
import Navigation
|
import Navigation
|
||||||
import View exposing (view)
|
import View exposing (view)
|
||||||
import Model exposing (Flags, Model, Msg(..), init, update)
|
import Model exposing (Flags, Model, Msg(..), init, update, subscriptions)
|
||||||
|
|
||||||
|
|
||||||
main : Program Flags Model Msg
|
main : Program Flags Model Msg
|
||||||
@ -11,5 +11,5 @@ main =
|
|||||||
{ init = init
|
{ init = init
|
||||||
, view = view
|
, view = view
|
||||||
, update = update
|
, update = update
|
||||||
, subscriptions = always Sub.none
|
, subscriptions = subscriptions
|
||||||
}
|
}
|
||||||
|
170
src/Mastodon.elm
170
src/Mastodon.elm
@ -12,7 +12,9 @@ module Mastodon
|
|||||||
, Reblog(..)
|
, Reblog(..)
|
||||||
, Status
|
, Status
|
||||||
, StatusRequestBody
|
, StatusRequestBody
|
||||||
|
, StreamType(..)
|
||||||
, Tag
|
, Tag
|
||||||
|
, WebSocketEventResult(..)
|
||||||
, reblog
|
, reblog
|
||||||
, unreblog
|
, unreblog
|
||||||
, favourite
|
, favourite
|
||||||
@ -22,6 +24,7 @@ module Mastodon
|
|||||||
, registrationEncoder
|
, registrationEncoder
|
||||||
, aggregateNotifications
|
, aggregateNotifications
|
||||||
, clientEncoder
|
, clientEncoder
|
||||||
|
, decodeWebSocketMessage
|
||||||
, getAuthorizationUrl
|
, getAuthorizationUrl
|
||||||
, getAccessToken
|
, getAccessToken
|
||||||
, fetchAccount
|
, fetchAccount
|
||||||
@ -31,6 +34,11 @@ module Mastodon
|
|||||||
, fetchUserTimeline
|
, fetchUserTimeline
|
||||||
, postStatus
|
, postStatus
|
||||||
, send
|
, send
|
||||||
|
, subscribeToWebSockets
|
||||||
|
, websocketEventDecoder
|
||||||
|
, notificationDecoder
|
||||||
|
, addNotificationToAggregates
|
||||||
|
, notificationToAggregate
|
||||||
)
|
)
|
||||||
|
|
||||||
import Http
|
import Http
|
||||||
@ -38,6 +46,8 @@ import HttpBuilder
|
|||||||
import Json.Decode.Pipeline as Pipe
|
import Json.Decode.Pipeline as Pipe
|
||||||
import Json.Decode as Decode
|
import Json.Decode as Decode
|
||||||
import Json.Encode as Encode
|
import Json.Encode as Encode
|
||||||
|
import Util
|
||||||
|
import WebSocket
|
||||||
import List.Extra exposing (groupWhile)
|
import List.Extra exposing (groupWhile)
|
||||||
|
|
||||||
|
|
||||||
@ -82,6 +92,12 @@ type alias Client =
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type alias WebSocketEvent =
|
||||||
|
{ event : String
|
||||||
|
, payload : String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
type Error
|
type Error
|
||||||
= MastodonError StatusCode StatusMsg String
|
= MastodonError StatusCode StatusMsg String
|
||||||
| ServerError StatusCode StatusMsg String
|
| ServerError StatusCode StatusMsg String
|
||||||
@ -211,6 +227,18 @@ type alias Request a =
|
|||||||
HttpBuilder.RequestBuilder a
|
HttpBuilder.RequestBuilder a
|
||||||
|
|
||||||
|
|
||||||
|
type WebSocketEventResult a b c
|
||||||
|
= EventError a
|
||||||
|
| NotificationResult b
|
||||||
|
| StatusResult c
|
||||||
|
|
||||||
|
|
||||||
|
type StreamType
|
||||||
|
= UserStream
|
||||||
|
| LocalPublicStream
|
||||||
|
| GlobalPublicStream
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- Msg
|
-- Msg
|
||||||
|
|
||||||
@ -367,6 +395,13 @@ statusDecoder =
|
|||||||
|> Pipe.required "visibility" Decode.string
|
|> Pipe.required "visibility" Decode.string
|
||||||
|
|
||||||
|
|
||||||
|
websocketEventDecoder : Decode.Decoder WebSocketEvent
|
||||||
|
websocketEventDecoder =
|
||||||
|
Pipe.decode WebSocketEvent
|
||||||
|
|> Pipe.required "event" Decode.string
|
||||||
|
|> Pipe.required "payload" Decode.string
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- Internal helpers
|
-- Internal helpers
|
||||||
|
|
||||||
@ -448,6 +483,77 @@ fetch client endpoint decoder =
|
|||||||
-- Public API
|
-- Public API
|
||||||
|
|
||||||
|
|
||||||
|
notificationToAggregate : Notification -> NotificationAggregate
|
||||||
|
notificationToAggregate notification =
|
||||||
|
NotificationAggregate
|
||||||
|
notification.type_
|
||||||
|
notification.status
|
||||||
|
[ notification.account ]
|
||||||
|
notification.created_at
|
||||||
|
|
||||||
|
|
||||||
|
addNotificationToAggregates : Notification -> List NotificationAggregate -> List NotificationAggregate
|
||||||
|
addNotificationToAggregates notification aggregates =
|
||||||
|
let
|
||||||
|
addNewAccountToSameStatus : NotificationAggregate -> Notification -> NotificationAggregate
|
||||||
|
addNewAccountToSameStatus aggregate notification =
|
||||||
|
case ( aggregate.status, notification.status ) of
|
||||||
|
( Just aggregateStatus, Just notificationStatus ) ->
|
||||||
|
if aggregateStatus.id == notificationStatus.id then
|
||||||
|
{ aggregate | accounts = notification.account :: aggregate.accounts }
|
||||||
|
else
|
||||||
|
aggregate
|
||||||
|
|
||||||
|
( _, _ ) ->
|
||||||
|
aggregate
|
||||||
|
|
||||||
|
{-
|
||||||
|
Let's try to find an already existing aggregate, matching the notification
|
||||||
|
we are trying to add.
|
||||||
|
If we find any aggregate, we modify it inplace. If not, we return the
|
||||||
|
aggregates unmodified
|
||||||
|
-}
|
||||||
|
newAggregates =
|
||||||
|
aggregates
|
||||||
|
|> List.map
|
||||||
|
(\aggregate ->
|
||||||
|
case ( aggregate.type_, notification.type_ ) of
|
||||||
|
{-
|
||||||
|
Notification and aggregate are of the follow type.
|
||||||
|
Add the new following account.
|
||||||
|
-}
|
||||||
|
( "follow", "follow" ) ->
|
||||||
|
{ aggregate | accounts = notification.account :: aggregate.accounts }
|
||||||
|
|
||||||
|
{-
|
||||||
|
Notification is of type follow, but current aggregate
|
||||||
|
is of another type. Let's continue then.
|
||||||
|
-}
|
||||||
|
( _, "follow" ) ->
|
||||||
|
aggregate
|
||||||
|
|
||||||
|
{-
|
||||||
|
If both types are the same check if we should
|
||||||
|
add the new account.
|
||||||
|
-}
|
||||||
|
( aggregateType, notificationType ) ->
|
||||||
|
if aggregateType == notificationType then
|
||||||
|
addNewAccountToSameStatus aggregate notification
|
||||||
|
else
|
||||||
|
aggregate
|
||||||
|
)
|
||||||
|
in
|
||||||
|
{-
|
||||||
|
If we did no modification to the old aggregates it's
|
||||||
|
because we didn't found any match. So me have to create
|
||||||
|
a new aggregate
|
||||||
|
-}
|
||||||
|
if newAggregates == aggregates then
|
||||||
|
notificationToAggregate (notification) :: aggregates
|
||||||
|
else
|
||||||
|
newAggregates
|
||||||
|
|
||||||
|
|
||||||
aggregateNotifications : List Notification -> List NotificationAggregate
|
aggregateNotifications : List Notification -> List NotificationAggregate
|
||||||
aggregateNotifications notifications =
|
aggregateNotifications notifications =
|
||||||
let
|
let
|
||||||
@ -600,3 +706,67 @@ unfavourite client id =
|
|||||||
HttpBuilder.post (client.server ++ "/api/v1/statuses/" ++ (toString id) ++ "/unfavourite")
|
HttpBuilder.post (client.server ++ "/api/v1/statuses/" ++ (toString id) ++ "/unfavourite")
|
||||||
|> HttpBuilder.withHeader "Authorization" ("Bearer " ++ client.token)
|
|> HttpBuilder.withHeader "Authorization" ("Bearer " ++ client.token)
|
||||||
|> HttpBuilder.withExpect (Http.expectJson statusDecoder)
|
|> HttpBuilder.withExpect (Http.expectJson statusDecoder)
|
||||||
|
|
||||||
|
|
||||||
|
subscribeToWebSockets : Client -> StreamType -> (String -> a) -> Sub a
|
||||||
|
subscribeToWebSockets client streamType message =
|
||||||
|
let
|
||||||
|
type_ =
|
||||||
|
case streamType of
|
||||||
|
UserStream ->
|
||||||
|
"user"
|
||||||
|
|
||||||
|
LocalPublicStream ->
|
||||||
|
"public:local"
|
||||||
|
|
||||||
|
GlobalPublicStream ->
|
||||||
|
"public:local"
|
||||||
|
|
||||||
|
url =
|
||||||
|
(Util.replace "https" "wss" client.server)
|
||||||
|
++ "/api/v1/streaming/?access_token="
|
||||||
|
++ client.token
|
||||||
|
++ "&stream="
|
||||||
|
++ type_
|
||||||
|
in
|
||||||
|
WebSocket.listen url message
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{-
|
||||||
|
Sorry for this beast, but the websocket connection return messages
|
||||||
|
containing an escaped JSON string under the `payload` key. This JSON string
|
||||||
|
can either represent a `Notification` when the event field of the returned json
|
||||||
|
is equal to 'notification' or a `Status` when the string is equal to
|
||||||
|
'update'.
|
||||||
|
If someone has a better way of doing this, I'me all for it
|
||||||
|
-}
|
||||||
|
|
||||||
|
|
||||||
|
decodeWebSocketMessage : String -> WebSocketEventResult String (Result String Notification) (Result String Status)
|
||||||
|
decodeWebSocketMessage message =
|
||||||
|
let
|
||||||
|
websocketEvent =
|
||||||
|
Decode.decodeString
|
||||||
|
websocketEventDecoder
|
||||||
|
message
|
||||||
|
in
|
||||||
|
case websocketEvent of
|
||||||
|
Ok event ->
|
||||||
|
if event.event == "notification" then
|
||||||
|
NotificationResult
|
||||||
|
(Decode.decodeString
|
||||||
|
notificationDecoder
|
||||||
|
event.payload
|
||||||
|
)
|
||||||
|
else if event.event == "update" then
|
||||||
|
StatusResult
|
||||||
|
(Decode.decodeString
|
||||||
|
statusDecoder
|
||||||
|
event.payload
|
||||||
|
)
|
||||||
|
else
|
||||||
|
EventError "Unknown event type for WebSocket"
|
||||||
|
|
||||||
|
Err error ->
|
||||||
|
EventError error
|
||||||
|
@ -61,6 +61,9 @@ type
|
|||||||
| Unreblog Int
|
| Unreblog Int
|
||||||
| Unreblogged (Result Mastodon.Error Mastodon.Status)
|
| Unreblogged (Result Mastodon.Error Mastodon.Status)
|
||||||
| UserTimeline (Result Mastodon.Error (List Mastodon.Status))
|
| UserTimeline (Result Mastodon.Error (List Mastodon.Status))
|
||||||
|
| NewWebsocketUserMessage String
|
||||||
|
| NewWebsocketGlobalMessage String
|
||||||
|
| NewWebsocketLocalMessage String
|
||||||
| ViewerEvent ViewerMsg
|
| ViewerEvent ViewerMsg
|
||||||
|
|
||||||
|
|
||||||
@ -535,3 +538,60 @@ update msg model =
|
|||||||
|
|
||||||
Err error ->
|
Err error ->
|
||||||
{ model | notifications = [], errors = (errorText error) :: model.errors } ! []
|
{ model | notifications = [], errors = (errorText error) :: model.errors } ! []
|
||||||
|
|
||||||
|
NewWebsocketUserMessage message ->
|
||||||
|
case (Mastodon.decodeWebSocketMessage message) of
|
||||||
|
Mastodon.EventError error ->
|
||||||
|
{ model | errors = error :: model.errors } ! []
|
||||||
|
|
||||||
|
Mastodon.NotificationResult result ->
|
||||||
|
case result of
|
||||||
|
Ok notification ->
|
||||||
|
{ model | notifications = Mastodon.addNotificationToAggregates notification model.notifications } ! []
|
||||||
|
|
||||||
|
Err error ->
|
||||||
|
{ model | errors = error :: model.errors } ! []
|
||||||
|
|
||||||
|
Mastodon.StatusResult result ->
|
||||||
|
case result of
|
||||||
|
Ok status ->
|
||||||
|
{ model | userTimeline = status :: model.userTimeline } ! []
|
||||||
|
|
||||||
|
Err error ->
|
||||||
|
{ model | errors = error :: model.errors } ! []
|
||||||
|
|
||||||
|
NewWebsocketLocalMessage message ->
|
||||||
|
-- @TODO
|
||||||
|
model ! []
|
||||||
|
|
||||||
|
NewWebsocketGlobalMessage message ->
|
||||||
|
-- @TODO
|
||||||
|
model ! []
|
||||||
|
|
||||||
|
|
||||||
|
subscriptions : Model -> Sub Msg
|
||||||
|
subscriptions model =
|
||||||
|
Sub.batch <|
|
||||||
|
case model.client of
|
||||||
|
Just client ->
|
||||||
|
[ Mastodon.subscribeToWebSockets
|
||||||
|
client
|
||||||
|
Mastodon.UserStream
|
||||||
|
NewWebsocketUserMessage
|
||||||
|
]
|
||||||
|
++ (if model.useGlobalTimeline then
|
||||||
|
[ Mastodon.subscribeToWebSockets
|
||||||
|
client
|
||||||
|
Mastodon.GlobalPublicStream
|
||||||
|
NewWebsocketGlobalMessage
|
||||||
|
]
|
||||||
|
else
|
||||||
|
[ Mastodon.subscribeToWebSockets
|
||||||
|
client
|
||||||
|
Mastodon.LocalPublicStream
|
||||||
|
NewWebsocketLocalMessage
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
Nothing ->
|
||||||
|
[]
|
||||||
|
6
src/Util.elm
Normal file
6
src/Util.elm
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
module Util exposing (..)
|
||||||
|
|
||||||
|
|
||||||
|
replace : String -> String -> String -> String
|
||||||
|
replace from to str =
|
||||||
|
String.split from str |> String.join to
|
@ -3,7 +3,6 @@ module ViewHelper
|
|||||||
( formatContent
|
( formatContent
|
||||||
, getMentionForLink
|
, getMentionForLink
|
||||||
, onClickWithPreventAndStop
|
, onClickWithPreventAndStop
|
||||||
, replace
|
|
||||||
, toVirtualDom
|
, toVirtualDom
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -14,6 +13,7 @@ import HtmlParser
|
|||||||
import Json.Decode as Decode
|
import Json.Decode as Decode
|
||||||
import Mastodon
|
import Mastodon
|
||||||
import Model exposing (Msg(OnLoadUserAccount))
|
import Model exposing (Msg(OnLoadUserAccount))
|
||||||
|
import Util
|
||||||
|
|
||||||
|
|
||||||
-- Custom Events
|
-- Custom Events
|
||||||
@ -34,18 +34,13 @@ onClickWithPreventAndStop msg =
|
|||||||
formatContent : String -> List Mastodon.Mention -> List (Html Msg)
|
formatContent : String -> List Mastodon.Mention -> List (Html Msg)
|
||||||
formatContent content mentions =
|
formatContent content mentions =
|
||||||
content
|
content
|
||||||
|> replace " ?" " ?"
|
|> Util.replace " ?" " ?"
|
||||||
|> replace " !" " !"
|
|> Util.replace " !" " !"
|
||||||
|> replace " :" " :"
|
|> Util.replace " :" " :"
|
||||||
|> HtmlParser.parse
|
|> HtmlParser.parse
|
||||||
|> toVirtualDom mentions
|
|> toVirtualDom mentions
|
||||||
|
|
||||||
|
|
||||||
replace : String -> String -> String -> String
|
|
||||||
replace from to str =
|
|
||||||
String.split from str |> String.join to
|
|
||||||
|
|
||||||
|
|
||||||
{-| Converts nodes to virtual dom nodes.
|
{-| Converts nodes to virtual dom nodes.
|
||||||
-}
|
-}
|
||||||
toVirtualDom : List Mastodon.Mention -> List HtmlParser.Node -> List (Html Msg)
|
toVirtualDom : List Mastodon.Mention -> List HtmlParser.Node -> List (Html Msg)
|
||||||
|
1
tests/.gitignore
vendored
Normal file
1
tests/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/elm-stuff/
|
208
tests/Fixtures.elm
Normal file
208
tests/Fixtures.elm
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
module Fixtures exposing (..)
|
||||||
|
|
||||||
|
import Mastodon exposing (Account, Notification, NotificationAggregate, Status)
|
||||||
|
|
||||||
|
|
||||||
|
accountSkro : Account
|
||||||
|
accountSkro =
|
||||||
|
{ acct = "SkroZoC"
|
||||||
|
, avatar = "https://mamot.fr/system/accounts/avatars/000/001/391/original/76be3c9d1b34f59b.jpeg?1493042489"
|
||||||
|
, created_at = "2017-04-24T20:25:37.398Z"
|
||||||
|
, display_name = "Skro"
|
||||||
|
, followers_count = 77
|
||||||
|
, following_count = 80
|
||||||
|
, header = "https://mamot.fr/system/accounts/headers/000/001/391/original/9fbb4ac980f04fe1.gif?1493042489"
|
||||||
|
, id = 1391
|
||||||
|
, locked = False
|
||||||
|
, note = "N'importe quoi très vite en 500 caractères. La responsabilité du triumvirat de ZoC ne peut être engagée."
|
||||||
|
, statuses_count = 161
|
||||||
|
, url = "https://mamot.fr/@SkroZoC"
|
||||||
|
, username = "SkroZoC"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
accountVjousse : Account
|
||||||
|
accountVjousse =
|
||||||
|
{ acct = "vjousse"
|
||||||
|
, avatar = "https://mamot.fr/system/accounts/avatars/000/026/303/original/b72c0dd565e5bc1e.png?1492698808"
|
||||||
|
, created_at = "2017-04-20T14:31:05.751Z"
|
||||||
|
, display_name = "Vincent Jousse"
|
||||||
|
, followers_count = 68
|
||||||
|
, following_count = 31
|
||||||
|
, header = "https://mamot.fr/headers/original/missing.png"
|
||||||
|
, id = 26303
|
||||||
|
, locked = False
|
||||||
|
, note = "Libriste, optimiste et utopiste. On est bien tintin."
|
||||||
|
, statuses_count = 88
|
||||||
|
, url = "https://mamot.fr/@vjousse"
|
||||||
|
, username = "vjousse"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
accountNico : Account
|
||||||
|
accountNico =
|
||||||
|
{ acct = "n1k0"
|
||||||
|
, avatar = "https://mamot.fr/system/accounts/avatars/000/017/784/original/40052904e484d9c0.jpg?1492158615"
|
||||||
|
, created_at = "2017-04-14T08:28:59.706Z"
|
||||||
|
, display_name = "NiKo`"
|
||||||
|
, followers_count = 162
|
||||||
|
, following_count = 79
|
||||||
|
, header = "https://mamot.fr/system/accounts/headers/000/017/784/original/ea87200d852018a8.jpg?1492158674"
|
||||||
|
, id = 17784
|
||||||
|
, locked = False
|
||||||
|
, note = "Transforme sa procrastination en pouets, la plupart du temps en français."
|
||||||
|
, statuses_count = 358
|
||||||
|
, url = "https://mamot.fr/@n1k0"
|
||||||
|
, username = "n1k0"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
accountPloum : Account
|
||||||
|
accountPloum =
|
||||||
|
{ acct = "ploum"
|
||||||
|
, avatar = "https://mamot.fr/system/accounts/avatars/000/006/840/original/593a817d651d9253.jpg?1491814416"
|
||||||
|
, created_at = "2017-04-08T09:37:34.931Z"
|
||||||
|
, display_name = "ploum"
|
||||||
|
, followers_count = 1129
|
||||||
|
, following_count = 91
|
||||||
|
, header = "https://mamot.fr/system/accounts/headers/000/006/840/original/7e0adc1f754dafbe.jpg?1491814416"
|
||||||
|
, id = 6840
|
||||||
|
, locked = False
|
||||||
|
, note = "Futurologue, conférencier, blogueur et écrivain électronique. Du moins, je l'espère. :bicyclist:"
|
||||||
|
, statuses_count = 601
|
||||||
|
, url = "https://mamot.fr/@ploum"
|
||||||
|
, username = "ploum"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
statusNicoToVjousse : Status
|
||||||
|
statusNicoToVjousse =
|
||||||
|
{ account = accountNico
|
||||||
|
, content = "<p><span class=\"h-card\"><a href=\"https://mamot.fr/@vjousse\" class=\"u-url mention\">@<span>vjousse</span></a></span> j'ai rien touché à ce niveau là non</p>"
|
||||||
|
, created_at = "2017-04-24T20:16:20.922Z"
|
||||||
|
, favourited = Nothing
|
||||||
|
, favourites_count = 0
|
||||||
|
, id = 737932
|
||||||
|
, in_reply_to_account_id = Just 26303
|
||||||
|
, in_reply_to_id = Just 737425
|
||||||
|
, media_attachments = []
|
||||||
|
, mentions =
|
||||||
|
[ { id = 26303
|
||||||
|
, url = "https://mamot.fr/@vjousse"
|
||||||
|
, username = "vjousse"
|
||||||
|
, acct = "vjousse"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
, reblog = Nothing
|
||||||
|
, reblogged = Nothing
|
||||||
|
, reblogs_count = 0
|
||||||
|
, sensitive = Just False
|
||||||
|
, spoiler_text = ""
|
||||||
|
, tags = []
|
||||||
|
, uri = "tag:mamot.fr,2017-04-24:objectId=737932:objectType=Status"
|
||||||
|
, url = "https://mamot.fr/@n1k0/737932"
|
||||||
|
, visibility = "public"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
statusNicoToVjousseAgain : Status
|
||||||
|
statusNicoToVjousseAgain =
|
||||||
|
{ account = accountNico
|
||||||
|
, content = "<p><span class=\"h-card\"><a href=\"https://mamot.fr/@vjousse\" class=\"u-url mention\">@<span>vjousse</span></a></span> oui j'ai vu, c'est super, après on est à +473 −13, à un moment tu vas te prendre la tête 😂</p>"
|
||||||
|
, created_at = "2017-04-25T07:41:23.492Z"
|
||||||
|
, favourited = Nothing
|
||||||
|
, favourites_count = 0
|
||||||
|
, id = 752169
|
||||||
|
, in_reply_to_account_id = Just 26303
|
||||||
|
, in_reply_to_id = Just 752153
|
||||||
|
, media_attachments = []
|
||||||
|
, mentions =
|
||||||
|
[ { id = 26303
|
||||||
|
, url = "https://mamot.fr/@vjousse"
|
||||||
|
, username = "vjousse"
|
||||||
|
, acct = "vjousse"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
, reblog = Nothing
|
||||||
|
, reblogged = Nothing
|
||||||
|
, reblogs_count = 0
|
||||||
|
, sensitive = Just False
|
||||||
|
, spoiler_text = ""
|
||||||
|
, tags = []
|
||||||
|
, uri = "tag:mamot.fr,2017-04-25:objectId=752169:objectType=Status"
|
||||||
|
, url = "https://mamot.fr/@n1k0/752169"
|
||||||
|
, visibility = "public"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
notificationNicoMentionVjousse : Notification
|
||||||
|
notificationNicoMentionVjousse =
|
||||||
|
{ id = 224284
|
||||||
|
, type_ = "mention"
|
||||||
|
, created_at = "2017-04-24T20:16:20.973Z"
|
||||||
|
, account = accountNico
|
||||||
|
, status = Just statusNicoToVjousse
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
notificationNicoMentionVjousseAgain : Notification
|
||||||
|
notificationNicoMentionVjousseAgain =
|
||||||
|
{ id = 226516
|
||||||
|
, type_ = "mention"
|
||||||
|
, created_at = "2017-04-25T07:41:23.546Z"
|
||||||
|
, account = accountNico
|
||||||
|
, status = Just statusNicoToVjousseAgain
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
notificationNicoFollowsVjousse : Notification
|
||||||
|
notificationNicoFollowsVjousse =
|
||||||
|
{ id = 224257
|
||||||
|
, type_ = "follow"
|
||||||
|
, created_at = "2017-04-24T20:13:47.431Z"
|
||||||
|
, account = accountNico
|
||||||
|
, status = Nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
notificationSkroFollowsVjousse : Notification
|
||||||
|
notificationSkroFollowsVjousse =
|
||||||
|
{ id = 224
|
||||||
|
, type_ = "follow"
|
||||||
|
, created_at = "2017-04-24T19:12:47.431Z"
|
||||||
|
, account = accountSkro
|
||||||
|
, status = Nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
notificationPloumFollowsVjousse : Notification
|
||||||
|
notificationPloumFollowsVjousse =
|
||||||
|
{ id = 220
|
||||||
|
, type_ = "follow"
|
||||||
|
, created_at = "2017-04-24T18:12:47.431Z"
|
||||||
|
, account = accountPloum
|
||||||
|
, status = Nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
accounts : List Account
|
||||||
|
accounts =
|
||||||
|
[ accountSkro, accountVjousse, accountNico ]
|
||||||
|
|
||||||
|
|
||||||
|
notifications : List Notification
|
||||||
|
notifications =
|
||||||
|
[ notificationNicoMentionVjousse
|
||||||
|
, notificationNicoFollowsVjousse
|
||||||
|
, notificationSkroFollowsVjousse
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
notificationAggregates : List NotificationAggregate
|
||||||
|
notificationAggregates =
|
||||||
|
[ { type_ = "mention"
|
||||||
|
, status = Nothing
|
||||||
|
, accounts = []
|
||||||
|
, created_at = ""
|
||||||
|
}
|
||||||
|
]
|
13
tests/Main.elm
Normal file
13
tests/Main.elm
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
port module Main exposing (..)
|
||||||
|
|
||||||
|
import NotificationTests
|
||||||
|
import Test.Runner.Node exposing (run, TestProgram)
|
||||||
|
import Json.Encode exposing (Value)
|
||||||
|
|
||||||
|
|
||||||
|
main : TestProgram
|
||||||
|
main =
|
||||||
|
run emit NotificationTests.all
|
||||||
|
|
||||||
|
|
||||||
|
port emit : ( String, Value ) -> Cmd msg
|
87
tests/NotificationTests.elm
Normal file
87
tests/NotificationTests.elm
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
module NotificationTests exposing (..)
|
||||||
|
|
||||||
|
import Test exposing (..)
|
||||||
|
import Expect
|
||||||
|
import String
|
||||||
|
import Mastodon
|
||||||
|
import Fixtures
|
||||||
|
|
||||||
|
|
||||||
|
all : Test
|
||||||
|
all =
|
||||||
|
describe "Notification test suite"
|
||||||
|
[ describe "Aggegate test"
|
||||||
|
[ test "Aggregate Notifications" <|
|
||||||
|
\() ->
|
||||||
|
Fixtures.notifications
|
||||||
|
|> Mastodon.aggregateNotifications
|
||||||
|
|> Expect.equal
|
||||||
|
[ { type_ = "mention"
|
||||||
|
, status = Just Fixtures.statusNicoToVjousse
|
||||||
|
, accounts = [ Fixtures.accountNico ]
|
||||||
|
, created_at = "2017-04-24T20:16:20.973Z"
|
||||||
|
}
|
||||||
|
, { type_ = "follow"
|
||||||
|
, status = Nothing
|
||||||
|
, accounts = [ Fixtures.accountNico, Fixtures.accountSkro ]
|
||||||
|
, created_at = "2017-04-24T20:13:47.431Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
, test "Add follows notification to aggregate" <|
|
||||||
|
\() ->
|
||||||
|
Fixtures.notifications
|
||||||
|
|> Mastodon.aggregateNotifications
|
||||||
|
|> (Mastodon.addNotificationToAggregates Fixtures.notificationPloumFollowsVjousse)
|
||||||
|
|> Expect.equal
|
||||||
|
[ { type_ = "mention"
|
||||||
|
, status = Just Fixtures.statusNicoToVjousse
|
||||||
|
, accounts = [ Fixtures.accountNico ]
|
||||||
|
, created_at = "2017-04-24T20:16:20.973Z"
|
||||||
|
}
|
||||||
|
, { type_ = "follow"
|
||||||
|
, status = Nothing
|
||||||
|
, accounts = [ Fixtures.accountPloum, Fixtures.accountNico, Fixtures.accountSkro ]
|
||||||
|
, created_at = "2017-04-24T20:13:47.431Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
, test "Add mention notification to aggregate" <|
|
||||||
|
\() ->
|
||||||
|
Fixtures.notifications
|
||||||
|
|> Mastodon.aggregateNotifications
|
||||||
|
|> (Mastodon.addNotificationToAggregates Fixtures.notificationNicoMentionVjousse)
|
||||||
|
|> Expect.equal
|
||||||
|
[ { type_ = "mention"
|
||||||
|
, status = Just Fixtures.statusNicoToVjousse
|
||||||
|
, accounts = [ Fixtures.accountNico, Fixtures.accountNico ]
|
||||||
|
, created_at = "2017-04-24T20:16:20.973Z"
|
||||||
|
}
|
||||||
|
, { type_ = "follow"
|
||||||
|
, status = Nothing
|
||||||
|
, accounts = [ Fixtures.accountNico, Fixtures.accountSkro ]
|
||||||
|
, created_at = "2017-04-24T20:13:47.431Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
, test "Add new mention notification to aggregate" <|
|
||||||
|
\() ->
|
||||||
|
Fixtures.notifications
|
||||||
|
|> Mastodon.aggregateNotifications
|
||||||
|
|> (Mastodon.addNotificationToAggregates Fixtures.notificationNicoMentionVjousseAgain)
|
||||||
|
|> Expect.equal
|
||||||
|
[ { type_ = "mention"
|
||||||
|
, status = Just Fixtures.statusNicoToVjousseAgain
|
||||||
|
, accounts = [ Fixtures.accountNico ]
|
||||||
|
, created_at = "2017-04-25T07:41:23.546Z"
|
||||||
|
}
|
||||||
|
, { type_ = "mention"
|
||||||
|
, status = Just Fixtures.statusNicoToVjousse
|
||||||
|
, accounts = [ Fixtures.accountNico ]
|
||||||
|
, created_at = "2017-04-24T20:16:20.973Z"
|
||||||
|
}
|
||||||
|
, { type_ = "follow"
|
||||||
|
, status = Nothing
|
||||||
|
, accounts = [ Fixtures.accountNico, Fixtures.accountSkro ]
|
||||||
|
, created_at = "2017-04-24T20:13:47.431Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
32
tests/elm-package.json
Normal file
32
tests/elm-package.json
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"summary": "Sample Elm Test",
|
||||||
|
"repository": "https://github.com/user/project.git",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"source-directories": [
|
||||||
|
".",
|
||||||
|
"../src"
|
||||||
|
],
|
||||||
|
"exposed-modules": [],
|
||||||
|
"dependencies": {
|
||||||
|
"elm-community/json-extra": "2.0.0 <= v < 3.0.0",
|
||||||
|
"elm-lang/html": "2.0.0 <= v < 3.0.0",
|
||||||
|
"mgold/elm-random-pcg": "4.0.2 <= v < 5.0.0",
|
||||||
|
"elm-lang/core": "5.0.0 <= v < 6.0.0",
|
||||||
|
"elm-community/elm-test": "3.0.0 <= v < 4.0.0",
|
||||||
|
"rtfeldman/node-test-runner": "3.0.0 <= v < 4.0.0",
|
||||||
|
|
||||||
|
"NoRedInk/elm-decode-pipeline": "3.0.0 <= v < 4.0.0",
|
||||||
|
"elm-community/list-extra": "6.0.0 <= v < 7.0.0",
|
||||||
|
"elm-lang/core": "5.1.1 <= v < 6.0.0",
|
||||||
|
"elm-lang/dom": "1.1.1 <= v < 2.0.0",
|
||||||
|
"elm-lang/html": "2.0.0 <= v < 3.0.0",
|
||||||
|
"elm-lang/http": "1.0.0 <= v < 2.0.0",
|
||||||
|
"elm-lang/navigation": "2.1.0 <= v < 3.0.0",
|
||||||
|
"elm-lang/websocket": "1.0.2 <= v < 2.0.0",
|
||||||
|
"evancz/url-parser": "2.0.1 <= v < 3.0.0",
|
||||||
|
"jinjor/elm-html-parser": "1.1.5 <= v < 2.0.0",
|
||||||
|
"lukewestby/elm-http-builder": "5.1.0 <= v < 6.0.0"
|
||||||
|
},
|
||||||
|
"elm-version": "0.18.0 <= v < 0.19.0"
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user