mirror of
https://github.com/nextcloud/server.git
synced 2026-06-13 18:50:47 -04:00
Merge pull request #43589 from nextcloud/feat/reminder-status
feat(files_reminders): Add reminder status indicator
This commit is contained in:
commit
6cf51f9448
18 changed files with 269 additions and 111 deletions
|
|
@ -77,7 +77,7 @@
|
|||
:key="action.id"
|
||||
:class="`files-list__row-action-${action.id}`"
|
||||
class="files-list__row-action--submenu"
|
||||
:close-after-click="false /* never close submenu, just go back */"
|
||||
close-after-click
|
||||
:data-cy-files-list-row-action="action.id"
|
||||
:title="action.title?.([source], currentView)"
|
||||
@click="onActionClick(action)">
|
||||
|
|
|
|||
|
|
@ -13,10 +13,6 @@ Set file reminders.
|
|||
<author>Christopher Ng</author>
|
||||
<namespace>FilesReminders</namespace>
|
||||
|
||||
<types>
|
||||
<dav />
|
||||
</types>
|
||||
|
||||
<category>files</category>
|
||||
|
||||
<bugs>https://github.com/nextcloud/server/issues</bugs>
|
||||
|
|
@ -33,10 +29,4 @@ Set file reminders.
|
|||
<commands>
|
||||
<command>OCA\FilesReminders\Command\ListCommand</command>
|
||||
</commands>
|
||||
|
||||
<sabre>
|
||||
<plugins>
|
||||
<plugin>OCA\FilesReminders\Dav\PropFindPlugin</plugin>
|
||||
</plugins>
|
||||
</sabre>
|
||||
</info>
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ return array(
|
|||
'OCA\\FilesReminders\\Exception\\UserNotFoundException' => $baseDir . '/../lib/Exception/UserNotFoundException.php',
|
||||
'OCA\\FilesReminders\\Listener\\LoadAdditionalScriptsListener' => $baseDir . '/../lib/Listener/LoadAdditionalScriptsListener.php',
|
||||
'OCA\\FilesReminders\\Listener\\NodeDeletedListener' => $baseDir . '/../lib/Listener/NodeDeletedListener.php',
|
||||
'OCA\\FilesReminders\\Listener\\SabrePluginAddListener' => $baseDir . '/../lib/Listener/SabrePluginAddListener.php',
|
||||
'OCA\\FilesReminders\\Listener\\UserDeletedListener' => $baseDir . '/../lib/Listener/UserDeletedListener.php',
|
||||
'OCA\\FilesReminders\\Migration\\Version10000Date20230725162149' => $baseDir . '/../lib/Migration/Version10000Date20230725162149.php',
|
||||
'OCA\\FilesReminders\\Model\\RichReminder' => $baseDir . '/../lib/Model/RichReminder.php',
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ class ComposerStaticInitFilesReminders
|
|||
'OCA\\FilesReminders\\Exception\\UserNotFoundException' => __DIR__ . '/..' . '/../lib/Exception/UserNotFoundException.php',
|
||||
'OCA\\FilesReminders\\Listener\\LoadAdditionalScriptsListener' => __DIR__ . '/..' . '/../lib/Listener/LoadAdditionalScriptsListener.php',
|
||||
'OCA\\FilesReminders\\Listener\\NodeDeletedListener' => __DIR__ . '/..' . '/../lib/Listener/NodeDeletedListener.php',
|
||||
'OCA\\FilesReminders\\Listener\\SabrePluginAddListener' => __DIR__ . '/..' . '/../lib/Listener/SabrePluginAddListener.php',
|
||||
'OCA\\FilesReminders\\Listener\\UserDeletedListener' => __DIR__ . '/..' . '/../lib/Listener/UserDeletedListener.php',
|
||||
'OCA\\FilesReminders\\Migration\\Version10000Date20230725162149' => __DIR__ . '/..' . '/../lib/Migration/Version10000Date20230725162149.php',
|
||||
'OCA\\FilesReminders\\Model\\RichReminder' => __DIR__ . '/..' . '/../lib/Model/RichReminder.php',
|
||||
|
|
|
|||
|
|
@ -26,9 +26,11 @@ declare(strict_types=1);
|
|||
|
||||
namespace OCA\FilesReminders\AppInfo;
|
||||
|
||||
use OCA\DAV\Events\SabrePluginAddEvent;
|
||||
use OCA\Files\Event\LoadAdditionalScriptsEvent;
|
||||
use OCA\FilesReminders\Listener\LoadAdditionalScriptsListener;
|
||||
use OCA\FilesReminders\Listener\NodeDeletedListener;
|
||||
use OCA\FilesReminders\Listener\SabrePluginAddListener;
|
||||
use OCA\FilesReminders\Listener\UserDeletedListener;
|
||||
use OCA\FilesReminders\Notification\Notifier;
|
||||
use OCP\AppFramework\App;
|
||||
|
|
@ -51,6 +53,8 @@ class Application extends App implements IBootstrap {
|
|||
public function register(IRegistrationContext $context): void {
|
||||
$context->registerNotifierService(Notifier::class);
|
||||
|
||||
$context->registerEventListener(SabrePluginAddEvent::class, SabrePluginAddListener::class);
|
||||
|
||||
$context->registerEventListener(NodeDeletedEvent::class, NodeDeletedListener::class);
|
||||
$context->registerEventListener(UserDeletedEvent::class, UserDeletedListener::class);
|
||||
|
||||
|
|
|
|||
51
apps/files_reminders/lib/Listener/SabrePluginAddListener.php
Normal file
51
apps/files_reminders/lib/Listener/SabrePluginAddListener.php
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright 2024 Christopher Ng <chrng8@gmail.com>
|
||||
*
|
||||
* @author Christopher Ng <chrng8@gmail.com>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\FilesReminders\Listener;
|
||||
|
||||
use OCA\DAV\Events\SabrePluginAddEvent;
|
||||
use OCA\FilesReminders\Dav\PropFindPlugin;
|
||||
use OCP\EventDispatcher\Event;
|
||||
use OCP\EventDispatcher\IEventListener;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
/** @template-implements IEventListener<SabrePluginAddEvent> */
|
||||
class SabrePluginAddListener implements IEventListener {
|
||||
public function __construct(
|
||||
private ContainerInterface $container,
|
||||
) {
|
||||
}
|
||||
|
||||
public function handle(Event $event): void {
|
||||
if (!($event instanceof SabrePluginAddEvent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$server = $event->getServer();
|
||||
$plugin = $this->container->get(PropFindPlugin::class);
|
||||
$server->addPlugin($plugin);
|
||||
}
|
||||
}
|
||||
|
|
@ -33,12 +33,12 @@ import { getVerboseDateString } from '../shared/utils.ts'
|
|||
export const action = new FileAction({
|
||||
id: 'clear-reminder',
|
||||
|
||||
displayName: () => t('files', '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', 'Clear reminder')} – ${getVerboseDateString(dueDate)}`
|
||||
return `${t('files_reminders', 'Clear reminder')} – ${getVerboseDateString(dueDate)}`
|
||||
},
|
||||
|
||||
iconSvgInline: () => AlarmOffSvg,
|
||||
|
|
|
|||
62
apps/files_reminders/src/actions/reminderStatusAction.ts
Normal file
62
apps/files_reminders/src/actions/reminderStatusAction.ts
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/**
|
||||
* @copyright 2024 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 { 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,
|
||||
})
|
||||
|
|
@ -28,7 +28,7 @@ import { pickCustomDate } from '../services/customPicker'
|
|||
|
||||
export const action = new FileAction({
|
||||
id: 'set-reminder-custom',
|
||||
displayName: () => t('files', 'Set custom reminder'),
|
||||
displayName: () => t('files_reminders', 'Set custom reminder'),
|
||||
title: () => t('files_reminders', 'Set reminder at custom date & time'),
|
||||
iconSvgInline: () => CalendarClockSvg,
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ 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,
|
||||
|
|
|
|||
|
|
@ -21,15 +21,15 @@
|
|||
-->
|
||||
|
||||
<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,21 +46,27 @@
|
|||
<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">
|
||||
|
|
@ -73,12 +79,12 @@ import { translate as t } from '@nextcloud/l10n'
|
|||
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 NcDialog from '@nextcloud/vue/dist/Components/NcDialog.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'
|
||||
import { clearReminder, setReminder } from '../services/reminderService.ts'
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'SetCustomReminderModal',
|
||||
|
|
@ -87,17 +93,18 @@ 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(),
|
||||
}
|
||||
},
|
||||
|
|
@ -111,7 +118,7 @@ export default Vue.extend({
|
|||
return this.node.basename
|
||||
},
|
||||
|
||||
title() {
|
||||
name() {
|
||||
return t('files_reminders', 'Set reminder for "{fileName}"', { fileName: this.fileName })
|
||||
},
|
||||
|
||||
|
|
@ -133,18 +140,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)
|
||||
},
|
||||
|
||||
|
|
@ -167,6 +179,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')
|
||||
|
|
@ -182,21 +207,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>
|
||||
|
|
|
|||
|
|
@ -21,13 +21,15 @@
|
|||
*/
|
||||
|
||||
import { registerDavProperty, registerFileAction } from '@nextcloud/files'
|
||||
import { action as menuAction } from './actions/setReminderMenuAction'
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -36,11 +36,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)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@
|
|||
*
|
||||
*/
|
||||
|
||||
import moment from '@nextcloud/moment'
|
||||
import { getCanonicalLocale } from '@nextcloud/l10n'
|
||||
|
||||
export enum DateTimePreset {
|
||||
|
|
@ -30,58 +29,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 +112,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 +124,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 +141,13 @@ export const getDateString = (dueDate: Date): string => {
|
|||
}
|
||||
}
|
||||
|
||||
if (dueDate.getFullYear() !== today.getFullYear()) {
|
||||
formatOptions = {
|
||||
...formatOptions,
|
||||
year: 'numeric',
|
||||
}
|
||||
}
|
||||
|
||||
return dueDate.toLocaleString(
|
||||
getCanonicalLocale(),
|
||||
formatOptions,
|
||||
|
|
@ -127,12 +155,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(
|
||||
|
|
|
|||
4
dist/files-main.js
vendored
4
dist/files-main.js
vendored
File diff suppressed because one or more lines are too long
2
dist/files-main.js.map
vendored
2
dist/files-main.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/files_reminders-init.js
vendored
4
dist/files_reminders-init.js
vendored
File diff suppressed because one or more lines are too long
2
dist/files_reminders-init.js.map
vendored
2
dist/files_reminders-init.js.map
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue