Skip to content

Commit

Permalink
Add alarms (reminder)
Browse files Browse the repository at this point in the history
  • Loading branch information
jaylinski committed Oct 9, 2024
1 parent 72c94da commit 80389cf
Show file tree
Hide file tree
Showing 14 changed files with 1,558 additions and 25 deletions.
6 changes: 0 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
"color-convert": "^2.0.1",
"debounce": "^2.1.0",
"ical.js": "^2.0.1",
"jstimezonedetect": "",
"linkify-it": "^5.0.0",
"markdown-it": "^14.1.0",
"markdown-it-emoji": "^3.0.0",
Expand Down
80 changes: 80 additions & 0 deletions src/components/AppSidebar/Alarm/AlarmDateTimePickerModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<template>
<NcModal @close="onClose()">
<div class="content">
<h3 class="content__heading">
{{ t('tasks', 'Create reminder') }}
</h3>
<NcDateTimePickerNative id="alarm-date-time-picker"
v-model="date"
type="datetime-local"
:label="t('tasks', 'Set reminder at custom date & time') " />
<div class="content__buttons">
<NcButton @click="onClose()">
{{ t('tasks', 'Cancel') }}
</NcButton>
<NcButton type="primary" @click="onSelectDateTime(date)">
{{ t('tasks', 'Create reminder') }}
</NcButton>
</div>
</div>
</NcModal>
</template>

<script>
import { getDefaultAbsoluteAlarms } from '../../../utils/alarms.js'
import { translate as t } from '@nextcloud/l10n'
import { NcButton, NcDateTimePickerNative, NcModal } from '@nextcloud/vue'
export default {
name: 'AlarmDateTimePickerModal',
components: {
NcButton,
NcDateTimePickerNative,
NcModal,
},
emits: [
'select-date-time',
'close',
],
data() {
return {
date: this.defaultAbsoluteAlarm(),
}
},
methods: {
t,
defaultAbsoluteAlarm() {
const timeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'
const alarms = getDefaultAbsoluteAlarms(timeZone)
return alarms[0]
},
onSelectDateTime(date) {
this.$emit('select-date-time', date)
},
onClose() {
this.$emit('close')
},
},
}
</script>

<style lang="scss" scoped>
.content {
padding: 14px;
&__heading {
margin-top: 0;
}
&__buttons {
display: flex;
gap: 8px;
margin-top: 14px;
justify-content: flex-end;
}
}
</style>
167 changes: 167 additions & 0 deletions src/components/AppSidebar/Alarm/AlarmList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
<!--
- SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
- SPDX-License-Identifier: AGPL-3.0-or-later
-->

<template>
<div class="component">
<div class="component__icon">
<slot name="icon" />
</div>
<div class="component__items">
<AlarmListItem v-for="(alarm, index) in alarmComponents"
:key="index"
:alarm="alarm"
:is-all-day="allDay"
:is-read-only="readOnly"
@remove-alarm="removeAlarm" />
<div class="new">
<div>
<p v-if="alarms.length === 0">
{{ t('tasks', 'No reminders') }}
</p>
</div>
<AlarmListNew v-if="!readOnly"
:has-start-date="hasStartDate"
:has-due-date="hasDueDate"
:is-all-day="allDay"
@add-alarm="addAlarm" />
</div>
</div>
</div>
</template>

<script>
import AlarmListNew from './AlarmListNew.vue'
import AlarmListItem from './AlarmListItem.vue'
import { mapAlarmComponentToAlarmObject } from '../../../models/alarm.js'

import { translate as t } from '@nextcloud/l10n'
import { AlarmComponent } from '@nextcloud/calendar-js'
import ICAL from 'ical.js'

export default {
name: 'AlarmList',
components: {
AlarmListItem,
AlarmListNew,
},
props: {
hasStartDate: {
type: Boolean,
required: true,
},
hasDueDate: {
type: Boolean,
required: true,
},
readOnly: {
type: Boolean,
required: true,
},
allDay: {
type: Boolean,
required: true,
},
alarms: {
type: Array,
required: true,
},
},
emits: [
'add-alarm',
'remove-alarm',
],
computed: {
alarmComponents() {
return this.alarms.map((alarm) => {
try {
return mapAlarmComponentToAlarmObject(AlarmComponent.fromICALJs(alarm))
} catch (e) {
// Instead of breaking the whole page when parsing an invalid alarm,
// we just print a warning on the console.
console.warn(e)
}
}).filter(Boolean)
},
},
methods: {
t,

/**
* Adds another of the default alarms to the event
*
* @param {object} alarm The alarm time or duration
* @param {number|Date} alarm.value Value of the trigger
* @param {object|undefined} alarm.parameter Name and value of the trigger parameter
*/
addAlarm({ value, parameter }) {
const valarm = {
action: 'DISPLAY',
repeat: 1,
trigger: { value: undefined, parameter },
}

if (typeof value === 'number') {
valarm.trigger.value = ICAL.Duration.fromSeconds(value)
} else if (value instanceof Date) {
valarm.trigger.value = ICAL.Time.fromJSDate(value, true)
}

this.$emit('add-alarm', valarm)
},

/**
* Removes an alarm from this event
*
* @param {object} alarm The alarm object
*/
removeAlarm(alarm) {
this.$emit('remove-alarm', alarm)
},
},
}
</script>

<style lang="scss" scoped>
.component {
display: flex;
border-bottom: 1px solid var(--color-border);
width: 100%;
color: var(--color-text-lighter);

.component {
&__icon {
display: flex;
height: 44px;
width: 44px;
min-width: 44px;
justify-content: center;

.material-design-icon__svg {
vertical-align: middle;
}
}

&__items {
display: flex;
flex-direction: column;
flex-grow: 1;
gap: 4px;
padding-inline-end: 4px;
padding-block: 4px;
overflow: auto;
text-overflow: ellipsis;
white-space: nowrap;

.new {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding-block: 2px;
}
}
}
}
</style>
Loading

0 comments on commit 80389cf

Please sign in to comment.