2
0
Эх сурвалжийг харах

feat: view page source + download page

NGPixel 1 жил өмнө
parent
commit
add3631b22

+ 1 - 0
server/db/migrations/3.0.0.mjs

@@ -186,6 +186,7 @@ export async function up (knex) {
       table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
       table.uuid('pageId').notNullable().index()
       table.string('action').defaultTo('updated')
+      table.string('reason')
       table.jsonb('affectedFields').notNullable().defaultTo('[]')
       table.string('locale', 10).notNullable().defaultTo('en')
       table.string('path').notNullable()

+ 13 - 4
server/models/pageHistory.mjs

@@ -77,21 +77,30 @@ export class PageHistory extends Model {
    */
   static async addVersion(opts) {
     await WIKI.db.pageHistory.query().insert({
-      pageId: opts.id,
-      siteId: opts.siteId,
+      action: opts.historyData?.action ?? 'updated',
+      affectedFields: JSON.stringify(opts.historyData?.affectedFields ?? []),
+      alias: opts.alias,
+      config: JSON.stringify(opts.config ?? {}),
       authorId: opts.authorId,
       content: opts.content,
       contentType: opts.contentType,
       description: opts.description,
       editor: opts.editor,
       hash: opts.hash,
-      publishState: opts.publishState,
+      icon: opts.icon,
       locale: opts.locale,
+      pageId: opts.id,
       path: opts.path,
       publishEndDate: opts.publishEndDate?.toISO(),
       publishStartDate: opts.publishStartDate?.toISO(),
+      publishState: opts.publishState,
+      reason: opts.historyData?.reason,
+      relations: JSON.stringify(opts.relations ?? []),
+      render: opts.render,
+      scripts: JSON.stringify(opts.scripts ?? {}),
+      siteId: opts.siteId,
       title: opts.title,
-      action: opts.action || 'updated',
+      toc: JSON.stringify(opts.toc ?? []),
       versionDate: opts.versionDate
     })
   }

+ 1 - 0
server/models/pages.mjs

@@ -428,6 +428,7 @@ export class Page extends Model {
     const patch = {}
     const historyData = {
       action: 'updated',
+      reason: opts.reasonForChange,
       affectedFields: []
     }
     let shouldUpdateSearch = false

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
ux/public/_assets/icons/fluent-code.svg


+ 1 - 1
ux/src/components/HeaderSearch.vue

@@ -196,7 +196,7 @@ onBeforeUnmount(() => {
   color: #FFF;
   padding: .5rem 1rem 1rem;
   width: 100%;
-  backdrop-filter: blur(7px);
+  backdrop-filter: blur(7px) saturate(180%);
   box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2), 0 1px 1px rgba(0, 0, 0, 0.14), 0 2px 1px -1px rgba(0, 0, 0, 0.12);
 
   &-header {

+ 4 - 0
ux/src/components/MainOverlayDialog.vue

@@ -31,6 +31,10 @@ const overlays = {
     loader: () => import('./NavEditOverlay.vue'),
     loadingComponent: LoadingGeneric
   }),
+  PageSource: defineAsyncComponent({
+    loader: () => import('./PageSourceOverlay.vue'),
+    loadingComponent: LoadingGeneric
+  }),
   TableEditor: defineAsyncComponent({
     loader: () => import('./TableEditorOverlay.vue'),
     loadingComponent: LoadingGeneric

+ 6 - 1
ux/src/components/PageActionsCol.vue

@@ -76,6 +76,7 @@
     icon='las la-code'
     :color='editorStore.isActive ? `white` : `grey`'
     aria-label='Page Source'
+    @click='viewPageSource'
     )
     q-tooltip(anchor='center left' self='center right') Page Source
   template(v-if='!(editorStore.isActive && editorStore.mode === `create`)')
@@ -87,7 +88,7 @@
       aria-label='Page Actions'
       )
       q-tooltip(anchor='center left' self='center right') Page Actions
-      q-menu(
+      q-menu.translucent-menu(
         anchor='top left'
         self='top right'
         auto-close
@@ -199,6 +200,10 @@ function togglePageData () {
   })
 }
 
+function viewPageSource () {
+  siteStore.$patch({ overlay: 'PageSource', overlayOpts: { } })
+}
+
 function duplicatePage () {
   $q.dialog({
     component: defineAsyncComponent(() => import('../components/TreeBrowserDialog.vue')),

+ 161 - 0
ux/src/components/PageSourceOverlay.vue

@@ -0,0 +1,161 @@
+<template lang="pug">
+q-layout(view='hHh lpR fFf', container)
+  q-header.card-header.q-px-md.q-py-sm
+    q-icon(name='img:/_assets/icons/fluent-code.svg', left, size='md')
+    span Page Source
+    q-space
+    transition(name='syncing')
+      q-spinner-tail.q-mr-sm(
+        v-show='state.loading > 0'
+        color='accent'
+        size='24px'
+      )
+    q-btn.q-mr-md(
+      icon='las la-download'
+      color='teal-3'
+      dense
+      flat
+      @click='download'
+      )
+      q-tooltip(anchor='bottom middle', self='top middle') {{t(`common.actions.download`)}}
+    q-btn(
+      icon='las la-times'
+      color='pink-2'
+      dense
+      flat
+      @click='close'
+      )
+      q-tooltip(anchor='bottom middle', self='top middle') {{t(`common.actions.close`)}}
+
+  q-page-container
+    q-page.bg-dark-6.text-white.font-robotomono.pagesource
+      q-scroll-area(
+        :thumb-style='thumb'
+        :bar-style='bar'
+        :horizontal-thumb-style='{ height: `5px` }'
+        style="width: 100%; height: calc(100vh - 100px);"
+        )
+        pre.q-px-md(v-text='state.content')
+</template>
+
+<script setup>
+import { useI18n } from 'vue-i18n'
+import { exportFile, useQuasar } from 'quasar'
+import { onBeforeUnmount, onMounted, reactive, ref } from 'vue'
+import gql from 'graphql-tag'
+import { cloneDeep } from 'lodash-es'
+
+import { usePageStore } from 'src/stores/page'
+import { useSiteStore } from 'src/stores/site'
+
+// QUASAR
+
+const $q = useQuasar()
+
+// STORES
+
+const pageStore = usePageStore()
+const siteStore = useSiteStore()
+
+// I18N
+
+const { t } = useI18n()
+
+// DATA
+
+const state = reactive({
+  loading: 0,
+  content: ''
+})
+
+const thumb = {
+  right: '2px',
+  borderRadius: '5px',
+  backgroundColor: '#FFF',
+  width: '5px',
+  opacity: 0.25
+}
+const bar = {
+  backgroundColor: '#000',
+  width: '9px',
+  opacity: 0.25
+}
+
+const contentTypes = {
+  markdown: {
+    ext: 'md',
+    mime: 'text/markdown'
+  },
+  html: {
+    ext: 'html',
+    mime: 'text/html'
+  }
+}
+
+// METHODS
+
+function download () {
+  const fileType = contentTypes[state.contentType] ?? { ext: 'txt', mime: 'text/plain' }
+  exportFile(`page.${fileType.ext}`, state.content, { mimeType: `${fileType.mime};charset=UTF-8` })
+}
+
+function close () {
+  siteStore.$patch({ overlay: '' })
+}
+
+async function load () {
+  state.loading++
+  $q.loading.show()
+  try {
+    const resp = await APOLLO_CLIENT.query({
+      query: gql`
+        query loadPageSource (
+          $id: UUID!
+          ) {
+          pageById(
+            id: $id
+          ) {
+            id
+            content
+            contentType
+          }
+        }
+      `,
+      variables: {
+        id: pageStore.id
+      },
+      fetchPolicy: 'network-only'
+    })
+    const pageData = cloneDeep(resp?.data?.pageById ?? {})
+    if (!pageData?.id) {
+      throw new Error('ERR_PAGE_NOT_FOUND')
+    }
+    state.content = pageData.content
+    state.contentType = pageData.contentType
+  } catch (err) {
+    $q.notify({
+      type: 'negative',
+      message: err.message
+    })
+  }
+  $q.loading.hide()
+  state.loading--
+}
+
+onMounted(() => {
+  load()
+})
+
+onBeforeUnmount(() => {
+  siteStore.overlayOpts = {}
+})
+</script>
+
+<style lang="scss" scoped>
+.pagesource {
+  > pre {
+    margin: 0;
+    overflow-x: auto;
+  }
+}
+</style>

+ 1 - 1
ux/src/css/app.scss

@@ -228,7 +228,7 @@ body::-webkit-scrollbar-thumb {
     background-color: rgba($dark,.7);
   }
 
-  backdrop-filter: blur(10px);
+  backdrop-filter: blur(10px) saturate(180%);
 
   > .q-card {
     background-color: transparent !important;

+ 1 - 1
ux/src/layouts/AdminLayout.vue

@@ -394,7 +394,7 @@ onMounted(async () => {
 .admin-overlay {
   > .q-dialog__backdrop {
     background-color: rgba(0,0,0,.6);
-    backdrop-filter: blur(5px);
+    backdrop-filter: blur(5px) saturate(180%);
   }
   > .q-dialog__inner {
     padding: 24px 64px;

+ 2 - 2
ux/src/layouts/MainLayout.vue

@@ -199,7 +199,7 @@ body.body--dark {
 .main-overlay {
   > .q-dialog__backdrop {
     background-color: rgba(0,0,0,.6);
-    backdrop-filter: blur(5px);
+    backdrop-filter: blur(5px) saturate(180%);
   }
   > .q-dialog__inner {
     padding: 24px 64px;
@@ -216,7 +216,7 @@ body.body--dark {
         background-image: linear-gradient(to bottom, $dark-4 10px, $dark-4 11px, $dark-3);
       }
       border-radius: 6px;
-      box-shadow: 0 0 0 1px rgba(0,0,0,.5);
+      box-shadow: 0 0 30px 0 rgba(0,0,0,.3);
     }
   }
 }

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно