|
@@ -7,6 +7,125 @@ function showFilterSidebar() {
|
|
Sidebar.setView('filter');
|
|
Sidebar.setView('filter');
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+class DateFilter {
|
|
|
|
+ constructor() {
|
|
|
|
+ this._dep = new Tracker.Dependency();
|
|
|
|
+ this.subField = ''; // Prevent name mangling in Filter
|
|
|
|
+ this._filter = null;
|
|
|
|
+ this._filterState = null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ _updateState(state) {
|
|
|
|
+ this._filterState = state;
|
|
|
|
+ showFilterSidebar();
|
|
|
|
+ this._dep.changed();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // past builds a filter for all dates before now
|
|
|
|
+ past() {
|
|
|
|
+ if (this._filterState == 'past') { this.reset(); return; }
|
|
|
|
+ this._filter = { $lte: moment().toDate() };
|
|
|
|
+ this._updateState('past');
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // today is a convenience method for calling relativeDay with 0
|
|
|
|
+ today() {
|
|
|
|
+ if (this._filterState == 'today') { this.reset(); return; }
|
|
|
|
+ this.relativeDay(0);
|
|
|
|
+ this._updateState('today');
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // tomorrow is a convenience method for calling relativeDay with 1
|
|
|
|
+ tomorrow() {
|
|
|
|
+ if (this._filterState == 'tomorrow') { this.reset(); return; }
|
|
|
|
+ this.relativeDay(1);
|
|
|
|
+ this._updateState('tomorrow');
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // thisWeek is a convenience method for calling relativeWeek with 1
|
|
|
|
+ thisWeek() {
|
|
|
|
+ this.relativeWeek(1);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // relativeDay builds a filter starting from now and including all
|
|
|
|
+ // days up to today +/- offset.
|
|
|
|
+ relativeDay(offset) {
|
|
|
|
+ if (this._filterState == 'day') { this.reset(); return; }
|
|
|
|
+
|
|
|
|
+ var startDay = moment().startOf('day').toDate(),
|
|
|
|
+ endDay = moment().endOf('day').add(offset, 'day').toDate();
|
|
|
|
+
|
|
|
|
+ if (offset >= 0) {
|
|
|
|
+ this._filter = { $gte: startDay, $lte: endDay };
|
|
|
|
+ } else {
|
|
|
|
+ this._filter = { $lte: startDay, $gte: endDay };
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this._updateState('day');
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // relativeWeek builds a filter starting from today and including all
|
|
|
|
+ // weeks up to today +/- offset. This considers the user's preferred
|
|
|
|
+ // start of week day (as defined by Meteor).
|
|
|
|
+ relativeWeek(offset) {
|
|
|
|
+ if (this._filterState == 'week') { this.reset(); return; }
|
|
|
|
+
|
|
|
|
+ // getStartDayOfWeek returns the offset from Sunday of the user's
|
|
|
|
+ // preferred starting day of the week. This date should be added
|
|
|
|
+ // to the moment start of week to get the real start of week date.
|
|
|
|
+ // The default is 1, meaning Monday.
|
|
|
|
+ const currentUser = Meteor.user();
|
|
|
|
+ const weekStartDay = currentUser ? currentUser.getStartDayOfWeek() : 1;
|
|
|
|
+
|
|
|
|
+ // Moments are mutable so they must be cloned before modification
|
|
|
|
+ var thisWeekStart = moment().startOf('day').startOf('week').add(weekStartDay, 'days');
|
|
|
|
+ var thisWeekEnd = thisWeekStart.clone().add(offset, 'week').endOf('day');
|
|
|
|
+ var startDate = thisWeekStart.toDate();
|
|
|
|
+ var endDate = thisWeekEnd.toDate();
|
|
|
|
+
|
|
|
|
+ if (offset >= 0) {
|
|
|
|
+ this._filter = { $gte: startDate, $lte: endDate };
|
|
|
|
+ } else {
|
|
|
|
+ this._filter = { $lte: startDate, $gte: endDate };
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this._updateState('week');
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // noDate builds a filter for items where date is not set
|
|
|
|
+ noDate() {
|
|
|
|
+ if (this._filterState == 'noDate') { this.reset(); return; }
|
|
|
|
+ this._filter = null;
|
|
|
|
+ this._updateState('noDate');
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ reset() {
|
|
|
|
+ this._filter = null;
|
|
|
|
+ this._filterState = null;
|
|
|
|
+ this._dep.changed();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ isSelected(val) {
|
|
|
|
+ this._dep.depend();
|
|
|
|
+ return this._filterState == val;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ _isActive() {
|
|
|
|
+ this._dep.depend();
|
|
|
|
+ return this._filterState !== null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ _getMongoSelector() {
|
|
|
|
+ this._dep.depend();
|
|
|
|
+ return this._filter;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ _getEmptySelector() {
|
|
|
|
+ this._dep.depend();
|
|
|
|
+ return null;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
// Use a "set" filter for a field that is a set of documents uniquely
|
|
// Use a "set" filter for a field that is a set of documents uniquely
|
|
// identified. For instance `{ labels: ['labelA', 'labelC', 'labelD'] }`.
|
|
// identified. For instance `{ labels: ['labelA', 'labelC', 'labelD'] }`.
|
|
// use "subField" for searching inside object Fields.
|
|
// use "subField" for searching inside object Fields.
|
|
@@ -462,6 +581,7 @@ Filter = {
|
|
assignees: new SetFilter(),
|
|
assignees: new SetFilter(),
|
|
archive: new SetFilter(),
|
|
archive: new SetFilter(),
|
|
hideEmpty: new SetFilter(),
|
|
hideEmpty: new SetFilter(),
|
|
|
|
+ dueAt: new DateFilter(),
|
|
customFields: new SetFilter('_id'),
|
|
customFields: new SetFilter('_id'),
|
|
advanced: new AdvancedFilter(),
|
|
advanced: new AdvancedFilter(),
|
|
lists: new AdvancedFilter(), // we need the ability to filter list by name as well
|
|
lists: new AdvancedFilter(), // we need the ability to filter list by name as well
|
|
@@ -472,6 +592,7 @@ Filter = {
|
|
'assignees',
|
|
'assignees',
|
|
'archive',
|
|
'archive',
|
|
'hideEmpty',
|
|
'hideEmpty',
|
|
|
|
+ 'dueAt',
|
|
'customFields',
|
|
'customFields',
|
|
],
|
|
],
|
|
|
|
|