Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/components/TaskBody.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
class="no-nav"
:cancelled="task.status === 'CANCELLED'"
:read-only="readOnly"
:recurring="task.recurring"
:priority-class="priorityClass"
@toggle-completed="toggleCompleted(task)" />
<!-- Info: title, progress & tags -->
Expand Down
7 changes: 7 additions & 0 deletions src/components/TaskCheckbox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
<CheckboxBlankOffOutline v-if="cancelled && !completed" :size="22" />
<CheckboxOutline v-else-if="completed" :size="22" />
<CheckboxBlank v-else-if="readOnly" :size="22" />
<Repeat v-else-if="recurring" :size="22" />
<CheckboxBlankOutline v-else :size="22" />
</label>
</div>
Expand All @@ -45,13 +46,15 @@ import CheckboxBlank from 'vue-material-design-icons/CheckboxBlank.vue'
import CheckboxBlankOffOutline from 'vue-material-design-icons/CheckboxBlankOffOutline.vue'
import CheckboxBlankOutline from 'vue-material-design-icons/CheckboxBlankOutline.vue'
import CheckboxOutline from 'vue-material-design-icons/CheckboxOutline.vue'
import Repeat from 'vue-material-design-icons/Repeat.vue'

export default {
components: {
CheckboxBlank,
CheckboxBlankOffOutline,
CheckboxBlankOutline,
CheckboxOutline,
Repeat,
},
props: {
completed: {
Expand All @@ -66,6 +69,10 @@ export default {
type: Boolean,
required: true,
},
recurring: {
type: Boolean,
required: true
},
priorityClass: {
type: String,
default: '',
Expand Down
35 changes: 35 additions & 0 deletions src/models/task.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export default class Task {
this._completed = !!comp
this._completedDate = comp ? comp.toJSDate() : null
this._completedDateMoment = moment(this._completedDate, 'YYYYMMDDTHHmmss')
this._recurrence = this.vtodo.getFirstPropertyValue('rrule')
this._status = this.vtodo.getFirstPropertyValue('status')
this._note = this.vtodo.getFirstPropertyValue('description') || ''
this._related = this.getParent()?.getFirstValue() || null
Expand Down Expand Up @@ -329,6 +330,19 @@ export default class Task {
return this._completedDateMoment.clone()
}

get recurrence() {
return this._recurrence
}

get recurring() {
if (this._start === null || this._recurrence === null) {
return false
}
const iter = this._recurrence.iterator(this.start);
iter.next();
return iter.next() !== null
}

get status() {
return this._status
}
Expand Down Expand Up @@ -674,6 +688,27 @@ export default class Task {
).toSeconds()
}

/**
* For completing a recurring task, tries to set the task start date to the next recurrence date.
*
* Does nothing if we are at the end of the recurrence (RRULE:UNTIL was reached).
*/
completeRecurring() {
// Get recurrence iterator, starting at start date
const iter = this.recurrence.iterator(this.start);
// Skip the start date itself
iter.next();
// If there is a next recurrence, update the start date to next recurrence date
let nextRecurrence = iter.next();
if (nextRecurrence !== null) {
this.start = nextRecurrence;
// If the due date now lies before start date, clear it
if (this.due !== null && this.due.compare(this.start) < 0) {
this.due = null
}
}
}

/**
* Checks if the task matches the search query
*
Expand Down
6 changes: 6 additions & 0 deletions src/store/tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -1038,6 +1038,12 @@ const actions = {
if (task.calendar.isSharedWithMe && task.class !== 'PUBLIC') {
return
}
// Don't complete a task if it is still recurring, but update its start date instead
if (task.recurring) {
task.completeRecurring()
await context.dispatch('updateTask', task)
return
}
if (task.completed) {
await context.dispatch('setPercentComplete', { task, complete: 0 })
} else {
Expand Down
9 changes: 9 additions & 0 deletions src/views/AppSidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
<TaskCheckbox :completed="task.completed"
:cancelled="task.status === 'CANCELLED'"
:read-only="readOnly"
:recurring="recurring"
:priority-class="priorityClass"
@toggle-completed="toggleCompleted(task)" />
</template>
Expand Down Expand Up @@ -518,6 +519,14 @@ export default {
readOnly() {
return this.task.calendar.readOnly || (this.task.calendar.isSharedWithMe && this.task.class !== 'PUBLIC')
},
/**
* Whether this is a recurring task.
*
* @return {boolean} Is the task recurring
*/
recurring() {
return this.task.recurring
},
/**
* Whether the dates of a task are all-day
* When no dates are set, we consider the last used value.
Expand Down