diff options
Diffstat (limited to 'apps/files_reminders/src')
-rw-r--r-- | apps/files_reminders/src/actions/clearReminderAction.ts | 54 | ||||
-rw-r--r-- | apps/files_reminders/src/actions/reminderStatusAction.ts | 45 | ||||
-rw-r--r-- | apps/files_reminders/src/actions/setReminderCustomAction.ts | 44 | ||||
-rw-r--r-- | apps/files_reminders/src/actions/setReminderMenuAction.ts | 39 | ||||
-rw-r--r-- | apps/files_reminders/src/actions/setReminderSuggestionActions.scss | 27 | ||||
-rw-r--r-- | apps/files_reminders/src/actions/setReminderSuggestionActions.ts | 57 | ||||
-rw-r--r-- | apps/files_reminders/src/components/SetCustomReminderModal.vue | 146 | ||||
-rw-r--r-- | apps/files_reminders/src/init.ts | 30 | ||||
-rw-r--r-- | apps/files_reminders/src/services/customPicker.ts | 25 | ||||
-rw-r--r-- | apps/files_reminders/src/services/reminderService.ts | 21 | ||||
-rw-r--r-- | apps/files_reminders/src/shared/logger.ts | 21 | ||||
-rw-r--r-- | apps/files_reminders/src/shared/types.ts | 23 | ||||
-rw-r--r-- | apps/files_reminders/src/shared/utils.ts | 142 |
13 files changed, 345 insertions, 329 deletions
diff --git a/apps/files_reminders/src/actions/clearReminderAction.ts b/apps/files_reminders/src/actions/clearReminderAction.ts new file mode 100644 index 00000000000..148861999f4 --- /dev/null +++ b/apps/files_reminders/src/actions/clearReminderAction.ts @@ -0,0 +1,54 @@ +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import Vue from 'vue' +import { FileAction, type Node } from '@nextcloud/files' +import { emit } from '@nextcloud/event-bus' +import { translate as t } from '@nextcloud/l10n' + +import AlarmOffSvg from '@mdi/svg/svg/alarm-off.svg?raw' + +import { clearReminder } from '../services/reminderService.ts' +import { getVerboseDateString } from '../shared/utils.ts' + +export const action = new FileAction({ + id: 'clear-reminder', + + displayName: () => t('files_reminders', 'Clear reminder'), + + title: (nodes: Node[]) => { + const node = nodes.at(0)! + const dueDate = new Date(node.attributes['reminder-due-date']) + return `${t('files_reminders', 'Clear reminder')} – ${getVerboseDateString(dueDate)}` + }, + + iconSvgInline: () => AlarmOffSvg, + + enabled: (nodes: Node[]) => { + // Only allow on a single node + if (nodes.length !== 1) { + return false + } + const node = nodes.at(0)! + const dueDate = node.attributes['reminder-due-date'] + return Boolean(dueDate) + }, + + async exec(node: Node) { + if (node.fileid) { + try { + await clearReminder(node.fileid) + Vue.set(node.attributes, 'reminder-due-date', '') + emit('files:node:updated', node) + return true + } catch (error) { + return false + } + } + return null + }, + + order: 19, +}) diff --git a/apps/files_reminders/src/actions/reminderStatusAction.ts b/apps/files_reminders/src/actions/reminderStatusAction.ts new file mode 100644 index 00000000000..6a2c9943d3b --- /dev/null +++ b/apps/files_reminders/src/actions/reminderStatusAction.ts @@ -0,0 +1,45 @@ +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { FileAction, type Node } from '@nextcloud/files' +import { translate as t } from '@nextcloud/l10n' + +import AlarmSvg from '@mdi/svg/svg/alarm.svg?raw' + +import { pickCustomDate } from '../services/customPicker.ts' +import { getVerboseDateString } from '../shared/utils.ts' + +export const action = new FileAction({ + id: 'reminder-status', + + inline: () => true, + + displayName: () => '', + + title: (nodes: Node[]) => { + const node = nodes.at(0)! + const dueDate = new Date(node.attributes['reminder-due-date']) + return `${t('files_reminders', 'Reminder set')} – ${getVerboseDateString(dueDate)}` + }, + + iconSvgInline: () => AlarmSvg, + + enabled: (nodes: Node[]) => { + // Only allow on a single node + if (nodes.length !== 1) { + return false + } + const node = nodes.at(0)! + const dueDate = node.attributes['reminder-due-date'] + return Boolean(dueDate) + }, + + async exec(node: Node) { + pickCustomDate(node) + return null + }, + + order: -15, +}) diff --git a/apps/files_reminders/src/actions/setReminderCustomAction.ts b/apps/files_reminders/src/actions/setReminderCustomAction.ts index efcf70dc311..ea21293ee52 100644 --- a/apps/files_reminders/src/actions/setReminderCustomAction.ts +++ b/apps/files_reminders/src/actions/setReminderCustomAction.ts @@ -1,25 +1,11 @@ /** - * @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/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { FileAction, Node } from '@nextcloud/files' + +import type { Node, View } from '@nextcloud/files' + +import { FileAction } from '@nextcloud/files' import { translate as t } from '@nextcloud/l10n' import CalendarClockSvg from '@mdi/svg/svg/calendar-clock.svg?raw' @@ -28,11 +14,23 @@ 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'), + displayName: () => t('files_reminders', 'Custom reminder'), + title: () => t('files_reminders', 'Reminder at custom date & time'), iconSvgInline: () => CalendarClockSvg, - enabled: () => true, + enabled: (nodes: Node[], view: View) => { + if (view.id === 'trashbin') { + return false + } + // Only allow on a single node + if (nodes.length !== 1) { + return false + } + const node = nodes.at(0)! + const dueDate = node.attributes['reminder-due-date'] + return dueDate !== undefined + }, + parent: SET_REMINDER_MENU_ID, async exec(file: Node) { diff --git a/apps/files_reminders/src/actions/setReminderMenuAction.ts b/apps/files_reminders/src/actions/setReminderMenuAction.ts index 11810413db1..d6ddcd90677 100644 --- a/apps/files_reminders/src/actions/setReminderMenuAction.ts +++ b/apps/files_reminders/src/actions/setReminderMenuAction.ts @@ -1,24 +1,10 @@ /** - * @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/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ + +import type { Node, View } from '@nextcloud/files' + import { FileAction } from '@nextcloud/files' import { translate as t } from '@nextcloud/l10n' import AlarmSvg from '@mdi/svg/svg/alarm.svg?raw' @@ -27,10 +13,21 @@ export const SET_REMINDER_MENU_ID = 'set-reminder-menu' export const action = new FileAction({ id: SET_REMINDER_MENU_ID, - displayName: () => t('files', 'Set reminder'), + displayName: () => t('files_reminders', 'Set reminder'), iconSvgInline: () => AlarmSvg, - enabled: () => true, + enabled: (nodes: Node[], view: View) => { + if (view.id === 'trashbin') { + return false + } + // Only allow on a single node + if (nodes.length !== 1) { + return false + } + const node = nodes.at(0)! + const dueDate = node.attributes['reminder-due-date'] + return dueDate !== undefined + }, async exec() { return null diff --git a/apps/files_reminders/src/actions/setReminderSuggestionActions.scss b/apps/files_reminders/src/actions/setReminderSuggestionActions.scss index 93a94bafa2b..1327500c964 100644 --- a/apps/files_reminders/src/actions/setReminderSuggestionActions.scss +++ b/apps/files_reminders/src/actions/setReminderSuggestionActions.scss @@ -1,23 +1,6 @@ /** - * @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/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ // TODO: remove when/if the actions API supports a separator // This the last preset action, so we need to add a separator @@ -27,14 +10,14 @@ &::before { content: ""; - margin: 3px 10px 3px 15px; + margin-block: 3px; + margin-inline: 15px 10px; border-bottom: 1px solid var(--color-border-dark); cursor: default; display: flex; height: 0; position: absolute; - left: 0; - right: 0; + inset-inline: 0; top: -10px; } } diff --git a/apps/files_reminders/src/actions/setReminderSuggestionActions.ts b/apps/files_reminders/src/actions/setReminderSuggestionActions.ts index eda95205182..f92b2f89108 100644 --- a/apps/files_reminders/src/actions/setReminderSuggestionActions.ts +++ b/apps/files_reminders/src/actions/setReminderSuggestionActions.ts @@ -1,27 +1,13 @@ /** - * @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/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ -import type { Node } from '@nextcloud/files' + +import Vue from 'vue' +import type { Node, View } from '@nextcloud/files' import { FileAction } from '@nextcloud/files' +import { emit } from '@nextcloud/event-bus' import { showError, showSuccess } from '@nextcloud/dialogs' import { translate as t } from '@nextcloud/l10n' @@ -45,7 +31,7 @@ const laterToday: ReminderOption = { label: t('files_reminders', 'Later today'), ariaLabel: t('files_reminders', 'Set reminder for later today'), dateString: '', - verboseDateString: '' + verboseDateString: '', } const tomorrow: ReminderOption = { @@ -53,7 +39,7 @@ const tomorrow: ReminderOption = { label: t('files_reminders', 'Tomorrow'), ariaLabel: t('files_reminders', 'Set reminder for tomorrow'), dateString: '', - verboseDateString: '' + verboseDateString: '', } const thisWeekend: ReminderOption = { @@ -61,7 +47,7 @@ const thisWeekend: ReminderOption = { label: t('files_reminders', 'This weekend'), ariaLabel: t('files_reminders', 'Set reminder for this weekend'), dateString: '', - verboseDateString: '' + verboseDateString: '', } const nextWeek: ReminderOption = { @@ -69,7 +55,7 @@ const nextWeek: ReminderOption = { label: t('files_reminders', 'Next week'), ariaLabel: t('files_reminders', 'Set reminder for next week'), dateString: '', - verboseDateString: '' + verboseDateString: '', } /** @@ -88,7 +74,19 @@ const generateFileAction = (option: ReminderOption): FileAction|null => { // Empty svg to hide the icon iconSvgInline: () => '<svg></svg>', - enabled: () => Boolean(getDateTime(option.dateTimePreset)), + enabled: (nodes: Node[], view: View) => { + if (view.id === 'trashbin') { + return false + } + // Only allow on a single node + if (nodes.length !== 1) { + return false + } + const node = nodes.at(0)! + const dueDate = node.attributes['reminder-due-date'] + return dueDate !== undefined && Boolean(getDateTime(option.dateTimePreset)) + }, + parent: SET_REMINDER_MENU_ID, async exec(node: Node) { @@ -101,7 +99,10 @@ const generateFileAction = (option: ReminderOption): FileAction|null => { // Set the reminder try { - await setReminder(node.fileid, getDateTime(option.dateTimePreset)!) + const dateTime = getDateTime(option.dateTimePreset)! + await setReminder(node.fileid, dateTime) + Vue.set(node.attributes, 'reminder-due-date', dateTime.toISOString()) + emit('files:node:updated', node) showSuccess(t('files_reminders', 'Reminder set for "{fileName}"', { fileName: node.basename })) } catch (error) { logger.error('Failed to set reminder', { error }) @@ -123,14 +124,14 @@ const generateFileAction = (option: ReminderOption): FileAction|null => { } option.dateString = getDateString(dateTime) option.verboseDateString = getVerboseDateString(dateTime) - + + // Update the date string every 30 minutes setInterval(() => { const dateTime = getDateTime(option.dateTimePreset) if (!dateTime) { return } - + // update the submenu remind options strings option.dateString = getDateString(dateTime) option.verboseDateString = getVerboseDateString(dateTime) diff --git a/apps/files_reminders/src/components/SetCustomReminderModal.vue b/apps/files_reminders/src/components/SetCustomReminderModal.vue index 4e3c5fb0fca..59c0886a009 100644 --- a/apps/files_reminders/src/components/SetCustomReminderModal.vue +++ b/apps/files_reminders/src/components/SetCustomReminderModal.vue @@ -1,35 +1,18 @@ <!-- - - @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/>. - - + - SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + - SPDX-License-Identifier: AGPL-3.0-or-later --> <template> - <NcModal v-if="opened" + <NcDialog v-if="opened" + :name="name" :out-transition="true" size="small" - @close="onClose"> - <form class="custom-reminder-modal" @submit.prevent="setCustom"> - <h2 class="custom-reminder-modal__title"> - {{ title }} - </h2> - + close-on-click-outside + @closing="onClose"> + <form id="set-custom-reminder-form" + class="custom-reminder-modal" + @submit.prevent="setCustom"> <NcDateTimePickerNative id="set-custom-reminder" v-model="customDueDate" :label="label" @@ -46,38 +29,45 @@ <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 #actions> + <!-- Cancel pick --> + <NcButton type="tertiary" @click="onClose"> + {{ t('files_reminders', 'Cancel') }} + </NcButton> + + <!-- Clear reminder --> + <NcButton v-if="hasDueDate" @click="clear"> + {{ t('files_reminders', 'Clear reminder') }} + </NcButton> + + <!-- Set reminder --> + <NcButton :disabled="!isValid" + type="primary" + form="set-custom-reminder-form" + native-type="submit"> + {{ t('files_reminders', 'Set reminder') }} + </NcButton> + </template> + </NcDialog> </template> <script lang="ts"> +import Vue from 'vue' import type { Node } from '@nextcloud/files' +import { emit } from '@nextcloud/event-bus' 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 NcButton from '@nextcloud/vue/components/NcButton' +import NcDateTime from '@nextcloud/vue/components/NcDateTime' +import NcDateTimePickerNative from '@nextcloud/vue/components/NcDateTimePickerNative' +import NcDialog from '@nextcloud/vue/components/NcDialog' +import NcNoteCard from '@nextcloud/vue/components/NcNoteCard' import { getDateString, getInitialCustomDueDate } from '../shared/utils.ts' import { logger } from '../shared/logger.ts' -import { setReminder } from '../services/reminderService.ts' +import { clearReminder, setReminder } from '../services/reminderService.ts' export default Vue.extend({ name: 'SetCustomReminderModal', @@ -86,36 +76,37 @@ export default Vue.extend({ NcButton, NcDateTime, NcDateTimePickerNative, - NcModal, + NcDialog, NcNoteCard, }, data() { return { node: undefined as Node | undefined, + hasDueDate: false, opened: false, isValid: true, - customDueDate: getInitialCustomDueDate() as '' | Date, + customDueDate: null as null | Date, nowDate: new Date(), } }, computed: { - fileId(): number { - return this.node.fileid + fileId(): number|undefined { + return this.node?.fileid }, - fileName(): string { - return this.node.basename + fileName(): string|undefined { + return this.node?.basename }, - title() { - return t('files_reminders', 'Set reminder for "{fileName}"', { fileName: this.fileName }) + name() { + return this.fileName ? t('files_reminders', 'Set reminder for "{fileName}"', { fileName: this.fileName }) : '' }, label(): string { - return t('files_reminders', 'Set reminder at custom date & time') + return t('files_reminders', 'Reminder at custom date & time') }, clearAriaLabel(): string { @@ -132,18 +123,23 @@ export default Vue.extend({ * and reset the state. * @param node The node to set a reminder for */ - async open(node: Node): Promise<void> { + open(node: Node): void { + const dueDate = node.attributes['reminder-due-date'] ? new Date(node.attributes['reminder-due-date']) : null + this.node = node + this.hasDueDate = Boolean(dueDate) this.isValid = true this.opened = true - this.customDueDate = getInitialCustomDueDate() + this.customDueDate = dueDate ?? 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() + if (!this.hasDueDate) { + input.showPicker() + } }, 300) }, @@ -156,6 +152,8 @@ export default Vue.extend({ try { await setReminder(this.fileId, this.customDueDate) + Vue.set(this.node.attributes, 'reminder-due-date', this.customDueDate.toISOString()) + emit('files:node:updated', this.node) showSuccess(t('files_reminders', 'Reminder set for "{fileName}"', { fileName: this.fileName })) this.onClose() } catch (error) { @@ -164,6 +162,19 @@ export default Vue.extend({ } }, + async clear(): Promise<void> { + try { + await clearReminder(this.fileId) + Vue.set(this.node.attributes, 'reminder-due-date', '') + emit('files:node:updated', this.node) + showSuccess(t('files_reminders', 'Reminder cleared for "{fileName}"', { fileName: this.fileName })) + this.onClose() + } catch (error) { + logger.error('Failed to clear reminder', { error }) + showError(t('files_reminders', 'Failed to clear reminder')) + } + }, + onClose(): void { this.opened = false this.$emit('close') @@ -179,21 +190,6 @@ export default Vue.extend({ <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; - } - } + margin: 0 12px; } </style> diff --git a/apps/files_reminders/src/init.ts b/apps/files_reminders/src/init.ts index 59f5d23ebe3..17da254d0f2 100644 --- a/apps/files_reminders/src/init.ts +++ b/apps/files_reminders/src/init.ts @@ -1,29 +1,19 @@ /** - * @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/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { registerFileAction } from '@nextcloud/files' + +import { registerDavProperty, registerFileAction } from '@nextcloud/files' +import { action as statusAction } from './actions/reminderStatusAction' +import { action as clearAction } from './actions/clearReminderAction' import { action as menuAction } from './actions/setReminderMenuAction' import { actions as suggestionActions } from './actions/setReminderSuggestionActions' import { action as customAction } from './actions/setReminderCustomAction' +registerDavProperty('nc:reminder-due-date', { nc: 'http://nextcloud.org/ns' }) + +registerFileAction(statusAction) +registerFileAction(clearAction) registerFileAction(menuAction) registerFileAction(customAction) suggestionActions.forEach((action) => registerFileAction(action)) diff --git a/apps/files_reminders/src/services/customPicker.ts b/apps/files_reminders/src/services/customPicker.ts index 46a0f917c0c..5cefffe39a5 100644 --- a/apps/files_reminders/src/services/customPicker.ts +++ b/apps/files_reminders/src/services/customPicker.ts @@ -1,23 +1,6 @@ /** - * @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/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ import type { Node } from '@nextcloud/files' @@ -36,11 +19,11 @@ const CustomReminderModal = new View({ el: mount, }) -export const pickCustomDate = async (node: Node): Promise<void> => { +export const pickCustomDate = (node: Node): Promise<void> => { CustomReminderModal.open(node) // Wait for the modal to close return new Promise((resolve) => { - CustomReminderModal.$on('close', resolve) + CustomReminderModal.$once('close', resolve) }) } diff --git a/apps/files_reminders/src/services/reminderService.ts b/apps/files_reminders/src/services/reminderService.ts index 06163b601cb..9f58d1bdae3 100644 --- a/apps/files_reminders/src/services/reminderService.ts +++ b/apps/files_reminders/src/services/reminderService.ts @@ -1,23 +1,6 @@ /** - * @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/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ import axios from '@nextcloud/axios' diff --git a/apps/files_reminders/src/shared/logger.ts b/apps/files_reminders/src/shared/logger.ts index 116acf0c81e..79d663cca16 100644 --- a/apps/files_reminders/src/shared/logger.ts +++ b/apps/files_reminders/src/shared/logger.ts @@ -1,23 +1,6 @@ /** - * @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/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ import { getLoggerBuilder } from '@nextcloud/logger' diff --git a/apps/files_reminders/src/shared/types.ts b/apps/files_reminders/src/shared/types.ts index c5725fd1d06..f8da6f6aed0 100644 --- a/apps/files_reminders/src/shared/types.ts +++ b/apps/files_reminders/src/shared/types.ts @@ -1,27 +1,10 @@ /** - * @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/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ export interface FileAttributes { - [key: string]: any + [key: string]: unknown id: number name: string } diff --git a/apps/files_reminders/src/shared/utils.ts b/apps/files_reminders/src/shared/utils.ts index 86182ba5106..5d583ad3ddd 100644 --- a/apps/files_reminders/src/shared/utils.ts +++ b/apps/files_reminders/src/shared/utils.ts @@ -1,26 +1,8 @@ /** - * @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/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ -import moment from '@nextcloud/moment' import { getCanonicalLocale } from '@nextcloud/l10n' export enum DateTimePreset { @@ -30,58 +12,82 @@ export enum DateTimePreset { NextWeek = 'next-week', } +const getFirstWorkdayOfWeek = () => { + const now = new Date() + now.setHours(0, 0, 0, 0) + now.setDate(now.getDate() - now.getDay() + 1) + return new Date(now) +} + +const getWeek = (date: Date) => { + const dateClone = new Date(date) + dateClone.setHours(0, 0, 0, 0) + const firstDayOfYear = new Date(date.getFullYear(), 0, 1, 0, 0, 0, 0) + const daysFromFirstDay = (date.getTime() - firstDayOfYear.getTime()) / 86400000 + return Math.ceil((daysFromFirstDay + firstDayOfYear.getDay() + 1) / 7) +} + +const isSameWeek = (a: Date, b: Date) => { + return getWeek(a) === getWeek(b) + && a.getFullYear() === b.getFullYear() +} + +const isSameDate = (a: Date, b: Date) => { + return a.getDate() === b.getDate() + && a.getMonth() === b.getMonth() + && a.getFullYear() === b.getFullYear() +} + export const getDateTime = (dateTime: DateTimePreset): null | Date => { const matchPreset: Record<DateTimePreset, () => null | Date> = { [DateTimePreset.LaterToday]: () => { - const now = moment() - const evening = moment() - .startOf('day') - .add(18, 'hour') - const cutoff = evening - .clone() - .subtract(1, 'hour') - if (now.isSameOrAfter(cutoff)) { + const now = new Date() + const evening = new Date() + evening.setHours(18, 0, 0, 0) + const cutoff = new Date() + cutoff.setHours(17, 0, 0, 0) + if (now >= cutoff) { return null } - return evening.toDate() + return evening }, [DateTimePreset.Tomorrow]: () => { - const day = moment() - .add(1, 'day') - .startOf('day') - .add(8, 'hour') - return day.toDate() + const now = new Date() + const day = new Date() + day.setDate(now.getDate() + 1) + day.setHours(8, 0, 0, 0) + return day }, [DateTimePreset.ThisWeekend]: () => { - const today = moment() + const today = new Date() if ( [ 5, // Friday 6, // Saturday - 7, // Sunday - ].includes(today.isoWeekday()) + 0, // Sunday + ].includes(today.getDay()) ) { return null } - const saturday = moment() - .startOf('isoWeek') - .add(5, 'day') - .add(8, 'hour') - return saturday.toDate() + const saturday = new Date() + const firstWorkdayOfWeek = getFirstWorkdayOfWeek() + saturday.setDate(firstWorkdayOfWeek.getDate() + 5) + saturday.setHours(8, 0, 0, 0) + return saturday }, [DateTimePreset.NextWeek]: () => { - const today = moment() - if (today.isoWeekday() === 7) { // Sunday + const today = new Date() + if (today.getDay() === 0) { // Sunday return null } - const workday = moment() - .startOf('isoWeek') - .add(1, 'week') - .add(8, 'hour') - return workday.toDate() + const workday = new Date() + const firstWorkdayOfWeek = getFirstWorkdayOfWeek() + workday.setDate(firstWorkdayOfWeek.getDate() + 7) + workday.setHours(8, 0, 0, 0) + return workday }, } @@ -89,11 +95,10 @@ export const getDateTime = (dateTime: DateTimePreset): null | Date => { } export const getInitialCustomDueDate = (): Date => { - const hour = moment().get('hour') - const dueDate = moment() - .startOf('day') - .add(hour + 2, 'hour') - return dueDate.toDate() + const now = new Date() + const dueDate = new Date() + dueDate.setHours(now.getHours() + 2, 0, 0, 0) + return dueDate } export const getDateString = (dueDate: Date): string => { @@ -102,17 +107,16 @@ export const getDateString = (dueDate: Date): string => { minute: '2-digit', } - const dueDateMoment = moment(dueDate) - const today = moment() + const today = new Date() - if (!dueDateMoment.isSame(today, 'date')) { + if (!isSameDate(dueDate, today)) { formatOptions = { ...formatOptions, weekday: 'short', } } - if (!dueDateMoment.isSame(today, 'week')) { + if (!isSameWeek(dueDate, today)) { formatOptions = { ...formatOptions, month: 'short', @@ -120,6 +124,13 @@ export const getDateString = (dueDate: Date): string => { } } + if (dueDate.getFullYear() !== today.getFullYear()) { + formatOptions = { + ...formatOptions, + year: 'numeric', + } + } + return dueDate.toLocaleString( getCanonicalLocale(), formatOptions, @@ -127,12 +138,21 @@ export const getDateString = (dueDate: Date): string => { } export const getVerboseDateString = (dueDate: Date): string => { - const formatOptions: Intl.DateTimeFormatOptions = { + let formatOptions: Intl.DateTimeFormatOptions = { + month: 'long', + day: 'numeric', weekday: 'long', hour: 'numeric', minute: '2-digit', - month: 'long', - day: 'numeric', + } + + const today = new Date() + + if (dueDate.getFullYear() !== today.getFullYear()) { + formatOptions = { + ...formatOptions, + year: 'numeric', + } } return dueDate.toLocaleString( |