chore(files_reminders): upgrade to 28 APIs

Signed-off-by: John Molakvoæ <skjnldsv@protonmail.com>
This commit is contained in:
John Molakvoæ 2023-10-25 09:43:58 +02:00
parent 72ffd4999a
commit cf6c921376
No known key found for this signature in database
GPG Key ID: 60C25B8C072916CF
17 changed files with 3812 additions and 3568 deletions

View File

@ -51,11 +51,11 @@
}"
:close-after-click="!isMenu(action.id)"
:data-cy-files-list-row-action="action.id"
:is-menu="isMenu(action.id)"
:title="action.title?.([source], currentView)"
@click="onActionClick(action)">
<template #icon>
<ChevronRightIcon v-if="isMenu(action.id)" />
<NcLoadingIcon v-else-if="loading === action.id" :size="18" />
<NcLoadingIcon v-if="loading === action.id" :size="18" />
<NcIconSvgWrapper v-else :svg="action.iconSvgInline([source], currentView)" />
</template>
{{ actionDisplayName(action) }}
@ -63,22 +63,22 @@
</template>
<!-- Submenu actions list-->
<template v-if="openedSubmenu && enabledSubmenuActions[openedSubmenu]">
<template v-if="openedSubmenu && enabledSubmenuActions[openedSubmenu?.id]">
<!-- Back to top-level button -->
<NcActionButton class="files-list__row-action-back" @click="openedSubmenu = ''">
<NcActionButton class="files-list__row-action-back" @click="openedSubmenu = null">
<template #icon>
<ArrowLeftIcon />
</template>
{{ t('files', 'Back') }}
{{ actionDisplayName(openedSubmenu) }}
</NcActionButton>
<NcActionSeparator />
<!-- Submenu actions -->
<NcActionButton v-for="action in enabledSubmenuActions[openedSubmenu]"
<NcActionButton v-for="action in enabledSubmenuActions[openedSubmenu?.id]"
:key="action.id"
:class="`files-list__row-action-${action.id}`"
class="files-list__row-action--submenu"
:close-after-click="true"
:close-after-click="false /* never close submenu, just go back */"
:data-cy-files-list-row-action="action.id"
:title="action.title?.([source], currentView)"
@click="onActionClick(action)">
@ -152,7 +152,7 @@ export default Vue.extend({
data() {
return {
openedSubmenu: '',
openedSubmenu: null as FileAction | null,
}
},
@ -267,10 +267,10 @@ export default Vue.extend({
return action.displayName([this.source], this.currentView)
},
async onActionClick(action) {
async onActionClick(action, isSubmenu = false) {
// If the action is a submenu, we open it
if (this.enabledSubmenuActions[action.id]) {
this.openedSubmenu = action.id
this.openedSubmenu = action
return
}
@ -299,6 +299,11 @@ export default Vue.extend({
// Reset the loading marker
this.$emit('update:loading', '')
Vue.set(this.source, 'status', undefined)
// If that was a submenu, we just go back after the action
if (isSubmenu) {
this.openedSubmenu = null
}
}
},
execDefaultAction(event) {

View File

@ -37,6 +37,7 @@
alt=""
class="files-list__row-icon-preview"
:class="{'files-list__row-icon-preview--loaded': backgroundFailed === false}"
loading="lazy"
:src="previewUrl"
@error="backgroundFailed = true"
@load="backgroundFailed = false">

View File

@ -63,39 +63,3 @@ registerRecentView()
// Register preview service worker
registerPreviewServiceWorker()
registerFileAction(new FileAction({
id: 'menu',
displayName: () => 'Menu',
iconSvgInline: () => MenuIcon,
exec: async () => true,
}))
registerFileAction(new FileAction({
id: 'submenu1',
displayName: () => 'Submenu 1',
iconSvgInline: () => MenuIcon,
exec: async () => alert('Hello 1'),
parent: 'menu',
}))
registerFileAction(new FileAction({
id: 'submenu2',
displayName: () => 'Submenu 2',
iconSvgInline: () => MenuIcon,
exec: async () => alert('Hello 2'),
parent: 'menu',
}))
registerFileAction(new FileAction({
id: 'submenu3',
displayName: () => 'Submenu 3',
iconSvgInline: () => MenuIcon,
exec: async () => alert('Hello 3'),
parent: 'menu',
}))
registerFileAction(new FileAction({
id: 'submenu4',
displayName: () => 'Submenu 4',
iconSvgInline: () => MenuIcon,
exec: async () => alert('Hello 4'),
parent: 'menu',
}))

View File

@ -36,8 +36,6 @@ if (!window.OCA.Files) {
Object.assign(window.OCA.Files, { Sidebar: new Sidebar() })
Object.assign(window.OCA.Files.Sidebar, { Tab })
console.debug('OCA.Files.Sidebar initialized')
window.addEventListener('DOMContentLoaded', function() {
const contentElement = document.querySelector('body > .content')
|| document.querySelector('body > #content')

View File

@ -47,6 +47,6 @@ class LoadAdditionalScriptsListener implements IEventListener {
return;
}
Util::addScript(Application::APP_ID, 'main');
Util::addInitScript(Application::APP_ID, 'init');
}
}

View File

@ -0,0 +1,45 @@
/**
* @copyright Copyright (c) 2023 John Molakvoæ <skjnldsv@protonmail.com>
*
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
* @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/>.
*
*/
import { FileAction, Node } from '@nextcloud/files'
import { translate as t } from '@nextcloud/l10n'
import CalendarClockSvg from '@mdi/svg/svg/calendar-clock.svg?raw'
import { SET_REMINDER_MENU_ID } from './setReminderMenuAction'
import { pickCustomDate } from '../services/customPicker'
export const action = new FileAction({
id: 'set-reminder-custom',
displayName: () => t('files', 'Set custom reminder'),
title: () => t('files_reminders', 'Set reminder at custom date & time'),
iconSvgInline: () => CalendarClockSvg,
enabled: () => true,
parent: SET_REMINDER_MENU_ID,
async exec(file: Node) {
pickCustomDate(file)
return null
},
// After presets
order: 22,
})

View File

@ -0,0 +1,40 @@
/**
* @copyright Copyright (c) 2023 John Molakvoæ <skjnldsv@protonmail.com>
*
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
* @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/>.
*
*/
import { FileAction } from '@nextcloud/files'
import { translate as t } from '@nextcloud/l10n'
import AlarmSvg from '@mdi/svg/svg/alarm.svg?raw'
export const SET_REMINDER_MENU_ID = 'set-reminder-menu'
export const action = new FileAction({
id: SET_REMINDER_MENU_ID,
displayName: () => t('files', 'Set reminder'),
iconSvgInline: () => AlarmSvg,
enabled: () => true,
async exec() {
return null
},
order: 20,
})

View File

@ -0,0 +1,38 @@
/**
* @copyright Copyright (c) 2023 John Molakvoæ <skjnldsv@protonmail.com>
*
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
* @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/>.
*
*/
// TODO: remove when/if the actions API supports a separator
// This the last preset action, so we need to add a separator
.files-list__row-action-set-reminder-3 {
margin-bottom: 13px;
&::after {
content: "";
margin: 3px 10px 3px 15px;
border-bottom: 1px solid var(--color-border-dark);
cursor: default;
display: flex;
height: 0;
position: absolute;
left: 0;
right: 0;
}
}

View File

@ -0,0 +1,106 @@
/**
* @copyright Copyright (c) 2023 John Molakvoæ <skjnldsv@protonmail.com>
*
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
* @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/>.
*
*/
import type { Node } from '@nextcloud/files'
import { FileAction } from '@nextcloud/files'
import { showError, showSuccess } from '@nextcloud/dialogs'
import { translate as t } from '@nextcloud/l10n'
import { DateTimePreset, getDateString, getDateTime, getVerboseDateString } from '../shared/utils'
import { logger } from '../shared/logger'
import { SET_REMINDER_MENU_ID } from './setReminderMenuAction'
import { setReminder } from '../services/reminderService'
import './setReminderSuggestionActions.scss'
interface ReminderOption {
dateTimePreset: DateTimePreset
label: string
ariaLabel: string
dateString?: string
action?: () => Promise<void>
}
const laterToday: ReminderOption = {
dateTimePreset: DateTimePreset.LaterToday,
label: t('files_reminders', 'Later today'),
ariaLabel: t('files_reminders', 'Set reminder for later today'),
}
const tomorrow: ReminderOption = {
dateTimePreset: DateTimePreset.Tomorrow,
label: t('files_reminders', 'Tomorrow'),
ariaLabel: t('files_reminders', 'Set reminder for tomorrow'),
}
const thisWeekend: ReminderOption = {
dateTimePreset: DateTimePreset.ThisWeekend,
label: t('files_reminders', 'This weekend'),
ariaLabel: t('files_reminders', 'Set reminder for this weekend'),
}
const nextWeek: ReminderOption = {
dateTimePreset: DateTimePreset.NextWeek,
label: t('files_reminders', 'Next week'),
ariaLabel: t('files_reminders', 'Set reminder for next week'),
}
// Generate the default preset actions
export const actions = [laterToday, tomorrow, thisWeekend, nextWeek].map((option): FileAction|null => {
const dateTime = getDateTime(option.dateTimePreset)
if (!dateTime) {
return null
}
return new FileAction({
id: `set-reminder-${option.dateTimePreset}`,
displayName: () => `${option.label} - ${getDateString(dateTime)}`,
title: () => `${option.ariaLabel} ${getVerboseDateString(dateTime)}`,
// Empty svg to hide the icon
iconSvgInline: () => '<svg></svg>',
enabled: () => true,
parent: SET_REMINDER_MENU_ID,
async exec(node: Node) {
// Can't really happen, but just in case™
if (!node.fileid) {
logger.error('Failed to set reminder, missing file id')
showError(t('files_reminders', 'Failed to set reminder'))
return null
}
// Set the reminder
try {
await setReminder(node.fileid, dateTime)
showSuccess(t('files_reminders', 'Reminder set for "{fileName}"', { fileName: node.basename }))
} catch (error) {
logger.error('Failed to set reminder', { error })
showError(t('files_reminders', 'Failed to set reminder'))
}
// Silent success as we display our own notification
return null
},
order: 21,
})
}).filter(Boolean) as FileAction[]

View File

@ -0,0 +1,199 @@
<!--
- @copyright 2023 Christopher Ng <chrng8@gmail.com>
-
- @author Christopher Ng <chrng8@gmail.com>
-
- @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>
<NcModal v-if="opened"
:out-transition="true"
size="small"
@close="onClose">
<form class="custom-reminder-modal" @submit.prevent="setCustom">
<h2 class="custom-reminder-modal__title">
{{ title }}
</h2>
<NcDateTimePickerNative id="set-custom-reminder"
v-model="customDueDate"
:label="label"
:min="nowDate"
:required="true"
type="datetime-local"
@input="onInput" />
<NcNoteCard v-if="isValid" type="info">
{{ t('files_reminders', 'We will remind you of this file') }}
<NcDateTime :timestamp="customDueDate" />
</NcNoteCard>
<NcNoteCard v-else type="error">
{{ t('files_reminders', 'Please choose a valid date & time') }}
</NcNoteCard>
<!-- Buttons -->
<div class="custom-reminder-modal__buttons">
<!-- Cancel pick -->
<NcButton @click="onClose">
{{ t('files_reminders', 'Cancel') }}
</NcButton>
<!-- Set reminder -->
<NcButton :disabled="!isValid" native-type="submit" type="primary">
{{ t('files_reminders', 'Set reminder') }}
</NcButton>
</div>
</form>
</NcModal>
</template>
<script lang="ts">
import type { Node } from '@nextcloud/files'
import { showError, showSuccess } from '@nextcloud/dialogs'
import { translate as t } from '@nextcloud/l10n'
import Vue from 'vue'
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
import NcDateTime from '@nextcloud/vue/dist/Components/NcDateTime.js'
import NcDateTimePickerNative from '@nextcloud/vue/dist/Components/NcDateTimePickerNative.js'
import NcModal from '@nextcloud/vue/dist/Components/NcModal.js'
import NcNoteCard from '@nextcloud/vue/dist/Components/NcNoteCard.js'
import { getDateString, getInitialCustomDueDate } from '../shared/utils.ts'
import { logger } from '../shared/logger.ts'
import { setReminder } from '../services/reminderService.ts'
export default Vue.extend({
name: 'SetCustomReminderModal',
components: {
NcButton,
NcDateTime,
NcDateTimePickerNative,
NcModal,
NcNoteCard,
},
data() {
return {
node: undefined as Node | undefined,
opened: false,
isValid: true,
customDueDate: getInitialCustomDueDate() as '' | Date,
nowDate: new Date(),
}
},
computed: {
fileId(): number {
return this.node.fileid
},
fileName(): string {
return this.node.basename
},
title() {
return t('files_reminders', 'Set reminder for "{fileName}"', { fileName: this.fileName })
},
label(): string {
return t('files_reminders', 'Set reminder at custom date & time')
},
clearAriaLabel(): string {
return t('files_reminders', 'Clear reminder')
},
},
methods: {
t,
getDateString,
/**
* Open the modal to set a custom reminder
* and reset the state.
* @param node The node to set a reminder for
*/
async open(node: Node): Promise<void> {
this.node = node
this.isValid = true
this.opened = true
this.customDueDate = getInitialCustomDueDate()
this.nowDate = new Date()
// Focus the input and show the picker after the animation
setTimeout(() => {
const input = document.getElementById('set-custom-reminder') as HTMLInputElement
input.focus()
input.showPicker()
}, 300)
},
async setCustom(): Promise<void> {
// Handle input cleared
if (this.customDueDate === '') {
showError(t('files_reminders', 'Please choose a valid date & time'))
return
}
try {
await setReminder(this.fileId, this.customDueDate)
showSuccess(t('files_reminders', 'Reminder set for "{fileName}"', { fileName: this.fileName }))
this.onClose()
} catch (error) {
logger.error('Failed to set reminder', { error })
showError(t('files_reminders', 'Failed to set reminder'))
}
},
onClose(): void {
this.opened = false
this.$emit('close')
},
onInput(): void {
const input = document.getElementById('set-custom-reminder') as HTMLInputElement
this.isValid = input.checkValidity()
},
},
})
</script>
<style lang="scss" scoped>
.custom-reminder-modal {
margin: 30px;
&__title {
font-size: 16px;
line-height: 2em;
}
&__buttons {
display: flex;
justify-content: flex-end;
margin-top: 30px;
button {
margin-left: 10px;
}
}
}
</style>

View File

@ -1,272 +0,0 @@
<!--
- @copyright 2023 Christopher Ng <chrng8@gmail.com>
-
- @author Christopher Ng <chrng8@gmail.com>
-
- @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>
<NcActions class="actions-secondary-vue"
:open.sync="open">
<NcActionButton @click="$emit('back')">
<template #icon>
<ArrowLeft :size="20" />
</template>
{{ t('files_reminders', 'Back') }}
</NcActionButton>
<NcActionButton v-if="Boolean(dueDate)"
:aria-label="clearAriaLabel"
@click="clear">
<template #icon>
<CloseCircleOutline :size="20" />
</template>
{{ t('files_reminders', 'Clear reminder') }} {{ getDateString(dueDate) }}
</NcActionButton>
<NcActionSeparator />
<NcActionButton v-for="({ label, ariaLabel, dateString, action }) in options"
:key="label"
:aria-label="ariaLabel"
@click="action">
{{ label }} {{ dateString }}
</NcActionButton>
<NcActionSeparator />
<NcActionInput type="datetime-local"
is-native-picker
:min="now"
v-model="customDueDate">
<template #icon>
<CalendarClock :size="20" />
</template>
</NcActionInput>
<NcActionButton :aria-label="customAriaLabel"
@click="setCustom">
<template #icon>
<Check :size="20" />
</template>
{{ t('files_reminders', 'Set custom reminder') }}
</NcActionButton>
</NcActions>
</template>
<script lang="ts">
import Vue, { type PropType } from 'vue'
import { translate as t } from '@nextcloud/l10n'
import { showError, showSuccess } from '@nextcloud/dialogs'
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
import NcActionInput from '@nextcloud/vue/dist/Components/NcActionInput.js'
import NcActions from '@nextcloud/vue/dist/Components/NcActions.js'
import NcActionSeparator from '@nextcloud/vue/dist/Components/NcActionSeparator.js'
import ArrowLeft from 'vue-material-design-icons/ArrowLeft.vue'
import CalendarClock from 'vue-material-design-icons/CalendarClock.vue'
import Check from 'vue-material-design-icons/Check.vue'
import CloseCircleOutline from 'vue-material-design-icons/CloseCircleOutline.vue'
import { clearReminder, setReminder } from '../services/reminderService.ts'
import {
DateTimePreset,
getDateString,
getDateTime,
getInitialCustomDueDate,
getVerboseDateString,
} from '../shared/utils.ts'
import { logger } from '../shared/logger.ts'
import type { FileAttributes } from '../shared/types.ts'
interface ReminderOption {
dateTimePreset: DateTimePreset
label: string
ariaLabel: string
dateString?: string
action?: () => Promise<void>
}
const laterToday: ReminderOption = {
dateTimePreset: DateTimePreset.LaterToday,
label: t('files_reminders', 'Later today'),
ariaLabel: t('files_reminders', 'Set reminder for later today'),
}
const tomorrow: ReminderOption = {
dateTimePreset: DateTimePreset.Tomorrow,
label: t('files_reminders', 'Tomorrow'),
ariaLabel: t('files_reminders', 'Set reminder for tomorrow'),
}
const thisWeekend: ReminderOption = {
dateTimePreset: DateTimePreset.ThisWeekend,
label: t('files_reminders', 'This weekend'),
ariaLabel: t('files_reminders', 'Set reminder for this weekend'),
}
const nextWeek: ReminderOption = {
dateTimePreset: DateTimePreset.NextWeek,
label: t('files_reminders', 'Next week'),
ariaLabel: t('files_reminders', 'Set reminder for next week'),
}
export default Vue.extend({
name: 'SetReminderActions',
components: {
ArrowLeft,
CalendarClock,
Check,
CloseCircleOutline,
NcActionButton,
NcActionInput,
NcActions,
NcActionSeparator,
},
props: {
file: {
type: Object as PropType<FileAttributes>,
required: true,
},
dueDate: {
type: Date as PropType<null | Date>,
default: null,
},
},
data() {
return {
open: true,
now: new Date(),
customDueDate: getInitialCustomDueDate() as '' | Date,
}
},
watch: {
open(isOpen) {
if (!isOpen) {
this.$emit('close')
}
},
},
computed: {
fileId(): number {
return this.file.id
},
fileName(): string {
return this.file.name
},
clearAriaLabel(): string {
return `${t('files_reminders', 'Clear reminder')} ${getVerboseDateString(this.dueDate as Date)}`
},
customAriaLabel(): null | string {
if (this.customDueDate === '') {
return null
}
return `${t('files_reminders', 'Set reminder at custom date & time')} ${getVerboseDateString(this.customDueDate)}`
},
options(): ReminderOption[] {
const computeOption = (option: ReminderOption): null | ReminderOption => {
const dateTime = getDateTime(option.dateTimePreset)
if (!dateTime) {
return null
}
return {
...option,
ariaLabel: `${option.ariaLabel} ${getVerboseDateString(dateTime)}`,
dateString: getDateString(dateTime),
action: () => this.set(dateTime),
}
}
const options = [
laterToday,
tomorrow,
thisWeekend,
nextWeek,
]
return options
.map(computeOption)
.filter(Boolean) as ReminderOption[]
},
},
methods: {
t,
getDateString,
async set(dueDate: Date): Promise<void> {
try {
await setReminder(this.fileId, dueDate)
showSuccess(t('files_reminders', 'Reminder set for "{fileName}"', { fileName: this.fileName }))
this.open = false
} catch (error) {
logger.error('Failed to set reminder', { error })
showError(t('files_reminders', 'Failed to set reminder'))
}
},
async setCustom(): Promise<void> {
// Handle input cleared
if (this.customDueDate === '') {
showError(t('files_reminders', 'Please choose a valid date & time'))
return
}
try {
await setReminder(this.fileId, this.customDueDate)
showSuccess(t('files_reminders', 'Reminder set for "{fileName}"', { fileName: this.fileName }))
this.open = false
} catch (error) {
logger.error('Failed to set reminder', { error })
showError(t('files_reminders', 'Failed to set reminder'))
}
},
async clear(): Promise<void> {
try {
await clearReminder(this.fileId)
showSuccess(t('files_reminders', 'Reminder cleared'))
this.open = false
} catch (error) {
logger.error('Failed to clear reminder', { error })
showError(t('files_reminders', 'Failed to clear reminder'))
}
},
},
})
</script>
<style lang="scss" scoped>
.actions-secondary-vue {
display: block !important;
float: right !important;
padding: 5px 0 0 4px !important;
pointer-events: none !important; // prevent activation of file row
}
</style>

View File

@ -0,0 +1,29 @@
/**
* @copyright 2023 Christopher Ng <chrng8@gmail.com>
*
* @author Christopher Ng <chrng8@gmail.com>
*
* @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/>.
*
*/
import { registerFileAction } from '@nextcloud/files'
import { action as menuAction } from './actions/setReminderMenuAction'
import { actions as suggestionActions } from './actions/setReminderSuggestionActions'
import { action as customAction } from './actions/setReminderCustomAction'
registerFileAction(menuAction)
registerFileAction(customAction)
suggestionActions.forEach((action) => registerFileAction(action))

View File

@ -1,102 +0,0 @@
/**
* @copyright 2023 Christopher Ng <chrng8@gmail.com>
*
* @author Christopher Ng <chrng8@gmail.com>
*
* @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/>.
*
*/
import Vue, { type ComponentInstance } from 'vue'
import { subscribe } from '@nextcloud/event-bus'
import { showError } from '@nextcloud/dialogs'
import { translate as t } from '@nextcloud/l10n'
import SetReminderActionsComponent from './components/SetReminderActions.vue'
import { getReminder } from './services/reminderService.js'
import { logger } from './shared/logger.js'
import type { FileAttributes } from './shared/types.js'
interface FileContext {
[key: string]: any
$file: JQuery<HTMLTableRowElement>
fileInfoModel: {
[key: string]: any
attributes: FileAttributes
}
}
interface EventPayload {
el: HTMLDivElement
context: FileContext
}
const handleOpen = async (payload: EventPayload) => {
const fileId = payload.context.fileInfoModel.attributes.id
const menuEl = payload.context.$file[0].querySelector('.fileactions .action-menu') as HTMLLinkElement
const linkEl = payload.el.querySelector('.action-setreminder-container .action-setreminder') as HTMLLinkElement
let dueDate: null | Date = null
let error: null | any = null
try {
dueDate = (await getReminder(fileId)).dueDate
} catch (e) {
error = e
logger.error(`Failed to load reminder for file with id: ${fileId}`, { error })
}
linkEl.addEventListener('click', (_event) => {
if (error) {
showError(t('files_reminders', 'Failed to load reminder'))
throw Error()
}
const mountPoint = document.createElement('div')
const SetReminderActions = Vue.extend(SetReminderActionsComponent)
const origDisplay = menuEl.style.display
menuEl.style.display = 'none'
menuEl.insertAdjacentElement('afterend', mountPoint)
const propsData = {
file: payload.context.fileInfoModel.attributes,
dueDate,
}
const actions = (new SetReminderActions({ propsData }) as ComponentInstance)
.$mount(mountPoint)
const cleanUp = () => {
actions.$destroy() // destroy popper
actions.$el.remove() // remove action menu button
menuEl.style.display = origDisplay
}
actions.$once('back', () => {
cleanUp()
menuEl.click() // reopen original actions menu
})
actions.$once('close', () => {
cleanUp()
})
}, {
once: true,
})
}
subscribe('files:action-menu:opened', handleOpen)

View File

@ -0,0 +1,48 @@
/**
* @copyright Copyright (c) 2023 John Molakvoæ <skjnldsv@protonmail.com>
*
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
* @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/>.
*
*/
import type { Node } from '@nextcloud/files'
import Vue from 'vue'
import SetCustomReminderModal from '../components/SetCustomReminderModal.vue'
const View = Vue.extend(SetCustomReminderModal)
const mount = document.createElement('div')
mount.id = 'set-custom-reminder-modal'
document.body.appendChild(mount)
// Create a new Vue instance and mount it to our modal container
const CustomReminderModal = new View({
name: 'SetCustomReminderModal',
el: mount,
})
export const pickCustomDate = async (node: Node): Promise<void> => {
console.debug('CustomReminderModal', mount, CustomReminderModal)
CustomReminderModal.open(node)
// Wait for the modal to close
return new Promise((resolve) => {
CustomReminderModal.$on('close', resolve)
})
}

6431
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -46,7 +46,7 @@
"@nextcloud/capabilities": "^1.0.4",
"@nextcloud/dialogs": "^5.0.0-beta.6",
"@nextcloud/event-bus": "^3.1.0",
"@nextcloud/files": "^3.0.0-beta.26",
"@nextcloud/files": "^3.0.0-beta.27",
"@nextcloud/initial-state": "^2.0.0",
"@nextcloud/l10n": "^2.1.0",
"@nextcloud/logger": "^2.5.0",

View File

@ -60,7 +60,7 @@ module.exports = {
init: path.join(__dirname, 'apps/files_external/src', 'init.ts'),
},
files_reminders: {
main: path.join(__dirname, 'apps/files_reminders/src', 'main.ts'),
init: path.join(__dirname, 'apps/files_reminders/src', 'init.ts'),
},
files_sharing: {
additionalScripts: path.join(__dirname, 'apps/files_sharing/src', 'additionalScripts.js'),