Просмотр исходного кода

Merge pull request #28019 from overleaf/em-dsmp-comment-event

DS mobile notification for comment added/replied to

GitOrigin-RevId: 602983c679ed415bf8dd8fbf83b328e5ee832e2b
Eric Mc Sween 11 месяцев назад
Родитель
Сommit
201e5c49aa

+ 24 - 0
package-lock.json

@@ -22014,6 +22014,29 @@
       "resolved": "https://registry.npmjs.org/events-listener/-/events-listener-1.1.0.tgz",
       "integrity": "sha512-Kd3EgYfODHueq6GzVfs/VUolh2EgJsS8hkO3KpnDrxVjU3eq63eXM2ujXkhPP+OkeUOhL8CxdfZbQXzryb5C4g=="
     },
+    "node_modules/eventsource-client": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/eventsource-client/-/eventsource-client-1.1.4.tgz",
+      "integrity": "sha512-CKnqZTwXCnHN2EqrEB9eLSjMMRqHum09VOsikkgSPoa2Jr2XgQnX7P1Fxhnnj/UHxi3GQ2xVsXDKIktEes07bg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "eventsource-parser": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=18.0.0"
+      }
+    },
+    "node_modules/eventsource-client/node_modules/eventsource-parser": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.3.tgz",
+      "integrity": "sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=20.0.0"
+      }
+    },
     "node_modules/eventsource-parser": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-1.1.2.tgz",
@@ -44791,6 +44814,7 @@
         "eslint-plugin-testing-library": "^7.1.1",
         "eslint-plugin-unicorn": "^56.0.0",
         "events": "^3.3.0",
+        "eventsource-client": "^1.1.4",
         "fake-indexeddb": "^6.0.0",
         "fetch-mock": "^12.5.2",
         "formik": "^2.2.9",

+ 1 - 0
services/web/package.json

@@ -300,6 +300,7 @@
     "eslint-plugin-testing-library": "^7.1.1",
     "eslint-plugin-unicorn": "^56.0.0",
     "events": "^3.3.0",
+    "eventsource-client": "^1.1.4",
     "fake-indexeddb": "^6.0.0",
     "fetch-mock": "^12.5.2",
     "formik": "^2.2.9",

+ 2 - 1
services/web/test/acceptance/src/DeletionTests.mjs

@@ -395,7 +395,8 @@ describe('Deleting a project', function () {
             expect(error).not.to.exist
             expect(res.statusCode).to.equal(200)
 
-            expect(MockChatApi.projects[this.projectId.toString()]).not.to.exist
+            expect(MockChatApi.projects.has(this.projectId.toString())).to.be
+              .false
             done()
           }
         )

+ 48 - 21
services/web/test/acceptance/src/mocks/MockChatApi.mjs

@@ -2,42 +2,69 @@ import AbstractMockApi from './AbstractMockApi.mjs'
 
 class MockChatApi extends AbstractMockApi {
   reset() {
-    this.projects = {}
+    this.projects = new Map()
   }
 
-  getGlobalMessages(req, res) {
-    res.json(this.projects[req.params.project_id] || [])
+  getThread(projectId, threadId) {
+    let threads = this.projects.get(projectId)
+    if (threads == null) {
+      threads = new Map()
+      this.projects.set(projectId, threads)
+    }
+    let thread = threads.get(threadId)
+    if (thread == null) {
+      thread = []
+      threads.set(threadId, thread)
+    }
+    return thread
   }
 
-  sendGlobalMessage(req, res) {
-    const projectId = req.params.project_id
+  sendMessage(projectId, threadId, props) {
     const message = {
       id: Math.random().toString(),
-      content: req.body.content,
+      content: props.content,
       timestamp: Date.now(),
-      user_id: req.body.user_id,
+      user_id: props.user_id,
     }
-    this.projects[projectId] = this.projects[projectId] || []
-    this.projects[projectId].push(message)
-    res.json(Object.assign({ room_id: projectId }, message))
+    const thread = this.getThread(projectId, threadId)
+    thread.push(message)
+    return { room_id: projectId, ...message }
   }
 
-  destroyProject(req, res) {
-    const projectId = req.params.project_id
-    delete this.projects[projectId]
-    res.sendStatus(204)
+  destroyProject(projectId) {
+    this.projects.delete(projectId)
   }
 
   applyRoutes() {
-    this.app.get('/project/:project_id/messages', (req, res) =>
-      this.getGlobalMessages(req, res)
-    )
-    this.app.post('/project/:project_id/messages', (req, res) =>
-      this.sendGlobalMessage(req, res)
+    this.app.get('/project/:project_id/messages', (req, res) => {
+      res.json(this.getThread(req.params.project_id, 'global'))
+    })
+    this.app.post('/project/:project_id/messages', (req, res) => {
+      res.json(this.sendMessage(req.params.project_id, 'global', req.body))
+    })
+    this.app.get(
+      '/project/:project_id/thread/:thread_id/messages',
+      (req, res) => {
+        res.json(this.getThread(req.params.project_id, req.params.thread_id))
+      }
     )
-    this.app.delete('/project/:project_id', (req, res) =>
-      this.destroyProject(req, res)
+    this.app.post(
+      '/project/:project_id/thread/:thread_id/messages',
+      (req, res) => {
+        res.json(
+          this.sendMessage(
+            req.params.project_id,
+            req.params.thread_id,
+            req.body
+          )
+        )
+      }
     )
+    this.app.delete('/project/:project_id', (req, res) => {
+      const projectId = req.params.project_id
+      this.destroyProject(projectId)
+      res.sendStatus(204)
+    })
   }
 }