diff --git a/src/components/TemplateSection.vue b/src/components/TemplateSection.vue new file mode 100644 index 0000000000..fbb5e389fb --- /dev/null +++ b/src/components/TemplateSection.vue @@ -0,0 +1,165 @@ + + + + + + diff --git a/src/views/OfficeOverview.vue b/src/views/OfficeOverview.vue index 4b8666b040..c0c7e15515 100644 --- a/src/views/OfficeOverview.vue +++ b/src/views/OfficeOverview.vue @@ -8,9 +8,14 @@ @@ -28,10 +33,14 @@ + + + + +
+ + +
@@ -75,26 +105,30 @@ import { sortNodes } from '@nextcloud/files' import { loadState } from '@nextcloud/initial-state' import { generateUrl } from '@nextcloud/router' -import { NcAppContent, NcAppNavigation, NcAppNavigationItem, NcContent, NcDateTime, NcEmptyContent, NcLoadingIcon, NcTextField } from '@nextcloud/vue' +import { NcAppContent, NcAppNavigation, NcAppNavigationItem, NcButton, NcContent, NcDateTime, NcDialog, NcEmptyContent, NcLoadingIcon, NcTextField } from '@nextcloud/vue' import FileDocumentOutline from 'vue-material-design-icons/FileDocumentOutline.vue' import FileCard from '../components/FileCard.vue' -import { getAllOfficeFiles, filterByMimes } from '../services/officeFiles.js' -import { getTemplates } from '../services/templates.js' +import TemplateSection from '../components/TemplateSection.vue' +import { getAllOfficeFiles, filterByMimes, invalidateOfficeFilesCache } from '../services/officeFiles.js' +import { getTemplates, createFromTemplate } from '../services/templates.js' export default { name: 'OfficeOverview', components: { FileCard, + FileDocumentOutline, NcAppContent, NcAppNavigation, NcAppNavigationItem, + NcButton, NcContent, NcDateTime, + NcDialog, NcEmptyContent, NcLoadingIcon, NcTextField, - FileDocumentOutline, + TemplateSection, }, data() { @@ -106,6 +140,12 @@ export default { error: null, previewEnabled: loadState('richdocuments', 'previewEnabled', false), searchQuery: '', + showCreateDialog: false, + newFileName: '', + pendingCreator: null, + pendingTemplate: null, + creating: false, + createError: '', } }, @@ -137,6 +177,12 @@ export default { }, methods: { + categoryName(creator) { + const base = creator.label.replace(/^new\s+/i, '').trim() + const capitalized = base.charAt(0).toUpperCase() + base.slice(1) + return capitalized.endsWith('s') ? capitalized : capitalized + 's' + }, + setCreator(creator) { this.activeCreator = creator }, @@ -155,13 +201,53 @@ export default { } }, - async fetchAll() { + onTemplateSelect(creator, template) { + this.pendingCreator = creator + this.pendingTemplate = template + this.newFileName = creator.label.replace(/^New\s+/i, '') + creator.extension + this.createError = '' + this.showCreateDialog = true + this.$nextTick(() => { + const input = this.$refs.createInput?.$el?.querySelector('input') + if (input) { + input.focus() + input.setSelectionRange(0, this.newFileName.length - creator.extension.length) + } + }) + }, + + async doCreateFromTemplate() { + if (!this.newFileName.trim() || this.creating) { + return + } + this.creating = true + this.createError = '' + try { + const filePath = '/' + this.newFileName.trim() + const templatePath = this.pendingTemplate?.filename ?? '' + const templateType = this.pendingTemplate ? 'user' : 'user_system' + await createFromTemplate(filePath, templatePath, templateType) + this.showCreateDialog = false + const previousCreator = this.activeCreator + invalidateOfficeFilesCache() + await this.fetchAll(previousCreator) + } catch (e) { + this.createError = t('richdocuments', 'A file with that name already exists') + } finally { + this.creating = false + } + }, + + async fetchAll(restoreCreator = null) { this.loading = true this.error = null try { this.creators = await getTemplates() - this.activeCreator = this.creators[0] ?? null + const match = restoreCreator + ? this.creators.find(c => c.app === restoreCreator.app && c.extension === restoreCreator.extension) + : null + this.activeCreator = match ?? this.creators[0] ?? null if (this.creators.length > 0) { const allMimes = this.creators.flatMap(c => c.mimetypes) @@ -211,4 +297,19 @@ export default { max-width: 400px; margin: 0 auto; } + +.office-overview__create-form { + min-height: calc(2 * var(--default-clickable-area)); +} + +.office-overview__nav-icon { + display: flex; + width: 20px; + height: 20px; + + :deep(svg) { + width: 100%; + height: 100%; + } +}