mirror of https://github.com/nextcloud/photos
332 lines
8.7 KiB
Vue
332 lines
8.7 KiB
Vue
<!--
|
|
- @copyright Copyright (c) 2022 Louis Chemineau <louis@chmn.me>
|
|
-
|
|
- @author Louis Chemineau <louis@chmn.me>
|
|
-
|
|
- @license AGPL-3.0-or-later
|
|
-
|
|
- This program is free software: you can redistribute it and/or modify
|
|
- it under the terms of the GNU Affero General Public License as
|
|
- published by the Free Software Foundation, either version 3 of the
|
|
- License, or (at your option) any later version.
|
|
-
|
|
- This program is distributed in the hope that it will be useful,
|
|
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
- GNU Affero General Public License for more details.
|
|
-
|
|
- You should have received a copy of the GNU Affero General Public License
|
|
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
-
|
|
-->
|
|
<template>
|
|
<div>
|
|
<CollectionContent ref="collectionContent"
|
|
:collection="album"
|
|
:collection-file-ids="albumFileIds"
|
|
:semaphore="semaphore"
|
|
:loading="loadingAlbum || loadingFiles"
|
|
:error="errorFetchingAlbum || errorFetchingFiles">
|
|
<!-- Header -->
|
|
<HeaderNavigation v-if="albumOriginalName !== ''"
|
|
key="navigation"
|
|
slot="header"
|
|
slot-scope="{selectedFileIds}"
|
|
:loading="loadingAlbum || loadingFiles"
|
|
:params="{ token }"
|
|
path="/"
|
|
:root-title="albumOriginalName"
|
|
:title="albumOriginalName"
|
|
@refresh="fetchAlbumContent">
|
|
<!-- TODO: enable upload on public albums -->
|
|
<!-- <UploadPicker :accept="allowedMimes"
|
|
:destination="folder.filename"
|
|
:multiple="true"
|
|
@uploaded="onUpload" /> -->
|
|
<div v-if="album.location !== ''" slot="subtitle" class="album__location">
|
|
<MapMarker />{{ album.location }}
|
|
</div>
|
|
<template v-if="album !== undefined" slot="right">
|
|
<NcActions :force-menu="true" :aria-label="t('photos', 'Open actions menu')">
|
|
<!-- TODO: enable download on public albums -->
|
|
<!-- <ActionDownload v-if="albumFileIds.length > 0"
|
|
:selected-file-ids="albumFileIds"
|
|
:title="t('photos', 'Download all files in album')">
|
|
<DownloadMultiple slot="icon" />
|
|
</ActionDownload> -->
|
|
|
|
<template v-if="selectedFileIds.length > 0">
|
|
<!-- TODO: enable download on public albums -->
|
|
<!-- <NcActionSeparator />
|
|
|
|
<ActionDownload :selected-file-ids="selectedFileIds" :title="t('photos', 'Download selected files')">
|
|
<Download slot="icon" />
|
|
</ActionDownload> -->
|
|
|
|
<NcActionButton :close-after-click="true"
|
|
@click="handleRemoveFilesFromAlbum(selectedFileIds)">
|
|
{{ t('photos', 'Remove selection from album') }}
|
|
<Close slot="icon" />
|
|
</NcActionButton>
|
|
</template>
|
|
</NcActions>
|
|
</template>
|
|
</HeaderNavigation>
|
|
|
|
<!-- No content -->
|
|
<NcEmptyContent slot="empty-content"
|
|
:title="t('photos', 'This album does not have any photos or videos yet!')"
|
|
class="album__empty">
|
|
<ImagePlus slot="icon" />
|
|
|
|
<NcButton slot="action"
|
|
type="primary"
|
|
:aria-label="t('photos', 'Add photos to this album')"
|
|
@click="showAddPhotosModal = true">
|
|
<Plus slot="icon" />
|
|
{{ t('photos', "Add") }}
|
|
</NcButton>
|
|
</NcEmptyContent>
|
|
</CollectionContent>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { mapActions, mapGetters } from 'vuex'
|
|
import { createClient, getPatcher } from 'webdav'
|
|
|
|
import MapMarker from 'vue-material-design-icons/MapMarker'
|
|
import Plus from 'vue-material-design-icons/Plus'
|
|
import ImagePlus from 'vue-material-design-icons/ImagePlus'
|
|
import Close from 'vue-material-design-icons/Close'
|
|
// import Download from 'vue-material-design-icons/Download'
|
|
// import DownloadMultiple from 'vue-material-design-icons/DownloadMultiple'
|
|
|
|
import { NcActions, NcActionButton, NcButton, NcEmptyContent, /** NcActionSeparator, */ isMobile } from '@nextcloud/vue'
|
|
import { showError } from '@nextcloud/dialogs'
|
|
import axios from '@nextcloud/axios'
|
|
import { generateRemoteUrl } from '@nextcloud/router'
|
|
|
|
import FetchFilesMixin from '../mixins/FetchFilesMixin.js'
|
|
import AbortControllerMixin from '../mixins/AbortControllerMixin.js'
|
|
import CollectionContent from '../components/Collection/CollectionContent.vue'
|
|
import HeaderNavigation from '../components/HeaderNavigation.vue'
|
|
// import ActionDownload from '../components/Actions/ActionDownload.vue'
|
|
import { fetchAlbum, fetchAlbumContent } from '../services/Albums.js'
|
|
import logger from '../services/logger.js'
|
|
|
|
const publicRootPath = 'dav'
|
|
|
|
// force our axios
|
|
const patcher = getPatcher()
|
|
patcher.patch('request', axios)
|
|
|
|
// init webdav client on default dav endpoint
|
|
const remote = generateRemoteUrl(publicRootPath)
|
|
const publicRemote = remote
|
|
|
|
export default {
|
|
name: 'PublicAlbumContent',
|
|
components: {
|
|
MapMarker,
|
|
Plus,
|
|
Close,
|
|
// Download,
|
|
// DownloadMultiple,
|
|
ImagePlus,
|
|
NcEmptyContent,
|
|
NcActions,
|
|
NcActionButton,
|
|
// NcActionSeparator,
|
|
NcButton,
|
|
CollectionContent,
|
|
// ActionDownload,
|
|
HeaderNavigation,
|
|
},
|
|
|
|
mixins: [
|
|
FetchFilesMixin,
|
|
AbortControllerMixin,
|
|
isMobile,
|
|
],
|
|
|
|
props: {
|
|
token: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
showAddPhotosModal: false,
|
|
loadingAlbum: false,
|
|
errorFetchingAlbum: null,
|
|
loadingCount: 0,
|
|
loadingAddFilesToAlbum: false,
|
|
albumOriginalName: '',
|
|
publicClient: createClient(publicRemote, {
|
|
username: this.token,
|
|
password: null,
|
|
}),
|
|
}
|
|
},
|
|
|
|
computed: {
|
|
...mapGetters([
|
|
'files',
|
|
'publicAlbums',
|
|
'publicAlbumsFiles',
|
|
]),
|
|
|
|
/**
|
|
* @return {object} The album information for the current albumName.
|
|
*/
|
|
album() {
|
|
return this.publicAlbums[this.albumName] || {}
|
|
},
|
|
|
|
/**
|
|
* @return {string} The album's name is the token.
|
|
*/
|
|
albumName() {
|
|
return this.token
|
|
},
|
|
|
|
/**
|
|
* @return {string[]} The list of files for the current albumName.
|
|
*/
|
|
albumFileIds() {
|
|
return this.publicAlbumsFiles[this.albumName] || []
|
|
},
|
|
},
|
|
|
|
async beforeMount() {
|
|
await this.fetchAlbumInfo()
|
|
await this.fetchAlbumContent()
|
|
},
|
|
|
|
methods: {
|
|
...mapActions([
|
|
'appendFiles',
|
|
'addPublicAlbums',
|
|
'addFilesToPublicAlbum',
|
|
'removeFilesFromPublicAlbum',
|
|
]),
|
|
|
|
async fetchAlbumInfo() {
|
|
if (this.loadingAlbum) {
|
|
return
|
|
}
|
|
|
|
try {
|
|
this.loadingAlbum = true
|
|
this.errorFetchingAlbum = null
|
|
|
|
const album = await fetchAlbum(
|
|
`/photospublic/${this.token}`,
|
|
this.abortController.signal,
|
|
'<nc:original-name />',
|
|
this.publicClient,
|
|
)
|
|
this.addPublicAlbums({ collections: [album] })
|
|
this.albumOriginalName = album.originalName
|
|
} catch (error) {
|
|
if (error.response?.status === 404) {
|
|
this.errorFetchingAlbum = 404
|
|
return
|
|
}
|
|
|
|
this.errorFetchingAlbum = error
|
|
logger.error('[PublicAlbumContent] Error fetching album', { error })
|
|
showError(this.t('photos', 'Failed to fetch album.'))
|
|
} finally {
|
|
this.loadingAlbum = false
|
|
}
|
|
},
|
|
|
|
async fetchAlbumContent() {
|
|
if (this.loadingFiles || this.showEditAlbumForm) {
|
|
return []
|
|
}
|
|
|
|
const semaphoreSymbol = await this.semaphore.acquire(() => 0, 'fetchFiles')
|
|
const fetchSemaphoreSymbol = await this.fetchSemaphore.acquire()
|
|
|
|
try {
|
|
this.errorFetchingFiles = null
|
|
this.loadingFiles = true
|
|
this.semaphoreSymbol = semaphoreSymbol
|
|
|
|
const fetchedFiles = await fetchAlbumContent(
|
|
`/photospublic/${this.token}`,
|
|
this.abortController.signal,
|
|
this.publicClient,
|
|
)
|
|
|
|
const fileIds = fetchedFiles
|
|
.map(file => file.fileid.toString())
|
|
|
|
this.appendFiles(fetchedFiles)
|
|
|
|
if (fetchedFiles.length > 0) {
|
|
await this.$store.commit('addFilesToPublicAlbum', { collectionId: this.albumName, fileIdsToAdd: fileIds })
|
|
}
|
|
|
|
return fetchedFiles
|
|
} catch (error) {
|
|
if (error.response?.status === 404) {
|
|
this.errorFetchingFiles = 404
|
|
return []
|
|
}
|
|
|
|
this.errorFetchingFiles = error
|
|
|
|
showError(this.t('photos', 'Failed to fetch albums list.'))
|
|
logger.error('[PublicAlbumContent] Error fetching album files', { error })
|
|
} finally {
|
|
this.loadingFiles = false
|
|
this.semaphore.release(semaphoreSymbol)
|
|
this.fetchSemaphore.release(fetchSemaphoreSymbol)
|
|
}
|
|
|
|
return []
|
|
},
|
|
|
|
async handleFilesPicked(fileIds) {
|
|
this.showAddPhotosModal = false
|
|
await this.addFilesToPublicAlbum({ collectionId: this.albumName, fileIdsToAdd: fileIds })
|
|
// Re-fetch album content to have the proper filenames.
|
|
await this.fetchAlbumContent()
|
|
},
|
|
|
|
async handleRemoveFilesFromAlbum(fileIds) {
|
|
this.$refs.collectionContent.onUncheckFiles(fileIds)
|
|
await this.removeFilesFromPublicAlbum({ collectionId: this.albumName, fileIdsToRemove: fileIds })
|
|
},
|
|
},
|
|
}
|
|
</script>
|
|
<style lang="scss" scoped>
|
|
.album {
|
|
display: flex;
|
|
flex-direction: column;
|
|
|
|
&__title {
|
|
width: 100%;
|
|
}
|
|
|
|
&__name {
|
|
overflow: hidden;
|
|
white-space: nowrap;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
&__location {
|
|
margin-left: -4px;
|
|
display: flex;
|
|
color: var(--color-text-lighter);
|
|
}
|
|
}
|
|
</style>
|