mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-01-01 05:06:53 +08:00
Add date (since: yesterday, before: last week) handling to search bar
This commit is contained in:
parent
9c9c9fa93f
commit
af0f389503
6 changed files with 106 additions and 0 deletions
|
@ -47,6 +47,7 @@ function isPastDate(inputDateObj, currentDate) {
|
|||
return inputMoment.isBefore(currentMoment);
|
||||
}
|
||||
|
||||
let _chronoPast = null;
|
||||
let _chronoFuture = null;
|
||||
let _chrono = null;
|
||||
|
||||
|
@ -93,6 +94,42 @@ function getChronoFuture() {
|
|||
return _chronoFuture;
|
||||
}
|
||||
|
||||
function getChronoPast() {
|
||||
if (_chronoPast) {
|
||||
return _chronoPast;
|
||||
}
|
||||
|
||||
const chrono = getChrono();
|
||||
const EnforcePastDate = new chrono.Refiner();
|
||||
EnforcePastDate.refine = (text, results) => {
|
||||
results.forEach(result => {
|
||||
const current = Object.assign({}, result.start.knownValues, result.start.impliedValues);
|
||||
|
||||
if (result.start.isCertain('weekday') && !result.start.isCertain('day')) {
|
||||
if (!isPastDate(current, result.ref)) {
|
||||
result.start.imply('day', result.start.impliedValues.day - 7);
|
||||
}
|
||||
}
|
||||
|
||||
if (result.start.isCertain('day') && !result.start.isCertain('month')) {
|
||||
if (!isPastDate(current, result.ref)) {
|
||||
result.start.imply('month', result.start.impliedValues.month - 1);
|
||||
}
|
||||
}
|
||||
if (result.start.isCertain('month') && !result.start.isCertain('year')) {
|
||||
if (!isPastDate(current, result.ref)) {
|
||||
result.start.imply('year', result.start.impliedValues.year - 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
_chronoPast = new chrono.Chrono(chrono.options.casualOption());
|
||||
_chronoPast.refiners.push(EnforcePastDate);
|
||||
return _chronoPast;
|
||||
}
|
||||
|
||||
const DateUtils = {
|
||||
// Localized format: ddd, MMM D, YYYY h:mmA
|
||||
DATE_FORMAT_LONG: 'llll',
|
||||
|
@ -187,6 +224,8 @@ const DateUtils = {
|
|||
return morning(now.add(1, 'month').date(1));
|
||||
},
|
||||
|
||||
getChronoPast,
|
||||
|
||||
parseDateString(dateLikeString) {
|
||||
const parsed = getChrono().parse(dateLikeString);
|
||||
const gotTime = { start: false, end: false };
|
||||
|
|
|
@ -19,6 +19,9 @@ class SearchQueryExpressionVisitor {
|
|||
visitFrom(node) {
|
||||
throw new Error('Abstract function not implemented!', node);
|
||||
}
|
||||
visitDate(node) {
|
||||
throw new Error('Abstract function not implemented!', node);
|
||||
}
|
||||
visitTo(node) {
|
||||
throw new Error('Abstract function not implemented!', node);
|
||||
}
|
||||
|
@ -147,6 +150,29 @@ class FromQueryExpression extends QueryExpression {
|
|||
}
|
||||
}
|
||||
|
||||
class DateQueryExpression extends QueryExpression {
|
||||
constructor(text, direction = 'before') {
|
||||
super();
|
||||
this.text = text;
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
accept(visitor) {
|
||||
visitor.visitDate(this);
|
||||
}
|
||||
|
||||
_computeIsMatchCompatible() {
|
||||
return false;
|
||||
}
|
||||
|
||||
equals(other) {
|
||||
if (!(other instanceof DateQueryExpression)) {
|
||||
return false;
|
||||
}
|
||||
return this.text.equals(other.text);
|
||||
}
|
||||
}
|
||||
|
||||
class ToQueryExpression extends QueryExpression {
|
||||
constructor(text) {
|
||||
super();
|
||||
|
@ -368,5 +394,6 @@ module.exports = {
|
|||
StarredStatusQueryExpression,
|
||||
MatchQueryExpression,
|
||||
InQueryExpression,
|
||||
DateQueryExpression,
|
||||
HasAttachmentQueryExpression,
|
||||
};
|
||||
|
|
|
@ -116,6 +116,10 @@ class IMAPSearchQueryExpressionVisitor extends SearchQueryExpressionVisitor {
|
|||
this._result = ['FROM', text];
|
||||
}
|
||||
|
||||
visitDate(node) {
|
||||
throw new Error('Function not implemented!', node);
|
||||
}
|
||||
|
||||
visitTo(node) {
|
||||
const text = this.visitAndGetResult(node.text);
|
||||
this._result = ['TO', text];
|
||||
|
|
|
@ -2,11 +2,13 @@ import {
|
|||
SearchQueryExpressionVisitor,
|
||||
OrQueryExpression,
|
||||
AndQueryExpression,
|
||||
DateQueryExpression,
|
||||
UnreadStatusQueryExpression,
|
||||
StarredStatusQueryExpression,
|
||||
HasAttachmentQueryExpression,
|
||||
MatchQueryExpression,
|
||||
} from './search-query-ast';
|
||||
import { DateUtils } from 'mailspring-exports';
|
||||
|
||||
/*
|
||||
* This class visits a match-compatible subtree and condenses it into a single
|
||||
|
@ -37,6 +39,8 @@ class MatchQueryExpressionVisitor extends SearchQueryExpressionVisitor {
|
|||
this._result = `(${lhs} OR ${rhs})`;
|
||||
}
|
||||
|
||||
visitDate(node) {}
|
||||
|
||||
visitFrom(node) {
|
||||
const text = this.visitAndGetResult(node.text);
|
||||
this._result = `(from_ : "${text}"*)`;
|
||||
|
@ -144,6 +148,10 @@ class MatchCompatibleQueryCondenser extends SearchQueryExpressionVisitor {
|
|||
this._result = new UnreadStatusQueryExpression(node.status);
|
||||
}
|
||||
|
||||
visitDate(node) {
|
||||
this._result = new DateQueryExpression(node.text, node.direction);
|
||||
}
|
||||
|
||||
visitStarred(node) {
|
||||
this._result = new StarredStatusQueryExpression(node.status);
|
||||
}
|
||||
|
@ -218,6 +226,17 @@ class StructuredSearchQueryVisitor extends SearchQueryExpressionVisitor {
|
|||
this._result = `(\`${this._className}\`.\`data\` LIKE '%"has_attachments":true%')`;
|
||||
}
|
||||
|
||||
visitDate(node) {
|
||||
const comparator = node.direction === 'before' ? '<' : '>';
|
||||
const date = DateUtils.getChronoPast().parseDate(node.text.token.s);
|
||||
if (!date) {
|
||||
this._result = '';
|
||||
return;
|
||||
}
|
||||
const ts = Math.floor(date.getTime() / 1000);
|
||||
this._result = `(\`${this._className}\`.\`lastMessageReceivedTimestamp\` ${comparator} ${ts})`;
|
||||
}
|
||||
|
||||
visitMatch(node) {
|
||||
const searchTable = `${this._className}Search`;
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
TextQueryExpression,
|
||||
UnreadStatusQueryExpression,
|
||||
StarredStatusQueryExpression,
|
||||
DateQueryExpression,
|
||||
InQueryExpression,
|
||||
HasAttachmentQueryExpression,
|
||||
} from './search-query-ast';
|
||||
|
@ -66,6 +67,9 @@ const reserved = [
|
|||
'in',
|
||||
'has',
|
||||
'attachment',
|
||||
'before',
|
||||
'since',
|
||||
'after',
|
||||
];
|
||||
|
||||
const mightBeReserved = text => {
|
||||
|
@ -272,6 +276,18 @@ const parseSimpleQuery = text => {
|
|||
}
|
||||
}
|
||||
|
||||
if (tok.s.toUpperCase() === 'SINCE' || tok.s.toUpperCase() === 'AFTER') {
|
||||
const afterColon = consumeExpectedToken(afterTok, ':');
|
||||
const [txt, afterTxt] = parseText(afterColon);
|
||||
return [new DateQueryExpression(txt, 'after'), afterTxt];
|
||||
}
|
||||
|
||||
if (tok.s.toUpperCase() === 'BEFORE') {
|
||||
const afterColon = consumeExpectedToken(afterTok, ':');
|
||||
const [txt, afterTxt] = parseText(afterColon);
|
||||
return [new DateQueryExpression(txt, 'before'), afterTxt];
|
||||
}
|
||||
|
||||
if (tok.s.toUpperCase() === 'IN') {
|
||||
const afterColon = consumeExpectedToken(afterTok, ':');
|
||||
const [txt, afterTxt] = parseText(afterColon);
|
||||
|
|
|
@ -64,6 +64,7 @@
|
|||
}
|
||||
|
||||
.search-accessory {
|
||||
|
||||
&.search {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
|
|
Loading…
Reference in a new issue