Browse Source

client, logs-content: Added change subscription setup and hooked it up to the logs-content page.

1.2
Gisle Aune 6 years ago
parent
commit
013c50d4f4
  1. 4
      marko/page/logs-content/components/edit-log-modal/component.js
  2. 90
      marko/page/logs-content/components/page/component.js
  3. 1254
      package-lock.json
  4. 4
      package.json
  5. 2
      routes/graphql.js
  6. 45
      rpdata/api/Log.js
  7. 87
      rpdata/client.js
  8. 1
      server.js

4
marko/page/logs-content/components/edit-log-modal/component.js

@ -19,16 +19,12 @@ module.exports = class {
}
onInput(input) {
if (input.log && !this.first) {
this.state.values = {
title: input.log.title,
event: input.log.eventName,
description: input.log.description,
open: input.log.open,
}
this.first = true
}
}
change(key, ev) {

90
marko/page/logs-content/components/page/component.js

@ -1,6 +1,7 @@
const moment = require("moment")
const {postApi} = require("../../../../../rpdata/api/Post")
const {logsApi} = require("../../../../../rpdata/api/Log")
module.exports = class {
onCreate(input) {
@ -11,6 +12,22 @@ module.exports = class {
}
}
onMount() {
this.sub = logsApi.subscribeChanges(this.state.log.id)
this.sub.on("data", data => {
this.handleChange(data.changes)
})
this.sub.connect()
}
onUnmount() {
if (this.sub != null) {
this.sub.disconnect()
}
}
open(modal) {
this.state.modal = modal
}
@ -30,8 +47,6 @@ module.exports = class {
for (const patch of patches) {
const index = posts.findIndex(p => p.id === patch.id)
if (index == -1) {
console.warn("Post not found for patch:", patch)
// TODO: Force full refresh
continue
}
@ -42,8 +57,7 @@ module.exports = class {
posts.sort((a, b) => a.position - b.position)
for (let i = 1; i < posts.length; ++i) {
if (posts[i].position !== posts[i - 1].position + 1) {
console.warn("Discontinuity detected!")
// TODO: Force full refresh
console.warn("Discontinuity detected:", i, posts)
}
}
@ -65,15 +79,7 @@ module.exports = class {
removePost(post) {
postApi.remove({id: post.id}).then(() => {
this.state.log.posts = this.state.log.posts.filter(p => p.id !== post.id)
for (const p of this.state.log.posts) {
if (p.position > post.position) {
p.position--
}
}
this.state.log = Object.assign({}, this.state.log)
this.postRemoved(post)
}).catch(errs => {
console.warn("Failed to delete:", errs)
this.state.error = "Failed to delete: " + (errs.message || errs[0].message)
@ -88,11 +94,69 @@ module.exports = class {
this.state.log = Object.assign(Object.assign({}, this.state.log), patch)
}
postRemoved(post) {
if (this.state.log.posts.find(p => p.id === post.id) == null) {
return
}
const posts = this.state.log.posts.filter(p => p.id !== post.id).map(p => {
if (p.position > post.position) {
return Object.assign({}, p, {position: p.position - 1})
}
return p
})
this.state.log = Object.assign({}, this.state.log, {posts: posts})
}
postAdded(post) {
this.state.log.posts = this.state.log.posts.concat([post])
this.state.log = Object.assign({}, this.state.log)
}
handleChange(change) {
switch (`${change.model}.${change.op}`) {
case "Post.add": {
for (const post of change.objects.filter(o => o.type === "Post")) {
this.postAdded(post)
}
break
}
case "Post.move":
case "Post.edit": {
this.patch(change.objects.filter(o => o.type === "Post"))
break
}
case "Post.remove": {
for (const post of change.objects.filter(o => o.type === "Post")) {
this.postRemoved(post)
}
}
case "Log.edit": {
const patch = change.objects.find(o => o.type === "Log")
if (patch == null) {
break
}
this.logEdited(patch)
break
}
case "Log.remove": {
this.logRemoved()
break
}
}
}
logRemoved() {
this.state.removed = true
}

1254
package-lock.json
File diff suppressed because it is too large
View File

4
package.json

@ -25,6 +25,7 @@
"express-http-proxy": "^1.2.0",
"express-session": "^1.15.6",
"graphql-query-compress": "^1.1.0",
"http-proxy-middleware": "^0.19.1",
"isomorphic-fetch": "^2.2.1",
"jsonwebtoken": "^8.3.0",
"lasso": "^3.2.6",
@ -41,5 +42,8 @@
"passport": "^0.4.0",
"passport-auth0": "^1.0.0",
"pluralize": "^7.0.0"
},
"devDependencies": {
"@types/http-proxy-middleware": "^0.19.0"
}
}

2
routes/graphql.js

@ -1,3 +1,4 @@
const proxy = require("http-proxy-middleware")
const express = require("express")
const jwt = require("jsonwebtoken")
@ -44,6 +45,7 @@ router.post("/", (req, res) => {
})
})
router.use("/", proxy(config.graphqlEndpoint, {ws: true}))
/**
* @param {string} user

45
rpdata/api/Log.js

@ -1,4 +1,4 @@
const {query} = require("../client")
const {query, Subscription} = require("../client")
const {Character} = require("./Character")
const {Channel} = require("./Channel")
@ -193,7 +193,6 @@ class LogAPI {
})
}
/**
* Call `removeLog(input)` mutation, returns the id
*
@ -211,6 +210,48 @@ class LogAPI {
return editLog
})
}
/**
* Subscribe to the changes.
*
* @param {string} logId The long logId, the short one won't do.
*/
subscribeChanges(logId) {
return new Subscription(`
subscription Changes($keys: [ChangeKeyInput!]!) {
changes(keys: $keys) {
id
model
op
author
listed
keys {
model
id
}
date
objects {
type: __typename
...on Log {
id
title
eventName
description
open
}
...on Post {
id
time
kind
nick
text
position
}
}
}
}
`, {keys: [{model: "Log", id: logId}]})
}
}
module.exports = { Log, logsApi: new LogAPI }

87
rpdata/client.js

@ -1,3 +1,4 @@
const EventEmitter = require("events")
const fetch = require("isomorphic-fetch")
const config = require("../config")
const compressQuery = require("graphql-query-compress")
@ -30,4 +31,88 @@ function query(query, variables = {}, options = {}) {
})
}
module.exports = { query }
class Subscription extends EventEmitter {
constructor(query, variables) {
super()
this.query = query
this.variables = Object.assign({}, variables)
this.websocket = null
this.verboseLogging = false
}
connect() {
const baseUrl = (window.location.protocol === 'https:' ? 'wss://' : 'ws://') + window.location.host
let id = 1
this.websocket = new WebSocket(baseUrl + config.graphqlEndpoint, "graphql-ws")
this.websocket.onopen = () => {
this.websocket.send(JSON.stringify({type: "connection_init", payload: {}}));
this.websocket.send(JSON.stringify({id: (id++).toFixed(0), type: "start", payload: {
query: this.query,
variables: this.variables,
extensions: {},
}}));
console.log("WS Open")
}
this.websocket.onmessage = (ev) => {
let data = {}
try {
data = JSON.parse(ev.data)
} catch (err) {
console.warn("Error", err, ev)
return
}
if (this.verboseLogging) {
console.log("WS Data:", data)
}
switch (data.type) {
case "connection_ack": {
this.emit("connect")
break;
}
case "connection_error": {
console.warn("WS Connection Error", data.payload.message)
this.websocket.close()
this.emit("error", new Error(data.payload.message))
break;
}
case "data": {
if (data.payload.errors != null && data.payload.errors.length > 0) {
this.emit("error", data.payload.errors)
} else {
this.emit("data", data.payload.data)
}
break;
}
}
}
this.websocket.onerror = (err) => {
console.warn("WS Error", err)
}
this.websocket.onclose = () => {
this.emit("disconnect")
}
}
disconnect() {
if (this.websocket != null) {
this.websocket.close();
}
}
}
module.exports = { query, Subscription }

1
server.js

@ -8,6 +8,7 @@ const config = require("./config")
// Express depedencies
const express = require("express")
const proxy = require("express-http-proxy")
const proxy2 = require("http-proxy-middleware")
const lasso = require("lasso")
const lassoMiddleware = require("lasso/middleware")
const markoExpress = require("marko/express")

Loading…
Cancel
Save