diff options
| author | Zephyrrus <[email protected]> | 2020-07-02 02:50:55 +0300 |
|---|---|---|
| committer | Zephyrrus <[email protected]> | 2020-07-02 02:50:55 +0300 |
| commit | 720ffaf0083564c85a07d66a6d303f34706add41 (patch) | |
| tree | 033405206c4dd981b32a76ba5033bec3692e8610 /src | |
| parent | chore: add compound launcher and basic prettier (diff) | |
| download | host.fuwn.me-720ffaf0083564c85a07d66a6d303f34706add41.tar.xz host.fuwn.me-720ffaf0083564c85a07d66a6d303f34706add41.zip | |
feat: start refactoring the code to actually use vuex
This includes creating multiple stores as needed for components and removing all complex states from components (since all those states should be stored in vuex)
Diffstat (limited to 'src')
| -rw-r--r-- | src/site/components/navbar/Navbar.vue | 10 | ||||
| -rw-r--r-- | src/site/layouts/default.vue | 66 | ||||
| -rw-r--r-- | src/site/middleware/admin.js | 4 | ||||
| -rw-r--r-- | src/site/middleware/auth.js | 2 | ||||
| -rw-r--r-- | src/site/pages/dashboard/index.vue | 124 | ||||
| -rw-r--r-- | src/site/pages/index.vue | 10 | ||||
| -rw-r--r-- | src/site/pages/login.vue | 39 | ||||
| -rw-r--r-- | src/site/pages/register.vue | 10 | ||||
| -rw-r--r-- | src/site/plugins/axios.js | 9 | ||||
| -rw-r--r-- | src/site/store/alert.js | 26 | ||||
| -rw-r--r-- | src/site/store/auth.js | 61 | ||||
| -rw-r--r-- | src/site/store/config.js | 19 | ||||
| -rw-r--r-- | src/site/store/images.js | 56 | ||||
| -rw-r--r-- | src/site/store/index.js | 62 |
14 files changed, 317 insertions, 181 deletions
diff --git a/src/site/components/navbar/Navbar.vue b/src/site/components/navbar/Navbar.vue index 47f90cb..aa4a672 100644 --- a/src/site/components/navbar/Navbar.vue +++ b/src/site/components/navbar/Navbar.vue @@ -65,6 +65,8 @@ </template> <script> +import { mapState, mapGetters } from 'vuex'; + export default { props: { isWhite: { @@ -76,12 +78,8 @@ export default { return { hamburger: false }; }, computed: { - loggedIn() { - return this.$store.state.loggedIn; - }, - config() { - return this.$store.state.config; - } + ...mapGetters({ loggedIn: 'auth/isLoggedIn' }), + ...mapState(['config']) }, methods: { logOut() { diff --git a/src/site/layouts/default.vue b/src/site/layouts/default.vue index 61a257e..08d733f 100644 --- a/src/site/layouts/default.vue +++ b/src/site/layouts/default.vue @@ -12,22 +12,18 @@ <script> import Navbar from '~/components/navbar/Navbar.vue'; import Footer from '~/components/footer/Footer'; +import { mapState } from 'vuex'; + export default { components: { - Navbar, + Navbar, Footer }, - computed: { - config() { - return this.$store.state.config; - }, - alert() { - return this.$store.state.alert; - } - }, + computed: mapState(['config', 'alert']), watch: { - alert() { - if (!this.alert) return; + 'alert.text'() { + console.log('aaaaaaaa'); + if (!this.alert.text) return; this.$buefy.toast.open({ duration: 3500, @@ -36,34 +32,40 @@ export default { type: this.alert.error ? 'is-danger' : 'is-success' }); - setTimeout(() => { - this.$store.dispatch('alert', null); - }, 3500); + this.$store.dispatch('alert/clear', null); } }, mounted() { - console.log(`%c lolisafe %c v${this.config.version} %c`, 'background:#35495e ; padding: 1px; border-radius: 3px 0 0 3px; color: #fff', 'background:#ff015b; padding: 1px; border-radius: 0 3px 3px 0; color: #fff', 'background:transparent'); + console.log( + `%c lolisafe %c v${this.config.version} %c`, + 'background:#35495e ; padding: 1px; border-radius: 3px 0 0 3px; color: #fff', + 'background:#ff015b; padding: 1px; border-radius: 0 3px 3px 0; color: #fff', + 'background:transparent' + ); } }; </script> + <style lang="scss"> - html { overflow: hidden !important; } - .is-fullheight { - min-height: 100vh !important; - height: max-content; - } - .nuxt-app > .section { - min-height: auto !important; - height: auto !important; - } - @import "~/assets/styles/style.scss"; - @import "~/assets/styles/icons.min.css"; +html { + overflow: hidden !important; +} +.is-fullheight { + min-height: 100vh !important; + height: max-content; +} +.nuxt-app > .section { + min-height: auto !important; + height: auto !important; +} +@import '~/assets/styles/style.scss'; +@import '~/assets/styles/icons.min.css'; </style> <style lang="scss" scoped> - .default-body { - align-items: baseline !important; - } - .scroll-area { - height: 100vh; - } +.default-body { + align-items: baseline !important; +} +.scroll-area { + height: 100vh; +} </style> diff --git a/src/site/middleware/admin.js b/src/site/middleware/admin.js index fcac9c6..5c09220 100644 --- a/src/site/middleware/admin.js +++ b/src/site/middleware/admin.js @@ -1,5 +1,5 @@ export default function({ store, redirect }) { // If the user is not authenticated - if (!store.state.user) return redirect('/login'); - if (!store.state.user.isAdmin) return redirect('/dashboard'); + if (!store.state.auth.user) return redirect('/login'); + if (!store.state.auth.user.isAdmin) return redirect('/dashboard'); } diff --git a/src/site/middleware/auth.js b/src/site/middleware/auth.js index 58a372e..c3f339c 100644 --- a/src/site/middleware/auth.js +++ b/src/site/middleware/auth.js @@ -1,6 +1,6 @@ export default function({ store, redirect }) { // If the user is not authenticated - if (!store.state.loggedIn) { + if (!store.state.auth.loggedIn) { return redirect('/login'); } } diff --git a/src/site/pages/dashboard/index.vue b/src/site/pages/dashboard/index.vue index 0eb9532..6c1b99b 100644 --- a/src/site/pages/dashboard/index.vue +++ b/src/site/pages/dashboard/index.vue @@ -6,27 +6,55 @@ <Sidebar /> </div> <div class="column"> - <h2 class="subtitle">Your uploaded files</h2> + <nav class="level"> + <div class="level-left"> + <div class="level-item"> + <h2 class="subtitle">Your uploaded files</h2> + </div> + </div> + <div class="level-right"> + <div class="level-item"> + <b-field> + <b-input + placeholder="Search" + type="search"/> + <p class="control"> + <button + outlined + class="button is-primary"> + Search + </button> + </p> + </b-field> + </div> + </div> + </nav> <hr> - <Grid v-if="count" - :files="files" - :enableSearch="false" - class="grid" /> + <b-loading :active="images.isLoading" /> - <b-pagination - v-if="count > perPage" - :total="count" - :per-page="perPage" - :current.sync="current" - class="pagination" - icon-prev="icon-interface-arrow-left" - icon-next="icon-interface-arrow-right" - icon-pack="icon" - aria-next-label="Next page" - aria-previous-label="Previous page" - aria-page-label="Page" - aria-current-label="Current page" /> + <Grid v-if="totalFiles" + :files="images.files" + :enableSearch="false" + class="grid"> + <template v-slot:pagination> + <b-pagination + v-if="shouldPaginate" + :total="totalFiles" + :per-page="limit" + :current.sync="current" + range-before="2" + range-after="2" + class="pagination-slot" + icon-prev="icon-interface-arrow-left" + icon-next="icon-interface-arrow-right" + icon-pack="icon" + aria-next-label="Next page" + aria-previous-label="Previous page" + aria-page-label="Page" + aria-current-label="Current page" /> + </template> + </Grid> </div> </div> </div> @@ -34,6 +62,8 @@ </template> <script> +import { mapState, mapGetters, mapActions } from 'vuex'; + import Sidebar from '~/components/sidebar/Sidebar.vue'; import Grid from '~/components/grid/Grid.vue'; @@ -42,44 +72,34 @@ export default { Sidebar, Grid }, - middleware: 'auth', + middleware: ['auth', ({ store }) => { + store.dispatch('images/fetch'); + }], data() { return { - files: [], - count: 0, - current: 1, - perPage: 30 + current: 1 }; }, + computed: { + ...mapGetters({ + totalFiles: 'images/getTotalFiles', + shouldPaginate: 'images/shouldPaginate', + limit: 'images/getLimit' + }), + ...mapState(['images']) + }, metaInfo() { return { title: 'Uploads' }; }, watch: { - current: 'getFiles' - }, - async asyncData({ $axios, route }) { - const perPage = 30; - const current = 1; // current page - - try { - const response = await $axios.$get(`files`, { params: { page: current, limit: perPage }}); - return { - files: response.files || [], - count: response.count || 0, - current, - perPage - }; - } catch (error) { - console.error(error); - return { files: [] }; - } + current: 'fetchPaginate' }, methods: { - async getFiles() { - // TODO: Cache a few pages once fetched - const response = await this.$axios.$get(`files`, { params: { page: this.current, limit: this.perPage }}); - this.files = response.files; - this.count = response.count; + ...mapActions({ + fetch: 'images/fetch' + }), + fetchPaginate() { + this.fetch(this.current) } } }; @@ -89,4 +109,14 @@ export default { div.grid { margin-bottom: 1rem; } -</style>
\ No newline at end of file + + .pagination-slot { + padding: 1rem 0; + } +</style> + +<style lang="scss"> + .pagination-slot > .pagination-previous, .pagination-slot > .pagination-next { + display: none !important; + } +</style> diff --git a/src/site/pages/index.vue b/src/site/pages/index.vue index 707ae67..bb35be3 100644 --- a/src/site/pages/index.vue +++ b/src/site/pages/index.vue @@ -28,6 +28,8 @@ </div> </template> <script> +import { mapState, mapGetters } from 'vuex'; + import Logo from '~/components/logo/Logo.vue'; import Uploader from '~/components/uploader/Uploader.vue'; import Links from '~/components/home/links/Links.vue'; @@ -43,12 +45,8 @@ export default { return { albums: [] }; }, computed: { - loggedIn() { - return this.$store.state.loggedIn; - }, - config() { - return this.$store.state.config; - } + ...mapGetters({ loggedIn: 'auth/isLoggedIn' }), + ...mapState(['config']) } }; </script> diff --git a/src/site/pages/login.vue b/src/site/pages/login.vue index 514cbc5..3c43755 100644 --- a/src/site/pages/login.vue +++ b/src/site/pages/login.vue @@ -63,6 +63,8 @@ </template> <script> +import { mapState } from 'vuex'; + export default { name: 'Login', data() { @@ -74,40 +76,31 @@ export default { isLoading: false }; }, - computed: { - config() { - return this.$store.state.config; - } - }, + computed: mapState(['config', 'auth']), metaInfo() { return { title: 'Login' }; }, + created() { + if (this.auth.loggedIn) { + this.redirect(); + } + }, methods: { async login() { - if (this.isLoading) return; - if (!this.username || !this.password) { - this.$store.dispatch('alert', { + if (this.auth.isLoading) return; + + const { username, password } = this; + if (!username || !password) { + this.$store.dispatch('alert/set', { text: 'Please fill both fields before attempting to log in.', error: true }); return; } - this.isLoading = true; - - try { - const data = await this.$axios.$post(`auth/login`, { - username: this.username, - password: this.password - }); - this.$axios.setToken(data.token, 'Bearer'); - document.cookie = `token=${encodeURIComponent(data.token)}`; - this.$store.dispatch('login', { token: data.token, user: data.user }); + await this.$store.dispatch('auth/login', { username, password }); + if (this.auth.loggedIn) { this.redirect(); - } catch (error) { - // - } finally { - this.isLoading = false; } }, /* @@ -126,7 +119,7 @@ export default { }); },*/ redirect() { - this.$store.commit('loggedIn', true); + console.log('redirect'); if (typeof this.$route.query.redirect !== 'undefined') { this.$router.push(this.$route.query.redirect); return; diff --git a/src/site/pages/register.vue b/src/site/pages/register.vue index c102abd..92eb35a 100644 --- a/src/site/pages/register.vue +++ b/src/site/pages/register.vue @@ -42,6 +42,8 @@ </template> <script> +import { mapState } from 'vuex'; + export default { name: 'Register', data() { @@ -52,11 +54,7 @@ export default { isLoading: false }; }, - computed: { - config() { - return this.$store.state.config; - } - }, + computed: mapState(['config', 'auth']), metaInfo() { return { title: 'Register' }; }, @@ -72,7 +70,7 @@ export default { } if (this.password !== this.rePassword) { this.$store.dispatch('alert', { - text: 'Passwords don\'t match', + text: "Passwords don't match", error: true }); return; diff --git a/src/site/plugins/axios.js b/src/site/plugins/axios.js index 843a258..cff149c 100644 --- a/src/site/plugins/axios.js +++ b/src/site/plugins/axios.js @@ -1,21 +1,22 @@ export default function({ $axios, store }) { $axios.setHeader('accept', 'application/vnd.lolisafe.json'); + $axios.onRequest(config => { - if (store.state.token) { - config.headers.common['Authorization'] = `bearer ${store.state.token}`; + if (store.state.auth.token) { + config.headers.common['Authorization'] = `bearer ${store.state.auth.token}`; } }); $axios.onError(error => { if (process.env.development) console.error('[AXIOS Error]', error); if (process.browser) { - store.dispatch('alert', { + store.dispatch('alert/set', { text: error.response.data.message, error: true }); if (error.response.data.message.indexOf('Token expired') !== -1) { - store.dispatch('logout'); + store.dispatch('auth/logout'); } } }); diff --git a/src/site/store/alert.js b/src/site/store/alert.js new file mode 100644 index 0000000..78c0eaf --- /dev/null +++ b/src/site/store/alert.js @@ -0,0 +1,26 @@ +/* eslint-disable no-shadow */ +const getDefaultState = () => ({ + text: null, + error: false +}); + +export const state = getDefaultState; + +export const actions = { + set({ commit }, data) { + commit('set', data); + }, + clear({ commit }) { + commit('clear'); + } +}; + +export const mutations = { + set(state, { text, error }) { + state.text = text; + state.error = error; + }, + clear(state) { + Object.assign(state, getDefaultState()); + } +}; diff --git a/src/site/store/auth.js b/src/site/store/auth.js new file mode 100644 index 0000000..a62a6ec --- /dev/null +++ b/src/site/store/auth.js @@ -0,0 +1,61 @@ +/* eslint-disable no-shadow */ +// only used so I could keep the convention of naming the first param as "state" in mutations +const getDefaultState = () => ({ + loggedIn: false, + isLoading: false, + user: null, + token: null +}); + +export const state = getDefaultState; + +export const getters = { + isLoggedIn: state => state.loggedIn +}; + +export const actions = { + async verify({ commit, dispatch }) { + try { + const response = await this.$axios.$get('verify'); + commit('loginSuccess', response); + } catch (e) { + dispatch('alert/set', { text: e.message, error: true }, { root: true }); + } + }, + async login({ commit, dispatch }, { username, password }) { + commit('loginRequest'); + + try { + const data = await this.$axios.$post(`auth/login`, { username, password }); + this.$axios.setToken(data.token, 'Bearer'); + + commit('setToken', data.token); + commit('loginSuccess', { token: data.token, user: data.user }); + } catch (e) { + dispatch('alert/set', { text: e.message, error: true }, { root: true }); + } + }, + logout({ commit }) { + commit('logout'); + } +}; + +export const mutations = { + setToken(state, token) { + state.token = token; + }, + loginRequest(state) { + state.isLoading = true; + }, + loginSuccess(state, { user }) { + this.$cookies.set('token', state.token); + state.user = user; + state.loggedIn = true; + state.isLoading = false; + }, + logout(state) { + this.$cookies.remove('token'); + // reset state to default + Object.assign(state, getDefaultState()); + } +}; diff --git a/src/site/store/config.js b/src/site/store/config.js new file mode 100644 index 0000000..6202f84 --- /dev/null +++ b/src/site/store/config.js @@ -0,0 +1,19 @@ +/* eslint-disable no-shadow */ +export const state = () => ({ + development: true, + version: '4.0.0', + URL: 'http://localhost:8080', + baseURL: 'http://localhost:8080/api', + serviceName: '', + maxFileSize: 100, + chunkSize: 90, + maxLinksPerAlbum: 5, + publicMode: false, + userAccounts: false +}); + +export const mutations = { + set(state, config) { + Object.assign(state, config); + } +}; diff --git a/src/site/store/images.js b/src/site/store/images.js new file mode 100644 index 0000000..e87dac1 --- /dev/null +++ b/src/site/store/images.js @@ -0,0 +1,56 @@ +/* eslint-disable no-shadow */ +export const state = () => ({ + files: [], + isLoading: false, + pagination: { + page: 1, + limit: 30, + totalFiles: 0 + } +}); + +export const getters = { + getTotalFiles: state => state.pagination.totalFiles, + getFetchedCount: state => state.files.length, + shouldPaginate: ({ pagination }) => pagination.totalFiles > pagination.limit, + getLimit: ({ pagination }) => pagination.limit +}; + +export const actions = { + async fetch({ commit, dispatch, state }, page) { + commit('setIsLoading'); + + page = page || 1; + + try { + const response = await this.$axios.$get(`files`, { params: { limit: state.pagination.limit, page } }); + + commit('updateFiles', { files: response.files }); + commit('updatePaginationMeta', { totalFiles: response.count, page }); + } catch (e) { + dispatch('alert/set', { text: e.message, error: true }, { root: true }); + } + }, + async fetchById({ commit, dispatch }) { + try { + const response = await this.$axios.$get('verify'); + commit('loginSuccess', response); + } catch (e) { + dispatch('alert/set', { text: e.message, error: true }, { root: true }); + } + } +}; + +export const mutations = { + setIsLoading(state) { + state.isLoading = true; + }, + updateFiles(state, { files }) { + state.files = files || []; + state.isLoading = false; + }, + updatePaginationMeta(state, { page, totalFiles }) { + state.pagination.page = page || 1; + state.pagination.totalFiles = totalFiles || 0; + } +}; diff --git a/src/site/store/index.js b/src/site/store/index.js index 1fc2272..8f910ae 100644 --- a/src/site/store/index.js +++ b/src/site/store/index.js @@ -1,66 +1,20 @@ import config from '../../../dist/config.json'; -export const state = () => ({ - loggedIn: false, - user: null, - token: null, - config: null, - alert: null -}); - -/* eslint-disable no-shadow */ -export const mutations = { - loggedIn(state, payload) { - state.loggedIn = payload; - }, - user(state, payload) { - state.user = payload; - }, - token(state, payload) { - state.token = payload; - }, - config(state, payload) { - state.config = payload; - }, - alert(state, payload) { - state.alert = payload; - } -}; export const actions = { - async nuxtClientInit({ commit, dispatch }, { app, req }) { - commit('config', config); + async nuxtClientInit({ commit, dispatch }) { + commit('config/set', config); const cookies = this.$cookies.getAll(); - if (!cookies.token) return dispatch('logout'); + if (!cookies.token) return dispatch('auth/logout'); - commit('token', cookies.token); - try { - const response = await this.$axios.$get('verify'); - dispatch('login', { - token: cookies.token, - user: response.user - }); - } catch (error) { - // dispatch('logout'); - } - }, - login({ commit }, { token, user }) { - this.$cookies.set('token', token); - commit('token', token); - commit('user', user); - commit('loggedIn', true); - }, - logout({ commit }) { - this.$cookies.remove('token'); - commit('token', null); - commit('user', null); - commit('loggedIn', false); - }, - alert({ commit }, payload) { + commit('auth/setToken', cookies.token); + await dispatch('auth/verify'); + } + /* alert({ commit }, payload) { if (!payload) return commit('alert', null); commit('alert', { text: payload.text, error: payload.error }); - } + } */ }; |