\\n\\t\\n\\t\\t\\n\\n\\t\\t{{ size }}\\n\\n\\t\\t\\n\\t\\t\\t•\\n\\t\\t\\t\\n\\t\\t\\n\\n\\t\\t\\n\\t\\t\\t•\\n\\t\\t\\t\\n\\t\\t\\n\\t
\\n\\n\\n\\n\"],\"sourceRoot\":\"\"}]);\n// Exports\n___CSS_LOADER_EXPORT___.locals = {\n\t\"filesSidebarSubname\": `_filesSidebarSubname_yXZVi`,\n\t\"filesSidebarSubname__separator\": `_filesSidebarSubname__separator__OQUr`,\n\t\"filesSidebarSubname__userBubble\": `_filesSidebarSubname__userBubble_XTdWm`\n};\nexport default ___CSS_LOADER_EXPORT___;\n","import { getDefaultPropfind, getRootPath, resultToNode } from '@nextcloud/files/dav';\nimport { join } from 'path';\nimport logger from '../logger.ts';\nimport { useFilesStore } from '../store/files.ts';\nimport { getPinia } from '../store/index.ts';\nimport { useSearchStore } from '../store/search.ts';\nimport { client } from './WebdavClient.ts';\nimport { searchNodes } from './WebDavSearch.ts';\n/**\n * Get contents implementation for the files view.\n * This also allows to fetch local search results when the user is currently filtering.\n *\n * @param path - The path to query\n * @param options - Options\n * @param options.signal - Abort signal to cancel the request\n */\nexport async function getContents(path = '/', options) {\n const searchStore = useSearchStore(getPinia());\n if (searchStore.query.length < 3) {\n return await defaultGetContents(path, options);\n }\n return await getLocalSearch(path, searchStore.query, options?.signal);\n}\n/**\n * Generic `getContents` implementation for the users files.\n *\n * @param path - The path to get the contents\n * @param options - Options\n * @param options.signal - Abort signal to cancel the request\n */\nexport async function defaultGetContents(path, options) {\n path = join(getRootPath(), path);\n const propfindPayload = getDefaultPropfind();\n const contentsResponse = await client.getDirectoryContents(path, {\n details: true,\n data: propfindPayload,\n includeSelf: true,\n signal: options?.signal,\n });\n const root = contentsResponse.data[0];\n const contents = contentsResponse.data.slice(1);\n if (root?.filename !== path && `${root?.filename}/` !== path) {\n logger.debug(`Exepected \"${path}\" but got filename \"${root.filename}\" instead.`);\n throw new Error('Root node does not match requested path');\n }\n return {\n folder: resultToNode(root),\n contents: contents.map((result) => {\n try {\n return resultToNode(result);\n }\n catch (error) {\n logger.error(`Invalid node detected '${result.basename}'`, { error });\n return null;\n }\n }).filter(Boolean),\n };\n}\n/**\n * Get the local search results for the current folder.\n *\n * @param path - The path\n * @param query - The current search query\n * @param signal - The aboort signal\n */\nasync function getLocalSearch(path, query, signal) {\n const filesStore = useFilesStore(getPinia());\n let folder = filesStore.getDirectoryByPath('files', path);\n if (!folder) {\n const rootPath = join(getRootPath(), path);\n const stat = await client.stat(rootPath, { details: true });\n folder = resultToNode(stat.data);\n }\n const contents = await searchNodes(query, { dir: path, signal });\n return {\n folder,\n contents,\n };\n}\n","/**\n * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\nimport { subscribe } from '@nextcloud/event-bus';\nimport { File, FileType, getNavigation } from '@nextcloud/files';\nimport { dirname } from '@nextcloud/paths';\nimport { defineStore } from 'pinia';\nimport Vue from 'vue';\nimport logger from '../logger.ts';\nimport { useFilesStore } from './files.ts';\n/**\n *\n * @param args\n */\nexport function usePathsStore(...args) {\n const files = useFilesStore(...args);\n const store = defineStore('paths', {\n state: () => ({\n paths: {},\n }),\n getters: {\n getPath: (state) => {\n return (service, path) => {\n if (!state.paths[service]) {\n return undefined;\n }\n return state.paths[service][path];\n };\n },\n },\n actions: {\n addPath(payload) {\n // If it doesn't exists, init the service state\n if (!this.paths[payload.service]) {\n Vue.set(this.paths, payload.service, {});\n }\n // Now we can set the provided path\n Vue.set(this.paths[payload.service], payload.path, payload.source);\n },\n deletePath(service, path) {\n // skip if service does not exist\n if (!this.paths[service]) {\n return;\n }\n Vue.delete(this.paths[service], path);\n },\n onCreatedNode(node) {\n const service = getNavigation()?.active?.id || 'files';\n if (!node.fileid) {\n logger.error('Node has no fileid', { node });\n return;\n }\n // Only add path if it's a folder\n if (node.type === FileType.Folder) {\n this.addPath({\n service,\n path: node.path,\n source: node.source,\n });\n }\n // Update parent folder children if exists\n // If the folder is the root, get it and update it\n this.addNodeToParentChildren(node);\n },\n onDeletedNode(node) {\n const service = getNavigation()?.active?.id || 'files';\n if (node.type === FileType.Folder) {\n // Delete the path\n this.deletePath(service, node.path);\n }\n this.deleteNodeFromParentChildren(node);\n },\n onMovedNode({ node, oldSource }) {\n const service = getNavigation()?.active?.id || 'files';\n // Update the path of the node\n if (node.type === FileType.Folder) {\n // Delete the old path if it exists\n const oldPath = Object.entries(this.paths[service]).find(([, source]) => source === oldSource);\n if (oldPath?.[0]) {\n this.deletePath(service, oldPath[0]);\n }\n // Add the new path\n this.addPath({\n service,\n path: node.path,\n source: node.source,\n });\n }\n // Dummy simple clone of the renamed node from a previous state\n const oldNode = new File({\n source: oldSource,\n owner: node.owner,\n mime: node.mime,\n root: node.root,\n });\n this.deleteNodeFromParentChildren(oldNode);\n this.addNodeToParentChildren(node);\n },\n deleteNodeFromParentChildren(node) {\n const service = getNavigation()?.active?.id || 'files';\n // Update children of a root folder\n const parentSource = dirname(node.source);\n const folder = (node.dirname === '/' ? files.getRoot(service) : files.getNode(parentSource));\n if (folder) {\n // ensure sources are unique\n const children = new Set(folder._children ?? []);\n children.delete(node.source);\n Vue.set(folder, '_children', [...children.values()]);\n logger.debug('Children updated', { parent: folder, node, children: folder._children });\n return;\n }\n logger.debug('Parent path does not exists, skipping children update', { node });\n },\n addNodeToParentChildren(node) {\n const service = getNavigation()?.active?.id || 'files';\n // Update children of a root folder\n const parentSource = dirname(node.source);\n const folder = (node.dirname === '/' ? files.getRoot(service) : files.getNode(parentSource));\n if (folder) {\n // ensure sources are unique\n const children = new Set(folder._children ?? []);\n children.add(node.source);\n Vue.set(folder, '_children', [...children.values()]);\n logger.debug('Children updated', { parent: folder, node, children: folder._children });\n return;\n }\n logger.debug('Parent path does not exists, skipping children update', { node });\n },\n },\n });\n const pathsStore = store(...args);\n // Make sure we only register the listeners once\n if (!pathsStore._initialized) {\n subscribe('files:node:created', pathsStore.onCreatedNode);\n subscribe('files:node:deleted', pathsStore.onDeletedNode);\n subscribe('files:node:moved', pathsStore.onMovedNode);\n pathsStore._initialized = true;\n }\n return pathsStore;\n}\n","// Imports\nimport ___CSS_LOADER_API_SOURCEMAP_IMPORT___ from \"../../../../node_modules/css-loader/dist/runtime/sourceMaps.js\";\nimport ___CSS_LOADER_API_IMPORT___ from \"../../../../node_modules/css-loader/dist/runtime/api.js\";\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_SOURCEMAP_IMPORT___);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, `.toast-loading-icon{margin-inline-start:-4px;min-width:26px}.app-content[data-v-3b8f401d]{display:flex;overflow:hidden;flex-direction:column;max-height:100%;position:relative !important}.files-list__header[data-v-3b8f401d]{display:flex;align-items:center;flex:0 0;max-width:100%;margin-block:var(--app-navigation-padding, 4px);margin-inline:calc(var(--default-clickable-area, 44px) + 2*var(--app-navigation-padding, 4px)) var(--app-navigation-padding, 4px)}.files-list__header--public[data-v-3b8f401d]{margin-inline:0 var(--app-navigation-padding, 4px)}.files-list__header>*[data-v-3b8f401d]{flex:0 0}.files-list__header-share-button[data-v-3b8f401d]{color:var(--color-text-maxcontrast) !important}.files-list__header-share-button--shared[data-v-3b8f401d]{color:var(--color-main-text) !important}.files-list__header-actions[data-v-3b8f401d]{min-width:fit-content !important;margin-inline:calc(var(--default-grid-baseline)*2)}.files-list__before[data-v-3b8f401d]{display:flex;flex-direction:column;gap:calc(var(--default-grid-baseline)*2);margin-inline:calc(var(--default-clickable-area) + 2*var(--app-navigation-padding))}.files-list__empty-view-wrapper[data-v-3b8f401d]{display:flex;height:100%}.files-list__refresh-icon[data-v-3b8f401d]{flex:0 0 var(--default-clickable-area);width:var(--default-clickable-area);height:var(--default-clickable-area)}.files-list__loading-icon[data-v-3b8f401d]{margin:auto}`, \"\",{\"version\":3,\"sources\":[\"webpack://./apps/files/src/views/FilesList.vue\"],\"names\":[],\"mappings\":\"AACA,oBAEC,wBAAA,CAEA,cAAA,CAGD,8BAEC,YAAA,CACA,eAAA,CACA,qBAAA,CACA,eAAA,CACA,4BAAA,CAIA,qCACC,YAAA,CACA,kBAAA,CAEA,QAAA,CACA,cAAA,CAEA,+CAAA,CACA,iIAAA,CAEA,6CAEC,kDAAA,CAGD,uCAGC,QAAA,CAGD,kDACC,8CAAA,CAEA,0DACC,uCAAA,CAIF,6CACC,gCAAA,CACA,kDAAA,CAIF,qCACC,YAAA,CACA,qBAAA,CACA,wCAAA,CACA,mFAAA,CAGD,iDACC,YAAA,CACA,WAAA,CAGD,2CACC,sCAAA,CACA,mCAAA,CACA,oCAAA,CAGD,2CACC,WAAA\",\"sourcesContent\":[\"\\n:global(.toast-loading-icon) {\\n\\t// Reduce start margin (it was made for text but this is an icon)\\n\\tmargin-inline-start: -4px;\\n\\t// 16px icon + 5px on both sides\\n\\tmin-width: 26px;\\n}\\n\\n.app-content {\\n\\t// Virtual list needs to be full height and is scrollable\\n\\tdisplay: flex;\\n\\toverflow: hidden;\\n\\tflex-direction: column;\\n\\tmax-height: 100%;\\n\\tposition: relative !important;\\n}\\n\\n.files-list {\\n\\t&__header {\\n\\t\\tdisplay: flex;\\n\\t\\talign-items: center;\\n\\t\\t// Do not grow or shrink (vertically)\\n\\t\\tflex: 0 0;\\n\\t\\tmax-width: 100%;\\n\\t\\t// Align with the navigation toggle icon\\n\\t\\tmargin-block: var(--app-navigation-padding, 4px);\\n\\t\\tmargin-inline: calc(var(--default-clickable-area, 44px) + 2 * var(--app-navigation-padding, 4px)) var(--app-navigation-padding, 4px);\\n\\n\\t\\t&--public {\\n\\t\\t\\t// There is no navigation toggle on public shares\\n\\t\\t\\tmargin-inline: 0 var(--app-navigation-padding, 4px);\\n\\t\\t}\\n\\n\\t\\t>* {\\n\\t\\t\\t// Do not grow or shrink (horizontally)\\n\\t\\t\\t// Only the breadcrumbs shrinks\\n\\t\\t\\tflex: 0 0;\\n\\t\\t}\\n\\n\\t\\t&-share-button {\\n\\t\\t\\tcolor: var(--color-text-maxcontrast) !important;\\n\\n\\t\\t\\t&--shared {\\n\\t\\t\\t\\tcolor: var(--color-main-text) !important;\\n\\t\\t\\t}\\n\\t\\t}\\n\\n\\t\\t&-actions {\\n\\t\\t\\tmin-width: fit-content !important;\\n\\t\\t\\tmargin-inline: calc(var(--default-grid-baseline) * 2);\\n\\t\\t}\\n\\t}\\n\\n\\t&__before {\\n\\t\\tdisplay: flex;\\n\\t\\tflex-direction: column;\\n\\t\\tgap: calc(var(--default-grid-baseline) * 2);\\n\\t\\tmargin-inline: calc(var(--default-clickable-area) + 2 * var(--app-navigation-padding));\\n\\t}\\n\\n\\t&__empty-view-wrapper {\\n\\t\\tdisplay: flex;\\n\\t\\theight: 100%;\\n\\t}\\n\\n\\t&__refresh-icon {\\n\\t\\tflex: 0 0 var(--default-clickable-area);\\n\\t\\twidth: var(--default-clickable-area);\\n\\t\\theight: var(--default-clickable-area);\\n\\t}\\n\\n\\t&__loading-icon {\\n\\t\\tmargin: auto;\\n\\t}\\n}\\n\"],\"sourceRoot\":\"\"}]);\n// Exports\nexport default ___CSS_LOADER_EXPORT___;\n","