enh: Migrate photos picker to use NcDialog

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
This commit is contained in:
Ferdinand Thiessen 2024-01-31 18:40:18 +01:00
parent 8f699e99c4
commit 5f03c45728
No known key found for this signature in database
GPG Key ID: 45FAE7268762B400
7 changed files with 155 additions and 161 deletions

View File

@ -44,30 +44,21 @@ Cypress.on('uncaught:exception', (err) => {
}
})
describe('Manage albums', () => {
describe('Manage albums', { testIsolation: true }, () => {
let user = null
before(function() {
beforeEach(function() {
cy.createRandomUser()
.then(_user => {
user = _user
uploadTestMedia(user)
cy.login(user)
cy.visit('/apps/photos')
})
})
beforeEach(() => {
cy.visit(`${Cypress.env('baseUrl')}/index.php/apps/photos/albums`)
createAnAlbumFromAlbums('albums_test')
addFilesToAlbumFromAlbum('albums_test', [0, 1, 2])
})
afterEach(() => {
deleteAnAlbumFromAlbumContent()
cy.contains('There is no album yet!').click()
})
it('Create an album, populate it and delete it', () => {
cy.get('[data-test="media"]').should('have.length', 3)
})
@ -156,7 +147,7 @@ describe('Manage albums', () => {
})
it('Delete a file that was added to an album', () => {
addFilesToAlbumFromAlbumFromHeader('albums_test', [0])
addFilesToAlbumFromAlbumFromHeader('albums_test', [3])
cy.get('[data-test="media"]').should('have.length', 4)
cy.visit('/apps/photos')
selectMedia([3])

View File

@ -69,12 +69,11 @@ export function addFilesToAlbumFromAlbum(albumName: string, itemsIndex: number[]
export function addFilesToAlbumFromAlbumFromHeader(albumName: string, itemsIndex: number[]) {
cy.contains('New').click()
cy.intercept({ times: 1, method: 'SEARCH', url: '**/dav/' }).as('search')
cy.contains('Add photos to this album').click()
cy.wait('@search')
cy.get('.photos-picker__file-list').within(() => {
selectMedia(itemsIndex)
})
cy.intercept({ times: 1, method: 'PROPFIND', url: `**/dav/photos/**/albums/${albumName}` }).as('propFind')
cy.contains(`Add to ${albumName}`).click()
cy.wait('@propFind')

34
package-lock.json generated
View File

@ -24,7 +24,7 @@
"@nextcloud/router": "^2.0.0",
"@nextcloud/sharing": "^0.1.0",
"@nextcloud/upload": "^1.0.4",
"@nextcloud/vue": "^8.5.0",
"@nextcloud/vue": "^8.6.1",
"camelcase": "^8.0.0",
"debounce": "^1.2.1",
"he": "^1.2.0",
@ -3808,9 +3808,9 @@
}
},
"node_modules/@nextcloud/vue": {
"version": "8.5.0",
"resolved": "https://registry.npmjs.org/@nextcloud/vue/-/vue-8.5.0.tgz",
"integrity": "sha512-PitpFCtBRWy3p7MEbjHzVwDf3Cx2+IfxDjj5gi8u1M+LKt2chSXidUOURKGsXhnttlqh1aC+sQWjHM2s3zRuEQ==",
"version": "8.6.1",
"resolved": "https://registry.npmjs.org/@nextcloud/vue/-/vue-8.6.1.tgz",
"integrity": "sha512-CPPo3A1Az0DDRNI0pn7w6Yn8pJtEDktTXnrfym2Pd+lTzqTj4P2ywf+spArXMHu0BPIB95Eero7XlCiRpmhg/g==",
"dependencies": {
"@floating-ui/dom": "^1.1.0",
"@linusborg/vue-simple-portal": "^0.1.5",
@ -3830,7 +3830,7 @@
"clone": "^2.1.2",
"debounce": "2.0.0",
"dompurify": "^3.0.5",
"emoji-mart-vue-fast": "^15.0.0",
"emoji-mart-vue-fast": "^15.0.1",
"escape-html": "^1.0.3",
"floating-vue": "^1.0.0-beta.19",
"focus-trap": "^7.4.3",
@ -3947,18 +3947,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@nextcloud/vue/node_modules/emoji-mart-vue-fast": {
"version": "15.0.0",
"resolved": "https://registry.npmjs.org/emoji-mart-vue-fast/-/emoji-mart-vue-fast-15.0.0.tgz",
"integrity": "sha512-3BzkDrs60JyT00dLHMAxWKbpFhbyaW9C+q1AjtqGovSxTu8TC2mYAGsvTmXNYKm39IRRAS56v92TihOcB98IsQ==",
"dependencies": {
"@babel/runtime": "^7.18.6",
"core-js": "^3.23.5"
},
"peerDependencies": {
"vue": ">2.0.0"
}
},
"node_modules/@nextcloud/vue/node_modules/is-plain-obj": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
@ -9499,6 +9487,18 @@
"url": "https://github.com/sindresorhus/emittery?sponsor=1"
}
},
"node_modules/emoji-mart-vue-fast": {
"version": "15.0.1",
"resolved": "https://registry.npmjs.org/emoji-mart-vue-fast/-/emoji-mart-vue-fast-15.0.1.tgz",
"integrity": "sha512-FcBio4MZsad+IwbaD2+1/obaK7W0F8EXlVXOXKgNCICaxkJD5WnA5bAtSXR0+FSBrMWz7DCAOqOojm7EapZ1eg==",
"dependencies": {
"@babel/runtime": "^7.18.6",
"core-js": "^3.23.5"
},
"peerDependencies": {
"vue": ">2.0.0"
}
},
"node_modules/emojis-list": {
"version": "3.0.0",
"dev": true,

View File

@ -53,7 +53,7 @@
"@nextcloud/router": "^2.0.0",
"@nextcloud/sharing": "^0.1.0",
"@nextcloud/upload": "^1.0.4",
"@nextcloud/vue": "^8.5.0",
"@nextcloud/vue": "^8.6.1",
"camelcase": "^8.0.0",
"debounce": "^1.2.1",
"he": "^1.2.0",

View File

@ -2,6 +2,7 @@
- @copyright Copyright (c) 2022 Louis Chemineau <louis@chmn.me>
-
- @author Louis Chemineau <louis@chmn.me>
- @author Ferdinand Thiessen <opensource@fthiessen.de>
-
- @license AGPL-3.0-or-later
-
@ -20,51 +21,45 @@
-
-->
<template>
<div class="photos-picker">
<div class="photos-picker__content">
<nav class="photos-picker__navigation" :class="{'photos-picker__navigation--placeholder': monthsList.length === 0}">
<ul>
<li v-for="month in monthsList"
:key="month"
class="photos-picker__navigation__month"
:class="{selected: targetMonth === month}">
<NcButton type="tertiary" :aria-label="t('photos', 'Jump to {date}', {date: dateMonthAndYear(month)})" @click="targetMonth = month">
{{ month | dateMonthAndYear }}
</NcButton>
</li>
</ul>
</nav>
<FilesListViewer class="photos-picker__file-list"
:class="{'photos-picker__file-list--placeholder': monthsList.length === 0}"
:file-ids-by-section="fileIdsByMonth"
:empty-message="t('photos', 'There are no photos or videos yet!')"
:sections="monthsList"
:loading="loadingFiles"
:base-height="100"
:section-header-height="50"
:scroll-to-section="targetMonth"
@need-content="getFiles"
@focusout.native="onFocusOut">
<template slot-scope="{file, height, isHeader, distance}">
<h3 v-if="isHeader"
:id="`photos-picker-section-header-${file.id}`"
:style="{ height: `${height}px`}"
class="section-header">
{{ file.id | dateMonthAndYear }}
</h3>
<File v-else
:file="files[file.id]"
:allow-selection="true"
:selected="selection[file.id] === true"
:distance="distance"
@select-toggled="onFileSelectToggle" />
<NcDialog content-classes="photos-picker"
:name="name"
:open="open"
out-transition
size="large"
@update:open="(open) => $emit('update:open', open)">
<!-- Navigation containing the months available -->
<template #navigation="{ isCollapsed }">
<!-- Mobile view -->
<NcSelect v-if="isCollapsed"
v-model="targetMonth"
:aria-label-combobox="t('photos', 'Jump to specific date in list')"
class="photos-picker__navigation__month-select"
:clearable="false"
:options="monthsList">
<template #selected-option="{ label }">
{{ dateMonthAndYear(label) }}
</template>
</FilesListViewer>
</div>
<template #option="{ label }">
{{ dateMonthAndYear(label) }}
</template>
</NcSelect>
<div class="photos-picker__actions">
<!-- Default view -->
<ul v-else>
<li v-for="month in monthsList"
:key="month"
class="photos-picker__navigation__month">
<NcButton :type="targetMonth === month ? 'secondary' : 'tertiary'"
:aria-label="t('photos', 'Jump to {date}', { date: dateMonthAndYear(month) })"
@click="targetMonth = month">
{{ dateMonthAndYear(month) }}
</NcButton>
</li>
</ul>
</template>
<!-- The actions on the bottom -->
<template #actions>
<UploadPicker :accept="allowedMimes"
:context="uploadContext"
:destination="photosLocationFolder"
@ -77,14 +72,44 @@
</template>
{{ t('photos', 'Add to {destination}', { destination }) }}
</NcButton>
</div>
</div>
</template>
<FilesListViewer class="photos-picker__file-list"
:class="{'photos-picker__file-list--placeholder': monthsList.length === 0}"
:file-ids-by-section="fileIdsByMonth"
:empty-message="t('photos', 'There are no photos or videos yet!')"
:sections="monthsList"
:loading="loadingFiles"
:base-height="100"
:section-header-height="50"
:scroll-to-section="targetMonth"
@need-content="getFiles"
@focusout.native="onFocusOut">
<template slot-scope="{file, height, isHeader, distance}">
<h3 v-if="isHeader"
:id="`photos-picker-section-header-${file.id}`"
:style="{ height: `${height}px`}"
class="section-header">
{{ dateMonthAndYear(file.id) }}
</h3>
<File v-else
:file="files[file.id]"
:allow-selection="true"
:selected="selection[file.id] === true"
:distance="distance"
@select-toggled="onFileSelectToggle" />
</template>
</FilesListViewer>
</NcDialog>
</template>
<script>
import { mapGetters } from 'vuex'
import { NcButton, NcLoadingIcon, useIsMobile } from '@nextcloud/vue'
import { UploadPicker } from '@nextcloud/upload'
import { NcButton, NcDialog, NcLoadingIcon, NcSelect, useIsMobile } from '@nextcloud/vue'
import { defineComponent } from 'vue'
import { mapGetters } from 'vuex'
import moment from '@nextcloud/moment'
import ImagePlus from 'vue-material-design-icons/ImagePlus.vue'
@ -98,40 +123,20 @@ import FilesByMonthMixin from '../mixins/FilesByMonthMixin.js'
import UserConfig from '../mixins/UserConfig.js'
import allowedMimes from '../services/AllowedMimes.js'
const isMobile = useIsMobile()
/**
* @param {string} date - In the following format: YYYYMM
*/
function dateMonthAndYear(date) {
if (isMobile.value) {
return moment(date, 'YYYYMM').format('MMM YYYY')
} else {
return moment(date, 'YYYYMM').format('MMMM YYYY')
}
}
export default {
name: 'FilesPicker',
export default defineComponent({
name: 'PhotosPicker',
components: {
File,
FilesListViewer,
ImagePlus,
NcButton,
NcDialog,
NcLoadingIcon,
NcSelect,
UploadPicker,
},
filters: {
/**
* @param {string} date - In the following format: YYYYMM
*/
dateMonthAndYear(date) {
return dateMonthAndYear(date)
},
},
mixins: [
FetchFilesMixin,
FilesByMonthMixin,
@ -140,6 +145,22 @@ export default {
],
props: {
/**
* If the photos picker should be opened
*/
open: {
type: Boolean,
default: true,
},
/**
* Name to be used as heading
*/
name: {
type: String,
required: true,
},
// Label to show in the submit button.
destination: {
type: String,
@ -159,6 +180,14 @@ export default {
},
},
emits: ['files-picked', 'update:open'],
setup() {
return {
isMobile: useIsMobile(),
}
},
data() {
return {
allowedMimes,
@ -208,43 +237,34 @@ export default {
* @param {string} date - In the following format: YYYYMM
*/
dateMonthAndYear(date) {
return dateMonthAndYear(date)
if (this.isMobile) {
return moment(date, 'YYYYMM').format('MMM YYYY')
}
return moment(date, 'YYYYMM').format('MMMM YYYY')
},
},
}
})
</script>
<style lang="scss" scoped>
.photos-picker {
display: flex;
flex-direction: column;
padding: 12px;
:deep(.photos-picker) {
// remove padding to move scrollbar to the very end
padding-inline-end: 0 !important;
}
&__content {
display: flex;
align-items: flex-start;
flex-grow: 1;
height: 500px;
padding: 0 2px;
}
.photos-picker {
&__navigation {
flex-basis: 200px;
overflow: scroll;
height: 100%;
padding: 0 2px;
@media only screen and (max-width: 1200px) {
flex-basis: 100px;
}
&--placeholder {
background: var(--color-primary-element-light);
border-radius: var(--border-radius-large);
}
&__month {
margin: 4px 0;
// For focus-visible outline
margin: 4px;
}
&__month-select {
flex: 1;
// align with other content
padding-inline-end: 12px;
padding-block-end: 6px;
}
}
@ -275,12 +295,5 @@ export default {
justify-content: center;
}
}
&__actions {
display: flex;
justify-content: space-between;
justify-items: center;
padding-top: 16px;
}
}
</style>

View File

@ -127,15 +127,11 @@
</NcEmptyContent>
</CollectionContent>
<NcModal v-if="showAddPhotosModal"
size="large"
<PhotosPicker :open.sync="showAddPhotosModal"
:blacklist-ids="albumFileIds"
:destination="album.basename"
:name="t('photos', 'Add photos to {albumName}', {albumName: albumName})"
@close="showAddPhotosModal = false">
<FilesPicker v-if="album !== undefined"
:destination="album.basename"
:blacklist-ids="albumFileIds"
@files-picked="handleFilesPicked" />
</NcModal>
@files-picked="handleFilesPicked" />
<NcModal v-if="showManageCollaboratorView && album !== undefined"
:name="t('photos', 'Manage collaborators')"
@ -196,7 +192,7 @@ import ActionFavorite from '../components/Actions/ActionFavorite.vue'
import AlbumForm from '../components/Albums/AlbumForm.vue'
import CollaboratorsSelectionForm from '../components/Albums/CollaboratorsSelectionForm.vue'
import CollectionContent from '../components/Collection/CollectionContent.vue'
import FilesPicker from '../components/FilesPicker.vue'
import PhotosPicker from '../components/PhotosPicker.vue'
import HeaderNavigation from '../components/HeaderNavigation.vue'
import allowedMimes from '../services/AllowedMimes.js'
@ -214,7 +210,7 @@ export default {
Delete,
// Download,
// DownloadMultiple,
FilesPicker,
PhotosPicker,
HeaderNavigation,
ImagePlus,
MapMarker,

View File

@ -111,23 +111,19 @@
</NcEmptyContent>
</CollectionContent>
<NcModal v-if="showAddPhotosModal"
size="large"
<PhotosPicker :open.sync="showAddPhotosModal"
:name="t('photos', 'Add photos to {albumName}', {albumName: albumOriginalName})"
@close="showAddPhotosModal = false">
<FilesPicker v-if="album !== undefined"
:destination="album.basename"
:blacklist-ids="albumFileIds"
:loading="loadingAddFilesToAlbum"
@files-picked="handleFilesPicked" />
</NcModal>
:destination="album.basename"
:blacklist-ids="albumFileIds"
:loading="loadingAddFilesToAlbum"
@files-picked="handleFilesPicked" />
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import { NcActions, NcActionButton, NcButton, NcModal, NcEmptyContent, NcActionSeparator, NcUserBubble, isMobile } from '@nextcloud/vue'
import { NcActions, NcActionButton, NcButton, NcEmptyContent, NcActionSeparator, NcUserBubble, isMobile } from '@nextcloud/vue'
import { Type } from '@nextcloud/sharing'
import { translate } from '@nextcloud/l10n'
@ -145,7 +141,7 @@ import FetchCollectionContentMixin from '../mixins/FetchCollectionContentMixin.j
import CollectionContent from '../components/Collection/CollectionContent.vue'
import HeaderNavigation from '../components/HeaderNavigation.vue'
// import ActionDownload from '../components/Actions/ActionDownload.vue'
import FilesPicker from '../components/FilesPicker.vue'
import PhotosPicker from '../components/PhotosPicker.vue'
export default {
name: 'SharedAlbumContent',
@ -162,11 +158,10 @@ export default {
NcActionButton,
NcActionSeparator,
NcButton,
NcModal,
NcUserBubble,
CollectionContent,
// ActionDownload,
FilesPicker,
PhotosPicker,
HeaderNavigation,
},