Переглянути джерело

Implement lazy loading and new UI layout (#17)

Merge branch 'feat/ui-layout' of git.bubblev.org:bubblev/bubble-web into feat/ui-layout

capitalize filename

fix: webpack config to add output default url

Merge branch 'master' into feat/ui-layout

Merge branch 'master' into feat/ui-layout

feat: integrate lazy loading and new page structure

feat: use different layout for new Pages

Co-authored-by: Tyler <everdev0923@gmail.com>
Co-authored-by: Jonathan Cobb <jonathan@kyuss.org>
Co-authored-by: jonathan <jonathan@noreply.git.bubblev.org>
Reviewed-on: https://git.bubblev.org/bubblev/bubble-web/pulls/17
pull/32/head
Tyler Chen 4 роки тому
committed by jonathan
джерело
коміт
41d4ba6bc6
12 змінених файлів з 346 додано та 141 видалено
  1. +2
    -1
      .babelrc
  2. +9
    -1
      package-lock.json
  3. +3
    -2
      package.json
  4. +10
    -0
      src/_pages/Layout.vue
  5. +10
    -0
      src/_pages/auth/Layout.vue
  6. +9
    -0
      src/_pages/auth/Login.vue
  7. +9
    -0
      src/_pages/auth/NewLogin.vue
  8. +10
    -0
      src/_pages/main/Layout.vue
  9. +9
    -0
      src/_pages/main/Test.vue
  10. +34
    -0
      src/_router/index.js
  11. +237
    -137
      src/app/App.vue
  12. +4
    -0
      webpack.config.js

+ 2
- 1
.babelrc Переглянути файл

@@ -7,6 +7,7 @@
"rootPathSuffix": "./src",
"rootPathPrefix": "~/"
}
]
],
["babel-plugin-syntax-dynamic-import"]
]
}

+ 9
- 1
package-lock.json Переглянути файл

@@ -685,6 +685,7 @@
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/babel-plugin-root-import/-/babel-plugin-root-import-6.5.0.tgz",
"integrity": "sha512-PTD8fPl4v1kwn01u9d4rgRavDs5Z+jv4qa4/y6iYtoSgM4/xmjwMqo66j5A/BTZQEMA6OV5iFgyZ1PIhroJqqg==",
"dev": true,
"requires": {
"slash": "^3.0.0"
},
@@ -692,7 +693,8 @@
"slash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
"dev": true
}
}
},
@@ -708,6 +710,12 @@
"integrity": "sha1-a8lj67FuzLrmuStZbrfzXDQqi5o=",
"dev": true
},
"babel-plugin-syntax-dynamic-import": {
"version": "6.18.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz",
"integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo=",
"dev": true
},
"babel-plugin-syntax-exponentiation-operator": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz",


+ 3
- 2
package.json Переглянути файл

@@ -11,7 +11,6 @@
"dev": "webpack-dev-server --open --config webpack.config.dev.js"
},
"dependencies": {
"babel-plugin-root-import": "^6.5.0",
"luxon": "^1.21.3",
"qrcode": "^1.4.4",
"safe-eval": "^0.4.1",
@@ -27,12 +26,14 @@
"devDependencies": {
"babel-core": "^6.26.3",
"babel-loader": "^7.1.5",
"babel-plugin-root-import": "^6.5.0",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"babel-preset-env": "^1.7.0",
"babel-preset-stage-3": "^6.24.1",
"babel-preset-vue": "^2.0.2",
"copy-webpack-plugin": "^5.1.1",
"file-loader": "^6.0.0",
"css-loader": "^2.1.1",
"file-loader": "^6.0.0",
"html-webpack-plugin": "^3.2.0",
"path": "^0.12.7",
"vue-loader": "^14.2.3",


+ 10
- 0
src/_pages/Layout.vue Переглянути файл

@@ -0,0 +1,10 @@
<template>
<div>
default Layout
<router-view></router-view>
</div>
</template>

<script>
export default {};
</script>

+ 10
- 0
src/_pages/auth/Layout.vue Переглянути файл

@@ -0,0 +1,10 @@
<template>
<div>
Auth Layout
<router-view></router-view>
</div>
</template>

<script>
export default {};
</script>

+ 9
- 0
src/_pages/auth/Login.vue Переглянути файл

@@ -0,0 +1,9 @@
<template>
<div>
New Login Page
</div>
</template>

<script>
export default {};
</script>

+ 9
- 0
src/_pages/auth/NewLogin.vue Переглянути файл

@@ -0,0 +1,9 @@
<template>
<div>
New Login Page
</div>
</template>

<script>
export default {};
</script>

+ 10
- 0
src/_pages/main/Layout.vue Переглянути файл

@@ -0,0 +1,10 @@
<template>
<div>
Main Layout
<router-view></router-view>
</div>
</template>

<script>
export default {};
</script>

+ 9
- 0
src/_pages/main/Test.vue Переглянути файл

@@ -0,0 +1,9 @@
<template>
<div>
Test Page
</div>
</template>

<script>
export default {};
</script>

+ 34
- 0
src/_router/index.js Переглянути файл

@@ -66,6 +66,35 @@ const newNetworkChildren = [
export const router = new Router({
mode: 'history',
routes: [
// new Pages & lazy loading
{
path: '/new_pages',
component: () => import('~/_pages/Layout'),
children: [
{
path: '',
component: () => import('~/_pages/auth/Layout'),
children: [
{
path: 'login',
component: () => import('~/_pages/auth/Login'),
},
],
},
{
path: '',
component: () => import('~/_pages/main/Layout'),
children: [
{
path: 'test',
component: () => import('~/_pages/main/Test'),
},
],
},
],
},

// existing pages
{ path: '', component: DashboardPage },
{ path: '/', component: DashboardPage },
{ path: '/legal', component: LegalPage },
@@ -166,6 +195,9 @@ const publicPages = [
'/auth',
'/activate',
'/legal',

// new Pages
'/new_pages',
];

router.beforeEach((to, from, next) => {
@@ -174,6 +206,7 @@ router.beforeEach((to, from, next) => {
publicPages.filter((p) => to.path.startsWith(p)).length === 0;
const user = util.currentUser();

console.log('test', to.path, authRequired);
if (authRequired) {
// redirect to login page if not logged in and trying to access a restricted page
if (!user) {
@@ -184,5 +217,6 @@ router.beforeEach((to, from, next) => {
// redirect to home page if not admin and trying to access an admin page
if (to.path.startsWith('/admin') && user.admin !== true) return next('/');
}
console.log('next');
next();
});

+ 237
- 137
src/app/App.vue Переглянути файл

@@ -1,165 +1,265 @@
<!-- Copyright (c) 2020 Bubble, Inc. All rights reserved. For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ -->
<template>
<div v-if="isNewPage">
<router-view></router-view>
</div>
<div v-else>
<div v-if="!configs"><img :src="loadingImgSrc" /></div>

<div v-else class="jumbotron">
<totp-modal/>
<totp-modal />

<table v-if="this.user !== null && status.loggedIn && activated && path && path !== '' && path !== '/'" class="dash-icon-panel">
<tr class="icon-dash-row"><td>
<router-link to="/">
<span class="icon-dash-title"><i aria-hidden="true" :class="'icon-dash-app '+messages.label_menu_dashboard_icon" :title="messages.label_menu_dashboard"></i><br/>{{messages.label_menu_dashboard}}</span>
</router-link>
</td></tr>
<tr v-for="app in dashApps" class="icon-dash-row"><td>
<router-link :to="app.href">
<span class="icon-dash-title"><i aria-hidden="true" :class="'icon-dash-app '+app.icon" :title="app.title"></i><br/>{{app.title}}</span>
</router-link>
</td></tr>
</table>
<table
v-if="
this.user !== null &&
status.loggedIn &&
activated &&
path &&
path !== '' &&
path !== '/'
"
class="dash-icon-panel"
>
<tr class="icon-dash-row">
<td>
<router-link to="/">
<span class="icon-dash-title"
><i
aria-hidden="true"
:class="'icon-dash-app ' + messages.label_menu_dashboard_icon"
:title="messages.label_menu_dashboard"
></i
><br />{{ messages.label_menu_dashboard }}</span
>
</router-link>
</td>
</tr>
<tr v-for="app in dashApps" class="icon-dash-row">
<td>
<router-link :to="app.href">
<span class="icon-dash-title"
><i
aria-hidden="true"
:class="'icon-dash-app ' + app.icon"
:title="app.title"
></i
><br />{{ app.title }}</span
>
</router-link>
</td>
</tr>
</table>

<div class="container">
<div class="row">
<div class="col-sm-6 offset-sm-2">
<div v-if="alert.message" :class="`alert ${alert.type}`">{{alert.message}}</div>
<router-view></router-view>
</div>
<div class="container">
<div class="row">
<div class="col-sm-6 offset-sm-2">
<div v-if="alert.message" :class="`alert ${alert.type}`">
{{ alert.message }}
</div>
<router-view></router-view>
</div>
</div>
<div v-if="this.locales && this.locales.length > 1">
<hr/>
<button @click="toggleLocaleView()">{{messages.button_label_set_locale}}</button>
<div v-if="showLocaleSelector">
<div class="form-group">
<label for="locale">{{messages.field_label_locale}}</label>
<select @change="updateLocale()" v-model="selectedLocale" name="locale" class="form-control">
<option value="detect">{{messages['locale_detect']}}</option>
<option v-for="opt in this.locales" v-bind:value="opt">{{messages['locale_'+opt]}}</option>
</select>
<div v-if="errors.has('locale')" class="invalid-feedback">{{ errors.first('locale') }}</div>
</div>
</div>
<div v-if="this.locales && this.locales.length > 1">
<hr />
<button @click="toggleLocaleView()">
{{ messages.button_label_set_locale }}
</button>
<div v-if="showLocaleSelector">
<div class="form-group">
<label for="locale">{{ messages.field_label_locale }}</label>
<select
@change="updateLocale()"
v-model="selectedLocale"
name="locale"
class="form-control"
>
<option value="detect">{{ messages['locale_detect'] }}</option>
<option v-for="opt in this.locales" v-bind:value="opt">{{
messages['locale_' + opt]
}}</option>
</select>
<div v-if="errors.has('locale')" class="invalid-feedback">
{{ errors.first('locale') }}
</div>
</div>
</div>
<div class="bubble-footer">
<a target="_blank" rel="noopener noreferrer" href="https://getbubblenow.com/">getbubblenow.com</a> |
<router-link to="/legal">{{messages.title_legal_topics}}</router-link>
<b v-if="configs.support.site"> | <router-link to="/support">{{messages.title_support}}</router-link></b>
</div>
</div>
<div class="bubble-footer">
<a
target="_blank"
rel="noopener noreferrer"
href="https://getbubblenow.com/"
>getbubblenow.com</a
>
|
<router-link to="/legal">{{ messages.title_legal_topics }}</router-link>
<b v-if="configs.support.site">
|
<router-link to="/support">{{
messages.title_support
}}</router-link></b
>
</div>
</div>
</div>
</template>

<script>
import {mapState, mapActions, mapGetters} from 'vuex'
import { util } from '~/_helpers'
import { mapState, mapActions, mapGetters } from 'vuex';
import { util } from '~/_helpers';
import { loadingImgSrc } from '~/_store';

export default {
name: 'app',
data() {
return {
showLocaleSelector: false,
selectedLocale: 'detect',
loadingImgSrc: loadingImgSrc
name: 'app',
data() {
return {
showLocaleSelector: false,
selectedLocale: 'detect',
loadingImgSrc: loadingImgSrc,
};
},
computed: {
...mapState('account', ['status', 'user', 'locale', 'registrationError']),
...mapState('system', [
'activated',
'configs',
'messages',
'messageGroupsLoaded',
]),
...mapState({
alert: (state) => state.alert,
}),
...mapGetters('system', ['dashboardApps']),
queryApp() {
if (
typeof this.$route.query.app !== 'undefined' &&
this.$route.query.app !== null &&
this.$route.query.app !== ''
) {
return this.$route.query.app;
}
return null;
},
dashApps() {
let appView = this.dashboardApps;
const qApp = this.queryApp;
if (qApp !== null) {
const appPath = this.$route.query.app;
for (let i = 0; i < appView.length; i++) {
const app = appView[i];
if (app.href === '/?app=' + appPath) {
appView = app.apps;
break;
}
}
}
return appView;
},
computed: {
...mapState('account', ['status', 'user', 'locale', 'registrationError']),
...mapState('system', ['activated', 'configs', 'messages', 'messageGroupsLoaded']),
...mapState({
alert: state => state.alert
}),
...mapGetters('system', ['dashboardApps']),
queryApp () {
if (typeof this.$route.query.app !== 'undefined' && this.$route.query.app !== null && this.$route.query.app !== '') {
return this.$route.query.app;
}
return null;
},
dashApps () {
let appView = this.dashboardApps;
const qApp = this.queryApp;
if (qApp !== null) {
const appPath = this.$route.query.app;
for (let i=0; i<appView.length; i++) {
const app = appView[i];
if (app.href === '/?app='+appPath) {
appView = app.apps;
break;
}
}
}
return appView;
},
locales () { return this.configs.locales; },
path () { return this.$route.path; },
isInRestoringMode () { return this.configs ? this.configs.awaitingRestore : undefined; }
locales() {
return this.configs.locales;
},
methods: {
...mapActions({ clearAlert: 'alert/clear' }),
...mapActions('account', ['setLocale', 'checkSession', 'logout']),
...mapActions('system', ['loadIsActivated', 'loadSystemConfigs', 'loadMessages', 'loadTimezones']),
toggleLocaleView() { this.showLocaleSelector = !this.showLocaleSelector; },
updateLocale() {
if (this.selectedLocale) {
this.setLocale({locale: this.selectedLocale, messages: this.messages, errors: this.errors});
}
},
reloadMessages() {
if (this.selectedLocale) {
for (let i = 0; i < this.messageGroupsLoaded.length; i++) {
this.loadMessages(this.messageGroupsLoaded[i], this.selectedLocale);
}
}
}
path() {
return this.$route.path;
},
isInRestoringMode() {
return this.configs ? this.configs.awaitingRestore : undefined;
},
isNewPage() {
const newPages = ['/new_pages'];
console.log(this.$route);
return (
newPages.includes(this.$route.path) ||
newPages.filter((p) => this.$route.path.startsWith(p)).length
);
},
},
methods: {
...mapActions({ clearAlert: 'alert/clear' }),
...mapActions('account', ['setLocale', 'checkSession', 'logout']),
...mapActions('system', [
'loadIsActivated',
'loadSystemConfigs',
'loadMessages',
'loadTimezones',
]),
toggleLocaleView() {
this.showLocaleSelector = !this.showLocaleSelector;
},
updateLocale() {
if (this.selectedLocale) {
this.setLocale({
locale: this.selectedLocale,
messages: this.messages,
errors: this.errors,
});
}
},
watch: {
$route (to, from){
// clear alert on location change
this.clearAlert();
},
activated (active) {
if (!active) {
this.$router.replace('/activate');
} else {
const user = util.currentUser();
if (user !== null && (typeof user.token !== 'undefined' && user.token !== null)) {
if (this.status.loggingIn || this.status.loggedIn) {
// console.log('activated: this.status.loggingIn='+this.status.loggingIn+', this.status.loggedIn='+this.status.loggedIn+', not checking session');
} else {
this.checkSession({messages: this.messages, errors: this.errors});
}
}
}
},
user (u) {
if (typeof u !== 'undefined' && u !== null) {
this.selectedLocale = u.locale;
this.reloadMessages();
this.loadMessages('post_auth', this.selectedLocale);
}
},
locale (loc) {
this.selectedLocale = loc;
this.reloadMessages()
},
isInRestoringMode (restoringMode) {
if (restoringMode === true && this.path !== '/restore') this.$router.replace('/restore');
reloadMessages() {
if (this.selectedLocale) {
for (let i = 0; i < this.messageGroupsLoaded.length; i++) {
this.loadMessages(this.messageGroupsLoaded[i], this.selectedLocale);
}
}
},
created() {
},
watch: {
$route(to, from) {
// clear alert on location change
this.clearAlert();
},
activated(active) {
if (!active) {
this.$router.replace('/activate');
} else {
const user = util.currentUser();
this.selectedLocale = (user !== null && typeof user.locale !== 'undefined' && user.locale !== null ? user.locale : 'detect');
this.loadIsActivated();
this.loadSystemConfigs();
this.loadTimezones();

this.loadMessages('pre_auth', this.selectedLocale);
this.loadMessages('countries', this.selectedLocale);
this.loadMessages('timezones', this.selectedLocale);
if (util.userLoggedIn()) {
this.loadMessages('post_auth', this.selectedLocale);
this.loadMessages('apps', this.selectedLocale);
if (
user !== null &&
(typeof user.token !== 'undefined' && user.token !== null)
) {
if (this.status.loggingIn || this.status.loggedIn) {
// console.log('activated: this.status.loggingIn='+this.status.loggingIn+', this.status.loggedIn='+this.status.loggedIn+', not checking session');
} else {
this.checkSession({ messages: this.messages, errors: this.errors });
}
}
}
},
user(u) {
if (typeof u !== 'undefined' && u !== null) {
this.selectedLocale = u.locale;
this.reloadMessages();
this.loadMessages('post_auth', this.selectedLocale);
}
},
locale(loc) {
this.selectedLocale = loc;
this.reloadMessages();
},
isInRestoringMode(restoringMode) {
if (restoringMode === true && this.path !== '/restore')
this.$router.replace('/restore');
},
},
created() {
const user = util.currentUser();
this.selectedLocale =
user !== null &&
typeof user.locale !== 'undefined' &&
user.locale !== null
? user.locale
: 'detect';
this.loadIsActivated();
this.loadSystemConfigs();
this.loadTimezones();

this.loadMessages('pre_auth', this.selectedLocale);
this.loadMessages('countries', this.selectedLocale);
this.loadMessages('timezones', this.selectedLocale);
if (util.userLoggedIn()) {
this.loadMessages('post_auth', this.selectedLocale);
this.loadMessages('apps', this.selectedLocale);
}
},
};
</script>
</script>

+ 4
- 0
webpack.config.js Переглянути файл

@@ -52,6 +52,10 @@ module.exports = {
],
devServer: {
historyApiFallback: true,
publicPath: '/',
},
output: {
publicPath: '/',
},
externals: {
// global app config object


Завантаження…
Відмінити
Зберегти