|
|
|
|
@ -3,11 +3,118 @@
|
|
|
|
|
- SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
|
-->
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import type { INode } from '@nextcloud/files'
|
|
|
|
|
|
|
|
|
|
import { showError, showSuccess } from '@nextcloud/dialogs'
|
|
|
|
|
import { emit as emitEventBus } from '@nextcloud/event-bus'
|
|
|
|
|
import { t } from '@nextcloud/l10n'
|
|
|
|
|
import { onBeforeMount, onMounted, ref } from 'vue'
|
|
|
|
|
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 { clearReminder, setReminder } from '../services/reminderService.ts'
|
|
|
|
|
import { logger } from '../shared/logger.ts'
|
|
|
|
|
import { getInitialCustomDueDate } from '../shared/utils.ts'
|
|
|
|
|
|
|
|
|
|
const props = defineProps<{
|
|
|
|
|
node: INode
|
|
|
|
|
}>()
|
|
|
|
|
|
|
|
|
|
const emit = defineEmits<{
|
|
|
|
|
close: [void]
|
|
|
|
|
}>()
|
|
|
|
|
|
|
|
|
|
const hasDueDate = ref(false)
|
|
|
|
|
const opened = ref(false)
|
|
|
|
|
const isValid = ref(true)
|
|
|
|
|
const customDueDate = ref<Date>()
|
|
|
|
|
const nowDate = ref(new Date())
|
|
|
|
|
|
|
|
|
|
onBeforeMount(() => {
|
|
|
|
|
const dueDate = props.node.attributes['reminder-due-date']
|
|
|
|
|
? new Date(props.node.attributes['reminder-due-date'])
|
|
|
|
|
: undefined
|
|
|
|
|
|
|
|
|
|
hasDueDate.value = Boolean(dueDate)
|
|
|
|
|
isValid.value = true
|
|
|
|
|
opened.value = true
|
|
|
|
|
customDueDate.value = dueDate ?? getInitialCustomDueDate()
|
|
|
|
|
nowDate.value = new Date()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
const input = document.getElementById('set-custom-reminder') as HTMLInputElement
|
|
|
|
|
input.focus()
|
|
|
|
|
if (!hasDueDate.value) {
|
|
|
|
|
input.showPicker()
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the custom reminder
|
|
|
|
|
*/
|
|
|
|
|
async function setCustom(): Promise<void> {
|
|
|
|
|
// Handle input cleared or invalid date
|
|
|
|
|
if (!(customDueDate.value instanceof Date) || isNaN(customDueDate.value.getTime())) {
|
|
|
|
|
showError(t('files_reminders', 'Please choose a valid date & time'))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await setReminder(props.node.fileid!, customDueDate.value)
|
|
|
|
|
const node = props.node.clone()
|
|
|
|
|
node.attributes['reminder-due-date'] = customDueDate.value.toISOString()
|
|
|
|
|
emitEventBus('files:node:updated', node)
|
|
|
|
|
showSuccess(t('files_reminders', 'Reminder set for "{fileName}"', { fileName: props.node.displayname }))
|
|
|
|
|
onClose()
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error('Failed to set reminder', { error })
|
|
|
|
|
showError(t('files_reminders', 'Failed to set reminder'))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Clear the reminder
|
|
|
|
|
*/
|
|
|
|
|
async function clear(): Promise<void> {
|
|
|
|
|
try {
|
|
|
|
|
await clearReminder(props.node.fileid!)
|
|
|
|
|
const node = props.node.clone()
|
|
|
|
|
node.attributes['reminder-due-date'] = ''
|
|
|
|
|
emitEventBus('files:node:updated', node)
|
|
|
|
|
showSuccess(t('files_reminders', 'Reminder cleared for "{fileName}"', { fileName: props.node.displayname }))
|
|
|
|
|
onClose()
|
|
|
|
|
} catch (error) {
|
|
|
|
|
logger.error('Failed to clear reminder', { error })
|
|
|
|
|
showError(t('files_reminders', 'Failed to clear reminder'))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Close the modal
|
|
|
|
|
*/
|
|
|
|
|
function onClose(): void {
|
|
|
|
|
opened.value = false
|
|
|
|
|
emit('close')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Validate the input on change
|
|
|
|
|
*/
|
|
|
|
|
function onInput(): void {
|
|
|
|
|
const input = document.getElementById('set-custom-reminder') as HTMLInputElement
|
|
|
|
|
isValid.value = input.checkValidity()
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
<NcDialog
|
|
|
|
|
v-if="opened"
|
|
|
|
|
:name="name"
|
|
|
|
|
:out-transition="true"
|
|
|
|
|
:name="t('files_reminders', `Set reminder for '{fileName}'`, { fileName: node.displayname })"
|
|
|
|
|
out-transition
|
|
|
|
|
size="small"
|
|
|
|
|
close-on-click-outside
|
|
|
|
|
@closing="onClose">
|
|
|
|
|
@ -18,13 +125,13 @@
|
|
|
|
|
<NcDateTimePickerNative
|
|
|
|
|
id="set-custom-reminder"
|
|
|
|
|
v-model="customDueDate"
|
|
|
|
|
:label="label"
|
|
|
|
|
:label="t('files_reminders', 'Reminder at custom date & time')"
|
|
|
|
|
:min="nowDate"
|
|
|
|
|
:required="true"
|
|
|
|
|
type="datetime-local"
|
|
|
|
|
@input="onInput" />
|
|
|
|
|
|
|
|
|
|
<NcNoteCard v-if="isValid" type="info">
|
|
|
|
|
<NcNoteCard v-if="isValid && customDueDate" type="info">
|
|
|
|
|
{{ t('files_reminders', 'We will remind you of this file') }}
|
|
|
|
|
<NcDateTime :timestamp="customDueDate" />
|
|
|
|
|
</NcNoteCard>
|
|
|
|
|
@ -56,142 +163,6 @@
|
|
|
|
|
</NcDialog>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script lang="ts">
|
|
|
|
|
import type { Node } from '@nextcloud/files'
|
|
|
|
|
|
|
|
|
|
import { showError, showSuccess } from '@nextcloud/dialogs'
|
|
|
|
|
import { emit } from '@nextcloud/event-bus'
|
|
|
|
|
import { translate as t } from '@nextcloud/l10n'
|
|
|
|
|
import Vue from 'vue'
|
|
|
|
|
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 { clearReminder, setReminder } from '../services/reminderService.ts'
|
|
|
|
|
import { logger } from '../shared/logger.ts'
|
|
|
|
|
import { getDateString, getInitialCustomDueDate } from '../shared/utils.ts'
|
|
|
|
|
|
|
|
|
|
export default Vue.extend({
|
|
|
|
|
name: 'SetCustomReminderModal',
|
|
|
|
|
|
|
|
|
|
components: {
|
|
|
|
|
NcButton,
|
|
|
|
|
NcDateTime,
|
|
|
|
|
NcDateTimePickerNative,
|
|
|
|
|
NcDialog,
|
|
|
|
|
NcNoteCard,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
data() {
|
|
|
|
|
return {
|
|
|
|
|
node: undefined as Node | undefined,
|
|
|
|
|
hasDueDate: false,
|
|
|
|
|
opened: false,
|
|
|
|
|
isValid: true,
|
|
|
|
|
|
|
|
|
|
customDueDate: null as null | Date,
|
|
|
|
|
nowDate: new Date(),
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
computed: {
|
|
|
|
|
fileId(): number | undefined {
|
|
|
|
|
return this.node?.fileid
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
fileName(): string | undefined {
|
|
|
|
|
return this.node?.basename
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
name() {
|
|
|
|
|
return this.fileName ? t('files_reminders', 'Set reminder for "{fileName}"', { fileName: this.fileName }) : ''
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
label(): string {
|
|
|
|
|
return t('files_reminders', '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
|
|
|
|
|
*/
|
|
|
|
|
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 = 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()
|
|
|
|
|
if (!this.hasDueDate) {
|
|
|
|
|
input.showPicker()
|
|
|
|
|
}
|
|
|
|
|
}, 300)
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
async setCustom(): Promise<void> {
|
|
|
|
|
// Handle input cleared or invalid date
|
|
|
|
|
if (!(this.customDueDate instanceof Date) || isNaN(this.customDueDate)) {
|
|
|
|
|
showError(t('files_reminders', 'Please choose a valid date & time'))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
logger.error('Failed to set reminder', { error })
|
|
|
|
|
showError(t('files_reminders', 'Failed to set reminder'))
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
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')
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
onInput(): void {
|
|
|
|
|
const input = document.getElementById('set-custom-reminder') as HTMLInputElement
|
|
|
|
|
this.isValid = input.checkValidity()
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
.custom-reminder-modal {
|
|
|
|
|
margin: 0 12px;
|
|
|
|
|
|