feat: GraphQL translations + login form

This commit is contained in:
NGPixel
2017-09-24 23:22:33 -04:00
parent 16a47e46ce
commit 32b7947704
32 changed files with 314 additions and 339 deletions

View File

@@ -6,10 +6,14 @@
import Vue from 'vue'
import VueResource from 'vue-resource'
import VueClipboards from 'vue-clipboards'
import { ApolloClient, createBatchingNetworkInterface } from 'apollo-client'
import store from './store'
import i18next from 'i18next'
import i18nextXHR from 'i18next-xhr-backend'
import VueI18Next from '@panter/vue-i18next'
// ====================================
// Load Modules
// ====================================
import localization from './modules/localization'
// ====================================
// Load Helpers
@@ -29,6 +33,7 @@ import editorFileComponent from './components/editor-file.vue'
import editorVideoComponent from './components/editor-video.vue'
import historyComponent from './components/history.vue'
import loadingSpinnerComponent from './components/loading-spinner.vue'
import loginComponent from './components/login.vue'
import modalCreatePageComponent from './components/modal-create-page.vue'
import modalCreateUserComponent from './components/modal-create-user.vue'
import modalDeleteUserComponent from './components/modal-delete-user.vue'
@@ -49,13 +54,24 @@ import contentViewComponent from './pages/content-view.component.js'
import editorComponent from './components/editor.component.js'
import sourceViewComponent from './pages/source-view.component.js'
// ====================================
// Initialize Apollo Client (GraphQL)
// ====================================
window.apollo = new ApolloClient({
networkInterface: createBatchingNetworkInterface({
uri: window.location.protocol + '//' + window.location.host + siteConfig.path + '/graphql'
}),
connectToDevTools: true
})
// ====================================
// Initialize Vue Modules
// ====================================
Vue.use(VueResource)
Vue.use(VueClipboards)
Vue.use(VueI18Next)
Vue.use(localization.VueI18Next)
Vue.use(helpers)
// ====================================
@@ -76,6 +92,7 @@ Vue.component('editorFile', editorFileComponent)
Vue.component('editorVideo', editorVideoComponent)
Vue.component('history', historyComponent)
Vue.component('loadingSpinner', loadingSpinnerComponent)
Vue.component('login', loginComponent)
Vue.component('modalCreatePage', modalCreatePageComponent)
Vue.component('modalCreateUser', modalCreateUserComponent)
Vue.component('modalDeleteUser', modalDeleteUserComponent)
@@ -89,20 +106,6 @@ Vue.component('sourceView', sourceViewComponent)
Vue.component('toggle', toggleComponent)
Vue.component('tree', treeComponent)
// ====================================
// Load Localization strings
// ====================================
i18next
.use(i18nextXHR)
.init({
backend: {
loadPath: siteConfig.path + '/js/i18n/{{lng}}.json'
},
lng: siteConfig.lang,
fallbackLng: siteConfig.lang
})
document.addEventListener('DOMContentLoaded', ev => {
// ====================================
// Notifications
@@ -116,13 +119,13 @@ document.addEventListener('DOMContentLoaded', ev => {
// Bootstrap Vue
// ====================================
const i18n = new VueI18Next(i18next)
const i18n = localization.init()
window.wiki = new Vue({
mixins: [helpers],
components: {},
store,
i18n,
el: '#root',
el: '#app',
methods: {
changeTheme(opts) {
this.$el.className = `has-stickynav is-primary-${opts.primary} is-alternate-${opts.alt}`

View File

@@ -0,0 +1,50 @@
<template lang="pug">
.login(:class='{ "is-error": error }')
.login-container(:class='{ "is-expanded": strategies.length > 1 }')
.login-error(v-if='error')
strong
i.icon-warning-outline
| {{ error.title }}
span {{ error.message }}
.login-providers(v-show='strategies.length > 1')
button.is-active(:title='$t("auth:providers.local")')
i.nc-icon-outline.ui-1_database
span {{ $t('auth:providers.local') }}
button(v-for='strategy in strategies', @onclick='selectProvider(strategy.key, strategy.useForm)', :title='strategy.title')
//-!= strategy.icon
span {{ strategy.title }}
.login-frame
h1 {{ siteTitle }}
h2 {{ $t('auth:loginrequired') }}
form(method='post', action='/login')
input#login-user(type='text', name='email', :placeholder='$t("auth:fields.emailuser")')
input#login-pass(type='password', name='password', :placeholder='$t("auth:fields.password")')
button.button.is-light-blue.is-fullwidth(type='submit')
span {{ $t('auth:actions.login') }}
.login-copyright
span {{ $t('footer.poweredby') }}
a(href='https://wiki.js.org', rel='external', title='Wiki.js') Wiki.js
</template>
<script>
export default {
name: 'login',
data() {
return {
error: false,
strategies: []
}
},
computed: {
siteTitle() {
return siteConfig.title
}
}
methods: {
selectProvider(key, useForm) {
}
}
}
</script>

View File

@@ -0,0 +1,57 @@
import i18next from 'i18next'
import i18nextXHR from 'i18next-xhr-backend'
import i18nextCache from 'i18next-localstorage-cache'
import gql from 'graphql-tag'
import VueI18Next from '@panter/vue-i18next'
import loSet from 'lodash/set'
/* global siteConfig */
module.exports = {
VueI18Next,
init() {
i18next
.use(i18nextXHR)
.use(i18nextCache)
.init({
backend: {
loadPath: '{{lng}}/{{ns}}',
parse: (data) => data,
ajax: (url, opts, cb, data) => {
let langParams = url.split('/')
console.info(langParams)
window.apollo.query({
query: gql`
{
translations(locale:"${langParams[0]}", namespace:"${langParams[1]}") {
key
value
}
}
`
}).then(resp => {
let ns = {}
if (resp.data.translations.length > 0) {
resp.data.translations.forEach(entry => {
loSet(ns, entry.key, entry.value)
})
}
return cb(ns, {status: '200'})
}).catch(err => {
console.error(err)
return cb(null, {status: '404'})
})
}
},
cache: {
enabled: true,
expiration: 60 * 60 * 1000
},
defaultNS: 'common',
lng: siteConfig.lang,
fallbackLng: siteConfig.lang,
ns: ['common', 'admin', 'auth']
})
return new VueI18Next(i18next)
}
}