From 5a634905aa6f85efeaa956c6c34a9f0241f269d5 Mon Sep 17 00:00:00 2001 From: Hew Date: Tue, 24 Mar 2020 10:52:13 -0400 Subject: [PATCH 01/23] Add Lasagna websocket middleware (first pass) --- client/package.json | 1 + client/sections-middleware.js | 17 ++++++ client/state/index.js | 1 + client/state/lasagna/actions.js | 5 ++ client/state/lasagna/middleware.js | 60 +++++++++++++++++++ client/state/lasagna/presence/index.js | 17 ++++++ .../private-post-channel/actions-to-events.js | 54 +++++++++++++++++ .../private-post-channel/events-to-actions.js | 23 +++++++ .../public-post-channel/actions-to-events.js | 38 ++++++++++++ .../public-post-channel/events-to-actions.js | 23 +++++++ client/state/lasagna/socket.js | 50 ++++++++++++++++ .../lasagna/user-channel/actions-to-events.js | 27 +++++++++ config/_shared.json | 1 + config/client.json | 1 + package-lock.json | 6 ++ package.json | 1 + 16 files changed, 325 insertions(+) create mode 100644 client/state/lasagna/actions.js create mode 100644 client/state/lasagna/middleware.js create mode 100644 client/state/lasagna/presence/index.js create mode 100644 client/state/lasagna/private-post-channel/actions-to-events.js create mode 100644 client/state/lasagna/private-post-channel/events-to-actions.js create mode 100644 client/state/lasagna/public-post-channel/actions-to-events.js create mode 100644 client/state/lasagna/public-post-channel/events-to-actions.js create mode 100644 client/state/lasagna/socket.js create mode 100644 client/state/lasagna/user-channel/actions-to-events.js diff --git a/client/package.json b/client/package.json index e2722eeafbe0ee..f1750334e4d62f 100644 --- a/client/package.json +++ b/client/package.json @@ -118,6 +118,7 @@ "page": "1.11.5", "path-browserify": "1.0.0", "percentage-regex": "3.0.0", + "phoenix": "1.4.16", "phone": "2.4.2", "photon": "file:../packages/photon", "prismjs": "1.17.1", diff --git a/client/sections-middleware.js b/client/sections-middleware.js index c0ad5e40a20869..179c147d0ebc03 100644 --- a/client/sections-middleware.js +++ b/client/sections-middleware.js @@ -16,6 +16,11 @@ import { pathToRegExp } from './utils'; import { receiveSections, load } from './sections-helper'; import isSectionEnabled from './sections-filter'; import { addReducerToStore } from 'state/add-reducer'; +import { + socketConnect as lasagnaSocketConnect, + socketDisconnect as lasagnaSocketDisconnect, +} from 'state/lasagna/actions'; +import { socket } from 'state/lasagna/socket'; import sections from './sections'; receiveSections( sections ); @@ -25,6 +30,16 @@ function activateSection( sectionDefinition, context ) { context.store.dispatch( activateNextLayoutFocus() ); } +function manageLasagnaSocket( sectionDefinition, context ) { + if ( sectionDefinition.name === 'reader' && ! socket ) { + context.store.dispatch( lasagnaSocketConnect() ); + } + + if ( sectionDefinition.name !== 'reader' && socket ) { + context.store.dispatch( lasagnaSocketDisconnect() ); + } +} + async function loadSection( context, sectionDefinition ) { context.store.dispatch( { type: 'SECTION_SET', isLoading: true } ); @@ -72,6 +87,8 @@ function createPageDefinition( path, sectionDefinition ) { page( pathRegex, async function( context, next ) { try { + manageLasagnaSocket( sectionDefinition, context ); + const loadedSection = _loadedSections[ sectionDefinition.module ]; if ( loadedSection ) { // wait for the promise if loading, do nothing when already loaded diff --git a/client/state/index.js b/client/state/index.js index 2d44a3224bd0e6..b5afe005fb8d2b 100644 --- a/client/state/index.js +++ b/client/state/index.js @@ -67,6 +67,7 @@ export function createReduxStore( initialState, reducer = initialReducer ) { noticesMiddleware, isBrowser && require( './happychat/middleware.js' ).default, isBrowser && require( './happychat/middleware-calypso.js' ).default, + isBrowser && require( './lasagna/middleware.js' ).default, isBrowser && require( './analytics/middleware.js' ).analyticsMiddleware, isBrowser && require( './lib/middleware.js' ).default, isAudioSupported && require( './audio/middleware.js' ).default, diff --git a/client/state/lasagna/actions.js b/client/state/lasagna/actions.js new file mode 100644 index 00000000000000..9e41d548373243 --- /dev/null +++ b/client/state/lasagna/actions.js @@ -0,0 +1,5 @@ +export const socketConnect = () => ( { type: 'LASAGNA_SOCKET_CONNECT' } ); +export const socketDisconnect = () => ( { type: 'LASAGNA_SOCKET_DISCONNECT' } ); + +export const socketConnected = () => ( { type: 'LASAGNA_SOCKET_CONNECTED' } ); +export const socketDisconnected = () => ( { type: 'LASAGNA_SOCKET_DISCONNECTED' } ); diff --git a/client/state/lasagna/middleware.js b/client/state/lasagna/middleware.js new file mode 100644 index 00000000000000..38643bc67f2030 --- /dev/null +++ b/client/state/lasagna/middleware.js @@ -0,0 +1,60 @@ +/** + * Internal dependencies + */ +import wpcom from 'lib/wp'; +import { getCurrentUser } from 'state/current-user/selectors'; +import { socketConnect, socketDisconnect } from './socket'; +import privatePostChannelMiddleware from './private-post-channel/actions-to-events'; +import publicPostChannelMiddleware from './public-post-channel/actions-to-events'; +import userChannelMiddleware from './user-channel/actions-to-events'; + +/** + * Compose a list of middleware into one middleware + * Props @rhc3 + * + * @param m middlewares to compose + */ +const combineMiddleware = ( ...m ) => { + return store => { + const initialized = m.map( middleware => middleware( store ) ); + return next => initialized.reduce( ( chain, mw ) => mw( chain ), next ); + }; +}; + +/** + * Connection management middleware + * + * We intentionally halt the CONNECT/DISCONNECT actions + * and dispatch news actions representing the results + * of the attempts instead. + * + * @param store middleware store + */ +const connectMiddleware = store => next => action => { + const user = getCurrentUser( store.getState() ); + + switch ( action.type ) { + case 'LASAGNA_SOCKET_CONNECT': + wpcom + .request( { + method: 'POST', + path: '/jwt/sign', + body: { payload: JSON.stringify( { user } ) }, + } ) + .then( ( { jwt } ) => socketConnect( store, jwt, user.ID ) ); + return; + + case 'LASAGNA_SOCKET_DISCONNECT': + socketDisconnect( store ); + return; + } + + return next( action ); +}; + +export default combineMiddleware( + connectMiddleware, + userChannelMiddleware, + privatePostChannelMiddleware, + publicPostChannelMiddleware +); diff --git a/client/state/lasagna/presence/index.js b/client/state/lasagna/presence/index.js new file mode 100644 index 00000000000000..a9acdafe031d00 --- /dev/null +++ b/client/state/lasagna/presence/index.js @@ -0,0 +1,17 @@ +/** + * External dependencies + */ +import { Presence } from 'phoenix'; + +/** + * Internal dependencies + */ +import { updateUsersViewingPostCount } from 'state/posts/actions'; + +export default ( channel, store, globalId ) => { + const presence = new Presence( channel ); + presence.onSync( () => { + const presenceCount = presence.list().length; + store.dispatch( updateUsersViewingPostCount( globalId, presenceCount ) ); + } ); +}; diff --git a/client/state/lasagna/private-post-channel/actions-to-events.js b/client/state/lasagna/private-post-channel/actions-to-events.js new file mode 100644 index 00000000000000..7e70ba67b47f65 --- /dev/null +++ b/client/state/lasagna/private-post-channel/actions-to-events.js @@ -0,0 +1,54 @@ +/** + * External dependencies + */ +import debugFactory from 'debug'; + +/** + * Internal Dependencies + */ +import { keyForPost, keyToString } from 'reader/post-key'; +import registerEventHandlers from './events-to-actions'; +import registerPresence from '../presence'; +import { socket } from '../socket'; + +let channel = null; +const debug = debugFactory( 'lasagna:channel:private-post' ); + +export default store => next => action => { + switch ( action.type ) { + case 'READER_POST_SEEN': { + // READER_POST_FULL_VIEW + const { + payload: { site, post }, + } = action; + + if ( ! site.is_private ) { + break; + } + + const postKey = keyToString( keyForPost( post ) ); + channel = socket.channel( `private_post:${ postKey }` ); + registerEventHandlers( channel, store ); + registerPresence( channel, store, postKey ); + channel + .join() + .receive( 'ok', () => debug( 'Channel join ok' ) ) + .receive( 'error', ( { reason } ) => { + debug( 'Channel join error', reason ); + channel.leave(); + channel = null; + } ); + + break; + } + + case 'ROUTE_SET': + if ( channel ) { + channel.leave(); + channel = null; + } + break; + } + + return next( action ); +}; diff --git a/client/state/lasagna/private-post-channel/events-to-actions.js b/client/state/lasagna/private-post-channel/events-to-actions.js new file mode 100644 index 00000000000000..563be380c0cbce --- /dev/null +++ b/client/state/lasagna/private-post-channel/events-to-actions.js @@ -0,0 +1,23 @@ +/** + * External dependencies + */ +import debugFactory from 'debug'; + +/** + * Internal dependencies + */ +import { receiveComments } from 'state/comments/actions'; + +const debug = debugFactory( 'lasagna:channel:private-post' ); + +export default function( channel, store ) { + channel.on( 'new_comment', comment => { + debug( 'New comment', comment ); + + if ( ! comment ) { + return; + } + + store.dispatch( receiveComments( comment.siteId, comment.postId, [ comment ], true ) ); + } ); +} diff --git a/client/state/lasagna/public-post-channel/actions-to-events.js b/client/state/lasagna/public-post-channel/actions-to-events.js new file mode 100644 index 00000000000000..f057c27e9076e8 --- /dev/null +++ b/client/state/lasagna/public-post-channel/actions-to-events.js @@ -0,0 +1,38 @@ +/** + * Internal Dependencies + */ +import registerEventHandlers from './events-to-actions'; +import registerPresence from '../presence'; +import { socket } from '../socket'; + +let channel = null; + +export default store => next => action => { + switch ( action.type ) { + case 'READER_POST_SEEN': { + // READER_POST_FULL_VIEW + const { + payload: { site, post }, + } = action; + + if ( site.is_private ) { + break; + } + + channel = socket.channel( `public_post:${ post.global_ID }` ); + registerEventHandlers( channel, store ); + registerPresence( channel, store, post.global_ID ); + channel.join(); + break; + } + + case 'ROUTE_SET': // READER_POST_FULL_VIEW_LEAVE + if ( channel ) { + channel.leave(); + channel = null; + } + break; + } + + return next( action ); +}; diff --git a/client/state/lasagna/public-post-channel/events-to-actions.js b/client/state/lasagna/public-post-channel/events-to-actions.js new file mode 100644 index 00000000000000..ea2f313563861b --- /dev/null +++ b/client/state/lasagna/public-post-channel/events-to-actions.js @@ -0,0 +1,23 @@ +/** + * External dependencies + */ +import debugFactory from 'debug'; + +/** + * Internal dependencies + */ +import { receiveComments } from 'state/comments/actions'; + +const debug = debugFactory( 'lasagna:channel:public-post' ); + +export default function( channel, store ) { + channel.on( 'new_comment', comment => { + debug( 'New comment', comment ); + + if ( ! comment ) { + return; + } + + store.dispatch( receiveComments( comment.siteId, comment.postId, [ comment ], true ) ); + } ); +} diff --git a/client/state/lasagna/socket.js b/client/state/lasagna/socket.js new file mode 100644 index 00000000000000..3cdac20944cee7 --- /dev/null +++ b/client/state/lasagna/socket.js @@ -0,0 +1,50 @@ +/** + * External Dependencies + */ +import { Socket } from 'phoenix'; +import createDebug from 'debug'; + +/** + * Internal dependencies + */ +import config from 'config'; +import { socketConnected, socketDisconnected } from 'state/lasagna/actions'; + +/** + * Module vars + */ +export let socket = null; + +const debug = createDebug( 'lasagna:socket' ); +const url = config( 'lasagna_url' ); + +export function socketConnect( store, calypsoJwt, userId ) { + if ( socket !== null ) { + return; + } + + socket = new Socket( url, { params: { calypso_jwt: calypsoJwt, user_id: userId } } ); + + socket.onOpen( () => { + debug( 'socket opened' ); + store.dispatch( socketConnected() ); + } ); + + socket.onClose( () => { + debug( 'socket closed' ); + // @TODO: verify this Phoenix.js state, dispatch attempting reconnect here? + } ); + + socket.onError( () => { + debug( 'socket error' ); + // @TODO: verify this Phoenix.js state, dispatch attempting reconnect here? + } ); + + socket.connect(); +} + +export function socketDisconnect( store ) { + socket && socket.disconnect(); + socket = null; + store.dispatch( socketDisconnected() ); +} diff --git a/client/state/lasagna/user-channel/actions-to-events.js b/client/state/lasagna/user-channel/actions-to-events.js new file mode 100644 index 00000000000000..51f4b4c0e43062 --- /dev/null +++ b/client/state/lasagna/user-channel/actions-to-events.js @@ -0,0 +1,27 @@ +/** + * Internal Dependencies + */ +import { getCurrentUserId } from 'state/current-user/selectors'; +// import registerEventHandlers from './events-to-actions'; +import { socket } from '../socket'; + +let channel = null; + +export default store => next => action => { + if ( ! channel && socket && action.type === 'LASAGNA_SOCKET_CONNECTED' ) { + const userId = getCurrentUserId( store.getState() ); + channel = socket.channel( `user:${ userId }` ); + // registerEventHandlers( channel, store ); + channel.join(); + } + + if ( ! channel ) { + return next( action ); + } + + // TBD: do stuff + switch ( action.type ) { + } + + return next( action ); +}; diff --git a/config/_shared.json b/config/_shared.json index aab396ee02e9c6..6ab72284653831 100644 --- a/config/_shared.json +++ b/config/_shared.json @@ -22,6 +22,7 @@ "hotjar_enabled": false, "hostname": false, "i18n_default_locale_slug": "en", + "lasagna_url": "wss://lasagna.pub/socket", "login_url": false, "logout_url": false, "mc_analytics_enabled": false, diff --git a/config/client.json b/config/client.json index 176c37bc66b583..60155c3fe18cb4 100644 --- a/config/client.json +++ b/config/client.json @@ -17,6 +17,7 @@ "hostname", "i18n_default_locale_slug", "languages", + "lasagna_url", "login_url", "logout_url", "hotjar_enabled", diff --git a/package-lock.json b/package-lock.json index 464f5ffd7e6bb4..4ab4190fb132e4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31812,6 +31812,12 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, + "phoenix": { + "version": "1.4.16", + "resolved": "https://registry.npmjs.org/phoenix/-/phoenix-1.4.16.tgz", + "integrity": "sha512-XmKVTlFQuRwTYBO1WU1OaN2tzywRAtY6haUpxHPGxNZuyPEgmo+Caw7+BMlIPhipM197+d962i6sGZLylBtCbA==", + "dev": true + }, "phone": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/phone/-/phone-2.4.2.tgz", diff --git a/package.json b/package.json index d2ebc17ab7b6ec..ae8dfc9a1e49d2 100644 --- a/package.json +++ b/package.json @@ -261,6 +261,7 @@ "npm-merge-driver": "2.3.5", "npm-package-json-lint": "4.6.0", "npm-run-all": "4.1.5", + "phoenix": "1.4.16", "postcss-cli": "6.1.3", "prettier": "npm:wp-prettier@1.19.1", "qs": "6.9.1", From 74f6fa34ec3b9b3fd1d524e95d76ce9023056400 Mon Sep 17 00:00:00 2001 From: Hew Date: Tue, 24 Mar 2020 23:40:03 -0400 Subject: [PATCH 02/23] Step forward --- client/state/lasagna/middleware.js | 16 +++++++--------- client/state/lasagna/presence/index.js | 2 +- .../private-post-channel/actions-to-events.js | 4 ++-- .../private-post-channel/events-to-actions.js | 2 +- .../public-post-channel/actions-to-events.js | 15 ++++++++++++++- .../public-post-channel/events-to-actions.js | 11 ++++++++++- 6 files changed, 35 insertions(+), 15 deletions(-) diff --git a/client/state/lasagna/middleware.js b/client/state/lasagna/middleware.js index 38643bc67f2030..5912e73c9b9b6a 100644 --- a/client/state/lasagna/middleware.js +++ b/client/state/lasagna/middleware.js @@ -24,17 +24,14 @@ const combineMiddleware = ( ...m ) => { /** * Connection management middleware * - * We intentionally halt the CONNECT/DISCONNECT actions - * and dispatch news actions representing the results - * of the attempts instead. - * * @param store middleware store */ const connectMiddleware = store => next => action => { - const user = getCurrentUser( store.getState() ); + next( action ); switch ( action.type ) { - case 'LASAGNA_SOCKET_CONNECT': + case 'LASAGNA_SOCKET_CONNECT': { + const user = getCurrentUser( store.getState() ); wpcom .request( { method: 'POST', @@ -42,14 +39,15 @@ const connectMiddleware = store => next => action => { body: { payload: JSON.stringify( { user } ) }, } ) .then( ( { jwt } ) => socketConnect( store, jwt, user.ID ) ); - return; + break; + } case 'LASAGNA_SOCKET_DISCONNECT': socketDisconnect( store ); - return; + break; } - return next( action ); + return; }; export default combineMiddleware( diff --git a/client/state/lasagna/presence/index.js b/client/state/lasagna/presence/index.js index a9acdafe031d00..79702341106655 100644 --- a/client/state/lasagna/presence/index.js +++ b/client/state/lasagna/presence/index.js @@ -12,6 +12,6 @@ export default ( channel, store, globalId ) => { const presence = new Presence( channel ); presence.onSync( () => { const presenceCount = presence.list().length; - store.dispatch( updateUsersViewingPostCount( globalId, presenceCount ) ); + //store.dispatch( updateUsersViewingPostCount( globalId, presenceCount ) ); } ); }; diff --git a/client/state/lasagna/private-post-channel/actions-to-events.js b/client/state/lasagna/private-post-channel/actions-to-events.js index 7e70ba67b47f65..c1b693ffb968cf 100644 --- a/client/state/lasagna/private-post-channel/actions-to-events.js +++ b/client/state/lasagna/private-post-channel/actions-to-events.js @@ -32,9 +32,9 @@ export default store => next => action => { registerPresence( channel, store, postKey ); channel .join() - .receive( 'ok', () => debug( 'Channel join ok' ) ) + .receive( 'ok', () => debug( 'channel join ok' ) ) .receive( 'error', ( { reason } ) => { - debug( 'Channel join error', reason ); + debug( 'channel join error', reason ); channel.leave(); channel = null; } ); diff --git a/client/state/lasagna/private-post-channel/events-to-actions.js b/client/state/lasagna/private-post-channel/events-to-actions.js index 563be380c0cbce..bab0dd723bdbc9 100644 --- a/client/state/lasagna/private-post-channel/events-to-actions.js +++ b/client/state/lasagna/private-post-channel/events-to-actions.js @@ -18,6 +18,6 @@ export default function( channel, store ) { return; } - store.dispatch( receiveComments( comment.siteId, comment.postId, [ comment ], true ) ); + store.dispatch( receiveComments( comment.siteId, comment.post.ID, [ comment ], true ) ); } ); } diff --git a/client/state/lasagna/public-post-channel/actions-to-events.js b/client/state/lasagna/public-post-channel/actions-to-events.js index f057c27e9076e8..3e97ef515ef48c 100644 --- a/client/state/lasagna/public-post-channel/actions-to-events.js +++ b/client/state/lasagna/public-post-channel/actions-to-events.js @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import debugFactory from 'debug'; + /** * Internal Dependencies */ @@ -6,6 +11,7 @@ import registerPresence from '../presence'; import { socket } from '../socket'; let channel = null; +const debug = debugFactory( 'lasagna:channel:public-post' ); export default store => next => action => { switch ( action.type ) { @@ -22,7 +28,14 @@ export default store => next => action => { channel = socket.channel( `public_post:${ post.global_ID }` ); registerEventHandlers( channel, store ); registerPresence( channel, store, post.global_ID ); - channel.join(); + channel + .join() + .receive( 'ok', () => debug( 'channel join ok' ) ) + .receive( 'error', ( { reason } ) => { + debug( 'channel join error', reason ); + channel.leave(); + channel = null; + } ); break; } diff --git a/client/state/lasagna/public-post-channel/events-to-actions.js b/client/state/lasagna/public-post-channel/events-to-actions.js index ea2f313563861b..115716f626cc52 100644 --- a/client/state/lasagna/public-post-channel/events-to-actions.js +++ b/client/state/lasagna/public-post-channel/events-to-actions.js @@ -18,6 +18,15 @@ export default function( channel, store ) { return; } - store.dispatch( receiveComments( comment.siteId, comment.postId, [ comment ], true ) ); + store.dispatch( + receiveComments( { + siteId: comment.post.site_ID, + postId: comment.post.ID, + comments: [ comment ], + commentById: true, + } ) + ); + + // { type: COMMENTS_COUNT_INCREMENT, siteId, postId } } ); } From 3a4186951e8ebe6a3dcd222b15b955d191cc256a Mon Sep 17 00:00:00 2001 From: Hew Date: Sun, 29 Mar 2020 16:11:44 -0400 Subject: [PATCH 03/23] Sync to latest changes --- .../state/lasagna/private-post-channel/actions-to-events.js | 2 +- .../state/lasagna/private-post-channel/events-to-actions.js | 2 +- client/state/lasagna/public-post-channel/actions-to-events.js | 2 +- client/state/lasagna/socket.js | 4 ++-- client/state/lasagna/user-channel/actions-to-events.js | 2 +- config/development.json | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/client/state/lasagna/private-post-channel/actions-to-events.js b/client/state/lasagna/private-post-channel/actions-to-events.js index c1b693ffb968cf..d9f21d2051ec4d 100644 --- a/client/state/lasagna/private-post-channel/actions-to-events.js +++ b/client/state/lasagna/private-post-channel/actions-to-events.js @@ -27,7 +27,7 @@ export default store => next => action => { } const postKey = keyToString( keyForPost( post ) ); - channel = socket.channel( `private_post:${ postKey }` ); + channel = socket.channel( `private:uni~presence:${ postKey }` ); registerEventHandlers( channel, store ); registerPresence( channel, store, postKey ); channel diff --git a/client/state/lasagna/private-post-channel/events-to-actions.js b/client/state/lasagna/private-post-channel/events-to-actions.js index bab0dd723bdbc9..cc8e10568e8ece 100644 --- a/client/state/lasagna/private-post-channel/events-to-actions.js +++ b/client/state/lasagna/private-post-channel/events-to-actions.js @@ -11,7 +11,7 @@ import { receiveComments } from 'state/comments/actions'; const debug = debugFactory( 'lasagna:channel:private-post' ); export default function( channel, store ) { - channel.on( 'new_comment', comment => { + channel.on( 'new_comment', ( { payload: comment } ) => { debug( 'New comment', comment ); if ( ! comment ) { diff --git a/client/state/lasagna/public-post-channel/actions-to-events.js b/client/state/lasagna/public-post-channel/actions-to-events.js index 3e97ef515ef48c..8e19c0590d6d79 100644 --- a/client/state/lasagna/public-post-channel/actions-to-events.js +++ b/client/state/lasagna/public-post-channel/actions-to-events.js @@ -25,7 +25,7 @@ export default store => next => action => { break; } - channel = socket.channel( `public_post:${ post.global_ID }` ); + channel = socket.channel( `public:uni~presence:${ post.global_ID }` ); registerEventHandlers( channel, store ); registerPresence( channel, store, post.global_ID ); channel diff --git a/client/state/lasagna/socket.js b/client/state/lasagna/socket.js index 3cdac20944cee7..9f36c1094ef541 100644 --- a/client/state/lasagna/socket.js +++ b/client/state/lasagna/socket.js @@ -18,12 +18,12 @@ export let socket = null; const debug = createDebug( 'lasagna:socket' ); const url = config( 'lasagna_url' ); -export function socketConnect( store, calypsoJwt, userId ) { +export function socketConnect( store, jwt, userId ) { if ( socket !== null ) { return; } - socket = new Socket( url, { params: { calypso_jwt: calypsoJwt, user_id: userId } } ); + socket = new Socket( url, { params: { auth_strategy: 'wpcom_api_jwt', jwt, user_id: userId } } ); socket.onOpen( () => { debug( 'socket opened' ); diff --git a/client/state/lasagna/user-channel/actions-to-events.js b/client/state/lasagna/user-channel/actions-to-events.js index 51f4b4c0e43062..185daa99e164b7 100644 --- a/client/state/lasagna/user-channel/actions-to-events.js +++ b/client/state/lasagna/user-channel/actions-to-events.js @@ -10,7 +10,7 @@ let channel = null; export default store => next => action => { if ( ! channel && socket && action.type === 'LASAGNA_SOCKET_CONNECTED' ) { const userId = getCurrentUserId( store.getState() ); - channel = socket.channel( `user:${ userId }` ); + channel = socket.channel( `user:wpcom:${ userId }` ); // registerEventHandlers( channel, store ); channel.join(); } diff --git a/config/development.json b/config/development.json index 567f9c702e4c03..868594ae098b91 100644 --- a/config/development.json +++ b/config/development.json @@ -133,7 +133,7 @@ "publicize-preview": true, "push-notifications": true, "reader": true, - "reader/comment-polling": true, + "reader/comment-polling": false, "reader/full-errors": true, "reader/user-mention-suggestions": true, "recommend-plugins": true, From be2ee42f7baefea1e5abe60da7aac15508b9d4a3 Mon Sep 17 00:00:00 2001 From: Hew Date: Sun, 29 Mar 2020 16:12:18 -0400 Subject: [PATCH 04/23] One other file --- client/state/lasagna/public-post-channel/events-to-actions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/state/lasagna/public-post-channel/events-to-actions.js b/client/state/lasagna/public-post-channel/events-to-actions.js index 115716f626cc52..ee7bd7dfa9e149 100644 --- a/client/state/lasagna/public-post-channel/events-to-actions.js +++ b/client/state/lasagna/public-post-channel/events-to-actions.js @@ -11,7 +11,7 @@ import { receiveComments } from 'state/comments/actions'; const debug = debugFactory( 'lasagna:channel:public-post' ); export default function( channel, store ) { - channel.on( 'new_comment', comment => { + channel.on( 'new_comment', ( { payload: comment } ) => { debug( 'New comment', comment ); if ( ! comment ) { From c8f2ce2c8b4e17f8f58df97dc314c095d8a92622 Mon Sep 17 00:00:00 2001 From: Hew Date: Sun, 29 Mar 2020 19:24:49 -0400 Subject: [PATCH 05/23] Fix topics --- .../state/lasagna/private-post-channel/actions-to-events.js | 4 +++- client/state/lasagna/public-post-channel/actions-to-events.js | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/client/state/lasagna/private-post-channel/actions-to-events.js b/client/state/lasagna/private-post-channel/actions-to-events.js index d9f21d2051ec4d..2aa2dcfb6bdb24 100644 --- a/client/state/lasagna/private-post-channel/actions-to-events.js +++ b/client/state/lasagna/private-post-channel/actions-to-events.js @@ -12,6 +12,8 @@ import registerPresence from '../presence'; import { socket } from '../socket'; let channel = null; +const channelTopicPrefix = 'private:uni~presence:wp_post:'; + const debug = debugFactory( 'lasagna:channel:private-post' ); export default store => next => action => { @@ -27,7 +29,7 @@ export default store => next => action => { } const postKey = keyToString( keyForPost( post ) ); - channel = socket.channel( `private:uni~presence:${ postKey }` ); + channel = socket.channel( channelTopicPrefix + postKey ); registerEventHandlers( channel, store ); registerPresence( channel, store, postKey ); channel diff --git a/client/state/lasagna/public-post-channel/actions-to-events.js b/client/state/lasagna/public-post-channel/actions-to-events.js index 8e19c0590d6d79..595de0cd573632 100644 --- a/client/state/lasagna/public-post-channel/actions-to-events.js +++ b/client/state/lasagna/public-post-channel/actions-to-events.js @@ -11,6 +11,8 @@ import registerPresence from '../presence'; import { socket } from '../socket'; let channel = null; +const channelTopicPrefix = 'public:uni~presence:wp_post:'; + const debug = debugFactory( 'lasagna:channel:public-post' ); export default store => next => action => { @@ -25,7 +27,7 @@ export default store => next => action => { break; } - channel = socket.channel( `public:uni~presence:${ post.global_ID }` ); + channel = socket.channel( channelTopicPrefix + post.global_ID ); registerEventHandlers( channel, store ); registerPresence( channel, store, post.global_ID ); channel From d8f2713df2e3ab62833d10c2e59e24a5ccd28ca1 Mon Sep 17 00:00:00 2001 From: Hew Date: Tue, 31 Mar 2020 01:26:47 -0400 Subject: [PATCH 06/23] Check in a bunch of stuff --- client/blocks/presence-button/README.md | 26 ++++++ .../blocks/presence-button/docs/example.jsx | 28 +++++++ client/blocks/presence-button/index.jsx | 79 +++++++++++++++++++ client/blocks/presence-button/style.scss | 36 +++++++++ client/blocks/reader-full-post/index.jsx | 15 +++- client/blocks/reader-post-actions/index.jsx | 12 +++ client/state/action-types.js | 1 + client/state/lasagna/presence/index.js | 7 +- .../private-post-channel/actions-to-events.js | 4 +- .../public-post-channel/actions-to-events.js | 2 +- client/state/presence/actions.js | 12 +++ client/state/presence/init.js | 7 ++ client/state/presence/reducer.js | 27 +++++++ client/state/presence/schema.js | 16 ++++ client/state/presence/selectors.js | 8 ++ config/development.local.bk | 3 + 16 files changed, 275 insertions(+), 8 deletions(-) create mode 100644 client/blocks/presence-button/README.md create mode 100644 client/blocks/presence-button/docs/example.jsx create mode 100644 client/blocks/presence-button/index.jsx create mode 100644 client/blocks/presence-button/style.scss create mode 100644 client/state/presence/actions.js create mode 100644 client/state/presence/init.js create mode 100644 client/state/presence/reducer.js create mode 100644 client/state/presence/schema.js create mode 100644 client/state/presence/selectors.js create mode 100644 config/development.local.bk diff --git a/client/blocks/presence-button/README.md b/client/blocks/presence-button/README.md new file mode 100644 index 00000000000000..ef10cff19fbecc --- /dev/null +++ b/client/blocks/presence-button/README.md @@ -0,0 +1,26 @@ +Comment Button +========= + +This component is used to display a button with an embedded number indicator. + +#### How to use: + +```js +import CommentButton from 'blocks/comment-button'; + +render() { + return ( + + ); +} +``` + +#### Props + +* `commentCount`: Number indicating the number of comments to be displayed next to the button. +* `href`: String URL destination to be used with a `tagName` of `a`. Defaults to `null`. +* `onClick`: Function to be executed when the user clicks the button. +* `showLabel`: Boolean indicating whether or not the label with the comments count is visible. Defaults to `true`. +* `size`: Number with the size of the comments icon to be displayed. Defaults to 24. +* `tagName`: String with the HTML tag we are going to use to render the component. Defaults to 'li'. +* `target`: String `target` attribute to be used with a `tagName` of `a`. Defaults to `null`. diff --git a/client/blocks/presence-button/docs/example.jsx b/client/blocks/presence-button/docs/example.jsx new file mode 100644 index 00000000000000..3ad8e805c4f25b --- /dev/null +++ b/client/blocks/presence-button/docs/example.jsx @@ -0,0 +1,28 @@ +/** + * External dependencies + */ + +import React from 'react'; + +/** + * Internal dependencies + */ +import PresenceButton from 'blocks/presence-button'; +import { Card } from '@automattic/components'; + +export default class PresenceButtonExample extends React.Component { + static displayName = 'PresenceButtonExample'; + + render() { + return ( +
+ + + + + + +
+ ); + } +} diff --git a/client/blocks/presence-button/index.jsx b/client/blocks/presence-button/index.jsx new file mode 100644 index 00000000000000..b0638f5da6dc1f --- /dev/null +++ b/client/blocks/presence-button/index.jsx @@ -0,0 +1,79 @@ +/** + * External dependencies + */ + +import PropTypes from 'prop-types'; +import React from 'react'; +import { connect } from 'react-redux'; +import { useTranslate } from 'i18n-calypso'; +import { isNull, noop, omitBy } from 'lodash'; + +/** + * Internal dependencies + */ +import Gridicon from 'components/gridicon'; +import { getPostPresenceCount } from 'state/presence/selectors'; + +/** + * Style dependencies + */ +import './style.scss'; + +function PresenceButton( props ) { + const { presenceCount, href, onClick, showLabel, tagName, target } = props; + const translate = useTranslate(); + + return React.createElement( + tagName, + omitBy( + { + className: 'presence-button', + href: 'a' === tagName ? href : null, + onClick, + target: 'a' === tagName ? target : null, + }, + isNull + ), + , + + { presenceCount } + + { showLabel && ( + + { translate( 'Viewing', 'Viewing', { + context: 'verb', + count: presenceCount, + } ) } + + ) } + + ); +} + +PresenceButton.propTypes = { + presenceCount: PropTypes.number, + href: PropTypes.string, + onClick: PropTypes.func, + showLabel: PropTypes.bool, + tagName: PropTypes.string, + target: PropTypes.string, +}; + +PresenceButton.defaultProps = { + presenceCount: 0, + href: null, + onClick: noop, + showLabel: true, + size: 24, + tagName: 'li', + target: null, +}; + +const mapStateToProps = ( state, ownProps ) => { + const { post: { global_ID: globalId } = {}, presenceCount } = ownProps; + return { + presenceCount: getPostPresenceCount( state, globalId ) || presenceCount, + }; +}; + +export default connect( mapStateToProps )( PresenceButton ); diff --git a/client/blocks/presence-button/style.scss b/client/blocks/presence-button/style.scss new file mode 100644 index 00000000000000..d6af5296257d24 --- /dev/null +++ b/client/blocks/presence-button/style.scss @@ -0,0 +1,36 @@ +.presence-button { + align-items: center; + box-sizing: border-box; + color: var( --color-text-subtle ); + cursor: pointer; + display: inline-flex; + list-style-type: none; + padding: 4px; + position: relative; + + &:hover, + &:focus, + &:active { + color: var( --color-primary ); + } + + .presence-button__label-count { + margin-right: 4px; + } + + .presence-button__label-status { + @include breakpoint( '<480px' ) { + display: none; + } + } +} + +.presence-button__label { + font-size: 17px; + margin-left: 4px; +} + +.presence-button__icon { + position: relative; + top: 2px; +} diff --git a/client/blocks/reader-full-post/index.jsx b/client/blocks/reader-full-post/index.jsx index 2123abc514b987..2bfc589c35f9e3 100644 --- a/client/blocks/reader-full-post/index.jsx +++ b/client/blocks/reader-full-post/index.jsx @@ -63,6 +63,7 @@ import isLikedPost from 'state/selectors/is-liked-post'; import QueryPostLikes from 'components/data/query-post-likes'; import getCurrentStream from 'state/selectors/get-reader-current-stream'; import { getNextItem, getPreviousItem } from 'state/reader/streams/selectors'; +import { getPostPresenceCount } from 'state/presence/selectors'; /** * Style dependencies @@ -278,7 +279,17 @@ export class FullPostView extends React.Component { }; render() { - const { post, site, feed, referralPost, referral, blogId, feedId, postId } = this.props; + const { + blogId, + feed, + feedId, + post, + postId, + presenceCount, + referral, + referralPost, + site, + } = this.props; if ( post.is_error ) { return ; @@ -411,6 +422,7 @@ export class FullPostView extends React.Component { site={ site } onCommentClick={ this.handleCommentClick } fullPost={ true } + presenceCount={ presenceCount } /> { showRelatedPosts && ( @@ -487,6 +499,7 @@ export default connect( post, liked: isLikedPost( state, siteId, post.ID ), postKey, + presenceCount: getPostPresenceCount( state, post.global_ID ), }; if ( ! isExternal && siteId ) { diff --git a/client/blocks/reader-post-actions/index.jsx b/client/blocks/reader-post-actions/index.jsx index 48ab2cd7f4ebc0..ee6d57e5ee7936 100644 --- a/client/blocks/reader-post-actions/index.jsx +++ b/client/blocks/reader-post-actions/index.jsx @@ -12,6 +12,7 @@ import CommentButton from 'blocks/comment-button'; import LikeButton from 'reader/like-button'; import ShareButton from 'blocks/reader-share'; import PostEditButton from 'blocks/post-edit-button'; +import PresenceButton from 'blocks/presence-button'; import ReaderPostOptionsMenu from 'blocks/reader-post-options-menu'; import { shouldShowComments } from 'blocks/comments/helper'; import { shouldShowLikes } from 'reader/like-helper'; @@ -94,6 +95,17 @@ const ReaderPostActions = props => { /> ) } + { /*shouldShowPresence( post )*/ true && ( +
  • + +
  • + ) } { shouldShowLikes( post ) && (
  • { +export default ( channel, store, entity, uid ) => { const presence = new Presence( channel ); presence.onSync( () => { - const presenceCount = presence.list().length; - //store.dispatch( updateUsersViewingPostCount( globalId, presenceCount ) ); + store.dispatch( setPresenceMeta( entity, uid, presence.list().length ) ); } ); }; diff --git a/client/state/lasagna/private-post-channel/actions-to-events.js b/client/state/lasagna/private-post-channel/actions-to-events.js index 2aa2dcfb6bdb24..552be8a7b18a2b 100644 --- a/client/state/lasagna/private-post-channel/actions-to-events.js +++ b/client/state/lasagna/private-post-channel/actions-to-events.js @@ -29,9 +29,9 @@ export default store => next => action => { } const postKey = keyToString( keyForPost( post ) ); - channel = socket.channel( channelTopicPrefix + postKey ); + channel = socket.channel( channelTopicPrefix + post.global_ID, { post_key: postKey } ); registerEventHandlers( channel, store ); - registerPresence( channel, store, postKey ); + registerPresence( channel, store, 'posts', post.global_ID ); channel .join() .receive( 'ok', () => debug( 'channel join ok' ) ) diff --git a/client/state/lasagna/public-post-channel/actions-to-events.js b/client/state/lasagna/public-post-channel/actions-to-events.js index 595de0cd573632..d03bc6763247ba 100644 --- a/client/state/lasagna/public-post-channel/actions-to-events.js +++ b/client/state/lasagna/public-post-channel/actions-to-events.js @@ -29,7 +29,7 @@ export default store => next => action => { channel = socket.channel( channelTopicPrefix + post.global_ID ); registerEventHandlers( channel, store ); - registerPresence( channel, store, post.global_ID ); + registerPresence( channel, store, 'posts', post.global_ID ); channel .join() .receive( 'ok', () => debug( 'channel join ok' ) ) diff --git a/client/state/presence/actions.js b/client/state/presence/actions.js new file mode 100644 index 00000000000000..4e728b0f9d9ea2 --- /dev/null +++ b/client/state/presence/actions.js @@ -0,0 +1,12 @@ +/** + * Internal dependencies + */ +import { PRESENCE_META_SET } from 'state/action-types'; +import 'state/presence/init'; + +export const setPresenceMeta = ( entity, uid, meta ) => ( { + type: PRESENCE_META_SET, + entity, + uid, + meta, +} ); diff --git a/client/state/presence/init.js b/client/state/presence/init.js new file mode 100644 index 00000000000000..673b59ecc58ecb --- /dev/null +++ b/client/state/presence/init.js @@ -0,0 +1,7 @@ +/** + * Internal dependencies + */ +import { registerReducer } from 'state/redux-store'; +import presenceReducer from './reducer'; + +registerReducer( [ 'presence' ], presenceReducer ); diff --git a/client/state/presence/reducer.js b/client/state/presence/reducer.js new file mode 100644 index 00000000000000..47007e88742d2b --- /dev/null +++ b/client/state/presence/reducer.js @@ -0,0 +1,27 @@ +/** + * Internal dependencies + */ +import { PRESENCE_META_SET } from 'state/action-types'; +import { combineReducers, withSchemaValidation } from 'state/utils'; +import { itemsSchema } from './schema'; + +const DEFAULT_STATE = { + posts: {}, +}; + +export const items = withSchemaValidation( itemsSchema, ( state = DEFAULT_STATE, action ) => { + switch ( action.type ) { + case PRESENCE_META_SET: + return { + ...state, + [ action.entity ]: { + ...state[ action.entity ], + [ action.uid ]: action.meta, + }, + }; + } + + return state; +} ); + +export default combineReducers( { items } ); diff --git a/client/state/presence/schema.js b/client/state/presence/schema.js new file mode 100644 index 00000000000000..c5a4f328db68e8 --- /dev/null +++ b/client/state/presence/schema.js @@ -0,0 +1,16 @@ +export const itemsSchema = { + type: 'object', + properties: { + posts: { + type: 'object', + patternProperties: { + // global_ID: view count + '^S_': { + type: 'integer', + minimum: 0, + }, + }, + }, + }, + additionalProperties: false, +}; diff --git a/client/state/presence/selectors.js b/client/state/presence/selectors.js new file mode 100644 index 00000000000000..2a3ff3016b6009 --- /dev/null +++ b/client/state/presence/selectors.js @@ -0,0 +1,8 @@ +/** + * Internal dependencies + */ +import 'state/presence/init'; + +export const getPostPresenceCount = ( state, globalId ) => { + return state.presence.items.posts[ globalId ] || 0; +}; diff --git a/config/development.local.bk b/config/development.local.bk new file mode 100644 index 00000000000000..722aee646b9df7 --- /dev/null +++ b/config/development.local.bk @@ -0,0 +1,3 @@ +{ + "lasagna_url": "ws://localhost:4000/socket" +} From f0e7ee9ff93d92ea5bf4805df2e1f31df0b2d781 Mon Sep 17 00:00:00 2001 From: Hew Date: Wed, 1 Apr 2020 17:10:33 -0400 Subject: [PATCH 07/23] Remove Presence for now --- client/state/lasagna/presence/index.js | 16 ----------- .../private-post-channel/actions-to-events.js | 6 ++--- .../public-post-channel/actions-to-events.js | 6 ++--- client/state/presence/actions.js | 12 --------- client/state/presence/init.js | 7 ----- client/state/presence/reducer.js | 27 ------------------- client/state/presence/schema.js | 16 ----------- client/state/presence/selectors.js | 8 ------ 8 files changed, 4 insertions(+), 94 deletions(-) delete mode 100644 client/state/lasagna/presence/index.js delete mode 100644 client/state/presence/actions.js delete mode 100644 client/state/presence/init.js delete mode 100644 client/state/presence/reducer.js delete mode 100644 client/state/presence/schema.js delete mode 100644 client/state/presence/selectors.js diff --git a/client/state/lasagna/presence/index.js b/client/state/lasagna/presence/index.js deleted file mode 100644 index 36908d8b168dea..00000000000000 --- a/client/state/lasagna/presence/index.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * External dependencies - */ -import { Presence } from 'phoenix'; - -/** - * Internal dependencies - */ -import { setPresenceMeta } from 'state/presence/actions'; - -export default ( channel, store, entity, uid ) => { - const presence = new Presence( channel ); - presence.onSync( () => { - store.dispatch( setPresenceMeta( entity, uid, presence.list().length ) ); - } ); -}; diff --git a/client/state/lasagna/private-post-channel/actions-to-events.js b/client/state/lasagna/private-post-channel/actions-to-events.js index 552be8a7b18a2b..403067d3945501 100644 --- a/client/state/lasagna/private-post-channel/actions-to-events.js +++ b/client/state/lasagna/private-post-channel/actions-to-events.js @@ -8,13 +8,12 @@ import debugFactory from 'debug'; */ import { keyForPost, keyToString } from 'reader/post-key'; import registerEventHandlers from './events-to-actions'; -import registerPresence from '../presence'; import { socket } from '../socket'; let channel = null; -const channelTopicPrefix = 'private:uni~presence:wp_post:'; +const channelTopicPrefix = 'private:push:wp_post:'; -const debug = debugFactory( 'lasagna:channel:private-post' ); +const debug = debugFactory( 'lasagna:channel:private:push:wp_post' ); export default store => next => action => { switch ( action.type ) { @@ -31,7 +30,6 @@ export default store => next => action => { const postKey = keyToString( keyForPost( post ) ); channel = socket.channel( channelTopicPrefix + post.global_ID, { post_key: postKey } ); registerEventHandlers( channel, store ); - registerPresence( channel, store, 'posts', post.global_ID ); channel .join() .receive( 'ok', () => debug( 'channel join ok' ) ) diff --git a/client/state/lasagna/public-post-channel/actions-to-events.js b/client/state/lasagna/public-post-channel/actions-to-events.js index d03bc6763247ba..6e5d5d6ad14bbe 100644 --- a/client/state/lasagna/public-post-channel/actions-to-events.js +++ b/client/state/lasagna/public-post-channel/actions-to-events.js @@ -7,13 +7,12 @@ import debugFactory from 'debug'; * Internal Dependencies */ import registerEventHandlers from './events-to-actions'; -import registerPresence from '../presence'; import { socket } from '../socket'; let channel = null; -const channelTopicPrefix = 'public:uni~presence:wp_post:'; +const channelTopicPrefix = 'public:push:wp_post:'; -const debug = debugFactory( 'lasagna:channel:public-post' ); +const debug = debugFactory( 'lasagna:channel:public:push:wp_post' ); export default store => next => action => { switch ( action.type ) { @@ -29,7 +28,6 @@ export default store => next => action => { channel = socket.channel( channelTopicPrefix + post.global_ID ); registerEventHandlers( channel, store ); - registerPresence( channel, store, 'posts', post.global_ID ); channel .join() .receive( 'ok', () => debug( 'channel join ok' ) ) diff --git a/client/state/presence/actions.js b/client/state/presence/actions.js deleted file mode 100644 index 4e728b0f9d9ea2..00000000000000 --- a/client/state/presence/actions.js +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Internal dependencies - */ -import { PRESENCE_META_SET } from 'state/action-types'; -import 'state/presence/init'; - -export const setPresenceMeta = ( entity, uid, meta ) => ( { - type: PRESENCE_META_SET, - entity, - uid, - meta, -} ); diff --git a/client/state/presence/init.js b/client/state/presence/init.js deleted file mode 100644 index 673b59ecc58ecb..00000000000000 --- a/client/state/presence/init.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Internal dependencies - */ -import { registerReducer } from 'state/redux-store'; -import presenceReducer from './reducer'; - -registerReducer( [ 'presence' ], presenceReducer ); diff --git a/client/state/presence/reducer.js b/client/state/presence/reducer.js deleted file mode 100644 index 47007e88742d2b..00000000000000 --- a/client/state/presence/reducer.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Internal dependencies - */ -import { PRESENCE_META_SET } from 'state/action-types'; -import { combineReducers, withSchemaValidation } from 'state/utils'; -import { itemsSchema } from './schema'; - -const DEFAULT_STATE = { - posts: {}, -}; - -export const items = withSchemaValidation( itemsSchema, ( state = DEFAULT_STATE, action ) => { - switch ( action.type ) { - case PRESENCE_META_SET: - return { - ...state, - [ action.entity ]: { - ...state[ action.entity ], - [ action.uid ]: action.meta, - }, - }; - } - - return state; -} ); - -export default combineReducers( { items } ); diff --git a/client/state/presence/schema.js b/client/state/presence/schema.js deleted file mode 100644 index c5a4f328db68e8..00000000000000 --- a/client/state/presence/schema.js +++ /dev/null @@ -1,16 +0,0 @@ -export const itemsSchema = { - type: 'object', - properties: { - posts: { - type: 'object', - patternProperties: { - // global_ID: view count - '^S_': { - type: 'integer', - minimum: 0, - }, - }, - }, - }, - additionalProperties: false, -}; diff --git a/client/state/presence/selectors.js b/client/state/presence/selectors.js deleted file mode 100644 index 2a3ff3016b6009..00000000000000 --- a/client/state/presence/selectors.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Internal dependencies - */ -import 'state/presence/init'; - -export const getPostPresenceCount = ( state, globalId ) => { - return state.presence.items.posts[ globalId ] || 0; -}; From e511e86aec28ac7bfb5c645f6b0c21159128d0ba Mon Sep 17 00:00:00 2001 From: Hew Date: Wed, 1 Apr 2020 17:11:55 -0400 Subject: [PATCH 08/23] Fix logging path --- client/state/lasagna/private-post-channel/events-to-actions.js | 2 +- client/state/lasagna/public-post-channel/events-to-actions.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/state/lasagna/private-post-channel/events-to-actions.js b/client/state/lasagna/private-post-channel/events-to-actions.js index cc8e10568e8ece..59a22ea76399a6 100644 --- a/client/state/lasagna/private-post-channel/events-to-actions.js +++ b/client/state/lasagna/private-post-channel/events-to-actions.js @@ -8,7 +8,7 @@ import debugFactory from 'debug'; */ import { receiveComments } from 'state/comments/actions'; -const debug = debugFactory( 'lasagna:channel:private-post' ); +const debug = debugFactory( 'lasagna:channel:private:push:wp_post' ); export default function( channel, store ) { channel.on( 'new_comment', ( { payload: comment } ) => { diff --git a/client/state/lasagna/public-post-channel/events-to-actions.js b/client/state/lasagna/public-post-channel/events-to-actions.js index ee7bd7dfa9e149..1683b39c9c7bba 100644 --- a/client/state/lasagna/public-post-channel/events-to-actions.js +++ b/client/state/lasagna/public-post-channel/events-to-actions.js @@ -8,7 +8,7 @@ import debugFactory from 'debug'; */ import { receiveComments } from 'state/comments/actions'; -const debug = debugFactory( 'lasagna:channel:public-post' ); +const debug = debugFactory( 'lasagna:channel:public:push:wp_post' ); export default function( channel, store ) { channel.on( 'new_comment', ( { payload: comment } ) => { From f496ca874b4511da90a5cfaa15b51397b7f437b0 Mon Sep 17 00:00:00 2001 From: Hew Date: Wed, 1 Apr 2020 17:17:45 -0400 Subject: [PATCH 09/23] Remove Presence stragglers --- client/blocks/presence-button/README.md | 26 ------ .../blocks/presence-button/docs/example.jsx | 28 ------- client/blocks/presence-button/index.jsx | 79 ------------------- client/blocks/presence-button/style.scss | 36 --------- 4 files changed, 169 deletions(-) delete mode 100644 client/blocks/presence-button/README.md delete mode 100644 client/blocks/presence-button/docs/example.jsx delete mode 100644 client/blocks/presence-button/index.jsx delete mode 100644 client/blocks/presence-button/style.scss diff --git a/client/blocks/presence-button/README.md b/client/blocks/presence-button/README.md deleted file mode 100644 index ef10cff19fbecc..00000000000000 --- a/client/blocks/presence-button/README.md +++ /dev/null @@ -1,26 +0,0 @@ -Comment Button -========= - -This component is used to display a button with an embedded number indicator. - -#### How to use: - -```js -import CommentButton from 'blocks/comment-button'; - -render() { - return ( - - ); -} -``` - -#### Props - -* `commentCount`: Number indicating the number of comments to be displayed next to the button. -* `href`: String URL destination to be used with a `tagName` of `a`. Defaults to `null`. -* `onClick`: Function to be executed when the user clicks the button. -* `showLabel`: Boolean indicating whether or not the label with the comments count is visible. Defaults to `true`. -* `size`: Number with the size of the comments icon to be displayed. Defaults to 24. -* `tagName`: String with the HTML tag we are going to use to render the component. Defaults to 'li'. -* `target`: String `target` attribute to be used with a `tagName` of `a`. Defaults to `null`. diff --git a/client/blocks/presence-button/docs/example.jsx b/client/blocks/presence-button/docs/example.jsx deleted file mode 100644 index 3ad8e805c4f25b..00000000000000 --- a/client/blocks/presence-button/docs/example.jsx +++ /dev/null @@ -1,28 +0,0 @@ -/** - * External dependencies - */ - -import React from 'react'; - -/** - * Internal dependencies - */ -import PresenceButton from 'blocks/presence-button'; -import { Card } from '@automattic/components'; - -export default class PresenceButtonExample extends React.Component { - static displayName = 'PresenceButtonExample'; - - render() { - return ( -
    - - - - - - -
    - ); - } -} diff --git a/client/blocks/presence-button/index.jsx b/client/blocks/presence-button/index.jsx deleted file mode 100644 index b0638f5da6dc1f..00000000000000 --- a/client/blocks/presence-button/index.jsx +++ /dev/null @@ -1,79 +0,0 @@ -/** - * External dependencies - */ - -import PropTypes from 'prop-types'; -import React from 'react'; -import { connect } from 'react-redux'; -import { useTranslate } from 'i18n-calypso'; -import { isNull, noop, omitBy } from 'lodash'; - -/** - * Internal dependencies - */ -import Gridicon from 'components/gridicon'; -import { getPostPresenceCount } from 'state/presence/selectors'; - -/** - * Style dependencies - */ -import './style.scss'; - -function PresenceButton( props ) { - const { presenceCount, href, onClick, showLabel, tagName, target } = props; - const translate = useTranslate(); - - return React.createElement( - tagName, - omitBy( - { - className: 'presence-button', - href: 'a' === tagName ? href : null, - onClick, - target: 'a' === tagName ? target : null, - }, - isNull - ), - , - - { presenceCount } - - { showLabel && ( - - { translate( 'Viewing', 'Viewing', { - context: 'verb', - count: presenceCount, - } ) } - - ) } - - ); -} - -PresenceButton.propTypes = { - presenceCount: PropTypes.number, - href: PropTypes.string, - onClick: PropTypes.func, - showLabel: PropTypes.bool, - tagName: PropTypes.string, - target: PropTypes.string, -}; - -PresenceButton.defaultProps = { - presenceCount: 0, - href: null, - onClick: noop, - showLabel: true, - size: 24, - tagName: 'li', - target: null, -}; - -const mapStateToProps = ( state, ownProps ) => { - const { post: { global_ID: globalId } = {}, presenceCount } = ownProps; - return { - presenceCount: getPostPresenceCount( state, globalId ) || presenceCount, - }; -}; - -export default connect( mapStateToProps )( PresenceButton ); diff --git a/client/blocks/presence-button/style.scss b/client/blocks/presence-button/style.scss deleted file mode 100644 index d6af5296257d24..00000000000000 --- a/client/blocks/presence-button/style.scss +++ /dev/null @@ -1,36 +0,0 @@ -.presence-button { - align-items: center; - box-sizing: border-box; - color: var( --color-text-subtle ); - cursor: pointer; - display: inline-flex; - list-style-type: none; - padding: 4px; - position: relative; - - &:hover, - &:focus, - &:active { - color: var( --color-primary ); - } - - .presence-button__label-count { - margin-right: 4px; - } - - .presence-button__label-status { - @include breakpoint( '<480px' ) { - display: none; - } - } -} - -.presence-button__label { - font-size: 17px; - margin-left: 4px; -} - -.presence-button__icon { - position: relative; - top: 2px; -} From d02422c5cf87ee592ebaf619d9d4928c88461d44 Mon Sep 17 00:00:00 2001 From: Hew Date: Wed, 1 Apr 2020 17:19:58 -0400 Subject: [PATCH 10/23] More Presence stragglers --- client/blocks/reader-full-post/index.jsx | 15 +-------------- client/blocks/reader-post-actions/index.jsx | 12 ------------ 2 files changed, 1 insertion(+), 26 deletions(-) diff --git a/client/blocks/reader-full-post/index.jsx b/client/blocks/reader-full-post/index.jsx index 2bfc589c35f9e3..2123abc514b987 100644 --- a/client/blocks/reader-full-post/index.jsx +++ b/client/blocks/reader-full-post/index.jsx @@ -63,7 +63,6 @@ import isLikedPost from 'state/selectors/is-liked-post'; import QueryPostLikes from 'components/data/query-post-likes'; import getCurrentStream from 'state/selectors/get-reader-current-stream'; import { getNextItem, getPreviousItem } from 'state/reader/streams/selectors'; -import { getPostPresenceCount } from 'state/presence/selectors'; /** * Style dependencies @@ -279,17 +278,7 @@ export class FullPostView extends React.Component { }; render() { - const { - blogId, - feed, - feedId, - post, - postId, - presenceCount, - referral, - referralPost, - site, - } = this.props; + const { post, site, feed, referralPost, referral, blogId, feedId, postId } = this.props; if ( post.is_error ) { return ; @@ -422,7 +411,6 @@ export class FullPostView extends React.Component { site={ site } onCommentClick={ this.handleCommentClick } fullPost={ true } - presenceCount={ presenceCount } /> { showRelatedPosts && ( @@ -499,7 +487,6 @@ export default connect( post, liked: isLikedPost( state, siteId, post.ID ), postKey, - presenceCount: getPostPresenceCount( state, post.global_ID ), }; if ( ! isExternal && siteId ) { diff --git a/client/blocks/reader-post-actions/index.jsx b/client/blocks/reader-post-actions/index.jsx index ee6d57e5ee7936..48ab2cd7f4ebc0 100644 --- a/client/blocks/reader-post-actions/index.jsx +++ b/client/blocks/reader-post-actions/index.jsx @@ -12,7 +12,6 @@ import CommentButton from 'blocks/comment-button'; import LikeButton from 'reader/like-button'; import ShareButton from 'blocks/reader-share'; import PostEditButton from 'blocks/post-edit-button'; -import PresenceButton from 'blocks/presence-button'; import ReaderPostOptionsMenu from 'blocks/reader-post-options-menu'; import { shouldShowComments } from 'blocks/comments/helper'; import { shouldShowLikes } from 'reader/like-helper'; @@ -95,17 +94,6 @@ const ReaderPostActions = props => { />
  • ) } - { /*shouldShowPresence( post )*/ true && ( -
  • - -
  • - ) } { shouldShowLikes( post ) && (
  • Date: Wed, 1 Apr 2020 19:16:38 -0400 Subject: [PATCH 11/23] More cleanup --- client/state/action-types.js | 1 - config/development.local.bk | 3 --- 2 files changed, 4 deletions(-) delete mode 100644 config/development.local.bk diff --git a/client/state/action-types.js b/client/state/action-types.js index 79b99fbd91c020..f067adfbed6c82 100644 --- a/client/state/action-types.js +++ b/client/state/action-types.js @@ -731,7 +731,6 @@ export const PREFERENCES_SAVE_SUCCESS = 'PREFERENCES_SAVE_SUCCESS'; export const PREFERENCES_SET = 'PREFERENCES_SET'; export const PRESALE_PRECANCELLATION_CHAT_AVAILABILITY_SET = 'PRESALE_PRECANCELLATION_CHAT_AVAILABILITY_SET'; -export const PRESENCE_META_SET = 'PRESENCE_META_SET'; export const PREVIEW_CUSTOMIZATIONS_CLEAR = 'PREVIEW_CUSTOMIZATIONS_CLEAR'; export const PREVIEW_CUSTOMIZATIONS_SAVED = 'PREVIEW_CUSTOMIZATIONS_SAVED'; export const PREVIEW_CUSTOMIZATIONS_UNDO = 'PREVIEW_CUSTOMIZATIONS_UNDO'; diff --git a/config/development.local.bk b/config/development.local.bk deleted file mode 100644 index 722aee646b9df7..00000000000000 --- a/config/development.local.bk +++ /dev/null @@ -1,3 +0,0 @@ -{ - "lasagna_url": "ws://localhost:4000/socket" -} From 6e23185a57434345b51618cd04745d2680d14c6c Mon Sep 17 00:00:00 2001 From: Hew Date: Wed, 1 Apr 2020 21:42:48 -0400 Subject: [PATCH 12/23] Iterate --- client/sections-middleware.js | 5 ++++- client/state/comments/actions.js | 14 ++++++++++++++ .../private-post-channel/events-to-actions.js | 13 +++++++++++-- .../public-post-channel/events-to-actions.js | 4 ++-- client/state/lasagna/socket.js | 2 +- .../lasagna/user-channel/actions-to-events.js | 4 +--- config/desktop-development.json | 1 + config/desktop.json | 1 + config/development.json | 1 + config/horizon.json | 1 + config/production.json | 1 + config/stage.json | 1 + config/test.json | 1 + config/wpcalypso.json | 1 + 14 files changed, 41 insertions(+), 9 deletions(-) diff --git a/client/sections-middleware.js b/client/sections-middleware.js index 179c147d0ebc03..41e440c8bec85c 100644 --- a/client/sections-middleware.js +++ b/client/sections-middleware.js @@ -87,7 +87,10 @@ function createPageDefinition( path, sectionDefinition ) { page( pathRegex, async function( context, next ) { try { - manageLasagnaSocket( sectionDefinition, context ); + if ( config.isEnabled( 'lasagna' ) ) { + // do realtime in the Reader section + manageLasagnaSocket( sectionDefinition, context ); + } const loadedSection = _loadedSections[ sectionDefinition.module ]; if ( loadedSection ) { diff --git a/client/state/comments/actions.js b/client/state/comments/actions.js index 6ef35729ef496b..026a1ff0936758 100644 --- a/client/state/comments/actions.js +++ b/client/state/comments/actions.js @@ -6,6 +6,7 @@ import { COMMENT_COUNTS_REQUEST, COMMENT_REQUEST, COMMENTS_CHANGE_STATUS, + COMMENTS_COUNT_INCREMENT, COMMENTS_DELETE, COMMENTS_EDIT, COMMENTS_LIKE, @@ -156,6 +157,19 @@ export const requestCommentCounts = ( siteId, postId ) => ( { postId, } ); +/** + * Creates an action that increments the comment count for a given post. + * + * @param {number} siteId Site identifier + * @param {number} [postId] Post identifier + * @returns {object} Action that increments comment count for a post. + */ +export const incrementCommentCount = ( siteId, postId ) => ( { + type: COMMENTS_COUNT_INCREMENT, + siteId, + postId, +} ); + /** * Creates an action that permanently deletes a comment * or removes a comment placeholder from the state diff --git a/client/state/lasagna/private-post-channel/events-to-actions.js b/client/state/lasagna/private-post-channel/events-to-actions.js index 59a22ea76399a6..49e471f8a1516a 100644 --- a/client/state/lasagna/private-post-channel/events-to-actions.js +++ b/client/state/lasagna/private-post-channel/events-to-actions.js @@ -6,7 +6,7 @@ import debugFactory from 'debug'; /** * Internal dependencies */ -import { receiveComments } from 'state/comments/actions'; +import { incrementCommentCount, receiveComments } from 'state/comments/actions'; const debug = debugFactory( 'lasagna:channel:private:push:wp_post' ); @@ -18,6 +18,15 @@ export default function( channel, store ) { return; } - store.dispatch( receiveComments( comment.siteId, comment.post.ID, [ comment ], true ) ); + store.dispatch( + receiveComments( { + siteId: comment.post.site_ID, + postId: comment.post.ID, + comments: [ comment ], + commentById: true, + } ) + ); + + store.dispatch( incrementCommentCount( comment.post.site_ID, comment.post.ID ) ); } ); } diff --git a/client/state/lasagna/public-post-channel/events-to-actions.js b/client/state/lasagna/public-post-channel/events-to-actions.js index 1683b39c9c7bba..7daff042e07312 100644 --- a/client/state/lasagna/public-post-channel/events-to-actions.js +++ b/client/state/lasagna/public-post-channel/events-to-actions.js @@ -6,7 +6,7 @@ import debugFactory from 'debug'; /** * Internal dependencies */ -import { receiveComments } from 'state/comments/actions'; +import { incrementCommentCount, receiveComments } from 'state/comments/actions'; const debug = debugFactory( 'lasagna:channel:public:push:wp_post' ); @@ -27,6 +27,6 @@ export default function( channel, store ) { } ) ); - // { type: COMMENTS_COUNT_INCREMENT, siteId, postId } + store.dispatch( incrementCommentCount( comment.post.site_ID, comment.post.ID ) ); } ); } diff --git a/client/state/lasagna/socket.js b/client/state/lasagna/socket.js index 9f36c1094ef541..05f3825fb8b5bf 100644 --- a/client/state/lasagna/socket.js +++ b/client/state/lasagna/socket.js @@ -23,7 +23,7 @@ export function socketConnect( store, jwt, userId ) { return; } - socket = new Socket( url, { params: { auth_strategy: 'wpcom_api_jwt', jwt, user_id: userId } } ); + socket = new Socket( url, { params: { jwt, user_id: userId } } ); socket.onOpen( () => { debug( 'socket opened' ); diff --git a/client/state/lasagna/user-channel/actions-to-events.js b/client/state/lasagna/user-channel/actions-to-events.js index 185daa99e164b7..bed96e29462eea 100644 --- a/client/state/lasagna/user-channel/actions-to-events.js +++ b/client/state/lasagna/user-channel/actions-to-events.js @@ -19,9 +19,7 @@ export default store => next => action => { return next( action ); } - // TBD: do stuff - switch ( action.type ) { - } + // TBD: switch, do stuff return next( action ); }; diff --git a/config/desktop-development.json b/config/desktop-development.json index a93953eba5f467..c690c876d0c8ad 100644 --- a/config/desktop-development.json +++ b/config/desktop-development.json @@ -52,6 +52,7 @@ "jetpack/happychat": true, "jetpack_core_inline_update": true, "jitms": false, + "lasagna": false, "keyboard-shortcuts": true, "legal-updates-banner": true, "login/magic-login": false, diff --git a/config/desktop.json b/config/desktop.json index e470a6c2a3daba..5be36da1459a9d 100644 --- a/config/desktop.json +++ b/config/desktop.json @@ -42,6 +42,7 @@ "jetpack/connect/remote-install": true, "jetpack/personalPlan": true, "jitms": false, + "lasagna": false, "legal-updates-banner": false, "login/magic-login": false, "login/native-login-links": false, diff --git a/config/development.json b/config/development.json index f9462aae79d36c..dbabdd8dc18925 100644 --- a/config/development.json +++ b/config/development.json @@ -82,6 +82,7 @@ "jetpack/scan-product": true, "jitms": true, "keyboard-shortcuts": true, + "lasagna": true, "legal-updates-banner": true, "login/magic-login": true, "login/native-login-links": true, diff --git a/config/horizon.json b/config/horizon.json index e2ad3b170e898d..da0f5b14539cf1 100644 --- a/config/horizon.json +++ b/config/horizon.json @@ -50,6 +50,7 @@ "jetpack/happychat": true, "jitms": true, "keyboard-shortcuts": true, + "lasagna": false, "legal-updates-banner": false, "login/native-login-links": true, "login/wp-login": true, diff --git a/config/production.json b/config/production.json index 198fd50f85b230..7b944efacab8b1 100644 --- a/config/production.json +++ b/config/production.json @@ -51,6 +51,7 @@ "jetpack/connect/woocommerce": true, "jetpack/happychat": true, "jitms": true, + "lasagna": false, "legal-updates-banner": false, "login/magic-login": true, "login/native-login-links": true, diff --git a/config/stage.json b/config/stage.json index 1e5c5ef9722c6b..3327cdf16820d1 100644 --- a/config/stage.json +++ b/config/stage.json @@ -55,6 +55,7 @@ "jetpack/happychat": true, "jetpack/search-product": true, "jitms": true, + "lasagna": false, "legal-updates-banner": false, "login/magic-login": true, "login/native-login-links": true, diff --git a/config/test.json b/config/test.json index 80f200f5cd3602..9c009b4c791eaf 100644 --- a/config/test.json +++ b/config/test.json @@ -45,6 +45,7 @@ "jetpack/connect/remote-install": true, "jetpack/happychat": true, "jitms": true, + "lasagna": false, "legal-updates-banner": true, "login/native-login-links": true, "login/wp-login": true, diff --git a/config/wpcalypso.json b/config/wpcalypso.json index ae0669517f84f6..4ecbec5dd99aa5 100644 --- a/config/wpcalypso.json +++ b/config/wpcalypso.json @@ -63,6 +63,7 @@ "jetpack/happychat": true, "jetpack/search-product": true, "jitms": true, + "lasagna": true, "legal-updates-banner": false, "login/magic-login": true, "login/native-login-links": true, From fa9700905a838b4f6eceaf2511a9e4a84d7f3858 Mon Sep 17 00:00:00 2001 From: Hew Date: Thu, 2 Apr 2020 13:21:51 -0400 Subject: [PATCH 13/23] Push connection management back into the middleware Duh? --- client/sections-middleware.js | 20 ---------------- client/state/lasagna/middleware.js | 38 ++++++++++++++++-------------- 2 files changed, 20 insertions(+), 38 deletions(-) diff --git a/client/sections-middleware.js b/client/sections-middleware.js index 41e440c8bec85c..c0ad5e40a20869 100644 --- a/client/sections-middleware.js +++ b/client/sections-middleware.js @@ -16,11 +16,6 @@ import { pathToRegExp } from './utils'; import { receiveSections, load } from './sections-helper'; import isSectionEnabled from './sections-filter'; import { addReducerToStore } from 'state/add-reducer'; -import { - socketConnect as lasagnaSocketConnect, - socketDisconnect as lasagnaSocketDisconnect, -} from 'state/lasagna/actions'; -import { socket } from 'state/lasagna/socket'; import sections from './sections'; receiveSections( sections ); @@ -30,16 +25,6 @@ function activateSection( sectionDefinition, context ) { context.store.dispatch( activateNextLayoutFocus() ); } -function manageLasagnaSocket( sectionDefinition, context ) { - if ( sectionDefinition.name === 'reader' && ! socket ) { - context.store.dispatch( lasagnaSocketConnect() ); - } - - if ( sectionDefinition.name !== 'reader' && socket ) { - context.store.dispatch( lasagnaSocketDisconnect() ); - } -} - async function loadSection( context, sectionDefinition ) { context.store.dispatch( { type: 'SECTION_SET', isLoading: true } ); @@ -87,11 +72,6 @@ function createPageDefinition( path, sectionDefinition ) { page( pathRegex, async function( context, next ) { try { - if ( config.isEnabled( 'lasagna' ) ) { - // do realtime in the Reader section - manageLasagnaSocket( sectionDefinition, context ); - } - const loadedSection = _loadedSections[ sectionDefinition.module ]; if ( loadedSection ) { // wait for the promise if loading, do nothing when already loaded diff --git a/client/state/lasagna/middleware.js b/client/state/lasagna/middleware.js index 5912e73c9b9b6a..7fb23daac6dc01 100644 --- a/client/state/lasagna/middleware.js +++ b/client/state/lasagna/middleware.js @@ -3,7 +3,7 @@ */ import wpcom from 'lib/wp'; import { getCurrentUser } from 'state/current-user/selectors'; -import { socketConnect, socketDisconnect } from './socket'; +import { socket, socketConnect, socketDisconnect } from './socket'; import privatePostChannelMiddleware from './private-post-channel/actions-to-events'; import publicPostChannelMiddleware from './public-post-channel/actions-to-events'; import userChannelMiddleware from './user-channel/actions-to-events'; @@ -27,27 +27,29 @@ const combineMiddleware = ( ...m ) => { * @param store middleware store */ const connectMiddleware = store => next => action => { - next( action ); + // bail unless this is a section set with the section definition + if ( action.type !== 'SECTION_SET' || ! action.section ) { + return next( action ); + } - switch ( action.type ) { - case 'LASAGNA_SOCKET_CONNECT': { - const user = getCurrentUser( store.getState() ); - wpcom - .request( { - method: 'POST', - path: '/jwt/sign', - body: { payload: JSON.stringify( { user } ) }, - } ) - .then( ( { jwt } ) => socketConnect( store, jwt, user.ID ) ); - break; - } + // connect if we are going to the reader without a socket + if ( ! socket && action.section.name === 'reader' ) { + const user = getCurrentUser( store.getState() ); + wpcom + .request( { + method: 'POST', + path: '/jwt/sign', + body: { payload: JSON.stringify( { user } ) }, + } ) + .then( ( { jwt } ) => socketConnect( store, jwt, user.ID ) ); + } - case 'LASAGNA_SOCKET_DISCONNECT': - socketDisconnect( store ); - break; + // disconnect if we are leaving the reader with a socket + else if ( socket && action.section.name !== 'reader' ) { + socketDisconnect( store ); } - return; + return next( action ); }; export default combineMiddleware( From f6ef74e48ff1ed2db71ec48ff36678dfd4dd421e Mon Sep 17 00:00:00 2001 From: Hew Date: Fri, 3 Apr 2020 13:05:53 -0400 Subject: [PATCH 14/23] Add Reader full post view state subtree We need a way to query what is onscreen in a Reader full post view if a user hits a reader link directly. The use case is joining related channel(s) after the websocket connects. --- client/state/action-types.js | 2 ++ client/state/lasagna/actions.js | 3 --- client/state/reader/action-types.js | 3 +-- client/state/reader/full-view/actions.js | 9 +++++++ client/state/reader/full-view/reducer.js | 27 +++++++++++++++++++ .../get-reader-full-view-post-key.js | 3 +++ client/state/reader/reducer.js | 2 ++ 7 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 client/state/reader/full-view/actions.js create mode 100644 client/state/reader/full-view/reducer.js create mode 100644 client/state/reader/full-view/selectors/get-reader-full-view-post-key.js diff --git a/client/state/action-types.js b/client/state/action-types.js index c7c118e9053097..874cda3adacfe8 100644 --- a/client/state/action-types.js +++ b/client/state/action-types.js @@ -496,6 +496,8 @@ export const KEYRING_SERVICES_RECEIVE = 'KEYRING_SERVICES_RECEIVE'; export const KEYRING_SERVICES_REQUEST = 'KEYRING_SERVICES_REQUEST'; export const KEYRING_SERVICES_REQUEST_FAILURE = 'KEYRING_SERVICES_REQUEST_FAILURE'; export const KEYRING_SERVICES_REQUEST_SUCCESS = 'KEYRING_SERVICES_REQUEST_SUCCESS'; +export const LASAGNA_SOCKET_CONNECTED = 'LASAGNA_SOCKET_CONNECTED'; +export const LASAGNA_SOCKET_DISCONNECTED = 'LASAGNA_SOCKET_DISCONNECTED'; export const LAYOUT_FOCUS_SET = 'LAYOUT_FOCUS_SET'; export const LAYOUT_NEXT_FOCUS_ACTIVATE = 'LAYOUT_NEXT_FOCUS_ACTIVATE'; export const LAYOUT_NEXT_FOCUS_SET = 'LAYOUT_NEXT_FOCUS_SET'; diff --git a/client/state/lasagna/actions.js b/client/state/lasagna/actions.js index 9e41d548373243..6a4f243f1c50f9 100644 --- a/client/state/lasagna/actions.js +++ b/client/state/lasagna/actions.js @@ -1,5 +1,2 @@ -export const socketConnect = () => ( { type: 'LASAGNA_SOCKET_CONNECT' } ); -export const socketDisconnect = () => ( { type: 'LASAGNA_SOCKET_DISCONNECT' } ); - export const socketConnected = () => ( { type: 'LASAGNA_SOCKET_CONNECTED' } ); export const socketDisconnected = () => ( { type: 'LASAGNA_SOCKET_DISCONNECTED' } ); diff --git a/client/state/reader/action-types.js b/client/state/reader/action-types.js index 55c34646850910..7f51e84bc79800 100644 --- a/client/state/reader/action-types.js +++ b/client/state/reader/action-types.js @@ -33,8 +33,7 @@ export const READER_FOLLOWS_SYNC_START = 'READER_FOLLOWS_SYNC_START'; export const READER_FOLLOWS_SYNC_COMPLETE = 'READER_FOLLOWS_SYNC_COMPLETE'; export const READER_FOLLOW_TAG_RECEIVE = 'READER_FOLLOW_TAG_RECEIVE'; export const READER_FOLLOW_TAG_REQUEST = 'READER_FOLLOW_TAG_REQUEST'; -export const READER_FULLPOST_HIDE = 'READER_FULLPOST_HIDE'; -export const READER_FULLPOST_SHOW = 'READER_FULLPOST_SHOW'; +export const READER_FULL_VIEW_POST_KEY_SET = 'READER_FULL_VIEW_POST_KEY_SET'; export const READER_LISTS_FOLLOW = 'READER_LISTS_FOLLOW'; export const READER_LISTS_FOLLOW_FAILURE = 'READER_LISTS_FOLLOW_FAILURE'; export const READER_LISTS_FOLLOW_SUCCESS = 'READER_LISTS_FOLLOW_SUCCESS'; diff --git a/client/state/reader/full-view/actions.js b/client/state/reader/full-view/actions.js new file mode 100644 index 00000000000000..fb09da49a05158 --- /dev/null +++ b/client/state/reader/full-view/actions.js @@ -0,0 +1,9 @@ +/** + * Internal dependencies + */ +import { READER_FULL_VIEW_POST_KEY_SET } from 'state/reader/action-types'; + +export const setReaderFullViewPostKey = postKey => ( { + type: READER_FULL_VIEW_POST_KEY_SET, + postKey, +} ); diff --git a/client/state/reader/full-view/reducer.js b/client/state/reader/full-view/reducer.js new file mode 100644 index 00000000000000..3429deccd04064 --- /dev/null +++ b/client/state/reader/full-view/reducer.js @@ -0,0 +1,27 @@ +/** + * Internal dependencies + */ +import { READER_FULL_VIEW_POST_KEY_SET, SERIALIZE } from 'state/reader/action-types'; +import { combineReducers } from 'state/utils'; + +/** + * Tracks the post key of the currently full viewed post + * + * @param {object} state Current state + * @param {object} action Action payload + * @returns {object} Updated state + */ +export function fullViewPostKey( state = null, action ) { + switch ( action.type ) { + case READER_FULL_VIEW_POST_KEY_SET: + return action.postKey; + + case SERIALIZE: + return null; + } + return state; +} + +export default combineReducers( { + fullViewPostKey, +} ); diff --git a/client/state/reader/full-view/selectors/get-reader-full-view-post-key.js b/client/state/reader/full-view/selectors/get-reader-full-view-post-key.js new file mode 100644 index 00000000000000..85b833331e8905 --- /dev/null +++ b/client/state/reader/full-view/selectors/get-reader-full-view-post-key.js @@ -0,0 +1,3 @@ +export const getReaderFullViewPostKey = state => { + return state.reader.fullView.fullViewPostKey; +}; diff --git a/client/state/reader/reducer.js b/client/state/reader/reducer.js index b26294ed63ae77..8fb24c7afa3be2 100644 --- a/client/state/reader/reducer.js +++ b/client/state/reader/reducer.js @@ -4,6 +4,7 @@ import { combineReducers, withStorageKey } from 'state/utils'; import conversations from './conversations/reducer'; +import fullView from './full-view/reducer'; import feeds from './feeds/reducer'; import feedSearches from './feed-searches/reducer'; import follows from './follows/reducer'; @@ -24,6 +25,7 @@ const combinedReducer = combineReducers( { feeds, feedSearches, follows, + fullView, lists, posts, recommendedSites, From 9bbae1b9213c86d7c1289b4dcfe12f18f32782cc Mon Sep 17 00:00:00 2001 From: Hew Date: Fri, 3 Apr 2020 13:10:10 -0400 Subject: [PATCH 15/23] Use new full view post state logic --- client/blocks/reader-full-post/index.jsx | 15 +++++++++------ client/reader/full-post/controller.js | 5 +++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/client/blocks/reader-full-post/index.jsx b/client/blocks/reader-full-post/index.jsx index 2123abc514b987..451eb73d4082f2 100644 --- a/client/blocks/reader-full-post/index.jsx +++ b/client/blocks/reader-full-post/index.jsx @@ -6,7 +6,7 @@ import React from 'react'; import { connect } from 'react-redux'; import { translate } from 'i18n-calypso'; import classNames from 'classnames'; -import { get, startsWith, pickBy } from 'lodash'; +import { get, startsWith } from 'lodash'; import config from 'config'; /** @@ -62,6 +62,8 @@ import { getPostByKey } from 'state/reader/posts/selectors'; import isLikedPost from 'state/selectors/is-liked-post'; import QueryPostLikes from 'components/data/query-post-likes'; import getCurrentStream from 'state/selectors/get-reader-current-stream'; +import { getReaderFullViewPostKey } from 'state/reader/full-view/selectors/get-reader-full-view-post-key'; +import { setReaderFullViewPostKey } from 'state/reader/full-view/actions'; import { getNextItem, getPreviousItem } from 'state/reader/streams/selectors'; /** @@ -124,6 +126,7 @@ export class FullPostView extends React.Component { } componentWillUnmount() { + this.props.setReaderFullViewPostKey( null ); KeyboardShortcuts.off( 'close-full-post', this.handleBack ); KeyboardShortcuts.off( 'like-selection', this.handleLike ); KeyboardShortcuts.off( 'move-selection-down', this.goToNextPost ); @@ -362,6 +365,7 @@ export class FullPostView extends React.Component { @@ -477,8 +481,7 @@ export class FullPostView extends React.Component { export default connect( ( state, ownProps ) => { - const { feedId, blogId, postId } = ownProps; - const postKey = pickBy( { feedId: +feedId, blogId: +blogId, postId: +postId } ); + const postKey = getReaderFullViewPostKey( state ); const post = getPostByKey( state, postKey ) || { _state: 'pending' }; const { site_ID: siteId, is_external: isExternal } = post; @@ -492,8 +495,8 @@ export default connect( if ( ! isExternal && siteId ) { props.site = getSite( state, siteId ); } - if ( feedId ) { - props.feed = getFeed( state, feedId ); + if ( ownProps.feedId ) { + props.feed = getFeed( state, ownProps.feedId ); } if ( ownProps.referral ) { props.referralPost = getPostByKey( state, ownProps.referral ); @@ -507,5 +510,5 @@ export default connect( return props; }, - { markPostSeen, likePost, unlikePost } + { markPostSeen, setReaderFullViewPostKey, likePost, unlikePost } )( FullPostView ); diff --git a/client/reader/full-post/controller.js b/client/reader/full-post/controller.js index a04b4983a0a219..0a201983caf854 100644 --- a/client/reader/full-post/controller.js +++ b/client/reader/full-post/controller.js @@ -8,6 +8,7 @@ import { defer } from 'lodash'; /** * Internal Dependencies */ +import { setReaderFullViewPostKey } from 'state/reader/full-view/actions'; import { trackPageLoad } from 'reader/controller-helper'; import AsyncLoad from 'components/async-load'; @@ -26,6 +27,8 @@ export function blogPost( context, next ) { basePath = '/read/blogs/:blog_id/posts/:post_id', fullPageTitle = analyticsPageTitle + ' > Blog Post > ' + blogId + ' > ' + postId; + context.store.dispatch( setReaderFullViewPostKey( { blogId, postId, feedId: 0 } ) ); + let referral; if ( context.query.ref_blog && context.query.ref_post ) { referral = { blogId: context.query.ref_blog, postId: context.query.ref_post }; @@ -54,6 +57,8 @@ export function feedPost( context, next ) { basePath = '/read/feeds/:feed_id/posts/:feed_item_id', fullPageTitle = analyticsPageTitle + ' > Feed Post > ' + feedId + ' > ' + postId; + context.store.dispatch( setReaderFullViewPostKey( { blogId: 0, postId, feedId } ) ); + trackPageLoad( basePath, fullPageTitle, 'full_post' ); function closer() { From 87b0a86a0cda277ede9569f69201b88c3aee565d Mon Sep 17 00:00:00 2001 From: Hew Date: Fri, 3 Apr 2020 13:11:32 -0400 Subject: [PATCH 16/23] Switch to dynamic load of Phoenix.js --- client/state/lasagna/middleware.js | 11 ++- .../private-post-channel/actions-to-events.js | 71 ++++++++++++------- .../public-post-channel/actions-to-events.js | 66 +++++++++++------ client/state/lasagna/socket.js | 39 +++++----- .../lasagna/user-channel/actions-to-events.js | 16 ++++- 5 files changed, 136 insertions(+), 67 deletions(-) diff --git a/client/state/lasagna/middleware.js b/client/state/lasagna/middleware.js index 7fb23daac6dc01..4e1a424010b92a 100644 --- a/client/state/lasagna/middleware.js +++ b/client/state/lasagna/middleware.js @@ -8,6 +8,8 @@ import privatePostChannelMiddleware from './private-post-channel/actions-to-even import publicPostChannelMiddleware from './public-post-channel/actions-to-events'; import userChannelMiddleware from './user-channel/actions-to-events'; +let socketConnecting = false; + /** * Compose a list of middleware into one middleware * Props @rhc3 @@ -33,15 +35,20 @@ const connectMiddleware = store => next => action => { } // connect if we are going to the reader without a socket - if ( ! socket && action.section.name === 'reader' ) { + if ( ! socket && ! socketConnecting && action.section.name === 'reader' ) { + socketConnecting = true; const user = getCurrentUser( store.getState() ); + wpcom .request( { method: 'POST', path: '/jwt/sign', body: { payload: JSON.stringify( { user } ) }, } ) - .then( ( { jwt } ) => socketConnect( store, jwt, user.ID ) ); + .then( ( { jwt } ) => { + socketConnect( store, jwt, user.ID ); + socketConnecting = false; + } ); } // disconnect if we are leaving the reader with a socket diff --git a/client/state/lasagna/private-post-channel/actions-to-events.js b/client/state/lasagna/private-post-channel/actions-to-events.js index 403067d3945501..e4307732219ed3 100644 --- a/client/state/lasagna/private-post-channel/actions-to-events.js +++ b/client/state/lasagna/private-post-channel/actions-to-events.js @@ -6,47 +6,70 @@ import debugFactory from 'debug'; /** * Internal Dependencies */ -import { keyForPost, keyToString } from 'reader/post-key'; +import { LASAGNA_SOCKET_CONNECTED, ROUTE_SET } from 'state/action-types'; +import { READER_POST_SEEN } from 'state/reader/action-types'; +import { getReaderFullViewPostKey } from 'state/reader/full-view/selectors/get-reader-full-view-post-key'; +import { getPostByKey } from 'state/reader/posts/selectors'; +import { getSite } from 'state/reader/sites/selectors'; import registerEventHandlers from './events-to-actions'; import { socket } from '../socket'; let channel = null; -const channelTopicPrefix = 'private:push:wp_post:'; +const channelTopicPrefix = 'private:push:wp_post:'; const debug = debugFactory( 'lasagna:channel:private:push:wp_post' ); +const joinChannel = ( store, site, post, postKey ) => { + if ( ! socket || ! site.is_private ) { + return; + } + + channel = socket.channel( channelTopicPrefix + post.global_ID, { post_key: postKey } ); + registerEventHandlers( channel, store ); + channel + .join() + .receive( 'ok', () => debug( 'channel join ok' ) ) + .receive( 'error', ( { reason } ) => { + debug( 'channel join error', reason ); + channel.leave(); + channel = null; + } ); +}; + +const leaveChannel = () => { + channel && channel.leave(); + channel = null; +}; + export default store => next => action => { switch ( action.type ) { - case 'READER_POST_SEEN': { - // READER_POST_FULL_VIEW - const { - payload: { site, post }, - } = action; + case LASAGNA_SOCKET_CONNECTED: { + const state = store.getState(); + const postKey = getReaderFullViewPostKey( state ); + const post = getPostByKey( state, postKey ); - if ( ! site.is_private ) { + if ( ! post ) { break; } - const postKey = keyToString( keyForPost( post ) ); - channel = socket.channel( channelTopicPrefix + post.global_ID, { post_key: postKey } ); - registerEventHandlers( channel, store ); - channel - .join() - .receive( 'ok', () => debug( 'channel join ok' ) ) - .receive( 'error', ( { reason } ) => { - debug( 'channel join error', reason ); - channel.leave(); - channel = null; - } ); + const site = getSite( state, post.site_ID ); + if ( ! site ) { + break; + } + + joinChannel( store, site, post, postKey ); break; } - case 'ROUTE_SET': - if ( channel ) { - channel.leave(); - channel = null; - } + case READER_POST_SEEN: { + const postKey = getReaderFullViewPostKey( store.getState() ); + joinChannel( store, action.payload.site, action.payload.post, postKey ); + break; + } + + case ROUTE_SET: + leaveChannel(); break; } diff --git a/client/state/lasagna/public-post-channel/actions-to-events.js b/client/state/lasagna/public-post-channel/actions-to-events.js index 6e5d5d6ad14bbe..770e7f518e2bde 100644 --- a/client/state/lasagna/public-post-channel/actions-to-events.js +++ b/client/state/lasagna/public-post-channel/actions-to-events.js @@ -6,6 +6,11 @@ import debugFactory from 'debug'; /** * Internal Dependencies */ +import { LASAGNA_SOCKET_CONNECTED, ROUTE_SET } from 'state/action-types'; +import { READER_POST_SEEN } from 'state/reader/action-types'; +import { getPostByKey } from 'state/reader/posts/selectors'; +import { getReaderFullViewPostKey } from 'state/reader/full-view/selectors/get-reader-full-view-post-key'; +import { getSite } from 'state/reader/sites/selectors'; import registerEventHandlers from './events-to-actions'; import { socket } from '../socket'; @@ -14,36 +19,55 @@ const channelTopicPrefix = 'public:push:wp_post:'; const debug = debugFactory( 'lasagna:channel:public:push:wp_post' ); +const joinChannel = ( store, site, post ) => { + if ( ! socket || site.is_private ) { + return; + } + + channel = socket.channel( channelTopicPrefix + post.global_ID ); + registerEventHandlers( channel, store ); + channel + .join() + .receive( 'ok', () => debug( 'channel join ok' ) ) + .receive( 'error', ( { reason } ) => { + debug( 'channel join error', reason ); + channel.leave(); + channel = null; + } ); +}; + +const leaveChannel = () => { + channel && channel.leave(); + channel = null; +}; + export default store => next => action => { switch ( action.type ) { - case 'READER_POST_SEEN': { - // READER_POST_FULL_VIEW - const { - payload: { site, post }, - } = action; + case LASAGNA_SOCKET_CONNECTED: { + const state = store.getState(); + const postKey = getReaderFullViewPostKey( state ); + const post = getPostByKey( state, postKey ); - if ( site.is_private ) { + if ( ! post ) { break; } - channel = socket.channel( channelTopicPrefix + post.global_ID ); - registerEventHandlers( channel, store ); - channel - .join() - .receive( 'ok', () => debug( 'channel join ok' ) ) - .receive( 'error', ( { reason } ) => { - debug( 'channel join error', reason ); - channel.leave(); - channel = null; - } ); + const site = getSite( state, post.site_ID ); + + if ( ! site ) { + break; + } + + joinChannel( store, site, post ); break; } - case 'ROUTE_SET': // READER_POST_FULL_VIEW_LEAVE - if ( channel ) { - channel.leave(); - channel = null; - } + case READER_POST_SEEN: + joinChannel( store, action.payload.site, action.payload.post ); + break; + + case ROUTE_SET: + leaveChannel(); break; } diff --git a/client/state/lasagna/socket.js b/client/state/lasagna/socket.js index 05f3825fb8b5bf..dcd8de7d47f8b2 100644 --- a/client/state/lasagna/socket.js +++ b/client/state/lasagna/socket.js @@ -1,7 +1,6 @@ /** * External Dependencies */ -import { Socket } from 'phoenix'; import createDebug from 'debug'; /** @@ -18,33 +17,35 @@ export let socket = null; const debug = createDebug( 'lasagna:socket' ); const url = config( 'lasagna_url' ); -export function socketConnect( store, jwt, userId ) { +export const socketConnect = ( store, jwt, userId ) => { if ( socket !== null ) { return; } - socket = new Socket( url, { params: { jwt, user_id: userId } } ); + import( 'phoenix' ).then( ( { Socket } ) => { + socket = new Socket( url, { params: { jwt, user_id: userId } } ); - socket.onOpen( () => { - debug( 'socket opened' ); - store.dispatch( socketConnected() ); - } ); + socket.onOpen( () => { + debug( 'socket opened' ); + store.dispatch( socketConnected() ); + } ); - socket.onClose( () => { - debug( 'socket closed' ); - // @TODO: verify this Phoenix.js state, dispatch attempting reconnect here? - } ); + socket.onClose( () => { + debug( 'socket closed' ); + // @TODO: verify this Phoenix.js state, dispatch attempting reconnect here? + } ); - socket.onError( () => { - debug( 'socket error' ); - // @TODO: verify this Phoenix.js state, dispatch attempting reconnect here? - } ); + socket.onError( () => { + debug( 'socket error' ); + // @TODO: verify this Phoenix.js state, dispatch attempting reconnect here? + } ); - socket.connect(); -} + socket.connect(); + } ); +}; -export function socketDisconnect( store ) { +export const socketDisconnect = store => { socket && socket.disconnect(); socket = null; store.dispatch( socketDisconnected() ); -} +}; diff --git a/client/state/lasagna/user-channel/actions-to-events.js b/client/state/lasagna/user-channel/actions-to-events.js index bed96e29462eea..e78db7f1fa8bdd 100644 --- a/client/state/lasagna/user-channel/actions-to-events.js +++ b/client/state/lasagna/user-channel/actions-to-events.js @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import debugFactory from 'debug'; + /** * Internal Dependencies */ @@ -7,12 +12,21 @@ import { socket } from '../socket'; let channel = null; +const debug = debugFactory( 'lasagna:channel:user:wpcom' ); + export default store => next => action => { if ( ! channel && socket && action.type === 'LASAGNA_SOCKET_CONNECTED' ) { const userId = getCurrentUserId( store.getState() ); channel = socket.channel( `user:wpcom:${ userId }` ); // registerEventHandlers( channel, store ); - channel.join(); + channel + .join() + .receive( 'ok', () => debug( 'channel join ok' ) ) + .receive( 'error', ( { reason } ) => { + debug( 'channel join error', reason ); + channel.leave(); + channel = null; + } ); } if ( ! channel ) { From 2c35393747b6302516f791712365d2d3d0f2a2c9 Mon Sep 17 00:00:00 2001 From: Hew Date: Fri, 3 Apr 2020 14:30:59 -0400 Subject: [PATCH 17/23] Additional cleanup --- client/state/comments/actions.js | 14 ----- .../private-post-channel/actions-to-events.js | 1 + .../public-post-channel/actions-to-events.js | 3 +- .../lasagna/user-channel/actions-to-events.js | 54 +++++++++++++------ 4 files changed, 40 insertions(+), 32 deletions(-) diff --git a/client/state/comments/actions.js b/client/state/comments/actions.js index 026a1ff0936758..6ef35729ef496b 100644 --- a/client/state/comments/actions.js +++ b/client/state/comments/actions.js @@ -6,7 +6,6 @@ import { COMMENT_COUNTS_REQUEST, COMMENT_REQUEST, COMMENTS_CHANGE_STATUS, - COMMENTS_COUNT_INCREMENT, COMMENTS_DELETE, COMMENTS_EDIT, COMMENTS_LIKE, @@ -157,19 +156,6 @@ export const requestCommentCounts = ( siteId, postId ) => ( { postId, } ); -/** - * Creates an action that increments the comment count for a given post. - * - * @param {number} siteId Site identifier - * @param {number} [postId] Post identifier - * @returns {object} Action that increments comment count for a post. - */ -export const incrementCommentCount = ( siteId, postId ) => ( { - type: COMMENTS_COUNT_INCREMENT, - siteId, - postId, -} ); - /** * Creates an action that permanently deletes a comment * or removes a comment placeholder from the state diff --git a/client/state/lasagna/private-post-channel/actions-to-events.js b/client/state/lasagna/private-post-channel/actions-to-events.js index e4307732219ed3..782227041c81f2 100644 --- a/client/state/lasagna/private-post-channel/actions-to-events.js +++ b/client/state/lasagna/private-post-channel/actions-to-events.js @@ -26,6 +26,7 @@ const joinChannel = ( store, site, post, postKey ) => { channel = socket.channel( channelTopicPrefix + post.global_ID, { post_key: postKey } ); registerEventHandlers( channel, store ); + channel .join() .receive( 'ok', () => debug( 'channel join ok' ) ) diff --git a/client/state/lasagna/public-post-channel/actions-to-events.js b/client/state/lasagna/public-post-channel/actions-to-events.js index 770e7f518e2bde..a2e2766c1b6ffb 100644 --- a/client/state/lasagna/public-post-channel/actions-to-events.js +++ b/client/state/lasagna/public-post-channel/actions-to-events.js @@ -15,8 +15,8 @@ import registerEventHandlers from './events-to-actions'; import { socket } from '../socket'; let channel = null; -const channelTopicPrefix = 'public:push:wp_post:'; +const channelTopicPrefix = 'public:push:wp_post:'; const debug = debugFactory( 'lasagna:channel:public:push:wp_post' ); const joinChannel = ( store, site, post ) => { @@ -26,6 +26,7 @@ const joinChannel = ( store, site, post ) => { channel = socket.channel( channelTopicPrefix + post.global_ID ); registerEventHandlers( channel, store ); + channel .join() .receive( 'ok', () => debug( 'channel join ok' ) ) diff --git a/client/state/lasagna/user-channel/actions-to-events.js b/client/state/lasagna/user-channel/actions-to-events.js index e78db7f1fa8bdd..0761c9579a830f 100644 --- a/client/state/lasagna/user-channel/actions-to-events.js +++ b/client/state/lasagna/user-channel/actions-to-events.js @@ -6,34 +6,54 @@ import debugFactory from 'debug'; /** * Internal Dependencies */ +import { LASAGNA_SOCKET_CONNECTED, LASAGNA_SOCKET_DISCONNECTED } from 'state/action-types'; import { getCurrentUserId } from 'state/current-user/selectors'; -// import registerEventHandlers from './events-to-actions'; import { socket } from '../socket'; let channel = null; const debug = debugFactory( 'lasagna:channel:user:wpcom' ); -export default store => next => action => { - if ( ! channel && socket && action.type === 'LASAGNA_SOCKET_CONNECTED' ) { - const userId = getCurrentUserId( store.getState() ); - channel = socket.channel( `user:wpcom:${ userId }` ); - // registerEventHandlers( channel, store ); - channel - .join() - .receive( 'ok', () => debug( 'channel join ok' ) ) - .receive( 'error', ( { reason } ) => { - debug( 'channel join error', reason ); - channel.leave(); - channel = null; - } ); +const joinChannel = store => { + if ( ! socket || channel ) { + return; } - if ( ! channel ) { - return next( action ); + const userId = getCurrentUserId( store.getState() ); + + if ( ! userId ) { + return; } - // TBD: switch, do stuff + channel = socket.channel( `user:wpcom:${ userId }` ); + // registerEventHandlers here + + channel + .join() + .receive( 'ok', () => debug( 'channel join ok' ) ) + .receive( 'error', ( { reason } ) => { + debug( 'channel join error', reason ); + channel.leave(); + channel = null; + } ); +}; + +const leaveChannel = () => { + channel && channel.leave(); + channel = null; +}; + +export default store => next => action => { + switch ( action.type ) { + case LASAGNA_SOCKET_CONNECTED: { + joinChannel( store ); + break; + } + + case LASAGNA_SOCKET_DISCONNECTED: + leaveChannel(); + break; + } return next( action ); }; From dde8e3bec131ce04f86ccb553e42f6be78d471f7 Mon Sep 17 00:00:00 2001 From: Hew Date: Mon, 6 Apr 2020 09:03:02 -0400 Subject: [PATCH 18/23] Remove stray addition To come in follow up PR --- client/blocks/reader-full-post/index.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/client/blocks/reader-full-post/index.jsx b/client/blocks/reader-full-post/index.jsx index 451eb73d4082f2..a52f13cf963f80 100644 --- a/client/blocks/reader-full-post/index.jsx +++ b/client/blocks/reader-full-post/index.jsx @@ -365,7 +365,6 @@ export class FullPostView extends React.Component { From 76a83faad2e9b4d4473b589da1b945b92a193f80 Mon Sep 17 00:00:00 2001 From: Hew Date: Mon, 6 Apr 2020 09:03:30 -0400 Subject: [PATCH 19/23] Add config.isEnabled check back for lasagna --- client/state/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/state/index.js b/client/state/index.js index b5afe005fb8d2b..28cdfca67dddd2 100644 --- a/client/state/index.js +++ b/client/state/index.js @@ -7,6 +7,7 @@ import { createStore, applyMiddleware, compose } from 'redux'; /** * Internal dependencies */ +import config from 'config'; import initialReducer from './reducer'; /** @@ -67,7 +68,7 @@ export function createReduxStore( initialState, reducer = initialReducer ) { noticesMiddleware, isBrowser && require( './happychat/middleware.js' ).default, isBrowser && require( './happychat/middleware-calypso.js' ).default, - isBrowser && require( './lasagna/middleware.js' ).default, + isBrowser && config.isEnabled( 'lasagna' ) && require( './lasagna/middleware.js' ).default, isBrowser && require( './analytics/middleware.js' ).analyticsMiddleware, isBrowser && require( './lib/middleware.js' ).default, isAudioSupported && require( './audio/middleware.js' ).default, From 7277061853bb594477ae267a66c551e64b9acd19 Mon Sep 17 00:00:00 2001 From: Hew Date: Tue, 7 Apr 2020 12:25:05 -0400 Subject: [PATCH 20/23] Add webpackchunkname to dynamic import --- client/state/lasagna/socket.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/state/lasagna/socket.js b/client/state/lasagna/socket.js index dcd8de7d47f8b2..2984d9b082f4ef 100644 --- a/client/state/lasagna/socket.js +++ b/client/state/lasagna/socket.js @@ -22,7 +22,7 @@ export const socketConnect = ( store, jwt, userId ) => { return; } - import( 'phoenix' ).then( ( { Socket } ) => { + import( /* webpackChunkName: "phoenix" */ 'phoenix' ).then( ( { Socket } ) => { socket = new Socket( url, { params: { jwt, user_id: userId } } ); socket.onOpen( () => { From be75f4fc6a97b1da69beca8fb0101e1159ecd3e6 Mon Sep 17 00:00:00 2001 From: Hew Date: Tue, 7 Apr 2020 15:03:04 -0400 Subject: [PATCH 21/23] To be handled in a subsequent PR --- .../state/lasagna/private-post-channel/events-to-actions.js | 4 +--- client/state/lasagna/public-post-channel/events-to-actions.js | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/client/state/lasagna/private-post-channel/events-to-actions.js b/client/state/lasagna/private-post-channel/events-to-actions.js index 49e471f8a1516a..2179e72e0a731c 100644 --- a/client/state/lasagna/private-post-channel/events-to-actions.js +++ b/client/state/lasagna/private-post-channel/events-to-actions.js @@ -6,7 +6,7 @@ import debugFactory from 'debug'; /** * Internal dependencies */ -import { incrementCommentCount, receiveComments } from 'state/comments/actions'; +import { receiveComments } from 'state/comments/actions'; const debug = debugFactory( 'lasagna:channel:private:push:wp_post' ); @@ -26,7 +26,5 @@ export default function( channel, store ) { commentById: true, } ) ); - - store.dispatch( incrementCommentCount( comment.post.site_ID, comment.post.ID ) ); } ); } diff --git a/client/state/lasagna/public-post-channel/events-to-actions.js b/client/state/lasagna/public-post-channel/events-to-actions.js index 7daff042e07312..887af882d9abe8 100644 --- a/client/state/lasagna/public-post-channel/events-to-actions.js +++ b/client/state/lasagna/public-post-channel/events-to-actions.js @@ -6,7 +6,7 @@ import debugFactory from 'debug'; /** * Internal dependencies */ -import { incrementCommentCount, receiveComments } from 'state/comments/actions'; +import { receiveComments } from 'state/comments/actions'; const debug = debugFactory( 'lasagna:channel:public:push:wp_post' ); @@ -26,7 +26,5 @@ export default function( channel, store ) { commentById: true, } ) ); - - store.dispatch( incrementCommentCount( comment.post.site_ID, comment.post.ID ) ); } ); } From 5a0be11a585875566d31b8fdeb10bffd77a9ccad Mon Sep 17 00:00:00 2001 From: undemian Date: Wed, 8 Apr 2020 11:57:17 +0300 Subject: [PATCH 22/23] Test without phoenix --- client/state/index.js | 2 +- client/state/lasagna/socket.js | 40 +++++++++++++++++----------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/client/state/index.js b/client/state/index.js index 28cdfca67dddd2..4b2daa0259dfb0 100644 --- a/client/state/index.js +++ b/client/state/index.js @@ -68,7 +68,7 @@ export function createReduxStore( initialState, reducer = initialReducer ) { noticesMiddleware, isBrowser && require( './happychat/middleware.js' ).default, isBrowser && require( './happychat/middleware-calypso.js' ).default, - isBrowser && config.isEnabled( 'lasagna' ) && require( './lasagna/middleware.js' ).default, + // isBrowser && config.isEnabled( 'lasagna' ) && require( './lasagna/middleware.js' ).default, isBrowser && require( './analytics/middleware.js' ).analyticsMiddleware, isBrowser && require( './lib/middleware.js' ).default, isAudioSupported && require( './audio/middleware.js' ).default, diff --git a/client/state/lasagna/socket.js b/client/state/lasagna/socket.js index 2984d9b082f4ef..cfbaa4eec69361 100644 --- a/client/state/lasagna/socket.js +++ b/client/state/lasagna/socket.js @@ -22,26 +22,26 @@ export const socketConnect = ( store, jwt, userId ) => { return; } - import( /* webpackChunkName: "phoenix" */ 'phoenix' ).then( ( { Socket } ) => { - socket = new Socket( url, { params: { jwt, user_id: userId } } ); - - socket.onOpen( () => { - debug( 'socket opened' ); - store.dispatch( socketConnected() ); - } ); - - socket.onClose( () => { - debug( 'socket closed' ); - // @TODO: verify this Phoenix.js state, dispatch attempting reconnect here? - } ); - - socket.onError( () => { - debug( 'socket error' ); - // @TODO: verify this Phoenix.js state, dispatch attempting reconnect here? - } ); - - socket.connect(); - } ); + // import( /* webpackChunkName: "phoenix" */ 'phoenix' ).then( ( { Socket } ) => { + // socket = new Socket( url, { params: { jwt, user_id: userId } } ); + // + // socket.onOpen( () => { + // debug( 'socket opened' ); + // store.dispatch( socketConnected() ); + // } ); + // + // socket.onClose( () => { + // debug( 'socket closed' ); + // // @TODO: verify this Phoenix.js state, dispatch attempting reconnect here? + // } ); + // + // socket.onError( () => { + // debug( 'socket error' ); + // // @TODO: verify this Phoenix.js state, dispatch attempting reconnect here? + // } ); + // + // socket.connect(); + // } ); }; export const socketDisconnect = store => { From dcd23e5dc8a205a93a81b089ba5839d348c02688 Mon Sep 17 00:00:00 2001 From: undemian Date: Wed, 8 Apr 2020 12:01:31 +0300 Subject: [PATCH 23/23] Load middleware --- client/state/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/state/index.js b/client/state/index.js index 4b2daa0259dfb0..28cdfca67dddd2 100644 --- a/client/state/index.js +++ b/client/state/index.js @@ -68,7 +68,7 @@ export function createReduxStore( initialState, reducer = initialReducer ) { noticesMiddleware, isBrowser && require( './happychat/middleware.js' ).default, isBrowser && require( './happychat/middleware-calypso.js' ).default, - // isBrowser && config.isEnabled( 'lasagna' ) && require( './lasagna/middleware.js' ).default, + isBrowser && config.isEnabled( 'lasagna' ) && require( './lasagna/middleware.js' ).default, isBrowser && require( './analytics/middleware.js' ).analyticsMiddleware, isBrowser && require( './lib/middleware.js' ).default, isAudioSupported && require( './audio/middleware.js' ).default,