Skip to content

Commit

Permalink
feat: Allow multiple source folders
Browse files Browse the repository at this point in the history
Signed-off-by: Louis Chemineau <[email protected]>
  • Loading branch information
artonge committed Apr 9, 2024
1 parent 16d6cda commit bc77dc4
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 42 deletions.
3 changes: 2 additions & 1 deletion lib/Service/UserConfigService.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ class UserConfigService {
public const DEFAULT_CONFIGS = [
'croppedLayout' => 'false',
'photosLocation' => '/Photos',
'photosSourceFolder' => '/Photos',
// TODO: add migration to retrieve value in photosSourceFolders
'photosSourceFolders' => '["/Photos"]',
];

private IConfig $config;
Expand Down
43 changes: 23 additions & 20 deletions src/components/Settings/PhotosSourceLocationsSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,22 @@
<template>
<div class="photos-locations-container">
<div class="photos-locations">
<PhotosFolder :path="photosSourceFolder" :root-folder-label="t('photos', 'All folders')" :root-folder-icon="FolderMultiple" />
<!-- TODO: uncomment when SEARCH on multiple folders is implemented. -->
<!-- <li v-for="(source, index) in photosSourceFolder"
<li v-for="(source, index) in photosSourceFolders"
:key="index">
<PhotosFolder :path="source"
:can-delete="photosSourceFolder.length !== 1"
:can-delete="photosSourceFolders.length !== 1"
:root-folder-label="t('photos', 'All folders')"
:root-folder-icon="FolderMultiple"
@remove-folder="removeSourceFolder(index)" />
</li> -->
</li>
</div>

<NcButton :aria-label="t('photos', 'Choose a Photos source for the timelines')"
<NcButton :aria-label="t('photos', 'Add a Photos source for the timelines')"
@click="debounceAddSourceFolder">
<!-- TODO: uncomment when SEARCH on multiple folders is implemented. -->
<!-- <template #icon>
<template #icon>
<Plus :size="20" />
</template> -->
{{ t('photos', 'Choose a different folder') }}
</template>
{{ t('photos', 'Add folder') }}
</NcButton>
</div>
</template>
Expand All @@ -50,6 +48,7 @@ import debounce from 'debounce'
import { defineComponent } from 'vue'

import FolderMultiple from 'vue-material-design-icons/FolderMultiple.vue'
import Plus from 'vue-material-design-icons/Plus.vue'

import { NcButton } from '@nextcloud/vue'
import { getFilePickerBuilder } from '@nextcloud/dialogs'
Expand All @@ -63,6 +62,7 @@ export default defineComponent({
components: {
NcButton,
PhotosFolder,
Plus,
},

data() {
Expand All @@ -72,9 +72,9 @@ export default defineComponent({
},

computed: {
/** @return {string} */
photosSourceFolder() {
return this.$store.state.userConfig.photosSourceFolder
/** @return {string[]} */
photosSourceFolders() {
return this.$store.state.userConfig.photosSourceFolders
},
},

Expand All @@ -97,17 +97,16 @@ export default defineComponent({

async addSourceFolder() {
const pickedFolder = await this.openFilePicker(t('photos', 'Select a source folder for your media'))
// TODO: uncomment when SEARCH on multiple folders is implemented.
// if (this.photosSourceFolder.includes(pickedFolder)) {
// return
// }
this.$store.dispatch('updateUserConfig', { key: 'photosSourceFolder', value: pickedFolder })
if (this.photosSourceFolders.includes(pickedFolder)) {
return
}
this.$store.dispatch('updateUserConfig', { key: 'photosSourceFolders', value: [...this.photosSourceFolders, pickedFolder] })
},

removeSourceFolder(index) {
const folders = [...this.photosSourceFolder]
const folders = [...this.photosSourceFolders]
folders.splice(index, 1)
this.$store.dispatch('updateUserConfig', { key: 'photosSourceFolder', value: folders })
this.$store.dispatch('updateUserConfig', { key: 'photosSourceFolders', value: folders })
},

t,
Expand All @@ -123,6 +122,10 @@ export default defineComponent({

.photos-locations {
margin-bottom: 16px;

li {
list-style: none;
}
}
}
</style>
2 changes: 1 addition & 1 deletion src/components/Settings/SettingsDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<CroppedLayoutSettings />
</NcAppSettingsSection>

<NcAppSettingsSection id="source-directories-settings" :name="t('photos', 'Media folder')">
<NcAppSettingsSection id="source-directories-settings" :name="t('photos', 'Media folders')">
<div class="setting-section-subline">
{{ t('photos', 'Choose the folders from where photos and videos are shown.') }}
</div>
Expand Down
3 changes: 2 additions & 1 deletion src/mixins/FetchFilesMixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ export default {
} catch (error) {
if (error.response?.status === 404) {
this.errorFetchingFiles = 404
const source = joinPaths(davRootPath, store.state.userConfig.photosSourceFolder ?? '/Photos') + '/'
// TODO improve error handling
const source = joinPaths(davRootPath, store.state.userConfig.photosSourceFolders ?? ['/Photos']) + '/'
logger.debug('Photo source does not exist, creating it.')
try {
await davGetClient().createDirectory(source)
Expand Down
26 changes: 10 additions & 16 deletions src/services/PhotoSearch.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@
*/

import { genFileInfo } from '../utils/fileUtils.js'
import { getCurrentUser } from '@nextcloud/auth'
import { allMimes } from './AllowedMimes.js'
import client from './DavClient.js'
import { props } from './DavRequest.js'
import moment from '@nextcloud/moment'
import store from '../store/index.js'
import { davRootPath } from '@nextcloud/files'

/**
* List files from a folder and filter out unwanted mimes
Expand All @@ -51,8 +51,6 @@ export default async function(options = {}) {
...options,
}

const prefixPath = `/files/${getCurrentUser().uid}`

// generating the search or condition
// based on the allowed mimetypes
const orMime = options.mimesType.reduce((str, mime) => `${str}
Expand Down Expand Up @@ -95,15 +93,14 @@ export default async function(options = {}) {
}).join('\n')}</d:or>`
: ''

// TODO: uncomment when SEARCH on multiple folders is implemented.
// const sourceFolders = store.state.userConfig.photosSourceFolder
// .map(folder => `
// <d:scope>
// <d:href>${davRootPath}/${folder}</d:href>
// <d:depth>infinity</d:depth>
// </d:scope>
// `)
// .join('\n')
const sourceFolders = store.state.userConfig.photosSourceFolders
.map(folder => `
<d:scope>
<d:href>${davRootPath}/${folder}</d:href>
<d:depth>infinity</d:depth>
</d:scope>`
)
.join('\n')

options = Object.assign({
method: 'SEARCH',
Expand All @@ -123,10 +120,7 @@ export default async function(options = {}) {
</d:prop>
</d:select>
<d:from>
<d:scope>
<d:href>${prefixPath}/${store.state.userConfig.photosSourceFolder ?? '/Photos'}</d:href>
<d:depth>infinity</d:depth>
</d:scope>
${sourceFolders}
</d:from>
<d:where>
<d:and>
Expand Down
4 changes: 2 additions & 2 deletions src/store/userConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export async function getFolder(path) {
/**
* @typedef {object} UserConfigState
* @property {boolean} croppedLayout
* @property {string} photosSourceFolder
* @property {string[]} photosSourceFolders
* @property {string} photosLocation
* @property {import('@nextcloud/files').Folder} [photosLocationFolder]
*/
Expand All @@ -68,7 +68,7 @@ const module = {
state() {
return {
croppedLayout: loadState('photos', 'croppedLayout', 'false') === 'true',
photosSourceFolder: loadState('photos', 'photosSourceFolder', ''),
photosSourceFolders: JSON.parse(loadState('photos', 'photosSourceFolders', '[]')),
photosLocation: loadState('photos', 'photosLocation', ''),
photosLocationFolder: undefined,
}
Expand Down
2 changes: 1 addition & 1 deletion src/views/Timeline.vue
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ export default {
},

handleUserConfigChange({ key }) {
if (key === 'photosSourceFolder') {
if (key === 'photosSourceFolders') {
this.resetFetchFilesState()
}
},
Expand Down

0 comments on commit bc77dc4

Please sign in to comment.