162 lines
4.1 KiB
TypeScript
162 lines
4.1 KiB
TypeScript
import { assign, setup } from "xstate";
|
|
import { Lifelog, LifelogsResponse, queryLifelogs } from "./lifelogs/query.ts";
|
|
import { writeLifelog } from "./lifelogs/write.ts";
|
|
import { addDay, isAfter, parse } from "@formkit/tempo";
|
|
import { LimitlessPluginSettings } from "../settings.ts";
|
|
import { Vault } from "obsidian";
|
|
|
|
export interface Context {
|
|
vault: Vault;
|
|
apiKey: string;
|
|
latestLog?: Date;
|
|
lifelogsDir: string;
|
|
cursor: string | null;
|
|
lifelogQueue: Lifelog[];
|
|
}
|
|
|
|
export type Input = LimitlessPluginSettings & {
|
|
vault: Vault;
|
|
};
|
|
|
|
export const pluginActor = setup({
|
|
types: {
|
|
context: {} as Context,
|
|
input: {} as Input,
|
|
},
|
|
actors: {
|
|
queryLifelogs,
|
|
writeLifelog,
|
|
},
|
|
guards: {
|
|
lifelogQueueNotEmpty: ({ context }) => context.lifelogQueue.length > 0,
|
|
apiReturnedLifelogs: (_, params: LifelogsResponse) =>
|
|
params.data.lifelogs.length > 0,
|
|
hasCursor: ({ context }) => Boolean(context.cursor),
|
|
},
|
|
actions: {
|
|
updateLatestLog: assign(({ context }, params: { endDate: Date }) => {
|
|
if (context.latestLog && isAfter(context.latestLog, params.endDate)) {
|
|
return context;
|
|
}
|
|
|
|
// TODO: Write back to settings
|
|
|
|
return {
|
|
latestLog: params.endDate,
|
|
};
|
|
}),
|
|
updateCursor: assign((_, params: { cursor: string | null }) => ({
|
|
cursor: params.cursor,
|
|
})),
|
|
queueLifelogs: assign(
|
|
({ context }, params: { lifelogs: LifelogsResponse }) => {
|
|
return {
|
|
lifelogQueue: context.lifelogQueue.concat(
|
|
params.lifelogs.data.lifelogs,
|
|
),
|
|
};
|
|
},
|
|
),
|
|
shiftLifelogQueue: assign(({ context }) => {
|
|
const newQueue = [...context.lifelogQueue];
|
|
newQueue.shift();
|
|
|
|
return {
|
|
lifelogQueue: newQueue,
|
|
};
|
|
}),
|
|
},
|
|
}).createMachine({
|
|
id: "obsidian-limitless-plugin",
|
|
initial: "querying",
|
|
context: ({ input }) => ({
|
|
apiKey: input.apiKey,
|
|
latestLog: input.latestLog ? parse(input.latestLog) : undefined,
|
|
lifelogsDir: input.lifelogsDir,
|
|
cursor: null,
|
|
lifelogQueue: [],
|
|
vault: input.vault,
|
|
}),
|
|
states: {
|
|
idle: {
|
|
after: {
|
|
[10 * 60 * 1000]: {
|
|
target: "querying",
|
|
},
|
|
},
|
|
},
|
|
querying: {
|
|
description: "Pull a page of lifelogs from the Limitless API.",
|
|
invoke: {
|
|
src: "queryLifelogs",
|
|
input: ({ context }) => ({
|
|
apiKey: context.apiKey,
|
|
cursor: context.cursor,
|
|
start: context.latestLog ?? addDay(new Date(), -7),
|
|
end: new Date(),
|
|
}),
|
|
onDone: [{
|
|
target: "writing",
|
|
guard: {
|
|
type: "apiReturnedLifelogs",
|
|
params: ({ event }) => event.output,
|
|
},
|
|
actions: [
|
|
{
|
|
type: "updateCursor",
|
|
params: ({ event }) => ({
|
|
cursor: event.output.meta.lifelogs.nextCursor ?? null,
|
|
}),
|
|
},
|
|
{
|
|
type: "queueLifelogs",
|
|
params: ({ event }) => ({
|
|
lifelogs: event.output,
|
|
}),
|
|
},
|
|
],
|
|
}, {
|
|
target: "idle",
|
|
}],
|
|
},
|
|
},
|
|
writing: {
|
|
description:
|
|
"Wait for all logs to be written before requesting the next page.",
|
|
invoke: {
|
|
src: "writeLifelog",
|
|
input: ({ context }) => ({
|
|
lifelog: context.lifelogQueue[0],
|
|
rootDir: context.lifelogsDir,
|
|
vault: context.vault,
|
|
}),
|
|
onDone: {
|
|
target: "evaluating",
|
|
actions: [
|
|
"shiftLifelogQueue",
|
|
{
|
|
type: "updateLatestLog",
|
|
params: ({ event }) => ({
|
|
endDate: event.output,
|
|
}),
|
|
},
|
|
],
|
|
},
|
|
},
|
|
},
|
|
evaluating: {
|
|
description: "Evaluate next step; either querying, writing, or stopping.",
|
|
always: [
|
|
{
|
|
target: "writing",
|
|
guard: "lifelogQueueNotEmpty",
|
|
},
|
|
{
|
|
target: "querying",
|
|
guard: "hasCursor",
|
|
},
|
|
"idle",
|
|
],
|
|
},
|
|
},
|
|
});
|