aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files_reminders/src
diff options
context:
space:
mode:
Diffstat (limited to 'apps/files_reminders/src')
-rw-r--r--apps/files_reminders/src/actions/clearReminderAction.ts54
-rw-r--r--apps/files_reminders/src/actions/reminderStatusAction.ts45
-rw-r--r--apps/files_reminders/src/actions/setReminderCustomAction.ts44
-rw-r--r--apps/files_reminders/src/actions/setReminderMenuAction.ts39
-rw-r--r--apps/files_reminders/src/actions/setReminderSuggestionActions.scss27
-rw-r--r--apps/files_reminders/src/actions/setReminderSuggestionActions.ts57
-rw-r--r--apps/files_reminders/src/components/SetCustomReminderModal.vue146
-rw-r--r--apps/files_reminders/src/init.ts30
-rw-r--r--apps/files_reminders/src/services/customPicker.ts25
-rw-r--r--apps/files_reminders/src/services/reminderService.ts21
-rw-r--r--apps/files_reminders/src/shared/logger.ts21
-rw-r--r--apps/files_reminders/src/shared/types.ts23
-rw-r--r--apps/files_reminders/src/shared/utils.ts142
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(