From dd9b30bede3d83bb0554869ca0653aff24178bf4 Mon Sep 17 00:00:00 2001 From: ivan-lednev Date: Sun, 24 Nov 2024 11:19:32 +0100 Subject: [PATCH] fix: improve scheduling code --- src/service/dataview-facade.ts | 8 +- .../multi-day/multi-day-grid.svelte | 7 +- src/ui/hooks/use-dataview-tasks.ts | 7 +- src/ui/hooks/use-search.svelte.ts | 78 ++++++++----------- src/ui/hooks/use-tasks-from-extra-sources.ts | 12 ++- src/util/create-hooks.svelte.ts | 10 +-- src/util/scheduler.ts | 8 +- src/util/use-remote-tasks.ts | 23 ++---- 8 files changed, 64 insertions(+), 89 deletions(-) diff --git a/src/service/dataview-facade.ts b/src/service/dataview-facade.ts index aa67890bb..af1edfde8 100644 --- a/src/service/dataview-facade.ts +++ b/src/service/dataview-facade.ts @@ -1,5 +1,5 @@ -import { App } from "obsidian"; -import { getAPI, STask, type DataviewApi } from "obsidian-dataview"; +import { STask, type DataviewApi } from "obsidian-dataview"; + import { type Scheduler, createBackgroundBatchScheduler, @@ -18,7 +18,8 @@ export class DataviewFacade { const paths: string[] = this.getDataview()?.pagePaths(source).array(); const pageQueries: Array<() => STask[]> = paths.map( - (path) => () => this.getDataview()?.page(path)?.file.tasks.array(), + (path) => () => + this.getDataview()?.page(path)?.file.tasks.array() || [], ); this.scheduler.enqueueTasks(pageQueries, (results) => { @@ -27,6 +28,7 @@ export class DataviewFacade { }); }; + // todo: remove legacy_getAllTasksFrom = (source: string) => { return this.getDataview()?.pages(source).file.tasks.array() || []; }; diff --git a/src/ui/components/multi-day/multi-day-grid.svelte b/src/ui/components/multi-day/multi-day-grid.svelte index 4efaa6e85..0fae8b650 100644 --- a/src/ui/components/multi-day/multi-day-grid.svelte +++ b/src/ui/components/multi-day/multi-day-grid.svelte @@ -16,6 +16,7 @@ getPreviousWorkWeek, } from "../../../util/range"; import * as r from "../../../util/range"; + import Search from "../../components/search.svelte"; import ControlButton from "../control-button.svelte"; import { createSlide } from "../defaults"; import { @@ -198,9 +199,9 @@ {#if visibleSideControls === "settings"} {/if} - - - + {#if visibleSideControls === "search"} + + {/if} {/if} diff --git a/src/ui/hooks/use-dataview-tasks.ts b/src/ui/hooks/use-dataview-tasks.ts index 5f3386960..c50ceb82e 100644 --- a/src/ui/hooks/use-dataview-tasks.ts +++ b/src/ui/hooks/use-dataview-tasks.ts @@ -18,10 +18,9 @@ export function useDataviewTasks({ return derived( [listsFromVisibleDailyNotes, tasksFromExtraSources, settingsStore], ([$listsFromVisibleDailyNotes, $tasksFromExtraSources, $settingsStore]) => { - const allTasks = dv.uniq([ - ...$listsFromVisibleDailyNotes, - ...$tasksFromExtraSources, - ]); + const allTasks = dv.uniq( + $listsFromVisibleDailyNotes.concat($tasksFromExtraSources), + ); return $settingsStore.showCompletedTasks ? allTasks diff --git a/src/ui/hooks/use-search.svelte.ts b/src/ui/hooks/use-search.svelte.ts index 45f9ffc25..94ca88fc6 100644 --- a/src/ui/hooks/use-search.svelte.ts +++ b/src/ui/hooks/use-search.svelte.ts @@ -1,67 +1,55 @@ -import { derived, get, writable, type Readable } from "svelte/store"; +import type { STask } from "obsidian-dataview"; +import { derived, writable, type Readable } from "svelte/store"; import { searchResultLimit } from "../../constants"; -import type { DataviewFacade } from "../../service/dataview-facade"; import * as dv from "../../util/dataview"; -import { getUpdateTrigger } from "../../util/store"; - -import { useDebounceWithDelay } from "./use-debounce-with-delay"; export function useSearch(props: { - dataviewFacade: DataviewFacade; + dataviewTasks: Readable>; dataviewSource: Readable; - taskUpdateTrigger: Readable; - keyDown: Readable; }) { - const { dataviewFacade, dataviewSource, taskUpdateTrigger, keyDown } = props; + const { dataviewSource, dataviewTasks } = props; - const sourceSignal = dataviewSource; const query = writable(""); - const updateSignal = derived([taskUpdateTrigger, query], getUpdateTrigger); - // todo: pass through update signal - const debouncedSearchSignal = useDebounceWithDelay( - updateSignal, - keyDown, - 250, + + const matchedStasks = derived( + [query, dataviewTasks, dataviewSource], + ([$query, $dataviewTasks]) => + $query.trim().length === 0 + ? [] + : $dataviewTasks.filter((task) => + task.text.toLowerCase().includes($query), + ), ); - const result = derived(debouncedSearchSignal, () => { - // todo: pass through update signal and remove this - const currentQuery = get(query).toLowerCase(); - const currentSource = get(sourceSignal); + const description = derived( + [query, matchedStasks], + ([$query, $matchedStasks]) => { + if ($query.trim().length === 0) { + return "Empty"; + } + + if ($matchedStasks.length === 0) { + return "No matches"; + } - if (currentQuery.trim().length === 0) { - return []; - } + if ($matchedStasks.length > searchResultLimit) { + return `Limited to ${searchResultLimit} entries. Try refining your search.`; + } + + return `${$matchedStasks.length} matches`; + }, + ); - // todo: use async + const result = derived(matchedStasks, ($matchedStasks) => { return ( - dataviewFacade - .legacy_getAllTasksFrom(currentSource) - .filter((task) => { - return task.text.toLowerCase().includes(currentQuery); - }) + $matchedStasks + .slice(0, searchResultLimit) // todo: remove moment .map((task) => dv.toUnscheduledTask(task, window.moment())) ); }); - const description = derived([query, result], ([$query, $result]) => { - if ($query.trim().length === 0) { - return "Empty"; - } - - if ($result.length === 0) { - return "No matches"; - } - - if ($result.length > searchResultLimit) { - return `The matches are limited to ${searchResultLimit} entries. Try refining your search.`; - } - - return `${$result.length} matches`; - }); - return { query, result, diff --git a/src/ui/hooks/use-tasks-from-extra-sources.ts b/src/ui/hooks/use-tasks-from-extra-sources.ts index 920bdf82e..4a271f024 100644 --- a/src/ui/hooks/use-tasks-from-extra-sources.ts +++ b/src/ui/hooks/use-tasks-from-extra-sources.ts @@ -1,7 +1,7 @@ +import type { STask } from "obsidian-dataview"; import { derived, type Readable } from "svelte/store"; import { DataviewFacade } from "../../service/dataview-facade"; -import type { STask } from "obsidian-dataview"; interface UseTasksFromExtraSourcesProps { dataviewSource: Readable; @@ -17,13 +17,11 @@ export function useTasksFromExtraSources({ return derived( [dataviewSource, refreshSignal], ([$dataviewSource], set: (tasks: STask[]) => void) => { - dataviewFacade - .getAllTasksFrom($dataviewSource) - .then(set, (reason) => { - console.error("Failed to fetch tasks from dataview source: ", reason); + dataviewFacade.getAllTasksFrom($dataviewSource).then(set, (reason) => { + console.error("Failed to fetch tasks from dataview source: ", reason); - set([]); - }); + set([]); + }); }, [], ); diff --git a/src/util/create-hooks.svelte.ts b/src/util/create-hooks.svelte.ts index bab3a0da5..6b2219ac5 100644 --- a/src/util/create-hooks.svelte.ts +++ b/src/util/create-hooks.svelte.ts @@ -65,8 +65,6 @@ export function useTasks(props: { settingsStore: Writable; combinedIcalSyncTrigger: Readable; debouncedTaskUpdateTrigger: Readable; - taskUpdateTrigger: Readable; - keyDown: Readable; isOnline: Readable; visibleDays: Readable; layoutReady: Readable; @@ -90,8 +88,6 @@ export function useTasks(props: { app, dataviewSource, currentTime, - taskUpdateTrigger, - keyDown, workspaceFacade, pointerDateTime, onUpdate, @@ -190,10 +186,8 @@ export function useTasks(props: { ); const search = useSearch({ - dataviewFacade, + dataviewTasks: tasksFromExtraSources, dataviewSource, - taskUpdateTrigger, - keyDown, }); const editContext = useEditContext({ @@ -308,8 +302,6 @@ export function createHooks({ app, dataviewSource, currentTime, - taskUpdateTrigger, - keyDown, workspaceFacade, onUpdate, pointerDateTime, diff --git a/src/util/scheduler.ts b/src/util/scheduler.ts index 8e2a8162f..f0dee5a4f 100644 --- a/src/util/scheduler.ts +++ b/src/util/scheduler.ts @@ -44,6 +44,7 @@ export function createBackgroundBatchScheduler(props: { let tasks: Array<() => T> = []; let currentTaskHandle: number | null = null; let currentOnFinish: (results: T[]) => void; + let currentOnCancel: (() => void) | undefined; function runTaskQueue(deadline: IdleDeadline) { while ( @@ -69,14 +70,17 @@ export function createBackgroundBatchScheduler(props: { function enqueueTasks( newTasks: Array<() => T>, onFinish: (results: T[]) => void, + onCancel?: () => void, ) { - currentOnFinish = onFinish; - if (currentTaskHandle) { cancelJob(currentTaskHandle); + currentOnCancel?.(); currentTaskHandle = null; } + currentOnFinish = onFinish; + currentOnCancel = onCancel; + tasks = newTasks; results = []; currentTaskHandle = enqueueJob(runTaskQueue); diff --git a/src/util/use-remote-tasks.ts b/src/util/use-remote-tasks.ts index 9a4c096d8..b02076c33 100644 --- a/src/util/use-remote-tasks.ts +++ b/src/util/use-remote-tasks.ts @@ -2,17 +2,15 @@ import { isEmpty } from "lodash/fp"; import type { Moment } from "moment"; import ical from "node-ical"; import { request } from "obsidian"; -import { derived, readable, type Readable } from "svelte/store"; +import { derived, type Readable } from "svelte/store"; import type { DayPlannerSettings } from "../settings"; import type { RemoteTask, WithTime } from "../task-types"; import type { WithIcalConfig } from "../types"; +import { useIdleDerived } from "../ui/hooks/use-idle-derived"; import { canHappenAfter, icalEventToTasks } from "./ical"; import { getEarliestMoment } from "./moment"; -import { - createBackgroundBatchScheduler, -} from "./scheduler"; function isVEvent(event: ical.CalendarComponent): event is ical.VEvent { return event.type === "VEVENT"; @@ -92,18 +90,11 @@ export function useRemoteTasks(props: { }, ); - const tasksFromEvents = readable>>( - [], - (set) => { - const scheduler = createBackgroundBatchScheduler< - ReturnType - >({ timeRemainingLowerLimit: 10 }); - - return schedulerQueue.subscribe((next) => { - scheduler.enqueueTasks(next, set); - }); - }, - ); + const tasksFromEvents = useIdleDerived({ + batch: schedulerQueue, + timeRemainingLowerLimit: 10, + initial: [], + }); return derived(tasksFromEvents, ($tasksFromEvents) => $tasksFromEvents