diff options
| author | Zephyrrus <[email protected]> | 2020-07-05 04:18:08 +0300 |
|---|---|---|
| committer | Zephyrrus <[email protected]> | 2020-07-05 04:18:08 +0300 |
| commit | 766e74cc51138b32482f65f0f2647eb9d943103e (patch) | |
| tree | d675cdaa4dc4a89356009131707b11b511069494 | |
| parent | feat: refactor single album page to use vuex (diff) | |
| download | host.fuwn.me-766e74cc51138b32482f65f0f2647eb9d943103e.tar.xz host.fuwn.me-766e74cc51138b32482f65f0f2647eb9d943103e.zip | |
feat: add video preview on hover to dashboard and apply new linter rules to some of the files
| -rw-r--r-- | src/site/components/footer/Footer.vue | 62 | ||||
| -rw-r--r-- | src/site/components/grid/Grid.vue | 435 | ||||
| -rw-r--r-- | src/site/components/grid/waterfall/Waterfall.vue | 56 | ||||
| -rw-r--r-- | src/site/components/grid/waterfall/WaterfallItem.vue | 29 | ||||
| -rw-r--r-- | src/site/constants/alertTypes.js | 10 | ||||
| -rw-r--r-- | src/site/plugins/notifier.js | 7 | ||||
| -rw-r--r-- | src/site/store/auth.js | 20 | ||||
| -rw-r--r-- | src/site/store/images.js | 4 |
8 files changed, 342 insertions, 281 deletions
diff --git a/src/site/components/footer/Footer.vue b/src/site/components/footer/Footer.vue index 44e34f4..19d18f2 100644 --- a/src/site/components/footer/Footer.vue +++ b/src/site/components/footer/Footer.vue @@ -1,12 +1,18 @@ <template> + <!-- eslint-disable max-len --> <footer> - <svg viewBox="0 0 1920 250" + <svg + viewBox="0 0 1920 250" class="waves"> - <path d="M1920 250H0V0s126.707 78.536 349.975 80.05c177.852 1.203 362.805-63.874 553.803-63.874 290.517 0 383.458 57.712 603.992 61.408 220.527 3.696 278.059-61.408 412.23-17.239" + + <path + d="M1920 250H0V0s126.707 78.536 349.975 80.05c177.852 1.203 362.805-63.874 553.803-63.874 290.517 0 383.458 57.712 603.992 61.408 220.527 3.696 278.059-61.408 412.23-17.239" class="wave-1" /> - <path d="M1920 144s-467.917 116.857-1027.243-17.294C369.986 1.322 0 45.578 0 45.578V250h1920V144z" + <path + d="M1920 144s-467.917 116.857-1027.243-17.294C369.986 1.322 0 45.578 0 45.578V250h1920V144z" class="wave-2" /> - <path d="M0 195.553s208.547-75.581 701.325-20.768c376.707 41.908 520.834-67.962 722.545-67.962 222.926 0 311.553 83.523 496.129 86.394V250H0v-54.447z" + <path + d="M0 195.553s208.547-75.581 701.325-20.768c376.707 41.908 520.834-67.962 722.545-67.962 222.926 0 311.553 83.523 496.129 86.394V250H0v-54.447z" class="wave-3" /> </svg> <div> @@ -15,7 +21,8 @@ <div class="column is-narrow"> <h4>lolisafe</h4> <span>© 2017-2020 - <a href="https://github.com/pitu" + <a + href="https://github.com/pitu" class="no-block">Pitu</a> </span><br> <span>v{{ version }}</span> @@ -24,20 +31,33 @@ <div class="columns is-gapless"> <div class="column" /> <div class="column"> - <nuxt-link to="/">Home</nuxt-link> - <nuxt-link to="/faq">FAQ</nuxt-link> + <nuxt-link to="/"> + Home + </nuxt-link> + <nuxt-link to="/faq"> + FAQ + </nuxt-link> </div> <div class="column"> - <nuxt-link to="/dashboard">Dashboard</nuxt-link> - <nuxt-link to="/dashboard">Files</nuxt-link> - <nuxt-link to="/dashboard/albums">Albums</nuxt-link> - <nuxt-link to="/dashboard/account">Account</nuxt-link> + <nuxt-link to="/dashboard"> + Dashboard + </nuxt-link> + <nuxt-link to="/dashboard"> + Files + </nuxt-link> + <nuxt-link to="/dashboard/albums"> + Albums + </nuxt-link> + <nuxt-link to="/dashboard/account"> + Account + </nuxt-link> </div> <div class="column"> <a href="https://github.com/weebdev/lolisafe">GitHub</a> </div> <div class="column"> - <a v-if="loggedIn" + <a + v-if="loggedIn" @click="createShareXThing">ShareX Config</a> <a href="https://chrome.google.com/webstore/detail/lolisafe-uploader/enkkmplljfjppcdaancckgilmgoiofnj">Chrome Extension</a> </div> @@ -48,7 +68,10 @@ </div> </footer> </template> + <script> +/* eslint-disable no-restricted-globals */ + import { mapState, mapGetters } from 'vuex'; import { saveAs } from 'file-saver'; @@ -56,19 +79,21 @@ export default { computed: { ...mapGetters({ loggedIn: 'auth/isLoggedIn' }), ...mapState({ - version: state => state.config.version - }) + version: (state) => state.config.version, + serviceName: (state) => state.config.serviceName, + token: (state) => state.auth.token, + }), }, methods: { createShareXThing() { const sharexFile = `{ - "Name": "${this.$store.state.config.serviceName}", + "Name": "${this.serviceName}", "DestinationType": "ImageUploader, FileUploader", "RequestType": "POST", "RequestURL": "${location.origin}/api/upload", "FileFormName": "files[]", "Headers": { - "authorization": "Bearer ${this.$store.state.token}", + "authorization": "Bearer ${this.token}", "accept": "application/vnd.lolisafe.json" }, "ResponseType": "Text", @@ -77,10 +102,11 @@ export default { }`; const sharexBlob = new Blob([sharexFile], { type: 'application/octet-binary' }); saveAs(sharexBlob, `${location.hostname}.sxcu`); - } - } + }, + }, }; </script> + <style lang="scss" scoped> @import '~/assets/styles/_colors.scss'; footer { diff --git a/src/site/components/grid/Grid.vue b/src/site/components/grid/Grid.vue index 0fff307..d4fe067 100644 --- a/src/site/components/grid/Grid.vue +++ b/src/site/components/grid/Grid.vue @@ -6,18 +6,13 @@ <slot name="pagination" /> </div> </div> - <div v-if="enableToolbar" - class="level-right toolbar"> + <div v-if="enableToolbar" class="level-right toolbar"> <div class="level-item"> <div class="block"> - <b-radio v-model="showList" - name="name" - :native-value="true"> + <b-radio v-model="showList" name="name" :native-value="true"> List </b-radio> - <b-radio v-model="showList" - name="name" - :native-value="false"> + <b-radio v-model="showList" name="name" :native-value="false"> Grid </b-radio> </div> @@ -26,66 +21,71 @@ </nav> <template v-if="!showList"> - <Waterfall v-if="showWaterfall" + <Waterfall + v-if="showWaterfall" :gutterWidth="10" :gutterHeight="4"> - <WaterfallItem v-for="(item, index) in gridFiles" + <WaterfallItem + v-for="(item, index) in gridFiles" :key="item.id" :width="width" - :video="!!item.preview" move-class="item-move"> <template v-if="isPublic"> - <a :href="`${item.url}`" - target="_blank"> + <a + :href="`${item.url}`" + class="preview-container" + target="_blank" + @mouseenter.self.stop.prevent="item.preview && mouseOver(item.id)" + @mouseleave.self.stop.prevent="item.preview && mouseOut(item.id)"> + <img :src="item.thumb ? item.thumb : blank"> - <span v-if="!item.thumb && item.name" - class="extension">{{ item.name.split('.').pop() }}</span> + <div v-if="item.preview && isHovered(item.id)" class="preview"> + <video ref="video" class="preview" autoplay loop muted> + <source :src="item.preview" type="video/mp4"> + </video> + </div> + + <span v-if="!item.thumb && item.name" class="extension">{{ + item.name.split('.').pop() + }}</span> </a> </template> <template v-else> - <img v-if="!item.preview" :class="{'hidden': item.preview}" - :src="item.thumb ? item.thumb : blank"> - <video v-if="item.preview" autoplay loop> - <source :src="item.preview" type="video/mp4" /> - </video> - <span v-if="!item.thumb && item.name" - class="extension">{{ item.name.split('.').pop() }}</span> - <div v-if="!isPublic" + <img :src="item.thumb ? item.thumb : blank"> + <div v-if="item.preview && isHovered(item.id)" class="preview"> + <video ref="video" class="preview" autoplay loop muted> + <source :src="item.preview" type="video/mp4"> + </video> + </div> + + <span v-if="!item.thumb && item.name" class="extension">{{ item.name.split('.').pop() }}</span> + <div + v-if="!isPublic" :class="{ fixed }" - class="actions"> - <b-tooltip label="Link" - position="is-top"> - <a :href="`${item.url}`" - target="_blank" - class="btn"> + class="actions" + @mouseenter.self.stop.prevent="item.preview && mouseOver(item.id)" + @mouseleave.self.stop.prevent="item.preview && mouseOut(item.id)"> + <b-tooltip label="Link" position="is-top"> + <a :href="`${item.url}`" target="_blank" class="btn"> <i class="icon-web-code" /> </a> </b-tooltip> - <b-tooltip label="Tags" - position="is-top"> - <a class="btn" - @click="manageTags(item)"> + <b-tooltip label="Tags" position="is-top"> + <a class="btn" @click="manageTags(item)"> <i class="icon-ecommerce-tag-c" /> </a> </b-tooltip> - <b-tooltip label="Albums" - position="is-top"> - <a class="btn" - @click="openAlbumModal(item)"> + <b-tooltip label="Albums" position="is-top"> + <a class="btn" @click="openAlbumModal(item)"> <i class="icon-interface-window" /> </a> - </b-tooltip> - <b-tooltip label="Delete" - position="is-top"> - <a class="btn" - @click="deleteFile(item, index)"> + </b-tooltip> + <b-tooltip label="Delete" position="is-top"> + <a class="btn" @click="deleteFile(item, index)"> <i class="icon-editorial-trash-a-l" /> </a> </b-tooltip> - <b-tooltip v-if="user && user.isAdmin" - label="More info" - position="is-top" - class="more"> + <b-tooltip v-if="user && user.isAdmin" label="More info" position="is-top" class="more"> <nuxt-link :to="`/dashboard/admin/file/${item.id}`"> <i class="icon-interface-more" /> </nuxt-link> @@ -96,58 +96,42 @@ </Waterfall> </template> <div v-else> - <b-table - :data="gridFiles || []" - :mobile-cards="true"> + <b-table :data="gridFiles || []" :mobile-cards="true"> <template slot-scope="props"> <template v-if="!props.row.hideFromList"> - <b-table-column field="url" - label="URL"> - <a :href="props.row.url" - target="_blank">{{ props.row.url }}</a> + <b-table-column field="url" label="URL"> + <a :href="props.row.url" target="_blank">{{ props.row.url }}</a> </b-table-column> - <b-table-column field="albums" - label="Albums" - centered> + <b-table-column field="albums" label="Albums" centered> <template v-for="(album, index) in props.row.albums"> - <nuxt-link :key="index" - :to="`/dashboard/albums/${album.id}`"> + <nuxt-link :key="index" :to="`/dashboard/albums/${album.id}`"> {{ album.name }} </nuxt-link> - <template v-if="index < props.row.albums.length - 1">, </template> + <template v-if="index < props.row.albums.length - 1"> + , + </template> </template> {{ props.row.username }} </b-table-column> - <b-table-column field="uploaded" - label="Uploaded" - centered> + <b-table-column field="uploaded" label="Uploaded" centered> <span><timeago :since="props.row.createdAt" /></span> </b-table-column> - <b-table-column field="purge" - centered> - <b-tooltip label="Albums" - position="is-top"> - <a class="btn" - @click="openAlbumModal(props.row)"> + <b-table-column field="purge" centered> + <b-tooltip label="Albums" position="is-top"> + <a class="btn" @click="openAlbumModal(props.row)"> <i class="icon-interface-window" /> </a> </b-tooltip> - <b-tooltip label="Delete" - position="is-top" - class="is-danger"> - <a class="is-danger" - @click="deleteFile(props.row)"> + <b-tooltip label="Delete" position="is-top" class="is-danger"> + <a class="is-danger" @click="deleteFile(props.row)"> <i class="icon-editorial-trash-a-l" /> </a> </b-tooltip> - <b-tooltip v-if="user && user.isAdmin" - label="More info" - position="is-top" - class="more"> + <b-tooltip v-if="user && user.isAdmin" label="More info" position="is-top" class="more"> <nuxt-link :to="`/dashboard/admin/file/${props.row.id}`"> <i class="icon-interface-more" /> </nuxt-link> @@ -170,21 +154,23 @@ </template> </b-table> </div> - <b-modal :active.sync="isAlbumsModalActive" - :width="640" - scroll="keep"> + <b-modal :active.sync="isAlbumsModalActive" :width="640" scroll="keep"> <div class="card albumsModal"> <div class="card-content"> <div class="content"> - <h3 class="subtitle">Select the albums this file should be a part of</h3> + <h3 class="subtitle"> + Select the albums this file should be a part of + </h3> <hr> + <div class="albums-container"> - <div v-for="(album, index) in albums" - :key="index" - class="album"> + <div v-for="(album, index) in albums" :key="index" class="album"> <div class="field"> - <b-checkbox :value="isAlbumSelected(album.id)" - @input="albumCheckboxClicked($event, album.id)">{{ album.name }}</b-checkbox> + <b-checkbox + :value="isAlbumSelected(album.id)" + @input="albumCheckboxClicked($event, album.id)"> + {{ album.name }} + </b-checkbox> </div> </div> </div> @@ -204,37 +190,37 @@ import WaterfallItem from './waterfall/WaterfallItem.vue'; export default { components: { Waterfall, - WaterfallItem + WaterfallItem, }, props: { files: { type: Array, - default: () => [] + default: () => [], }, total: { type: Number, - default: 0 + default: 0, }, fixed: { type: Boolean, - default: false + default: false, }, isPublic: { type: Boolean, - default: false + default: false, }, width: { type: Number, - default: 150 + default: 150, }, enableSearch: { type: Boolean, - default: true + default: true, }, enableToolbar: { type: Boolean, - default: true - } + default: true, + }, }, data() { return { @@ -242,18 +228,20 @@ export default { searchTerm: null, showList: false, albums: [], + hoveredItems: [], isAlbumsModalActive: false, showingModalForFile: null, filesOffsetWaterfall: 0, filesOffsetEndWaterfall: 50, - filesPerPageWaterfall: 50 + filesPerPageWaterfall: 50, }; }, computed: { ...mapState({ - user: state => state.auth.user + user: (state) => state.auth.user, }), blank() { + // eslint-disable-next-line global-require, import/no-unresolved return require('@/assets/images/blank.png'); }, gridFiles() { @@ -262,12 +250,7 @@ export default { }, methods: { async search() { - const data = await this.$search.do(this.searchTerm, [ - 'name', - 'original', - 'type', - 'albums:name' - ]); + const data = await this.$search.do(this.searchTerm, ['name', 'original', 'type', 'albums:name']); console.log('> Search result data', data); }, deleteFile(file, index) { @@ -290,13 +273,13 @@ export default { }); } return this.$buefy.toast.open(response.message); - } + }, }); }, isAlbumSelected(id) { - if (!this.showingModalForFile) return; - const found = this.showingModalForFile.albums.find(el => el.id === id); - return found && found.id ? true : false; + if (!this.showingModalForFile) return false; + const found = this.showingModalForFile.albums.find((el) => el.id === id); + return !!(found && found.id); }, async openAlbumModal(file) { this.showingModalForFile = file; @@ -311,7 +294,7 @@ export default { async albumCheckboxClicked(value, id) { const response = await this.$axios.$post(`file/album/${value ? 'add' : 'del'}`, { albumId: id, - fileId: this.showingModalForFile.id + fileId: this.showingModalForFile.id, }); this.$buefy.toast.open(response.message); @@ -319,134 +302,166 @@ export default { this.$parent.getFiles(); }, async getAlbums() { - const response = await this.$axios.$get(`albums/dropdown`); + const response = await this.$axios.$get('albums/dropdown'); this.albums = response.albums; this.$forceUpdate(); }, - mouseOverTest() { - console.log('aaaaaa'); - } - } + mouseOver(id) { + console.log('in', id); + const foundIndex = this.hoveredItems.indexOf(id); + if (foundIndex > -1) return; + this.hoveredItems.push(id); + /// XXX: THIS IS NOT OK! + this.$nextTick(() => { + this.$refs.video.forEach((e) => e.play().catch(() => {})); + }); + }, + mouseOut(id) { + console.log('out', id); + const foundIndex = this.hoveredItems.indexOf(id); + if (foundIndex > -1) this.hoveredItems.splice(foundIndex, 1); + }, + isHovered(id) { + return this.hoveredItems.includes(id); + }, + }, }; </script> <style lang="scss" scoped> - @import '~/assets/styles/_colors.scss'; - .item-move { - transition: all .25s cubic-bezier(.55,0,.1,1); - } +@import '~/assets/styles/_colors.scss'; +.item-move { + transition: all 0.25s cubic-bezier(0.55, 0, 0.1, 1); +} - div.toolbar { - padding: 1rem; +div.toolbar { + padding: 1rem; - .block { - text-align: right; - } + .block { + text-align: right; } +} - span.extension { - position: absolute; - width: 100%; - height: 100%; - z-index: 0; - top: 0; - left: 0; - display: flex; - align-items: center; - justify-content: center; - font-size: 2rem; - pointer-events: none; - opacity: .75; - max-width: 150px; - } +span.extension { + position: absolute; + width: 100%; + height: 100%; + z-index: 0; + top: 0; + left: 0; + display: flex; + align-items: center; + justify-content: center; + font-size: 2rem; + pointer-events: none; + opacity: 0.75; + max-width: 150px; +} - div.actions { - opacity: 0; - -webkit-transition: opacity 0.1s linear; - -moz-transition: opacity 0.1s linear; - -ms-transition: opacity 0.1s linear; - -o-transition: opacity 0.1s linear; - transition: opacity 0.1s linear; - position: absolute; - top: 0px; - left: 0px; - width: 100%; - height: calc(100% - 6px); - background: rgba(0, 0, 0, 0.5); - display: flex; - justify-content: center; - align-items: center; - - span { - padding: 3px; - &.more { - position: absolute; - top: 0; - right: 0; - } +div.preview { + position: absolute; + top: 0px; + left: 0px; + width: 100%; + height: calc(100% - 6px); + overflow: hidden; +} - &:nth-child(1), &:nth-child(2) { - align-items: flex-end; - } +.preview-container { + display: inline-block; +} - &:nth-child(1), &:nth-child(3) { - justify-content: flex-end; - } +div.actions { + opacity: 0; + -webkit-transition: opacity 0.1s linear; + -moz-transition: opacity 0.1s linear; + -ms-transition: opacity 0.1s linear; + -o-transition: opacity 0.1s linear; + transition: opacity 0.1s linear; + position: absolute; + top: 0px; + left: 0px; + width: 100%; + height: calc(100% - 6px); + // background: rgba(0, 0, 0, 0.5); + background: linear-gradient(to top, rgba(0, 0, 0, 0.5) 0px, rgba(0, 0, 0, 0) 60px), + linear-gradient(to bottom, rgba(0, 0, 0, 0.5) 0px, rgba(0, 0, 0, 0) 45px); + display: flex; + justify-content: center; + align-items: flex-end; + + span { + padding: 3px; + + &.more { + position: absolute; + top: 0; + right: 0; + } + + &:nth-child(1), + &:nth-child(2) { + align-items: flex-end; + padding-bottom: 10px; + } + + &:nth-child(3), + &:nth-child(4) { + justify-content: flex-end; + padding-bottom: 10px; + } - a { + a { + width: 30px; + height: 30px; + color: white; + justify-content: center; + align-items: center; + display: flex; + &.btn:before { + content: ''; width: 30px; height: 30px; - color: white; - justify-content: center; - align-items: center; - display: flex; - &.btn:before { - content: ''; - width: 30px; - height: 30px; - border: 1px solid white; - border-radius: 50%; - position: absolute; - } + border: 1px solid white; + border-radius: 50%; + position: absolute; } } + } - &.fixed { - position: relative; - opacity: 1; - background: none; - - a { - width: auto; - height: auto; - color: $defaultTextColor; - &:before { - display: none; - } - } + &.fixed { + position: relative; + opacity: 1; + background: none; + a { + width: auto; + height: auto; + color: $defaultTextColor; + &:before { + display: none; + } } } +} - .albums-container { - display: flex; - flex-direction: row; - flex-wrap: wrap; - .album { - flex-basis: 33%; - text-align: left; - } +.albums-container { + display: flex; + flex-direction: row; + flex-wrap: wrap; + .album { + flex-basis: 33%; + text-align: left; } +} - img.hidden { - display: none; - } -</style> +.hidden { + display: none; +} -<style lang="scss"> - .waterfall-item:hover { - div.actions { - opacity: 1 - } +.waterfall-item:hover { + div.actions { + opacity: 1; } +} </style> diff --git a/src/site/components/grid/waterfall/Waterfall.vue b/src/site/components/grid/waterfall/Waterfall.vue index af1bd72..cccc3ac 100644 --- a/src/site/components/grid/waterfall/Waterfall.vue +++ b/src/site/components/grid/waterfall/Waterfall.vue @@ -9,8 +9,6 @@ </div> </template> <script> -// import {quickSort, getMinIndex, _, sum} from './util' - const quickSort = (arr, type) => { const left = []; const right = []; @@ -25,10 +23,10 @@ const quickSort = (arr, type) => { right.push(arr[i]); } } - return quickSort(left, type).concat(povis, quickSort(right, type)) + return quickSort(left, type).concat(povis, quickSort(right, type)); }; -const getMinIndex = arr => { +const getMinIndex = (arr) => { let pos = 0; for (let i = 0; i < arr.length; i++) { if (arr[pos] > arr[i]) { @@ -44,49 +42,53 @@ const _ = { }, off(el, type, func, capture = false) { el.removeEventListener(type, func, capture); - } + }, }; -const sum = arr => arr.reduce((sum, val) => sum + val); +const sum = (arr) => arr.reduce((acc, val) => acc + val, 0); + export default { name: 'Waterfall', props: { gutterWidth: { type: Number, - default: 0 + default: 0, }, gutterHeight: { type: Number, - default: 0 + default: 0, }, resizable: { type: Boolean, - default: true + default: true, }, align: { type: String, - default: 'center' + default: 'center', }, fixWidth: { - type: Number + type: Number, + default: 0, }, minCol: { type: Number, - default: 1 + default: 1, }, maxCol: { - type: Number + type: Number, + default: 0, }, percent: { - type: Array - } + type: Array, + default: null, + }, }, data() { return { timer: null, colNum: 0, lastWidth: 0, - percentWidthArr: [] + percentWidthArr: [], }; }, created() { @@ -105,16 +107,16 @@ export default { }, methods: { calulate(arr) { - let pageWidth = this.fixWidth ? this.fixWidth : this.$el.offsetWidth; + const pageWidth = this.fixWidth ? this.fixWidth : this.$el.offsetWidth; // 百分比布局计算 if (this.percent) { this.colNum = this.percent.length; const total = sum(this.percent); - this.percentWidthArr = this.percent.map(value => (value / total) * pageWidth); + this.percentWidthArr = this.percent.map((value) => (value / total) * pageWidth); this.lastWidth = 0; // 正常布局计算 } else { - this.colNum = parseInt(pageWidth / (arr.width + this.gutterWidth)); + this.colNum = parseInt(pageWidth / (arr.width + this.gutterWidth), 10); if (this.minCol && this.colNum < this.minCol) { this.colNum = this.minCol; this.lastWidth = 0; @@ -136,14 +138,14 @@ export default { render() { // 重新排序 let childArr = []; - childArr = this.$children.map(child => child.getMeta()); + childArr = this.$children.map((child) => child.getMeta()); childArr = quickSort(childArr, 'order'); // 计算列数 - this.calulate(childArr[0]) - let offsetArr = Array(this.colNum).fill(0); + this.calulate(childArr[0]); + const offsetArr = Array(this.colNum).fill(0); // 渲染 - childArr.forEach(child => { - let position = getMinIndex(offsetArr); + childArr.forEach((child) => { + const position = getMinIndex(offsetArr); // 百分比布局渲染 if (this.percent) { let left = 0; @@ -171,10 +173,10 @@ export default { } child.el.style.top = `${offsetArr[position]}px`; offsetArr[position] += child.height + this.gutterHeight; - this.$el.style.height = `${Math.max.apply(Math, offsetArr)}px`; + this.$el.style.height = `${Math.max(...offsetArr)}px`; }); this.$emit('rendered', this); - } - } + }, + }, }; </script> diff --git a/src/site/components/grid/waterfall/WaterfallItem.vue b/src/site/components/grid/waterfall/WaterfallItem.vue index 7c4d814..2a5c69a 100644 --- a/src/site/components/grid/waterfall/WaterfallItem.vue +++ b/src/site/components/grid/waterfall/WaterfallItem.vue @@ -12,26 +12,27 @@ <script> import imagesLoaded from 'imagesloaded'; + export default { name: 'WaterfallItem', props: { order: { type: Number, - default: 0 + default: 0, }, width: { type: Number, - default: 150 + default: 150, }, video: { type: Boolean, - default: false - } + default: false, + }, }, data() { return { itemWidth: 0, - height: 0 + height: 0, }; }, created() { @@ -42,18 +43,18 @@ export default { this.$el.style.width = `${this.width}px`; this.emit(); if (this.video) { - const videoEl = this.$slots.default.find(e => e.tag?.toLowerCase() === 'video'); + // find first video object + const videoEl = this.$slots.default.find((e) => e.tag?.toLowerCase() === 'video'); const el = videoEl.elm; - console.log(videoEl); - console.log(el); + + // add event listener for video loaded el.onloadeddata = () => { - console.log('loaded'); this.$el.style.left = '-9999px'; this.$el.style.top = '-9999px'; this.$el.style.display = 'block'; this.height = el.offsetHeight + 5; this.itemWidth = el.offsetWidth; - } + }; } else { imagesLoaded(this.$el, () => { this.$el.style.left = '-9999px'; @@ -73,9 +74,9 @@ export default { el: this.$el, height: this.height, width: this.itemWidth, - order: this.order + order: this.order, }; - } - } -} + }, + }, +}; </script> diff --git a/src/site/constants/alertTypes.js b/src/site/constants/alertTypes.js new file mode 100644 index 0000000..1b830bc --- /dev/null +++ b/src/site/constants/alertTypes.js @@ -0,0 +1,10 @@ +export default { + PRIMARY: 'is-primary', + INFO: 'is-info', + SUCCESS: 'is-success', + WARNING: 'is-warning', + ERROR: 'is-danger', + DARK: 'is-dark', + LIGHT: 'is-light', + WHITE: 'is-white', +}; diff --git a/src/site/plugins/notifier.js b/src/site/plugins/notifier.js new file mode 100644 index 0000000..bcf7878 --- /dev/null +++ b/src/site/plugins/notifier.js @@ -0,0 +1,7 @@ +export default ({ store }, inject) => { + inject('notifier', { + showMessage({ content = '', type = '' }) { + store.commit('alert/set', { content, color }); + } + }); +}; diff --git a/src/site/store/auth.js b/src/site/store/auth.js index 73976d6..55009ce 100644 --- a/src/site/store/auth.js +++ b/src/site/store/auth.js @@ -4,14 +4,14 @@ const getDefaultState = () => ({ loggedIn: false, isLoading: false, user: null, - token: null + token: null, }); export const state = getDefaultState; export const getters = { - isLoggedIn: state => state.loggedIn, - getApiKey: state => state.user?.apiKey + isLoggedIn: (state) => state.loggedIn, + getApiKey: (state) => state.user?.apiKey, }; export const actions = { @@ -27,7 +27,7 @@ export const actions = { commit('loginRequest'); try { - const data = await this.$axios.$post(`auth/login`, { username, password }); + const data = await this.$axios.$post('auth/login', { username, password }); this.$axios.setToken(data.token, 'Bearer'); commit('setToken', data.token); @@ -38,7 +38,7 @@ export const actions = { }, async fetchCurrentUser({ commit, dispatch }) { try { - const data = await this.$axios.$get(`users/me`); + const data = await this.$axios.$get('users/me'); commit('setUser', data.user); } catch (e) { dispatch('alert/set', { text: e.message, error: true }, { root: true }); @@ -46,9 +46,9 @@ export const actions = { }, async changePassword({ dispatch }, { password, newPassword }) { try { - const response = await this.$axios.$post(`user/password/change`, { + const response = await this.$axios.$post('user/password/change', { password, - newPassword + newPassword, }); return response; @@ -58,7 +58,7 @@ export const actions = { }, async requestAPIKey({ commit, dispatch }) { try { - const response = await this.$axios.$post(`user/apikey/change`); + const response = await this.$axios.$post('user/apikey/change'); commit('setApiKey', response.apiKey); return response; @@ -68,7 +68,7 @@ export const actions = { }, logout({ commit }) { commit('logout'); - } + }, }; export const mutations = { @@ -94,5 +94,5 @@ export const mutations = { this.$cookies.remove('token'); // reset state to default Object.assign(state, getDefaultState()); - } + }, }; diff --git a/src/site/store/images.js b/src/site/store/images.js index f6dae1b..14f475a 100644 --- a/src/site/store/images.js +++ b/src/site/store/images.js @@ -25,7 +25,7 @@ export const actions = { try { const response = await this.$axios.$get(`files`, { params: { limit: state.pagination.limit, page } }); - commit('updateFiles', { files: response.files }); + commit('setFiles', { files: response.files }); commit('updatePaginationMeta', { totalFiles: response.count, page }); } catch (e) { dispatch('alert/set', { text: e.message, error: true }, { root: true }); @@ -45,7 +45,7 @@ export const mutations = { setIsLoading(state) { state.isLoading = true; }, - updateFiles(state, { files }) { + setFiles(state, { files }) { state.files = files || []; state.isLoading = false; }, |