Browse Source
chore(files_reminders): upgrade to 28 APIs
chore(files_reminders): upgrade to 28 APIs
Signed-off-by: John Molakvoæ <skjnldsv@protonmail.com>pull/41010/head
No known key found for this signature in database
GPG Key ID: 60C25B8C072916CF
17 changed files with 7414 additions and 7170 deletions
-
25apps/files/src/components/FileEntry/FileEntryActions.vue
-
1apps/files/src/components/FileEntry/FileEntryPreview.vue
-
36apps/files/src/init.ts
-
2apps/files/src/sidebar.js
-
2apps/files_reminders/lib/Listener/LoadAdditionalScriptsListener.php
-
45apps/files_reminders/src/actions/setReminderCustomAction.ts
-
40apps/files_reminders/src/actions/setReminderMenuAction.ts
-
38apps/files_reminders/src/actions/setReminderSuggestionActions.scss
-
106apps/files_reminders/src/actions/setReminderSuggestionActions.ts
-
199apps/files_reminders/src/components/SetCustomReminderModal.vue
-
272apps/files_reminders/src/components/SetReminderActions.vue
-
29apps/files_reminders/src/init.ts
-
102apps/files_reminders/src/main.ts
-
48apps/files_reminders/src/services/customPicker.ts
-
13635package-lock.json
-
2package.json
-
2webpack.modules.js
@ -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, |
|||
}) |
@ -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, |
|||
}) |
@ -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; |
|||
} |
|||
} |
@ -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[] |
@ -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> |
@ -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> |
@ -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)) |
@ -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) |
@ -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) |
|||
}) |
|||
} |
13635
package-lock.json
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
Write
Preview
Loading…
Cancel
Save
Reference in new issue