|
3 | 3 | - SPDX-License-Identifier: AGPL-3.0-or-later |
4 | 4 | --> |
5 | 5 |
|
| 6 | +<script setup lang="ts"> |
| 7 | +import type { INode } from '@nextcloud/files' |
| 8 | +
|
| 9 | +import { showError, showSuccess } from '@nextcloud/dialogs' |
| 10 | +import { emit as emitEventBus } from '@nextcloud/event-bus' |
| 11 | +import { t } from '@nextcloud/l10n' |
| 12 | +import { onBeforeMount, onMounted, ref } from 'vue' |
| 13 | +import NcButton from '@nextcloud/vue/components/NcButton' |
| 14 | +import NcDateTime from '@nextcloud/vue/components/NcDateTime' |
| 15 | +import NcDateTimePickerNative from '@nextcloud/vue/components/NcDateTimePickerNative' |
| 16 | +import NcDialog from '@nextcloud/vue/components/NcDialog' |
| 17 | +import NcNoteCard from '@nextcloud/vue/components/NcNoteCard' |
| 18 | +import { clearReminder, setReminder } from '../services/reminderService.ts' |
| 19 | +import { logger } from '../shared/logger.ts' |
| 20 | +import { getInitialCustomDueDate } from '../shared/utils.ts' |
| 21 | +
|
| 22 | +const props = defineProps<{ |
| 23 | + node: INode |
| 24 | +}>() |
| 25 | +
|
| 26 | +const emit = defineEmits<{ |
| 27 | + close: [void] |
| 28 | +}>() |
| 29 | +
|
| 30 | +const hasDueDate = ref(false) |
| 31 | +const opened = ref(false) |
| 32 | +const isValid = ref(true) |
| 33 | +const customDueDate = ref<Date>() |
| 34 | +const nowDate = ref(new Date()) |
| 35 | +
|
| 36 | +onBeforeMount(() => { |
| 37 | + const dueDate = props.node.attributes['reminder-due-date'] |
| 38 | + ? new Date(props.node.attributes['reminder-due-date']) |
| 39 | + : undefined |
| 40 | +
|
| 41 | + hasDueDate.value = Boolean(dueDate) |
| 42 | + isValid.value = true |
| 43 | + opened.value = true |
| 44 | + customDueDate.value = dueDate ?? getInitialCustomDueDate() |
| 45 | + nowDate.value = new Date() |
| 46 | +}) |
| 47 | +
|
| 48 | +onMounted(() => { |
| 49 | + const input = document.getElementById('set-custom-reminder') as HTMLInputElement |
| 50 | + input.focus() |
| 51 | + if (!hasDueDate.value) { |
| 52 | + input.showPicker() |
| 53 | + } |
| 54 | +}) |
| 55 | +
|
| 56 | +/** |
| 57 | + * Set the custom reminder |
| 58 | + */ |
| 59 | +async function setCustom(): Promise<void> { |
| 60 | + // Handle input cleared or invalid date |
| 61 | + if (!(customDueDate.value instanceof Date) || isNaN(customDueDate.value.getTime())) { |
| 62 | + showError(t('files_reminders', 'Please choose a valid date & time')) |
| 63 | + return |
| 64 | + } |
| 65 | +
|
| 66 | + try { |
| 67 | + await setReminder(props.node.fileid!, customDueDate.value) |
| 68 | + const node = props.node.clone() |
| 69 | + node.attributes['reminder-due-date'] = customDueDate.value.toISOString() |
| 70 | + emitEventBus('files:node:updated', node) |
| 71 | + showSuccess(t('files_reminders', 'Reminder set for "{fileName}"', { fileName: props.node.displayname })) |
| 72 | + onClose() |
| 73 | + } catch (error) { |
| 74 | + logger.error('Failed to set reminder', { error }) |
| 75 | + showError(t('files_reminders', 'Failed to set reminder')) |
| 76 | + } |
| 77 | +} |
| 78 | +
|
| 79 | +/** |
| 80 | + * Clear the reminder |
| 81 | + */ |
| 82 | +async function clear(): Promise<void> { |
| 83 | + try { |
| 84 | + await clearReminder(props.node.fileid!) |
| 85 | + const node = props.node.clone() |
| 86 | + node.attributes['reminder-due-date'] = '' |
| 87 | + emitEventBus('files:node:updated', node) |
| 88 | + showSuccess(t('files_reminders', 'Reminder cleared for "{fileName}"', { fileName: props.node.displayname })) |
| 89 | + onClose() |
| 90 | + } catch (error) { |
| 91 | + logger.error('Failed to clear reminder', { error }) |
| 92 | + showError(t('files_reminders', 'Failed to clear reminder')) |
| 93 | + } |
| 94 | +} |
| 95 | +
|
| 96 | +/** |
| 97 | + * Close the modal |
| 98 | + */ |
| 99 | +function onClose(): void { |
| 100 | + opened.value = false |
| 101 | + emit('close') |
| 102 | +} |
| 103 | +
|
| 104 | +/** |
| 105 | + * Validate the input on change |
| 106 | + */ |
| 107 | +function onInput(): void { |
| 108 | + const input = document.getElementById('set-custom-reminder') as HTMLInputElement |
| 109 | + isValid.value = input.checkValidity() |
| 110 | +} |
| 111 | +</script> |
| 112 | + |
6 | 113 | <template> |
7 | 114 | <NcDialog |
8 | 115 | v-if="opened" |
9 | | - :name="name" |
10 | | - :out-transition="true" |
| 116 | + :name="t('files_reminders', `Set reminder for '{fileName}'`, { fileName: node.displayname })" |
| 117 | + out-transition |
11 | 118 | size="small" |
12 | 119 | close-on-click-outside |
13 | 120 | @closing="onClose"> |
|
18 | 125 | <NcDateTimePickerNative |
19 | 126 | id="set-custom-reminder" |
20 | 127 | v-model="customDueDate" |
21 | | - :label="label" |
| 128 | + :label="t('files_reminders', 'Reminder at custom date & time')" |
22 | 129 | :min="nowDate" |
23 | 130 | :required="true" |
24 | 131 | type="datetime-local" |
25 | 132 | @input="onInput" /> |
26 | 133 |
|
27 | | - <NcNoteCard v-if="isValid" type="info"> |
| 134 | + <NcNoteCard v-if="isValid && customDueDate" type="info"> |
28 | 135 | {{ t('files_reminders', 'We will remind you of this file') }} |
29 | 136 | <NcDateTime :timestamp="customDueDate" /> |
30 | 137 | </NcNoteCard> |
|
56 | 163 | </NcDialog> |
57 | 164 | </template> |
58 | 165 |
|
59 | | -<script lang="ts"> |
60 | | -import type { Node } from '@nextcloud/files' |
61 | | -
|
62 | | -import { showError, showSuccess } from '@nextcloud/dialogs' |
63 | | -import { emit } from '@nextcloud/event-bus' |
64 | | -import { translate as t } from '@nextcloud/l10n' |
65 | | -import Vue from 'vue' |
66 | | -import NcButton from '@nextcloud/vue/components/NcButton' |
67 | | -import NcDateTime from '@nextcloud/vue/components/NcDateTime' |
68 | | -import NcDateTimePickerNative from '@nextcloud/vue/components/NcDateTimePickerNative' |
69 | | -import NcDialog from '@nextcloud/vue/components/NcDialog' |
70 | | -import NcNoteCard from '@nextcloud/vue/components/NcNoteCard' |
71 | | -import { clearReminder, setReminder } from '../services/reminderService.ts' |
72 | | -import { logger } from '../shared/logger.ts' |
73 | | -import { getDateString, getInitialCustomDueDate } from '../shared/utils.ts' |
74 | | -
|
75 | | -export default Vue.extend({ |
76 | | - name: 'SetCustomReminderModal', |
77 | | -
|
78 | | - components: { |
79 | | - NcButton, |
80 | | - NcDateTime, |
81 | | - NcDateTimePickerNative, |
82 | | - NcDialog, |
83 | | - NcNoteCard, |
84 | | - }, |
85 | | -
|
86 | | - data() { |
87 | | - return { |
88 | | - node: undefined as Node | undefined, |
89 | | - hasDueDate: false, |
90 | | - opened: false, |
91 | | - isValid: true, |
92 | | -
|
93 | | - customDueDate: null as null | Date, |
94 | | - nowDate: new Date(), |
95 | | - } |
96 | | - }, |
97 | | -
|
98 | | - computed: { |
99 | | - fileId(): number | undefined { |
100 | | - return this.node?.fileid |
101 | | - }, |
102 | | -
|
103 | | - fileName(): string | undefined { |
104 | | - return this.node?.basename |
105 | | - }, |
106 | | -
|
107 | | - name() { |
108 | | - return this.fileName ? t('files_reminders', 'Set reminder for "{fileName}"', { fileName: this.fileName }) : '' |
109 | | - }, |
110 | | -
|
111 | | - label(): string { |
112 | | - return t('files_reminders', 'Reminder at custom date & time') |
113 | | - }, |
114 | | -
|
115 | | - clearAriaLabel(): string { |
116 | | - return t('files_reminders', 'Clear reminder') |
117 | | - }, |
118 | | - }, |
119 | | -
|
120 | | - methods: { |
121 | | - t, |
122 | | - getDateString, |
123 | | -
|
124 | | - /** |
125 | | - * Open the modal to set a custom reminder |
126 | | - * and reset the state. |
127 | | - * |
128 | | - * @param node The node to set a reminder for |
129 | | - */ |
130 | | - open(node: Node): void { |
131 | | - const dueDate = node.attributes['reminder-due-date'] ? new Date(node.attributes['reminder-due-date']) : null |
132 | | -
|
133 | | - this.node = node |
134 | | - this.hasDueDate = Boolean(dueDate) |
135 | | - this.isValid = true |
136 | | - this.opened = true |
137 | | - this.customDueDate = dueDate ?? getInitialCustomDueDate() |
138 | | - this.nowDate = new Date() |
139 | | -
|
140 | | - // Focus the input and show the picker after the animation |
141 | | - setTimeout(() => { |
142 | | - const input = document.getElementById('set-custom-reminder') as HTMLInputElement |
143 | | - input.focus() |
144 | | - if (!this.hasDueDate) { |
145 | | - input.showPicker() |
146 | | - } |
147 | | - }, 300) |
148 | | - }, |
149 | | -
|
150 | | - async setCustom(): Promise<void> { |
151 | | - // Handle input cleared or invalid date |
152 | | - if (!(this.customDueDate instanceof Date) || isNaN(this.customDueDate)) { |
153 | | - showError(t('files_reminders', 'Please choose a valid date & time')) |
154 | | - return |
155 | | - } |
156 | | -
|
157 | | - try { |
158 | | - await setReminder(this.fileId, this.customDueDate) |
159 | | - Vue.set(this.node.attributes, 'reminder-due-date', this.customDueDate.toISOString()) |
160 | | - emit('files:node:updated', this.node) |
161 | | - showSuccess(t('files_reminders', 'Reminder set for "{fileName}"', { fileName: this.fileName })) |
162 | | - this.onClose() |
163 | | - } catch (error) { |
164 | | - logger.error('Failed to set reminder', { error }) |
165 | | - showError(t('files_reminders', 'Failed to set reminder')) |
166 | | - } |
167 | | - }, |
168 | | -
|
169 | | - async clear(): Promise<void> { |
170 | | - try { |
171 | | - await clearReminder(this.fileId) |
172 | | - Vue.set(this.node.attributes, 'reminder-due-date', '') |
173 | | - emit('files:node:updated', this.node) |
174 | | - showSuccess(t('files_reminders', 'Reminder cleared for "{fileName}"', { fileName: this.fileName })) |
175 | | - this.onClose() |
176 | | - } catch (error) { |
177 | | - logger.error('Failed to clear reminder', { error }) |
178 | | - showError(t('files_reminders', 'Failed to clear reminder')) |
179 | | - } |
180 | | - }, |
181 | | -
|
182 | | - onClose(): void { |
183 | | - this.opened = false |
184 | | - this.$emit('close') |
185 | | - }, |
186 | | -
|
187 | | - onInput(): void { |
188 | | - const input = document.getElementById('set-custom-reminder') as HTMLInputElement |
189 | | - this.isValid = input.checkValidity() |
190 | | - }, |
191 | | - }, |
192 | | -}) |
193 | | -</script> |
194 | | - |
195 | 166 | <style lang="scss" scoped> |
196 | 167 | .custom-reminder-modal { |
197 | 168 | margin: 0 12px; |
|
0 commit comments