Skip to content
This repository has been archived by the owner on Apr 21, 2020. It is now read-only.

Private pusher channel #138

Merged
merged 24 commits into from
Jul 10, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d3f5efb
WIP: Private pusher channel [skip ci]
bitamar Jul 7, 2017
a1348fc
Re-adding offline interop [skip ci]
bitamar Jul 7, 2017
ee7995c
Pusher auth [skip ci]
bitamar Jul 7, 2017
0ade019
Subscribing to the private channel [skip ci]
bitamar Jul 8, 2017
4f1c80a
Cleaning up me endpoint [skip ci]
bitamar Jul 8, 2017
b44ccbf
Fetching user's pusher channel from backend [skip ci]
bitamar Jul 8, 2017
5b62d1b
Logging into pusher only after user is fetched [skip ci]
bitamar Jul 8, 2017
d0b4b32
Updating dummyUser
bitamar Jul 8, 2017
54475bf
Sniffer fixes
bitamar Jul 8, 2017
9a79611
Merge branch 'master' into 123-private-pusher-channel
bitamar Jul 9, 2017
0a74252
Cleaning up pusher login call [skip ci]
bitamar Jul 9, 2017
a539ed1
Adding private note on items [skip ci]
bitamar Jul 9, 2017
95384b8
Displaying private note
bitamar Jul 9, 2017
006bb53
Removing unused pusher parts, and wiring pusher logout
bitamar Jul 9, 2017
fde3b02
Fixing pusher test
bitamar Jul 9, 2017
7e77f7c
Updating the pusher model when calling pusherLogin and pusherLogout […
bitamar Jul 10, 2017
3babb2e
Ordering msgs [skip ci]
bitamar Jul 10, 2017
034394c
Cleaning up item view [skip ci]
bitamar Jul 10, 2017
e6d7ef6
cleanup login message creation [skip ci]
bitamar Jul 10, 2017
14507ff
showMaybe privateNote [skip ci]
bitamar Jul 10, 2017
47cc8ff
Fixes
bitamar Jul 10, 2017
73b2f88
Replacing "access private pusher channel" with "access private fields"
bitamar Jul 10, 2017
7dcf9be
Adding information about the private pusher channel to the readme
bitamar Jul 10, 2017
6c49099
Updating pusher description
bitamar Jul 10, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,22 @@ For the backend, check the [server README.md](https://github.com/Gizra/drupal-el

For the frontend, check the [client README.md](https://github.com/Gizra/drupal-elm-starter/blob/master/client/README.md)

## Pusher
When the item will be updated (E.g. via the backend on
[/node/1/edit](http://localhost/drupal-elm-starter/server/www/node/1/edit)),
it will fire Pusher messages that will update the Elm application in real time.

Editing an item produces pusher messages on two different channels: `general`
and `private-general`. The private channel is only accessible by users with
`access private fields` permission, and currently exposes the item's "Private
note" field, which normal users can't access.

Log in to the Elm app for example with `admin` / `admin` to see also the item
private note field (on [/#/item/1](http://localhost:3000/#/item/1) for
example), and get notifications through the private channel.
Log in with a normal user (For exmaple `alice` / `alice`), to get notifications
through the public pusher channel.

## Credits

[Gizra](https://gizra.com)
Expand Down
5 changes: 5 additions & 0 deletions client/src/elm/App/Model.elm
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import App.PageType exposing (Page(..))
import Config.Model
import Date exposing (Date)
import Pages.Login.Model exposing (emptyModel, Model)
import Pusher.Model
import RemoteData exposing (RemoteData(..), WebData)
import ItemManager.Model exposing (emptyModel, Model)
import Time exposing (Time)
Expand All @@ -21,6 +22,8 @@ type Msg
= HandleOfflineEvent (Result String Bool)
| Logout
| MsgItemManager ItemManager.Model.Msg
| MsgPusher Pusher.Model.Msg
| NoOp
| PageLogin Pages.Login.Model.Msg
| SetActivePage Page
| SetCurrentDate Date
Expand All @@ -36,6 +39,7 @@ type alias Model =
, offline : Bool
, pageLogin : Pages.Login.Model.Model
, pageItem : ItemManager.Model.Model
, pusher : Pusher.Model.Model
, sidebarOpen : Bool
, user : WebData User
}
Expand All @@ -61,6 +65,7 @@ emptyModel =
, offline = False
, pageLogin = Pages.Login.Model.emptyModel
, pageItem = ItemManager.Model.emptyModel
, pusher = Pusher.Model.emptyModel
, sidebarOpen = False
, user = NotAsked
}
2 changes: 1 addition & 1 deletion client/src/elm/App/PageType.elm
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module App.PageType exposing (Page(..))

{-| Prevent circula dependency.
{-| Prevent circular dependency.
-}


Expand Down
6 changes: 5 additions & 1 deletion client/src/elm/App/Test.elm
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,11 @@ getPageAsAuthenticated : Page -> Page
getPageAsAuthenticated page =
let
dummyUser =
{ id = 100, name = "Foo", avatarUrl = "https://example.com" }
{ id = 100
, name = "Foo"
, avatarUrl = "https://example.com"
, pusherChannel = "general"
}

model =
{ emptyModel | user = Success dummyUser }
Expand Down
88 changes: 69 additions & 19 deletions client/src/elm/App/Update.elm
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@ import App.PageType exposing (Page(..))
import Config
import Date
import Dict
import Http
import ItemManager.Model
import ItemManager.Update
import Json.Decode exposing (bool, decodeValue)
import Json.Encode exposing (Value)
import Pages.Login.Update
import Pusher.Model
import Pusher.Utils exposing (getClusterName)
import Pusher.Update
import RemoteData exposing (RemoteData(..), WebData)
import Task
import Time exposing (minute)
Expand All @@ -30,12 +29,7 @@ init flags =
Just config ->
let
defaultCmds =
[ pusherKey
( config.pusherKey.key
, getClusterName config.pusherKey.cluster
, Pusher.Model.eventNames
)
, Task.perform SetCurrentDate Date.now
[ Task.perform SetCurrentDate Date.now
]

( cmds, activePage_ ) =
Expand Down Expand Up @@ -87,13 +81,21 @@ update msg model =
model ! []

Logout ->
( { emptyModel
| accessToken = ""
, activePage = Login
, config = model.config
}
, accessTokenPort ""
)
let
( modelUpdated, pusherLogoutCmd ) =
update (MsgPusher Pusher.Model.Logout) model
in
( { emptyModel
| accessToken = ""
, activePage = Login
, config = model.config
, pusher = modelUpdated.pusher
}
, Cmd.batch
[ accessTokenPort ""
, pusherLogoutCmd
]
)

MsgItemManager subMsg ->
case model.user of
Expand Down Expand Up @@ -124,15 +126,31 @@ update msg model =
-- If we don't have a user, we have nothing to do.
model ! []

MsgPusher subMsg ->
let
( val, cmd ) =
Pusher.Update.update backendUrl subMsg model.pusher
in
( { model | pusher = val }
, Cmd.map MsgPusher cmd
)

NoOp ->
model ! []

PageLogin msg ->
let
( val, cmds, ( webDataUser, accessToken ) ) =
Pages.Login.Update.update backendUrl msg model.pageLogin

( pusherModelUpdated, pusherLoginCmd ) =
pusherLogin model webDataUser accessToken

modelUpdated =
{ model
| pageLogin = val
, accessToken = accessToken
, pusher = pusherModelUpdated
, user = webDataUser
}

Expand Down Expand Up @@ -166,6 +184,7 @@ update msg model =
[ Cmd.map PageLogin cmds
, accessTokenPort accessToken
, setActivePageCmds
, pusherLoginCmd
]
)

Expand Down Expand Up @@ -240,6 +259,7 @@ subscriptions model =
[ Sub.map MsgItemManager <| ItemManager.Update.subscriptions model.pageItem model.activePage
, Time.every minute Tick
, offline (decodeValue bool >> HandleOfflineEvent)
, Sub.map MsgPusher <| Pusher.Update.subscription
]


Expand All @@ -248,11 +268,41 @@ subscriptions model =
port accessTokenPort : String -> Cmd msg


{-| Send Pusher key and cluster to JS.
{-| Get a singal if internet connection is lost.
-}
port pusherKey : ( String, String, List String ) -> Cmd msg
port offline : (Value -> msg) -> Sub msg


{-| Get a singal if internet connection is lost.
{-| Login to pusher.
Either subscribes to the private channel, or to the general channel, according
to user.pusherChannel.
-}
port offline : (Value -> msg) -> Sub msg
pusherLogin : Model -> WebData User -> String -> ( Pusher.Model.Model, Cmd Msg )
pusherLogin model webDataUser accessToken =
let
-- Create the pusher login Msg, wrapped as a MsgPusher.
pusherLoginMsg pusherKey pusherChannel =
MsgPusher <|
Pusher.Model.Login
pusherKey
pusherChannel
(Pusher.Model.AccessToken accessToken)

-- Create a MsgPusher for login, or NoOp in case the user or the config
-- are missing.
msg =
RemoteData.toMaybe webDataUser
|> Maybe.map
(\user ->
RemoteData.toMaybe model.config
|> Maybe.map (\config -> pusherLoginMsg config.pusherKey user.pusherChannel)
|> Maybe.withDefault NoOp
)
|> Maybe.withDefault NoOp

( updatedModel, pusherLoginCmd ) =
update msg model
in
-- Return the pusher part of the model, as the pusher login action
-- shouldn't change other parts.
( updatedModel.pusher, pusherLoginCmd )
1 change: 1 addition & 0 deletions client/src/elm/Item/Decoder.elm
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ decodeItem =
decode Item
|> required "label" string
|> optionalAt [ "image", "styles", "large" ] string "http://placehold.it/350x150"
|> optional "private_note" (nullable string) Nothing


decodeItemsDict : Decoder ItemsDict
Expand Down
1 change: 1 addition & 0 deletions client/src/elm/Item/Model.elm
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type alias ItemId =
type alias Item =
{ name : String
, image : String
, privateNote : Maybe String
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

}


Expand Down
43 changes: 25 additions & 18 deletions client/src/elm/Pages/Item/View.elm
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,34 @@ import Html.Attributes exposing (..)
import Pages.Item.Model exposing (Msg(..))
import Item.Model exposing (ItemId, Item)
import User.Model exposing (User)
import Utils.Html exposing (divider, showMaybe)


view : Date -> User -> ItemId -> Item -> Html Msg
view currentDate currentUser itemId item =
div []
[ div
[ class "ui secondary pointing fluid menu" ]
[ h2
[ class "ui header" ]
[ text item.name ]
, div
[ class "right menu" ]
[ a
[ class "ui active item" ]
[ text "Overview" ]
let
privateNote =
showMaybe <|
Maybe.map
(\note -> div [ class "private-note" ] [ text note ])
item.privateNote
in
div []
[ div
[ class "ui secondary pointing fluid menu" ]
[ h2
[ class "ui header" ]
[ text item.name ]
, div
[ class "right menu" ]
[ a
[ class "ui active item" ]
[ text "Overview" ]
]
]
, div []
[ img [ src item.image, alt item.name ] []
]
, divider
, privateNote
]
, div []
[ img [ src item.image, alt item.name ] []
]
, div
[ class "ui divider" ]
[]
]
7 changes: 0 additions & 7 deletions client/src/elm/Pages/Login/Decoder.elm
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,6 @@ module Pages.Login.Decoder exposing (..)
import Base64 exposing (encode)
import Json.Decode as Decode
import Pages.Login.Model exposing (AccessToken)
import User.Decoder as UserDecoder exposing (decodeUser)
import User.Model exposing (User)


decodeUser : Decode.Decoder User
decodeUser =
Decode.at [ "data", "0" ] <| UserDecoder.decodeUser


decodeAccessToken : Decode.Decoder AccessToken
Expand Down
1 change: 1 addition & 0 deletions client/src/elm/Pages/Login/Update.elm
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import User.Model exposing (..)
import Pages.Login.Model as Login exposing (..)
import Pages.Login.Decoder exposing (..)
import RemoteData exposing (RemoteData(..), WebData)
import User.Decoder exposing (decodeUser)
import Utils.WebData exposing (sendWithHandler)


Expand Down
53 changes: 53 additions & 0 deletions client/src/elm/Pusher/Model.elm
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,19 @@ module Pusher.Model exposing (..)
import Item.Model exposing (Item, ItemId)


type alias Model =
{ connectionStatus : ConnectionStatus
, errors : List PusherError
}


emptyModel : Model
emptyModel =
{ connectionStatus = Initialized
, errors = []
}


type Cluster
= ApSouthEast1
| EuWest1
Expand All @@ -25,8 +38,48 @@ type PusherEventData
= ItemUpdate Item


type AccessToken
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

= AccessToken String


type alias PusherChannel =
String


type alias PusherConfig =
{ key : String
, cluster : String
, authEndpoint : String
, channel : String
, eventNames : List String
}


type ConnectionStatus
= Initialized
| Connecting (Maybe Int)
| Connected
| Unavailable (Maybe Int)
| Failed
| Disconnected
| Other String


type alias PusherError =
{ code : Maybe Int
, message : Maybe String
}


{-| Return the event names that should be added via JS.
-}
eventNames : List String
eventNames =
[ "item__update" ]


type Msg
= HandleError PusherError
| HandleStateChange String
| Login PusherAppKey PusherChannel AccessToken
| Logout
1 change: 1 addition & 0 deletions client/src/elm/Pusher/Test.elm
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ decodeTest =
, data =
{ name = "new-item"
, image = "http://placehold.it/350x150"
, privateNote = Nothing
}
|> ItemUpdate
}
Expand Down
Loading