diff options
| author | Zephyrrus <[email protected]> | 2020-07-10 01:17:00 +0300 |
|---|---|---|
| committer | GitHub <[email protected]> | 2020-07-10 01:17:00 +0300 |
| commit | a721681944e9eb06742e5b3f71c71aed9c1c117d (patch) | |
| tree | 93ff9fd13a0434d91fb1ae7ca0da48d6929c4d00 /src/site/components/album | |
| parent | feat: backend pagination for albums (diff) | |
| parent | refactor: finish refactoring all the components to use vuex (diff) | |
| download | host.fuwn.me-a721681944e9eb06742e5b3f71c71aed9c1c117d.tar.xz host.fuwn.me-a721681944e9eb06742e5b3f71c71aed9c1c117d.zip | |
Merge pull request #1 from Zephyrrus/feature/store_refactor
Feature/store refactor
Diffstat (limited to 'src/site/components/album')
| -rw-r--r-- | src/site/components/album/AlbumDetails.vue | 211 | ||||
| -rw-r--r-- | src/site/components/album/AlbumEntry.vue | 187 |
2 files changed, 398 insertions, 0 deletions
diff --git a/src/site/components/album/AlbumDetails.vue b/src/site/components/album/AlbumDetails.vue new file mode 100644 index 0000000..b411f13 --- /dev/null +++ b/src/site/components/album/AlbumDetails.vue @@ -0,0 +1,211 @@ +<template> + <div class="details"> + <h2>Public links for this album:</h2> + + <b-table + :data="details.links || []" + :mobile-cards="true"> + <template slot-scope="props"> + <b-table-column + field="identifier" + label="Link" + centered> + <a + :href="`${config.URL}/a/${props.row.identifier}`" + target="_blank"> + {{ props.row.identifier }} + </a> + </b-table-column> + + <b-table-column + field="views" + label="Views" + centered> + {{ props.row.views }} + </b-table-column> + + <b-table-column + field="enableDownload" + label="Allow download" + centered> + <b-switch + v-model="props.row.enableDownload" + @input="updateLinkOptions(albumId, props.row)" /> + </b-table-column> + + <b-table-column + field="enabled" + numeric> + <button + :class="{ 'is-loading': isDeleting(props.row.identifier) }" + class="button is-danger" + :disabled="isDeleting(props.row.identifier)" + @click="promptDeleteAlbumLink(albumId, props.row.identifier)"> + Delete link + </button> + </b-table-column> + </template> + <template slot="empty"> + <div class="has-text-centered"> + <i class="icon-misc-mood-sad" /> + </div> + <div class="has-text-centered"> + Nothing here + </div> + </template> + + <template slot="footer"> + <div class="level is-paddingless"> + <div class="level-left"> + <div class="level-item"> + <button + :class="{ 'is-loading': isCreatingLink }" + class="button is-primary" + style="float: left" + @click="createLink(albumId)"> + Create new link + </button> + </div> + <div class="level-item"> + <span class="has-text-default">{{ details.links.length }} / {{ config.maxLinksPerAlbum }} links created</span> + </div> + </div> + + <div class="level-right"> + <div class="level-item"> + <button + class="button is-danger" + style="float: right" + @click="promptDeleteAlbum(albumId)"> + Delete album + </button> + </div> + </div> + </div> + </template> + </b-table> + </div> +</template> + +<script> +import { mapState, mapActions } from 'vuex'; + +export default { + props: { + albumId: { + type: Number, + default: 0, + }, + details: { + type: Object, + default: () => ({}), + }, + }, + data() { + return { + isCreatingLink: false, + isDeletingLinks: [], + }; + }, + computed: mapState(['config']), + methods: { + ...mapActions({ + deleteAlbumAction: 'albums/deleteAlbum', + deleteAlbumLinkAction: 'albums/deleteLink', + updateLinkOptionsAction: 'albums/updateLinkOptions', + createLinkAction: 'albums/createLink', + alert: 'alert/set', + }), + promptDeleteAlbum(id) { + this.$buefy.dialog.confirm({ + type: 'is-danger', + message: 'Are you sure you want to delete this album?', + onConfirm: () => this.deleteAlbum(id), + }); + }, + promptDeleteAlbumLink(albumId, identifier) { + this.$buefy.dialog.confirm({ + type: 'is-danger', + message: 'Are you sure you want to delete this album link?', + onConfirm: () => this.deleteAlbumLink(albumId, identifier), + }); + }, + async deleteAlbum(id) { + try { + const response = await this.deleteAlbumAction(id); + + this.alert({ text: response.message, error: false }); + } catch (e) { + this.alert({ text: e.message, error: true }); + } + }, + async deleteAlbumLink(albumId, identifier) { + this.isDeletingLinks.push(identifier); + try { + const response = await this.deleteAlbumLinkAction({ albumId, identifier }); + + this.alert({ text: response.message, error: false }); + } catch (e) { + this.alert({ text: e.message, error: true }); + } finally { + this.isDeletingLinks = this.isDeletingLinks.filter((e) => e !== identifier); + } + }, + async createLink(albumId) { + this.isCreatingLink = true; + try { + const response = await this.createLinkAction(albumId); + + this.alert({ text: response.message, error: false }); + } catch (e) { + this.alert({ text: e.message, error: true }); + } finally { + this.isCreatingLink = false; + } + }, + async updateLinkOptions(albumId, linkOpts) { + try { + const response = await this.updateLinkOptionsAction({ albumId, linkOpts }); + + this.alert({ text: response.message, error: false }); + } catch (e) { + this.alert({ text: e.message, error: true }); + } + }, + isDeleting(identifier) { + return this.isDeletingLinks.indexOf(identifier) > -1; + }, + }, +}; +</script> + +<style lang="scss" scoped> + @import '~/assets/styles/_colors.scss'; + + div.details { + flex: 0 1 100%; + padding-left: 2em; + padding-top: 1em; + min-height: 50px; + + .b-table { + padding: 2em 0em; + + .table-wrapper { + -webkit-box-shadow: $boxShadowLight; + box-shadow: $boxShadowLight; + } + } + } +</style> + +<style lang="scss"> + @import '~/assets/styles/_colors.scss'; + + .b-table { + .table-wrapper { + -webkit-box-shadow: $boxShadowLight; + box-shadow: $boxShadowLight; + } + } +</style> diff --git a/src/site/components/album/AlbumEntry.vue b/src/site/components/album/AlbumEntry.vue new file mode 100644 index 0000000..2723b49 --- /dev/null +++ b/src/site/components/album/AlbumEntry.vue @@ -0,0 +1,187 @@ +<template> + <div class="album"> + <div + class="arrow-container" + @click="toggleDetails(album)"> + <i + :class="{ active: isExpanded }" + class="icon-arrow" /> + </div> + <div class="thumb"> + <figure class="image is-64x64 thumb"> + <img src="~/assets/images/blank_darker.png"> + </figure> + </div> + <div class="info"> + <h4> + <router-link :to="`/dashboard/albums/${album.id}`"> + {{ album.name }} + </router-link> + </h4> + <span> + Created <span class="is-inline has-text-weight-semibold"><timeago :since="album.createdAt" /></span> + </span> + <span>{{ album.fileCount || 0 }} files</span> + </div> + <div class="latest is-hidden-mobile"> + <template v-if="album.fileCount > 0"> + <div + v-for="file of album.files" + :key="file.id" + class="thumb"> + <figure class="image is-64x64"> + <a + :href="file.url" + target="_blank"> + <img :src="file.thumbSquare"> + </a> + </figure> + </div> + <div + v-if="album.fileCount > 5" + class="thumb more no-background"> + <router-link :to="`/dashboard/albums/${album.id}`"> + {{ album.fileCount - 5 }}+ more + </router-link> + </div> + </template> + <template v-else> + <span class="no-files">Nothing to show here</span> + </template> + </div> + + <AlbumDetails + v-if="isExpanded" + :details="getDetails(album.id)" + :albumId="album.id" /> + </div> +</template> + +<script> +import { mapGetters } from 'vuex'; +import AlbumDetails from '~/components/album/AlbumDetails.vue'; + +export default { + components: { + AlbumDetails, + }, + props: { + album: { + type: Object, + default: () => ({}), + }, + }, + computed: { + ...mapGetters({ + isExpandedGetter: 'albums/isExpanded', + getDetails: 'albums/getDetails', + }), + isExpanded() { + return this.isExpandedGetter(this.album.id); + }, + }, + methods: { + async toggleDetails(album) { + if (!this.isExpanded) { + await this.$store.dispatch('albums/fetchDetails', album.id); + } + this.$store.commit('albums/toggleExpandedState', album.id); + }, + }, +}; +</script> + +<style lang="scss" scoped> + @import '~/assets/styles/_colors.scss'; + + div.album { + display: flex; + flex-wrap: wrap; + margin-bottom: 10px; + + div.arrow-container { + width: 2em; + height: 64px; + position: relative; + cursor: pointer; + + i { + border: 2px solid $defaultTextColor; + border-right: 0; + border-top: 0; + display: block; + height: 1em; + position: absolute; + transform: rotate(-135deg); + transform-origin: center; + width: 1em; + z-index: 4; + top: 22px; + + -webkit-transition: transform 0.1s linear; + -moz-transition: transform 0.1s linear; + -ms-transition: transform 0.1s linear; + -o-transition: transform 0.1s linear; + transition: transform 0.1s linear; + + &.active { + transform: rotate(-45deg); + } + } + } + + div.thumb { + width: 64px; + height: 64px; + -webkit-box-shadow: $boxShadowLight; + box-shadow: $boxShadowLight; + } + + div.info { + margin-left: 15px; + text-align: left; + h4 { + font-size: 1.5rem; + a { + color: $defaultTextColor; + font-weight: 400; + &:hover { text-decoration: underline; } + } + } + span { display: block; } + span:nth-child(3) { + font-size: 0.9rem; + } + } + + div.latest { + flex-grow: 1; + justify-content: flex-end; + display: flex; + margin-left: 15px; + + span.no-files { + font-size: 1.5em; + color: #b1b1b1; + padding-top: 17px; + } + + div.more { + width: 64px; + height: 64px; + background: white; + display: flex; + align-items: center; + padding: 10px; + text-align: center; + a { + line-height: 1rem; + color: $defaultTextColor; + &:hover { text-decoration: underline; } + } + } + } + } + + div.no-background { background: none !important; } +</style> |