From b6f1e1805ec8306558b4a19fc05b2ab9406a5792 Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 12 Jul 2019 23:56:44 -0400 Subject: [PATCH] feat: locales availability + IE display mode --- .babelrc | 57 ++- .gitignore | 1 + client/components/admin/admin-locale.vue | 93 +++-- .../components/admin/admin-utilities-auth.vue | 4 +- .../admin/admin-utilities-cache.vue | 4 +- .../admin/admin-utilities-content.vue | 68 ++++ client/components/admin/admin-utilities.vue | 7 + client/components/common/search-results.vue | 4 +- client/components/editor/editor-markdown.vue | 2 +- .../graph/admin/locale/locale-query-list.gql | 1 + ...ilities-mutation-content-migratelocale.gql | 12 + client/index-legacy.js | 3 + client/index-setup.js | 3 +- client/scss/app.scss | 2 +- client/scss/legacy.scss | 218 +++++++++++ client/themes/default/components/page.vue | 2 +- dev/templates/legacy.pug | 127 +++++++ dev/webpack/webpack.dev.js | 12 +- dev/webpack/webpack.prod.js | 12 +- package.json | 10 +- server/controllers/common.js | 10 +- server/db/migrations-sqlite/2.0.0-beta.217.js | 13 + server/db/migrations/2.0.0-beta.217.js | 13 + server/graph/resolvers/localization.js | 2 +- server/graph/resolvers/page.js | 10 + server/graph/schemas/localization.graphql | 1 + server/graph/schemas/page.graphql | 2 + server/jobs/fetch-graph-locale.js | 27 +- server/jobs/sync-graph-locales.js | 8 +- server/models/locales.js | 3 +- yarn.lock | 355 +++++++++++++++++- 31 files changed, 1015 insertions(+), 71 deletions(-) create mode 100644 client/components/admin/admin-utilities-content.vue create mode 100644 client/graph/admin/utilities/utilities-mutation-content-migratelocale.gql create mode 100644 client/index-legacy.js create mode 100644 client/scss/legacy.scss create mode 100644 dev/templates/legacy.pug create mode 100644 server/db/migrations-sqlite/2.0.0-beta.217.js create mode 100644 server/db/migrations/2.0.0-beta.217.js diff --git a/.babelrc b/.babelrc index b5d40d62..e511eb2f 100644 --- a/.babelrc +++ b/.babelrc @@ -16,13 +16,66 @@ "@babel/plugin-proposal-function-sent", "@babel/plugin-proposal-export-namespace-from", "@babel/plugin-proposal-numeric-separator", - "@babel/plugin-proposal-throw-expressions" + "@babel/plugin-proposal-throw-expressions", + [ + "prismjs", { + "languages": [ + "markup", + "css", + "clike", + "javascript", + "c", + "bash", + "basic", + "cpp", + "csharp", + "arduino", + "ruby", + "elixir", + "fsharp", + "go", + "graphql", + "handlebars", + "haskell", + "ini", + "java", + "json", + "kotlin", + "latex", + "less", + "makefile", + "markdown", + "matlab", + "nginx", + "objectivec", + "perl", + "php", + "powershell", + "pug", + "python", + "typescript", + "rust", + "scss", + "scala", + "smalltalk", + "sql", + "stylus", + "swift", + "vbnet", + "yaml" + ], + "plugins": ["line-numbers"], + "theme": "dark", + "css": true + } + ] ], "presets": [ [ "@babel/preset-env", { "useBuiltIns": "entry", - "corejs": 3 + "corejs": 3, + "debug": true } ] ] diff --git a/.gitignore b/.gitignore index a35d5683..6e55106e 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ npm-debug.log* # Generated assets /assets server/views/master.pug +server/views/legacy.pug server/views/setup.pug # Webpack diff --git a/client/components/admin/admin-locale.vue b/client/components/admin/admin-locale.vue index 7fefc486..ec9cd9cc 100644 --- a/client/components/admin/admin-locale.vue +++ b/client/components/admin/admin-locale.vue @@ -52,8 +52,6 @@ v-toolbar(color='primary', dark, dense, flat) v-toolbar-title .subheading {{ $t('admin:locale.namespacing') }} - v-spacer - v-chip(label, color='white', small).primary--text coming soon v-card-text v-switch( v-model='namespacing' @@ -102,28 +100,35 @@ v-card.animated.fadeInUp.wait-p4s v-toolbar(color='teal', dark, dense, flat) v-toolbar-title - .subheading {{ $t('admin:locale.download') }} - v-list(two-line, dense) - template(v-for='(lc, idx) in locales') - v-list-tile(:key='lc.code') - v-list-tile-avatar - v-avatar.teal.white--text(size='40') {{lc.code.toUpperCase()}} - v-list-tile-content - v-list-tile-title(v-html='lc.name') - v-list-tile-sub-title(v-html='lc.nativeName') - v-list-tile-action(v-if='lc.isRTL') - v-chip(label, small, :class='$vuetify.dark ? `text--lighten-5` : `text--darken-2`').caption.grey--text RTL - v-list-tile-action(v-if='lc.isInstalled && lc.installDate < lc.updatedAt') - v-btn(icon, @click='download(lc)') - v-icon.blue--text cached - v-list-tile-action(v-else-if='lc.isInstalled') + .subheading {{ $t('admin:locale.downloadTitle') }} + v-data-table( + :headers='headers', + :items='locales', + hide-actions, + item-key='code', + :rows-per-page-items='[-1]' + ) + template(v-slot:items='lc') + td + v-chip.white--text(label, color='teal', small) {{lc.item.code}} + td + strong {{lc.item.name}} + td + span {{ lc.item.nativeName }} + td.text-xs-center + v-icon(v-if='lc.item.isRTL') check + td + .d-flex.align-center.pl-4 + .caption.mr-2(:class='lc.item.availability <= 33 ? `red--text` : (lc.item.availability <= 66) ? `orange--text` : `green--text`') {{lc.item.availability}}% + v-progress-circular(:value='lc.item.availability', width='2', size='20', :color='lc.item.availability <= 33 ? `red` : (lc.item.availability <= 66) ? `orange` : `green`') + td.text-xs-center + v-progress-circular(v-if='lc.item.isDownloading', indeterminate, color='blue', size='20', :width='2') + v-btn(v-else-if='lc.item.isInstalled && lc.item.installDate < lc.item.updatedAt', icon, @click='download(lc.item)') + v-icon.blue--text cached + v-btn(v-else-if='lc.item.isInstalled', icon, @click='download(lc.item)') v-icon.green--text check - v-list-tile-action(v-else-if='lc.isDownloading') - v-progress-circular(indeterminate, color='blue', size='20', :width='3') - v-list-tile-action(v-else) - v-btn(icon, @click='download(lc)') - v-icon.grey--text cloud_download - v-divider.my-0(inset, v-if='idx < locales.length - 1') + v-btn(v-else, icon, @click='download(lc.item)') + v-icon.grey--text cloud_download v-card.wiki-form.mt-3.animated.fadeInUp.wait-p5s v-toolbar(color='teal', dark, dense, flat) v-toolbar-title @@ -158,6 +163,46 @@ export default { computed: { installedLocales() { return _.filter(this.locales, ['isInstalled', true]) + }, + headers() { + return [ + { + text: this.$t('admin:locale.code'), + align: 'left', + value: 'code', + width: 10 + }, + { + text: this.$t('admin:locale.name'), + align: 'left', + value: 'name' + }, + { + text: this.$t('admin:locale.nativeName'), + align: 'left', + value: 'nativeName' + }, + { + text: this.$t('admin:locale.rtl'), + align: 'center', + value: 'isRTL', + sortable: false, + width: 10 + }, + { + text: this.$t('admin:locale.availability'), + align: 'center', + value: 'availability', + width: 100 + }, + { + text: this.$t('admin:locale.download'), + align: 'center', + value: 'code', + sortable: false, + width: 100 + } + ] } }, methods: { @@ -173,6 +218,8 @@ export default { if (resp.succeeded) { lc.isDownloading = false lc.isInstalled = true + lc.updatedAt = new Date().toISOString() + lc.installDate = lc.updatedAt this.$store.commit('showNotification', { message: `Locale ${lc.name} has been installed successfully.`, style: 'success', diff --git a/client/components/admin/admin-utilities-auth.vue b/client/components/admin/admin-utilities-auth.vue index e697fb8b..12695a0a 100644 --- a/client/components/admin/admin-utilities-auth.vue +++ b/client/components/admin/admin-utilities-auth.vue @@ -3,14 +3,14 @@ v-toolbar(flat, color='primary', dark, dense) .subheading {{ $t('admin:utilities.authTitle') }} v-card-text - v-subheader.pl-0 Generate New Authentication Public / Private Key Certificates + v-subheader.pl-0.primary--text Generate New Authentication Public / Private Key Certificates .body-1 This will invalidate all current session tokens and cause all users to be logged out. .body-1.red--text You will need to log back in after the operation. v-btn(outline, color='primary', @click='regenCerts', :disabled='loading').ml-0.mt-3 v-icon(left) build span Proceed v-divider.my-3 - v-subheader.pl-0 Reset Guest User + v-subheader.pl-0.primary--text Reset Guest User .body-1 This will reset the guest user to its default parameters and permissions. v-btn(outline, color='primary', @click='resetGuest', :disabled='loading').ml-0.mt-3 v-icon(left) build diff --git a/client/components/admin/admin-utilities-cache.vue b/client/components/admin/admin-utilities-cache.vue index d4e3c215..8e54d1fa 100644 --- a/client/components/admin/admin-utilities-cache.vue +++ b/client/components/admin/admin-utilities-cache.vue @@ -3,13 +3,13 @@ v-toolbar(flat, color='primary', dark, dense) .subheading {{ $t('admin:utilities.cacheTitle') }} v-card-text - v-subheader.pl-0 Flush Pages and Assets Cache + v-subheader.pl-0.primary--text Flush Pages and Assets Cache .body-1 Pages and Assets are cached to disk for better performance. You can flush the cache to force all content to be fetched from the DB again. v-btn(outline, color='primary', @click='flushCache', :disabled='loading').ml-0.mt-3 v-icon(left) build span Proceed v-divider.my-3 - v-subheader.pl-0 Flush Temporary Uploads + v-subheader.pl-0.primary--text Flush Temporary Uploads .body-1 New uploads are temporarily saved to disk while they are being processed. They are automatically deleted after processing, but you can force an immediate cleanup using this tool. .body-1.red--text Note that performing this action while an upload is in progress can result in a failed upload. v-btn(outline, color='primary', @click='flushUploads', :disabled='loading').ml-0.mt-3 diff --git a/client/components/admin/admin-utilities-content.vue b/client/components/admin/admin-utilities-content.vue new file mode 100644 index 00000000..e836163c --- /dev/null +++ b/client/components/admin/admin-utilities-content.vue @@ -0,0 +1,68 @@ + + + + + diff --git a/client/components/admin/admin-utilities.vue b/client/components/admin/admin-utilities.vue index 3e4b96a7..2b05e8bc 100644 --- a/client/components/admin/admin-utilities.vue +++ b/client/components/admin/admin-utilities.vue @@ -35,6 +35,7 @@ export default { components: { UtilityAuth: () => import(/* webpackChunkName: "admin" */ './admin-utilities-auth.vue'), + UtilityContent: () => import(/* webpackChunkName: "admin" */ './admin-utilities-content.vue'), UtilityCache: () => import(/* webpackChunkName: "admin" */ './admin-utilities-cache.vue'), UtilityImportv1: () => import(/* webpackChunkName: "admin" */ './admin-utilities-importv1.vue'), UtilityTelemetry: () => import(/* webpackChunkName: "admin" */ './admin-utilities-telemetry.vue') @@ -49,6 +50,12 @@ export default { i18nKey: 'auth', isAvailable: true }, + { + key: 'UtilityContent', + icon: 'insert_drive_file', + i18nKey: 'content', + isAvailable: true + }, { key: 'UtilityCache', icon: 'invert_colors', diff --git a/client/components/common/search-results.vue b/client/components/common/search-results.vue index ddf86a08..ee721cf5 100644 --- a/client/components/common/search-results.vue +++ b/client/components/common/search-results.vue @@ -91,7 +91,7 @@ export default { return this.response.suggestions ? this.response.suggestions : [] }, paginationLength() { - return this.response.totalHits > 0 ? 0 : Math.ceil(this.response.totalHits / 10) + return (this.response.totalHits > 0) ? 0 : Math.ceil(this.response.totalHits / 10) } }, watch: { @@ -107,7 +107,7 @@ export default { }, mounted() { this.$root.$on('searchMove', (dir) => { - this.cursor += (dir === 'up' ? -1 : 1) + this.cursor += ((dir === 'up') ? -1 : 1) if (this.cursor < -1) { this.cursor = -1 } else if (this.cursor > this.results.length + this.suggestions.length - 1) { diff --git a/client/components/editor/editor-markdown.vue b/client/components/editor/editor-markdown.vue index 658c3a44..8b697afd 100644 --- a/client/components/editor/editor-markdown.vue +++ b/client/components/editor/editor-markdown.vue @@ -190,7 +190,7 @@ import mdMark from 'markdown-it-mark' import mdImsize from 'markdown-it-imsize' // Prism (Syntax Highlighting) -import Prism from '@/libs/prism/prism.js' +import Prism from 'prismjs' // ======================================== // INIT diff --git a/client/graph/admin/locale/locale-query-list.gql b/client/graph/admin/locale/locale-query-list.gql index 63402965..c91e2d90 100644 --- a/client/graph/admin/locale/locale-query-list.gql +++ b/client/graph/admin/locale/locale-query-list.gql @@ -1,6 +1,7 @@ { localization { locales { + availability code createdAt isInstalled diff --git a/client/graph/admin/utilities/utilities-mutation-content-migratelocale.gql b/client/graph/admin/utilities/utilities-mutation-content-migratelocale.gql new file mode 100644 index 00000000..43fb27a2 --- /dev/null +++ b/client/graph/admin/utilities/utilities-mutation-content-migratelocale.gql @@ -0,0 +1,12 @@ +mutation { + pages { + migrateToLocale { + responseResult { + succeeded + errorCode + slug + message + } + } + } +} diff --git a/client/index-legacy.js b/client/index-legacy.js new file mode 100644 index 00000000..65dc2027 --- /dev/null +++ b/client/index-legacy.js @@ -0,0 +1,3 @@ +require('./scss/legacy.scss') + +window.WIKI = null diff --git a/client/index-setup.js b/client/index-setup.js index 4edfddf1..a0c8b2cb 100644 --- a/client/index-setup.js +++ b/client/index-setup.js @@ -1,4 +1,5 @@ -require('@babel/polyfill') +require('core-js/stable') +require('regenerator-runtime/runtime') require('vuetify/src/stylus/main.styl') require('./scss/app.scss') diff --git a/client/scss/app.scss b/client/scss/app.scss index 61bd915a..b5e6e4be 100644 --- a/client/scss/app.scss +++ b/client/scss/app.scss @@ -18,7 +18,7 @@ @import 'layout/md2'; // @import '../libs/twemoji/twemoji-awesome'; -@import '../libs/prism/prism.css'; +// @import '../libs/prism/prism.css'; @import '~vue-tour/dist/vue-tour.css'; @import '~vue-status-indicator/styles.css'; @import '~xterm/dist/xterm.css'; diff --git a/client/scss/legacy.scss b/client/scss/legacy.scss new file mode 100644 index 00000000..1290ef4e --- /dev/null +++ b/client/scss/legacy.scss @@ -0,0 +1,218 @@ +@import "global"; + +@import "./base/fonts.scss"; +@import "./base/icons.scss"; + +html { + box-sizing: border-box; + background-color: mc('grey', '50'); + font-size: 14px; +} +*, *:before, *:after { + box-sizing: inherit; +} +* { + margin: 0; + padding: 0; +} + +.is-hidden { + display: none; +} + +body { + margin: 0; + padding: 0; + font-family: "Roboto",sans-serif; + line-height: 1.5; + min-height: 100vh; +} + +.header { + background-color: #000; + color: #FFF; + height: 64px; + padding: 0 16px; + display: flex; + justify-content: space-between; + align-items: center; + + &-title { + margin: 0; + font-size: 16px; + font-weight: 500; + letter-spacing: .02em; + } + + &-deprecated { + color: mc('red', '100'); + + a { + color: mc('pink', '400'); + } + } + + &-login { + a { + text-decoration: none; + color: #FFF; + transition: color .3s ease; + + &:hover { + color: mc('blue', '500'); + } + } + } +} + +.main { + display: flex; + align-items: stretch; + min-height: calc(100vh - 64px); + height: 50vh; + + &-container { + flex-grow: 1; + } +} + +.sidebar { + width: 300px; + background-color: mc('blue', '700'); + color: #FFF; + padding: 8px 0; + + .sidebar-link { + height: 40px; + font-size: 13px; + display: flex; + align-items: center; + padding: 0 16px; + transition: background .3s cubic-bezier(.25,.8,.5,1); + font-weight: 400; + color: #FFF; + text-decoration: none; + + &:hover { + background: hsla(0,0%,100%,.08); + } + } + + i.material-icons { + width: 56px; + padding-left: 8px; + } + + .sidebar-divider { + border-top: 1px solid hsla(0,0%,100%,.12); + margin: 8px 0; + } + + .sidebar-title { + font-size: 13px; + height: 40px; + display: flex; + align-items: center; + padding: 0 16px 0 24px; + font-weight: 500; + color: hsla(0,0%,100%,.7); + } +} + +.page-header { + background-color: mc('grey', '100'); + padding: 0 24px; + height: 90px; + display: flex; + align-items: center; + border-bottom: 1px solid mc('grey', '200'); + + h1 { + font-size: 24px; + font-weight: 400; + line-height: 32px; + color: mc('grey', '800'); + } + + h2 { + color: mc('grey', '600'); + font-size: 12px; + font-weight: 400; + } + + &-left { + flex-grow: 1; + } + + &-right { + flex: 0 0 324px; + padding-left: 16px; + + &-title { + color: mc('grey', '500'); + font-size: 12px; + } + &-author { + color: mc('grey', '800'); + font-weight: 500; + } + &-updated { + color: mc('grey', '600'); + font-size: 12px; + } + } +} + +.page-contents { + display: flex; +} + +.toc { + flex: 0 0 348px; + background-color: mc('grey', '200'); + padding: 4px 0; + + &-title { + font-size: 13px; + height: 40px; + display: flex; + color: mc('blue', '600'); + align-items: center; + font-weight: 500; + padding: 0 16px; + } + + &-tile { + text-decoration: none; + height: 40px; + display: flex; + font-size: 13px; + align-items: center; + padding: 0 16px; + color: mc('grey', '800'); + transition: background-color .3s ease; + + &.inset { + padding-left: 32px; + } + + &:hover { + background-color: rgba(0,0,0,.06); + } + } + + &-divider { + border-top: 1px solid rgba(0,0,0,.12); + margin: 0 0 0 24px; + + &.inset { + margin-left: 40px; + } + } +} + +@import "../themes/default/scss/app.scss"; + +.contents { + flex-grow: 1; +} diff --git a/client/themes/default/components/page.vue b/client/themes/default/components/page.vue index e2051cd4..700ef6c9 100644 --- a/client/themes/default/components/page.vue +++ b/client/themes/default/components/page.vue @@ -137,7 +137,7 @@