frontend/src/models/task.js
konrad cf136132e3 Namespaces & Lists Page (#160)
Center list backgrounds

Better alignment of new namespace and filter button

Make creating new namespace button clear

Hide archived lists unless the user wants it

Make all cards responsive

Cleanup

Show it if a namespace is archived

Show if a list is archived

Fix not updating the list in store after updating the background

Make task cards smaller

Display list backgrounds in cards and look good while doing it

lighter shadow

Change background to stripes

Set list backgrounds as card backgrounds

Add background color check to color appropriatly

Move color check to mixin

Use background color from tasks

Change list card color

Make create new namespace button stick to the right

Shadow all the things

Don't keep list backgrounds set when navigating back

Make links to list clickable

Add seperate page for namespaces

Co-authored-by: kolaente <k@knt.li>
Reviewed-on: vikunja/frontend#160
2020-06-16 22:20:37 +00:00

211 lines
5.6 KiB
JavaScript

import AbstractModel from './abstractModel';
import UserModel from './user'
import LabelModel from './label'
import AttachmentModel from './attachment'
export default class TaskModel extends AbstractModel {
defaultColor = '198CFF'
constructor(data) {
super(data)
this.id = Number(this.id)
this.listId = Number(this.listId)
// Make date objects from timestamps
this.dueDate = this.dueDate ? new Date(this.dueDate) : null
this.startDate = this.startDate ? new Date(this.startDate) : null
this.endDate = this.endDate ? new Date(this.endDate) : null
// Cancel all scheduled notifications for this task to be sure to only have available notifications
this.cancelScheduledNotifications()
.then(() => {
this.reminderDates = this.reminderDates.map(d => {
d = new Date(d)
// Every time we see a reminder, we schedule a notification for it
this.scheduleNotification(d)
return d
})
this.reminderDates.push(null) // To trigger the datepicker
})
// Parse the repeat after into something usable
this.parseRepeatAfter()
// Parse the assignees into user models
this.assignees = this.assignees.map(a => {
return new UserModel(a)
})
this.createdBy = new UserModel(this.createdBy)
this.labels = this.labels.map(l => {
return new LabelModel(l)
})
// Set the default color
if (this.hexColor === '') {
this.hexColor = this.defaultColor
}
if (this.hexColor.substring(0, 1) !== '#') {
this.hexColor = '#' + this.hexColor
}
// Make all subtasks to task models
Object.keys(this.relatedTasks).forEach(relationKind => {
this.relatedTasks[relationKind] = this.relatedTasks[relationKind].map(t => {
return new TaskModel(t)
})
})
// Make all attachments to attachment models
this.attachments = this.attachments.map(a => {
return new AttachmentModel(a)
})
// Set the task identifier to empty if the list does not have one
if(this.identifier === `-${this.index}`) {
this.identifier = ''
}
this.created = new Date(this.created)
this.updated = new Date(this.updated)
}
defaults() {
return {
id: 0,
text: '',
description: '',
done: false,
priority: 0,
labels: [],
assignees: [],
dueDate: 0,
startDate: 0,
endDate: 0,
repeatAfter: 0,
repeatFromCurrentDate: false,
reminderDates: [],
parentTaskId: 0,
hexColor: '',
percentDone: 0,
relatedTasks: {},
attachments: [],
identifier: '',
index: 0,
createdBy: UserModel,
created: null,
updated: null,
listId: 0, // Meta, only used when creating a new task
}
}
/////////////////
// Helper functions
///////////////
/**
* Parses the "repeat after x seconds" from the task into a usable js object inside the task.
* This function should only be called from the constructor.
*/
parseRepeatAfter() {
let repeatAfterHours = (this.repeatAfter / 60) / 60
this.repeatAfter = {type: 'hours', amount: repeatAfterHours}
// if its dividable by 24, its something with days, otherwise hours
if (repeatAfterHours % 24 === 0) {
let repeatAfterDays = repeatAfterHours / 24
if (repeatAfterDays % 7 === 0) {
this.repeatAfter.type = 'weeks'
this.repeatAfter.amount = repeatAfterDays / 7
} else if (repeatAfterDays % 30 === 0) {
this.repeatAfter.type = 'months'
this.repeatAfter.amount = repeatAfterDays / 30
} else if (repeatAfterDays % 365 === 0) {
this.repeatAfter.type = 'years'
this.repeatAfter.amount = repeatAfterDays / 365
} else {
this.repeatAfter.type = 'days'
this.repeatAfter.amount = repeatAfterDays
}
}
}
async cancelScheduledNotifications() {
if (!('showTrigger' in Notification.prototype)) {
console.debug('This browser does not support triggered notifications')
return
}
const registration = await navigator.serviceWorker.getRegistration()
if (typeof registration === 'undefined') {
return
}
// Get all scheduled notifications for this task and cancel them
const scheduledNotifications = await registration.getNotifications({
tag: `vikunja-task-${this.id}`,
includeTriggered: true,
})
console.debug('Already scheduled notifications:', scheduledNotifications)
scheduledNotifications.forEach(n => n.close())
}
async scheduleNotification(date) {
if(date < new Date()) {
console.debug('Date is in the past, not scheduling a notification. Date is ', date)
return
}
if (!('showTrigger' in Notification.prototype)) {
console.debug('This browser does not support triggered notifications')
return
}
const {state} = await navigator.permissions.request({name: 'notifications'});
if (state !== 'granted') {
console.debug('Notification permission not granted, not showing notifications')
return
}
const registration = await navigator.serviceWorker.getRegistration()
if (typeof registration === 'undefined') {
console.error('No service worker registration available')
return
}
// Register the actual notification
registration.showNotification('Vikunja Reminder', {
tag: `vikunja-task-${this.id}`, // Group notifications by task id so we're only showing one notification per task
body: this.text,
// eslint-disable-next-line no-undef
showTrigger: new TimestampTrigger(date),
badge: '/images/icons/badge-monochrome.png',
icon: '/images/icons/android-chrome-512x512.png',
data: {taskId: this.id},
actions: [
{
action: 'mark-as-done',
title: 'Done'
},
{
action: 'show-task',
title: 'Show task'
},
],
})
.then(() => {
console.debug('Notification scheduled for ' + date)
})
.catch(e => {
console.debug('Error scheduling notification', e)
})
}
}