Skip to content

Commit 783cbc2

Browse files
committed
plugin: add support for 'time' parameter in show
1 parent 2facd9a commit 783cbc2

File tree

9 files changed

+297
-7
lines changed

9 files changed

+297
-7
lines changed

plugin/src/data/dueDate.test.ts

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -883,3 +883,169 @@ describe("formatAsHeader", () => {
883883
});
884884
}
885885
});
886+
887+
describe("formatTimeOnly", () => {
888+
type Testcase = {
889+
description: string;
890+
dueDate: DueDate;
891+
expected: string;
892+
};
893+
894+
const testcases: Testcase[] = [
895+
{
896+
description: "no time component",
897+
dueDate: {
898+
start: {
899+
raw: makeDate(2024, 0, 1),
900+
hasTime: false,
901+
isOverdue: false,
902+
isCurrentYear: true,
903+
flag: "today",
904+
},
905+
end: undefined,
906+
},
907+
expected: "",
908+
},
909+
{
910+
description: "single time",
911+
dueDate: {
912+
start: {
913+
raw: makeDate(2024, 0, 1, 14, 30),
914+
hasTime: true,
915+
isOverdue: false,
916+
isCurrentYear: true,
917+
flag: "today",
918+
},
919+
end: undefined,
920+
},
921+
expected: "2:30 PM",
922+
},
923+
{
924+
description: "same-day duration",
925+
dueDate: {
926+
start: {
927+
raw: makeDate(2024, 0, 1, 14, 30),
928+
hasTime: true,
929+
isOverdue: false,
930+
isCurrentYear: true,
931+
flag: "today",
932+
},
933+
end: {
934+
raw: makeDate(2024, 0, 1, 15, 30),
935+
hasTime: true,
936+
isOverdue: false,
937+
isCurrentYear: true,
938+
flag: "today",
939+
},
940+
},
941+
expected: "2:30 PM - 3:30 PM",
942+
},
943+
{
944+
description: "same-day duration (different times)",
945+
dueDate: {
946+
start: {
947+
raw: makeDate(2024, 1, 15, 9, 0),
948+
hasTime: true,
949+
isOverdue: false,
950+
isCurrentYear: true,
951+
flag: undefined,
952+
},
953+
end: {
954+
raw: makeDate(2024, 1, 15, 17, 0),
955+
hasTime: true,
956+
isOverdue: false,
957+
isCurrentYear: true,
958+
flag: undefined,
959+
},
960+
},
961+
expected: "9:00 AM - 5:00 PM",
962+
},
963+
{
964+
description: "multi-day duration (today to tomorrow)",
965+
dueDate: {
966+
start: {
967+
raw: makeDate(2024, 0, 1, 14, 0),
968+
hasTime: true,
969+
isOverdue: false,
970+
isCurrentYear: true,
971+
flag: "today",
972+
},
973+
end: {
974+
raw: makeDate(2024, 0, 2, 10, 0),
975+
hasTime: true,
976+
isOverdue: false,
977+
isCurrentYear: true,
978+
flag: "tomorrow",
979+
},
980+
},
981+
expected: "2:00 PM - Tomorrow at 10:00 AM",
982+
},
983+
{
984+
description: "multi-day duration (tomorrow to next week)",
985+
dueDate: {
986+
start: {
987+
raw: makeDate(2024, 0, 2, 9, 0),
988+
hasTime: true,
989+
isOverdue: false,
990+
isCurrentYear: true,
991+
flag: "tomorrow",
992+
},
993+
end: {
994+
raw: makeDate(2024, 0, 5, 17, 0),
995+
hasTime: true,
996+
isOverdue: false,
997+
isCurrentYear: true,
998+
flag: "nextWeek",
999+
},
1000+
},
1001+
expected: "9:00 AM - Friday at 5:00 PM",
1002+
},
1003+
{
1004+
description: "multi-day duration (specific dates)",
1005+
dueDate: {
1006+
start: {
1007+
raw: makeDate(2024, 0, 3, 12, 0),
1008+
hasTime: true,
1009+
isOverdue: false,
1010+
isCurrentYear: true,
1011+
flag: undefined,
1012+
},
1013+
end: {
1014+
raw: makeDate(2024, 0, 8, 12, 0),
1015+
hasTime: true,
1016+
isOverdue: false,
1017+
isCurrentYear: true,
1018+
flag: undefined,
1019+
},
1020+
},
1021+
expected: "12:00 PM - Jan 8 at 12:00 PM",
1022+
},
1023+
{
1024+
description: "multi-day duration (different years)",
1025+
dueDate: {
1026+
start: {
1027+
raw: makeDate(2024, 11, 30, 9, 0),
1028+
hasTime: true,
1029+
isOverdue: false,
1030+
isCurrentYear: true,
1031+
flag: undefined,
1032+
},
1033+
end: {
1034+
raw: makeDate(2025, 0, 2, 17, 0),
1035+
hasTime: true,
1036+
isOverdue: false,
1037+
isCurrentYear: false,
1038+
flag: undefined,
1039+
},
1040+
},
1041+
expected: "9:00 AM - Jan 2, 2025 at 5:00 PM",
1042+
},
1043+
];
1044+
1045+
for (const tc of testcases) {
1046+
it(tc.description, () => {
1047+
const actual = DueDate.formatTimeOnly(tc.dueDate);
1048+
expect(actual).toBe(tc.expected);
1049+
});
1050+
}
1051+
});

plugin/src/data/dueDate.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,10 +203,33 @@ const formatDueDateHeader = (due: DueDate): string => {
203203
return parts.join(" ‧ ");
204204
};
205205

206+
const formatDueDateTimeOnly = (dueDate: DueDate): string => {
207+
if (!dueDate.start.hasTime) {
208+
return "";
209+
}
210+
211+
const i18n = t().dates;
212+
const startTime = getFormatter("time").format(dueDate.start.raw);
213+
214+
if (dueDate.end === undefined) {
215+
return startTime;
216+
}
217+
218+
const endTime = getFormatter("time").format(dueDate.end.raw);
219+
220+
if (isSameDay(dueDate.start.raw, dueDate.end.raw)) {
221+
return i18n.timeDuration(startTime, endTime);
222+
}
223+
224+
const endDate = formatDate(dueDate.end);
225+
return i18n.timeDurationDifferentDays(startTime, endDate, endTime);
226+
};
227+
206228
export const DueDate = {
207229
parse: parseDueDate,
208230
format: formatDueDate,
209231
formatHeader: formatDueDateHeader,
232+
formatTimeOnly: formatDueDateTimeOnly,
210233
};
211234

212235
function isSameDay(a: Date, b: Date): boolean {

plugin/src/i18n/langs/en.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,8 @@ export const en: Translations = {
251251
jsonQuery:
252252
"This query is written using JSON. This is deprecated and will be removed in a future version. Please use YAML instead.",
253253
unknownKey: (key: string) => `Found unexpected query key '${key}'. Is this a typo?`,
254+
dueAndTime:
255+
"Both 'due' and 'time' show options are set. The 'time' option will be ignored when 'due' is present.",
254256
},
255257
groupedHeaders: {
256258
noDueDate: "No due date",
@@ -290,5 +292,11 @@ export const en: Translations = {
290292
): string => {
291293
return `${startDate} at ${startTime} - ${endDate} at ${endTime}`;
292294
},
295+
timeDuration: (startTime: string, endTime: string) => {
296+
return `${startTime} - ${endTime}`;
297+
},
298+
timeDurationDifferentDays: (startTime: string, endDate: string, endTime: string) => {
299+
return `${startTime} - ${endDate} at ${endTime}`;
300+
},
293301
},
294302
};

plugin/src/i18n/translation.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ export type Translations = {
226226
header: string;
227227
jsonQuery: string;
228228
unknownKey: (key: string) => string;
229+
dueAndTime: string;
229230
};
230231
groupedHeaders: {
231232
noDueDate: string;
@@ -256,5 +257,7 @@ export type Translations = {
256257
endDate: string,
257258
endTime: string,
258259
) => string;
260+
timeDuration: (startTime: string, endTime: string) => string;
261+
timeDurationDifferentDays: (startTime: string, endDate: string, endTime: string) => string;
259262
};
260263
};

plugin/src/query/__snapshots__/parser.test.ts.snap

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,9 @@ exports[`parseQuery - error message snapshots > show array must contain strings
6666
[Error: Field 'show' has the following issues:
6767
Invalid input: expected "none"
6868
Field 'show' elements have the following issues:
69-
Item 'show[0]': Invalid option: expected one of "due"|"date"|"description"|"labels"|"project"|"deadline"
70-
Item 'show[1]': Invalid option: expected one of "due"|"date"|"description"|"labels"|"project"|"deadline"
71-
Item 'show[2]': Invalid option: expected one of "due"|"date"|"description"|"labels"|"project"|"deadline"]
69+
Item 'show[0]': Invalid option: expected one of "due"|"date"|"description"|"labels"|"project"|"deadline"|"time"
70+
Item 'show[1]': Invalid option: expected one of "due"|"date"|"description"|"labels"|"project"|"deadline"|"time"
71+
Item 'show[2]': Invalid option: expected one of "due"|"date"|"description"|"labels"|"project"|"deadline"|"time"]
7272
`;
7373

7474
exports[`parseQuery - error message snapshots > show field - invalid literal (not 'none') 1`] = `
@@ -81,8 +81,8 @@ exports[`parseQuery - error message snapshots > show must have valid enum values
8181
[Error: Field 'show' has the following issues:
8282
Invalid input: expected "none"
8383
Field 'show' elements have the following issues:
84-
Item 'show[0]': Invalid option: expected one of "due"|"date"|"description"|"labels"|"project"|"deadline"
85-
Item 'show[1]': Invalid option: expected one of "due"|"date"|"description"|"labels"|"project"|"deadline"]
84+
Item 'show[0]': Invalid option: expected one of "due"|"date"|"description"|"labels"|"project"|"deadline"|"time"
85+
Item 'show[1]': Invalid option: expected one of "due"|"date"|"description"|"labels"|"project"|"deadline"|"time"]
8686
`;
8787

8888
exports[`parseQuery - error message snapshots > sorting array must contain strings 1`] = `

plugin/src/query/parser.test.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,39 @@ describe("parseQuery", () => {
236236
show: new Set(),
237237
}),
238238
},
239+
{
240+
description: "with show including time only",
241+
input: {
242+
filter: "bar",
243+
show: ["time"],
244+
},
245+
expectedOutput: makeQuery({
246+
filter: "bar",
247+
show: new Set([ShowMetadataVariant.Time]),
248+
}),
249+
},
250+
{
251+
description: "with show including time and project",
252+
input: {
253+
filter: "bar",
254+
show: ["time", "project"],
255+
},
256+
expectedOutput: makeQuery({
257+
filter: "bar",
258+
show: new Set([ShowMetadataVariant.Time, ShowMetadataVariant.Project]),
259+
}),
260+
},
261+
{
262+
description: "with show including both due and time",
263+
input: {
264+
filter: "bar",
265+
show: ["due", "time"],
266+
},
267+
expectedOutput: makeQuery({
268+
filter: "bar",
269+
show: new Set([ShowMetadataVariant.Due, ShowMetadataVariant.Time]),
270+
}),
271+
},
239272
];
240273

241274
for (const tc of testcases) {
@@ -275,6 +308,17 @@ describe("parseQuery - warnings", () => {
275308
"Found unexpected query key 'namee'. Is this a typo?",
276309
],
277310
},
311+
{
312+
description: "Both due and time in show options",
313+
input: {
314+
filter: "bar",
315+
show: ["due", "time"],
316+
},
317+
expectedWarnings: [
318+
"This query is written using JSON. This is deprecated and will be removed in a future version. Please use YAML instead.",
319+
"Both 'due' and 'time' show options are set. The 'time' option will be ignored when 'due' is present.",
320+
],
321+
},
278322
];
279323

280324
for (const tc of testcases) {

plugin/src/query/parser.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ const showSchema = lookupToEnum({
104104
labels: ShowMetadataVariant.Labels,
105105
project: ShowMetadataVariant.Project,
106106
deadline: ShowMetadataVariant.Deadline,
107+
time: ShowMetadataVariant.Time,
107108
});
108109

109110
const groupBySchema = lookupToEnum({
@@ -161,13 +162,19 @@ function parseObjectZod(query: Record<string, unknown>): [Query, QueryWarning[]]
161162
throw new ParsingError(formatZodError(out.error));
162163
}
163164

165+
const show = new Set(out.data.show);
166+
167+
if (show.has(ShowMetadataVariant.Due) && show.has(ShowMetadataVariant.Time)) {
168+
warnings.push(t().query.warning.dueAndTime);
169+
}
170+
164171
return [
165172
{
166173
name: out.data.name,
167174
filter: out.data.filter,
168175
autorefresh: out.data.autorefresh,
169176
sorting: out.data.sorting,
170-
show: new Set(out.data.show),
177+
show,
171178
groupBy: out.data.groupBy,
172179
},
173180
warnings,

plugin/src/query/query.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export enum ShowMetadataVariant {
1414
Labels = 2,
1515
Description = 3,
1616
Deadline = 4,
17+
Time = 5,
1718
}
1819

1920
export enum GroupVariant {

0 commit comments

Comments
 (0)