Skip to content
This repository was archived by the owner on Jul 18, 2025. It is now read-only.
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
2 changes: 1 addition & 1 deletion manifest-beta.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "obsidian-projects",
"name": "Projects",
"version": "0.0.0-999",
"version": "0.0.0-996",
"minAppVersion": "1.0.0",
"description": "Plain text project planning.",
"author": "Marcus Olsson",
Expand Down
18 changes: 18 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,12 @@
"svelte-i18next": "^2.2.2",
"svelte-media-queries": "^1.6.2",
"svelte-preprocess": "^5.1.4",
"temporal-polyfill": "^0.2.5",
"ts-essentials": "^10.0.3",
"ts-jest": "^29.1.3",
"tslib": "^2.6.2",
"typescript": "^5.4.5",
"uuid": "^11.0.3",
"yaml": "^2.4.2"
}
}
}
17 changes: 7 additions & 10 deletions src/lib/dataApi.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import dayjs from "dayjs";
import { produce } from "immer";
import moment from "moment";
import { get } from "svelte/store";
Expand Down Expand Up @@ -185,17 +184,15 @@ export function doUpdateRecord(
(field) =>
field.name === entry[0] &&
field.type === DataFieldType.Date &&
(field.typeConfig?.time ||
entry[1].getHours() ||
entry[1].getMinutes() ||
entry[1].getSeconds() ||
entry[1].getMilliseconds())
(field.typeConfig?.time || entry[1].hour)
// TODO: double check
);

return produce(entry, (draft) => {
draft[1] = dayjs(entry[1]).format(
isDatetime ? "YYYY-MM-DDTHH:mm" : "YYYY-MM-DD"
);
draft[1] = isDatetime
? entry[1]
.toPlainDateTime()
.toString({ smallestUnit: "minute" })
: entry[1].toPlainDate().toString();
});
}
return entry;
Expand Down
56 changes: 30 additions & 26 deletions src/lib/dataframe/dataframe.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { FieldConfig } from "src/settings/settings";
import type { RecordError } from "../datasources/frontmatter/datasource";
import { Temporal } from "temporal-polyfill";

/**
* DataFrame is the core data structure that contains structured data for a
Expand Down Expand Up @@ -77,32 +78,32 @@ export type DataValue =
| string
| number
| boolean
| Date
| Temporal.ZonedDateTime
| Array<Optional<DataValue>>;

export function isOptionalDataValue(
value: unknown
): value is Optional<DataValue> {
switch (typeof value) {
case "string":
return true;
case "number":
return true;
case "boolean":
return true;
default:
return false;
}
}
// export function isOptionalDataValue(
// value: unknown
// ): value is Optional<DataValue> {
// switch (typeof value) {
// case "string":
// return true;
// case "number":
// return true;
// case "boolean":
// return true;
// default:
// return false;
// }
// }

export function isRepeatedDataValue(
value: unknown
): value is Array<Optional<DataValue>> {
if (Array.isArray(value)) {
return value.every(isOptionalDataValue);
}
return false;
}
// export function isRepeatedDataValue(
// value: unknown
// ): value is Array<Optional<DataValue>> {
// if (Array.isArray(value)) {
// return value.every(isOptionalDataValue);
// }
// return false;
// }

export type Optional<T> =
| T
Expand Down Expand Up @@ -138,8 +139,11 @@ export function isNumber(
return typeof value === "number";
}

export function isDate(value: Optional<DataValue> | DataValue): value is Date {
return value instanceof Date;
export function isDate(
value: Optional<DataValue> | DataValue
): value is Temporal.ZonedDateTime {
return value instanceof Temporal.ZonedDateTime;
//TODO: can be lessen to ZonedDateTimeLike ?
}

// export function hasValue(value: Optional<DataValue>): value is DataValue {
Expand Down Expand Up @@ -179,7 +183,7 @@ export function isOptionalNumber(

export function isOptionalDate(
value: Optional<DataValue>
): value is Optional<Date> {
): value is Optional<Temporal.ZonedDateTime> {
return isDate(value) || isOptional(value);
}

Expand Down
4 changes: 2 additions & 2 deletions src/lib/datasources/dataview/standardize.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import dayjs from "dayjs";
import type { Link } from "obsidian-dataview";
import type { DataValue, Optional } from "src/lib/dataframe/dataframe";
import { Temporal } from "temporal-polyfill";

/**
* standardizeValues converts a Dataview data structure of values to the common
Expand Down Expand Up @@ -36,6 +36,6 @@ function standardizeObject(value: any) {
return (value as Link).toString();
}
if ("ts" in value) {
return dayjs(value.ts).format("YYYY-MM-DD");
return Temporal.PlainDateTime.from(value.c).toString()
}
}
40 changes: 35 additions & 5 deletions src/lib/datasources/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import dayjs from "dayjs";

import {
DataFieldType,
type DataField,
type DataRecord,
type DataValue,
type Optional,
} from "../dataframe/dataframe";
import { Temporal } from "temporal-polyfill";

/**
* Parses the values for each record based on the detected field types.
Expand All @@ -29,15 +28,44 @@ export function parseRecords(
const value = record.values[field.name];

switch (field.type) {
case DataFieldType.Date:
case DataFieldType.Date: //TODO: extract to helpers, and processing numbers
if (typeof value === "string") {
record.values[field.name] = dayjs(value).toDate();
let parsedValue = null;
try {
// Attempt to parse as ZonedDateTime
parsedValue = Temporal.ZonedDateTime.from(value);
} catch {
try {
// Attempt to parse as Instant and convert to ZonedDateTime
parsedValue =
Temporal.Instant.from(value).toZonedDateTimeISO(value);
} catch {
try {
// Attempt to create ZonedDateTime using the current time and a PlainDate
parsedValue = Temporal.PlainDateTime.from(
value
).toZonedDateTime(Temporal.Now.timeZoneId());
} catch {
parsedValue = null; // Default to null if all parsing fails
}
}
}

// Assign the parsed value to the record
record.values[field.name] = parsedValue;
}
break;
case DataFieldType.Number:
if (typeof value === "string") {
record.values[field.name] = parseFloat(value);
}
// else if (typeof value === "number") {
// record.values[field.name] =
// Temporal.ZonedDateTime.from(value.toString()) ??
// Temporal.Instant.fromEpochMilliseconds(value).toZonedDateTimeISO(
// "UTC" // possible thru Now, for user local zone
// );
// }
break;
case DataFieldType.Boolean:
if (typeof value === "string") {
Expand Down Expand Up @@ -123,7 +151,9 @@ export function detectCellType(value: unknown): DataFieldType {
// Standard types
if (typeof value === "string") {
if (
/^\d{4}-\d{2}-\d{2}(T)?(\d{2})?(:\d{2})?(:\d{2})?(.\d{3})?$/.test(value)
/^\d{4}-\d{2}-\d{2}(?:[Tt ](?:\d{2})?(?::\d{2})?(?::\d{2})?(?:.\d+)?(?:[+-]\d{2}(?::?\d{2})?|[Zz])?)?$/.test(
value
)
) {
return DataFieldType.Date;
}
Expand Down
6 changes: 0 additions & 6 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import dayjs from "dayjs";
import isoWeek from "dayjs/plugin/isoWeek";
import localizedFormat from "dayjs/plugin/localizedFormat";
import { either, task, taskEither } from "fp-ts";
import { pipe } from "fp-ts/lib/function";
import { Plugin, TFolder, WorkspaceLeaf, addIcon } from "obsidian";
Expand All @@ -25,9 +22,6 @@ import {
} from "./settings/settings";
import { ProjectsView, VIEW_TYPE_PROJECTS } from "./view";

dayjs.extend(isoWeek);
dayjs.extend(localizedFormat);

const PROJECTS_PLUGIN_ID = "obsidian-projects";

export default class ProjectsPlugin extends Plugin {
Expand Down
50 changes: 38 additions & 12 deletions src/ui/app/filterFunctions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { produce } from "immer";
import dayjs from "dayjs";
import {
type DataFrame,
type DataRecord,
Expand Down Expand Up @@ -27,6 +26,7 @@ import {
type DateFilterOperator,
type ListFilterOperator,
} from "src/settings/settings";
import { Temporal } from "temporal-polyfill";

export function matchesCondition(
cond: FilterCondition,
Expand Down Expand Up @@ -61,10 +61,30 @@ export function matchesCondition(
} else if (isOptionalBoolean(value) && isBooleanFilterOperator(operator)) {
return booleanFns[operator](value);
} else if (isOptionalDate(value) && isDateFilterOperator(operator)) {
return dateFns[operator](
value,
cond.value ? dayjs(cond.value).toDate() : undefined
);
let parsedValue = null; // TODO: extract to help functions / processing numbers
if (cond.value) {
try {
// Attempt to parse as ZonedDateTime
parsedValue = Temporal.ZonedDateTime.from(cond.value);
} catch {
try {
// Attempt to parse as Instant and convert to ZonedDateTime
parsedValue = Temporal.Instant.from(cond.value).toZonedDateTimeISO(
cond.value
);
} catch {
try {
// Attempt to create ZonedDateTime using the current time and a PlainDate
parsedValue = Temporal.PlainDateTime.from(
cond.value
).toZonedDateTime(Temporal.Now.timeZoneId());
} catch {
parsedValue = null; // Default to null if all parsing fails
}
}
}
}
return dateFns[operator](value, parsedValue ?? undefined);
}

return false;
Expand Down Expand Up @@ -137,22 +157,28 @@ export const booleanFns: Record<
};

export const dateFns: Record<
//TODO: handle datetime
DateFilterOperator,
(left: Optional<Date>, right?: Optional<Date>) => boolean
(
left: Optional<Temporal.ZonedDateTime>,
right?: Optional<Temporal.ZonedDateTime>
) => boolean // TODO: refactor
> = {
"is-on": (left, right) => {
return left && right ? left.getTime() == right.getTime() : false;
return left && right ? left.equals(right) : false;
},
"is-not-on": (left, right) =>
left && right ? left.getTime() != right.getTime() : true,
left?.toPlainDate() && right?.toPlainDate()
? !left.toPlainDate().equals(right.toPlainDate())
: true,
"is-before": (left, right) =>
left && right ? left.getTime() < right.getTime() : false,
left && right ? Temporal.PlainDate.compare(left, right) == -1 : false,
"is-after": (left, right) =>
left && right ? left.getTime() > right.getTime() : false,
left && right ? Temporal.PlainDate.compare(left, right) == 1 : false,
"is-on-and-before": (left, right) =>
left && right ? left.getTime() <= right.getTime() : false,
left && right ? Temporal.PlainDate.compare(left, right) < 1 : false,
"is-on-and-after": (left, right) =>
left && right ? left.getTime() >= right.getTime() : false,
left && right ? Temporal.PlainDate.compare(left, right) > -1 : false,
};

export const listFns_multitext: Record<
Expand Down
Loading
Loading