Pārlūkot izejas kodu

feat: save conflict check polling

NGPixel 5 gadi atpakaļ
vecāks
revīzija
4398573645

+ 34 - 0
client/components/editor.vue

@@ -13,6 +13,9 @@
           full-width
           full-width
         )
         )
       template(slot='actions')
       template(slot='actions')
+        v-btn.mr-3.animated.fadeIn(color='amber', outlined, small, v-if='isConflict')
+          .overline.amber--text.mr-3 Conflict
+          status-indicator(intermediary, pulse)
         v-btn.animated.fadeInDown(
         v-btn.animated.fadeInDown(
           text
           text
           color='green'
           color='green'
@@ -55,9 +58,11 @@
 
 
 <script>
 <script>
 import _ from 'lodash'
 import _ from 'lodash'
+import gql from 'graphql-tag'
 import { get, sync } from 'vuex-pathify'
 import { get, sync } from 'vuex-pathify'
 import { AtomSpinner } from 'epic-spinners'
 import { AtomSpinner } from 'epic-spinners'
 import { Base64 } from 'js-base64'
 import { Base64 } from 'js-base64'
+import { StatusIndicator } from 'vue-status-indicator'
 
 
 import createPageMutation from 'gql/editor/create.gql'
 import createPageMutation from 'gql/editor/create.gql'
 import updatePageMutation from 'gql/editor/update.gql'
 import updatePageMutation from 'gql/editor/update.gql'
@@ -72,6 +77,7 @@ export default {
   i18nOptions: { namespaces: 'editor' },
   i18nOptions: { namespaces: 'editor' },
   components: {
   components: {
     AtomSpinner,
     AtomSpinner,
+    StatusIndicator,
     editorApi: () => import(/* webpackChunkName: "editor-api", webpackMode: "lazy" */ './editor/editor-api.vue'),
     editorApi: () => import(/* webpackChunkName: "editor-api", webpackMode: "lazy" */ './editor/editor-api.vue'),
     editorCode: () => import(/* webpackChunkName: "editor-code", webpackMode: "lazy" */ './editor/editor-code.vue'),
     editorCode: () => import(/* webpackChunkName: "editor-code", webpackMode: "lazy" */ './editor/editor-code.vue'),
     editorCkeditor: () => import(/* webpackChunkName: "editor-ckeditor", webpackMode: "lazy" */ './editor/editor-ckeditor.vue'),
     editorCkeditor: () => import(/* webpackChunkName: "editor-ckeditor", webpackMode: "lazy" */ './editor/editor-ckeditor.vue'),
@@ -122,10 +128,15 @@ export default {
     pageId: {
     pageId: {
       type: Number,
       type: Number,
       default: 0
       default: 0
+    },
+    checkoutDate: {
+      type: String,
+      default: new Date().toISOString()
     }
     }
   },
   },
   data() {
   data() {
     return {
     return {
+      isConflict: false,
       dialogProps: false,
       dialogProps: false,
       dialogProgress: false,
       dialogProgress: false,
       dialogEditorSelector: false,
       dialogEditorSelector: false,
@@ -322,6 +333,29 @@ export default {
         }
         }
       }, 500)
       }, 500)
     }
     }
+  },
+  apollo: {
+    isConflict: {
+      query: gql`
+        query ($id: Int!, $checkoutDate: Date!) {
+          pages {
+            checkConflicts(id: $id, checkoutDate: $checkoutDate)
+          }
+        }
+      `,
+      fetchPolicy: 'network-only',
+      pollInterval: 5000,
+      variables () {
+        return {
+          id: this.pageId,
+          checkoutDate: this.checkoutDate
+        }
+      },
+      update: (data) => _.cloneDeep(data.pages.checkConflicts),
+      skip () {
+        return this.mode === 'create' || !this.isDirty
+      }
+    }
   }
   }
 }
 }
 </script>
 </script>

+ 2 - 1
server/controllers/common.js

@@ -147,7 +147,8 @@ router.get(['/e', '/e/*'], async (req, res, next) => {
       mode: 'create',
       mode: 'create',
       content: null,
       content: null,
       title: null,
       title: null,
-      description: null
+      description: null,
+      updatedAt: new Date().toISOString()
     }
     }
 
 
     // -> From Template
     // -> From Template

+ 18 - 0
server/graph/resolvers/page.js

@@ -252,6 +252,24 @@ module.exports = {
         }
         }
         return result
         return result
       }, [])
       }, [])
+    },
+    /**
+     * CHECK FOR EDITING CONFLICT
+     */
+    async checkConflicts (obj, args, context, info) {
+      let page = await WIKI.models.pages.query().select('path', 'localeCode', 'updatedAt').findById(args.id)
+      if (page) {
+        if (WIKI.auth.checkAccess(context.req.user, ['write:pages', 'manage:pages'], {
+          path: page.path,
+          locale: page.localeCode
+        })) {
+          return page.updatedAt !== args.checkoutDate
+        } else {
+          throw new WIKI.Error.PageUpdateForbidden()
+        }
+      } else {
+        throw new WIKI.Error.PageNotFound()
+      }
     }
     }
   },
   },
   PageMutation: {
   PageMutation: {

+ 5 - 0
server/graph/schemas/page.graphql

@@ -61,6 +61,11 @@ type PageQuery {
   links(
   links(
     locale: String!
     locale: String!
   ): [PageLinkItem] @auth(requires: ["manage:system", "read:pages"])
   ): [PageLinkItem] @auth(requires: ["manage:system", "read:pages"])
+
+  checkConflicts(
+    id: Int!
+    checkoutDate: Date!
+  ): Boolean! @auth(requires: ["write:pages", "manage:pages", "manage:system"])
 }
 }
 
 
 # -----------------------------------------------
 # -----------------------------------------------

+ 1 - 0
server/views/editor.pug

@@ -17,4 +17,5 @@ block body
       init-mode=page.mode
       init-mode=page.mode
       init-editor=page.editorKey
       init-editor=page.editorKey
       init-content=page.content
       init-content=page.content
+      checkout-date=page.updatedAt
       )
       )