refactor(reminders): make Reminders.vue take ITaskReminder[] directly

Reminders.vue only read three task fields (dueDate/startDate/endDate)
and wrote one back (reminders). The ITask coupling was accidental.

Flip the prop to ITaskReminder[] and pass defaultRelativeTo / allowAbsolute
as plain props. TaskDetailView now owns the due/start/end priority
computation and binds v-model="task.reminders" directly. This also lets
the settings page reuse Reminders.vue for configuring default reminders.
This commit is contained in:
kolaente 2026-04-11 23:41:18 +02:00 committed by kolaente
parent b61ad9d46a
commit dc4b7a5510
2 changed files with 31 additions and 34 deletions

View File

@ -3,7 +3,7 @@
<div
v-for="(r, index) in reminders"
:key="index"
:data-is-overdue="r.reminder < now || undefined"
:data-is-overdue="r.reminder && r.reminder < now || undefined"
class="reminder-input"
>
<ReminderDetail
@ -11,6 +11,7 @@
class="reminder-detail"
:disabled="disabled"
:default-relative-to="defaultRelativeTo"
:allow-absolute="allowAbsolute"
@update:modelValue="updateData"
/>
<BaseButton
@ -26,31 +27,36 @@
:disabled="disabled"
:clear-after-update="true"
:default-relative-to="defaultRelativeTo"
:allow-absolute="allowAbsolute"
@update:modelValue="addNewReminder"
/>
</div>
</template>
<script setup lang="ts">
import {ref, watch, computed} from 'vue'
import {ref, watch} from 'vue'
import type {ITaskReminder} from '@/modelTypes/ITaskReminder'
import type {IReminderPeriodRelativeTo} from '@/types/IReminderPeriodRelativeTo'
import BaseButton from '@/components/base/BaseButton.vue'
import ReminderDetail from '@/components/tasks/partials/ReminderDetail.vue'
import type {ITask} from '@/modelTypes/ITask'
import {REMINDER_PERIOD_RELATIVE_TO_TYPES} from '@/types/IReminderPeriodRelativeTo'
import { useNow } from '@vueuse/core'
import {useNow} from '@vueuse/core'
const props = withDefaults(defineProps<{
modelValue: ITask,
modelValue?: ITaskReminder[],
defaultRelativeTo?: IReminderPeriodRelativeTo | null,
disabled?: boolean,
allowAbsolute?: boolean,
}>(), {
modelValue: () => [],
defaultRelativeTo: null,
disabled: false,
allowAbsolute: true,
})
const emit = defineEmits<{
'update:modelValue': [ITask]
'update:modelValue': [ITaskReminder[]]
}>()
const reminders = ref<ITaskReminder[]>([])
@ -58,38 +64,15 @@ const reminders = ref<ITaskReminder[]>([])
const now = useNow({interval: 1000})
watch(
() => props.modelValue.reminders,
() => props.modelValue,
(newVal) => {
reminders.value = newVal
reminders.value = [...(newVal ?? [])]
},
{immediate: true, deep: true}, // deep watcher so that we get the resolved date after updating the task
)
const defaultRelativeTo = computed(() => {
if (typeof props.modelValue === 'undefined') {
return null
}
if (props.modelValue?.dueDate) {
return REMINDER_PERIOD_RELATIVE_TO_TYPES.DUEDATE
}
if (props.modelValue.dueDate === null && props.modelValue.startDate !== null) {
return REMINDER_PERIOD_RELATIVE_TO_TYPES.STARTDATE
}
if (props.modelValue.dueDate === null && props.modelValue.startDate === null && props.modelValue.endDate !== null) {
return REMINDER_PERIOD_RELATIVE_TO_TYPES.ENDDATE
}
return null
})
function updateData() {
emit('update:modelValue', {
...props.modelValue,
reminders: reminders.value,
})
emit('update:modelValue', [...reminders.value])
}
function addNewReminder(newReminder: ITaskReminder|null) {

View File

@ -250,7 +250,8 @@
</div>
<Reminders
:ref="e => setFieldRef('reminders', e)"
v-model="task"
v-model="task.reminders"
:default-relative-to="remindersDefaultRelativeTo"
:disabled="!canWrite"
@update:modelValue="saveTask()"
/>
@ -672,6 +673,7 @@ import {getProjectTitle} from '@/helpers/getProjectTitle'
import {isAppleDevice} from '@/helpers/isAppleDevice'
import {scrollIntoView} from '@/helpers/scrollIntoView'
import {TASK_REPEAT_MODES} from '@/types/IRepeatMode'
import {REMINDER_PERIOD_RELATIVE_TO_TYPES} from '@/types/IReminderPeriodRelativeTo'
import {playPopSound} from '@/helpers/playPop'
import {useTaskStore} from '@/stores/tasks'
@ -707,6 +709,18 @@ const baseStore = useBaseStore()
const task = ref<ITask>(new TaskModel())
const hasAttachments = computed(() => (task.value.attachments?.length ?? 0) > 0)
const remindersDefaultRelativeTo = computed(() => {
if (task.value.dueDate) {
return REMINDER_PERIOD_RELATIVE_TO_TYPES.DUEDATE
}
if (task.value.startDate) {
return REMINDER_PERIOD_RELATIVE_TO_TYPES.STARTDATE
}
if (task.value.endDate) {
return REMINDER_PERIOD_RELATIVE_TO_TYPES.ENDDATE
}
return null
})
const taskNotFound = ref(false)
const taskTitle = computed(() => task.value.title)
useTitle(taskTitle)