Skip to content

Commit

Permalink
resolves #910 synchronise les états avec du server-sent (#942)
Browse files Browse the repository at this point in the history
  • Loading branch information
ggrossetie authored Sep 26, 2023
1 parent 312ea7d commit 2b4bcf5
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 8 deletions.
48 changes: 46 additions & 2 deletions front/src/components/Articles.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Loading, Modal as GeistModal, useModal, Button as GeistButton } from '@geist-ui/core'
import React, { useCallback, useMemo, useState } from 'react'
import React, { useCallback, useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { shallowEqual, useSelector } from 'react-redux'
import { CurrentUserContext } from '../contexts/CurrentUser'
Expand All @@ -21,6 +21,7 @@ import TagsList from './tag/TagsList.jsx'

export default function Articles () {
const { t } = useTranslation()
const backendEndpoint = useSelector(state => state.applicationConfig.backendEndpoint)
const currentUser = useSelector(state => state.activeUser, shallowEqual)
const selectedTagIds = useSelector((state) => state.activeUser.selectedTagIds || [])
const {
Expand Down Expand Up @@ -90,12 +91,55 @@ export default function Articles () {
}
}, [articles])

const handleStateUpdated = useCallback((event) => {
const parsedData = JSON.parse(event.data)
if (parsedData.articleStateUpdated) {
const articleStateUpdated = parsedData.articleStateUpdated
const updatedArticles = articles.map((article) => {
if (article._id === articleStateUpdated._id) {
return {
...article,
soloSession: articleStateUpdated.soloSession,
collaborativeSession: articleStateUpdated.collaborativeSession,
}
}
return article
})
if (activeWorkspaceId) {
mutate({
workspace: {
...data.workspace,
articles: updatedArticles
}
}, { revalidate: false })
} else {
mutate({
articles: updatedArticles
}, { revalidate: false })
}
}
}, [articles])

useEffect(() => {
let events
if (!isLoading) {
events = new EventSource(`${backendEndpoint}/events`)
events.onmessage = (event) => {
handleStateUpdated(event)
}
}
return () => {
if (events) {
events.close()
}
}
}, [isLoading, handleStateUpdated])

const keepArticles = useMemo(() => articles
.filter((article) => {
if (selectedTagIds.length === 0) {
return true
}

// if we find at least one matching tag in the selected list, we keep the article
return selectedTagIds.some((tagId) => article.tags.find(({ _id }) => _id === tagId))
})
Expand Down
2 changes: 1 addition & 1 deletion front/src/components/solo/SoloSessionAction.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export default function SoloSessionAction ({ collaborativeSession, soloSession,
}
}
}
}, [collaborativeSession])
}, [soloSession])

if (collaborativeSession) {
return <></>
Expand Down
1 change: 1 addition & 0 deletions front/src/components/tag/TagsList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export default function TagsList () {
name={`filterTag-${t._id}`}
onClick={handleTagSelected}
disableAction={false}
selected={selectedTagIds.includes(t._id)}
/>
</li>
))}
Expand Down
2 changes: 1 addition & 1 deletion front/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export default defineConfig(async ({ mode }) => {
prependPath: false
},
// as in infrastructure/files/stylo.huma-num.fr.conf
'^/(login/openid|login/local|login/zotero|logout|authorization-code)': {
'^/(login/openid|login/local|login/zotero|logout|authorization-code|events)': {
target: 'http://127.0.0.1:3030'
}
}
Expand Down
11 changes: 7 additions & 4 deletions graphql/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const MongoStore = require('connect-mongo')(session)
const passport = require('passport')
const OidcStrategy = require('passport-openidconnect').Strategy
const LocalStrategy = require('passport-local').Strategy
const OAuthStrategy = require('passport-oauth').OAuthStrategy;
const OAuthStrategy = require('passport-oauth').OAuthStrategy
const { logger } = require('./logger')
const pino = require('pino-http')({
logger
Expand All @@ -30,6 +30,7 @@ const { createTagLoader, createUserLoader, createArticleLoader, createVersionLoa

const { setupWSConnection } = require('y-websocket/bin/utils')
const WebSocket = require('ws')
const { handleEvents } = require('./events')
const wss = new WebSocket.Server({ noServer: true })

const app = express()
Expand Down Expand Up @@ -184,6 +185,8 @@ app.get('/version', (req, res) => res.json({
version: pkg.version
}))

app.get('/events', handleEvents)

app.get('/login/openid', async (req, res, next) => {
if (req.user) {
const { email } = req.user
Expand Down Expand Up @@ -251,7 +254,7 @@ app.use('/authorization-code/callback',
})

app.get('/logout', (req, res, next) => {
req.logout(function(err) {
req.logout(function (err) {
if (err) {
return next(err)
}
Expand All @@ -277,7 +280,7 @@ app.post('/login/local',
}
)

function createLoaders() {
function createLoaders () {
return {
tags: createTagLoader(),
users: createUserLoader(),
Expand Down Expand Up @@ -319,7 +322,7 @@ mongoose
logger.info('Listening on http://localhost:%s', listenPort)
const server = app.listen(listenPort)
server.on('upgrade', (request, socket, head) => {
wss.handleUpgrade(request, socket, head, function handleAuth(ws) {
wss.handleUpgrade(request, socket, head, function handleAuth (ws) {
// const jwtToken = new URL('http://localhost' + request.url).searchParams.get("token")
// TODO: check token and permissions
wss.emit('connection', ws, request)
Expand Down
38 changes: 38 additions & 0 deletions graphql/events.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
let clients = []

const handleEvents = (request, response) => {
const headers = {
'Content-Type': 'text/event-stream',
'Connection': 'keep-alive',
'Cache-Control': 'no-cache'
}
response.writeHead(200, headers)
const clientId = Date.now()
const newClient = {
id: clientId,
response
}
clients.push(newClient)
request.on('close', () => {
console.log(`${clientId} Connection closed`)
clients = clients.filter(client => client.id !== clientId)
})
}

function notifyArticleStatusChange (article) {
clients.forEach(client => {
client.response.write(`data: ${JSON.stringify({
articleStateUpdated: {
collaborativeSession: article.collaborativeSession,
soloSession: article.soloSession,
title: article.title,
_id: article._id
}
})}\n\n`)
})
}

module.exports = {
handleEvents,
notifyArticleStatusChange
}
3 changes: 3 additions & 0 deletions graphql/resolvers/articleResolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const { ApiError } = require('../helpers/errors')
const { reformat } = require('../helpers/metadata.js')
const { computeMajorVersion, computeMinorVersion } = require('../helpers/versions')
const { previewEntries } = require('../helpers/bibliography')
const { notifyArticleStatusChange } = require('../events')


async function getUser (userId) {
Expand Down Expand Up @@ -405,6 +406,7 @@ module.exports = {
}
article.soloSession = soloSession
await article.save()
notifyArticleStatusChange(article)
return soloSession
},

Expand All @@ -415,6 +417,7 @@ module.exports = {
}
article.soloSession = null
await article.save()
notifyArticleStatusChange(article)
}
// no solo session to stop (ignore)
return article
Expand Down

0 comments on commit 2b4bcf5

Please sign in to comment.