quak files COLLECTION fetches that collection's files alone (closes #217)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
`quak files COLLECTION` used to wait for a full refresh, which on an empty cache fetched the files of every collection before it listed one. It now reads the collection list, then calls the new `Library.refreshCollection()`, which brings that one collection up to date in the store as a refresh would, and saves it. It sends no request for the files when the stored copy is current, and it keeps a newer copy that a refresh stores while it fetches. It leaves the refresh cursor alone, so the next refresh still fetches every other collection that changed. `quak files` with no argument still waits for a refresh. Model: opus-5-5
This commit is contained in:
+51
-7
@@ -20,9 +20,12 @@
|
||||
//
|
||||
// `freshCollections()` is the fresh read of the collection list alone (issue
|
||||
// #210): one collections request, no collection's files, and nothing stored,
|
||||
// for a caller such as `quak collections` that needs no more. A caller that
|
||||
// asks for each refresh it needs can also open the library with
|
||||
// `refreshOnOpen: false`, so that `open()` starts none.
|
||||
// for a caller such as `quak collections` that needs no more.
|
||||
// `refreshCollection()` brings one collection from that list up to date in the
|
||||
// store (issue #217): it fetches that collection's files alone, as a refresh
|
||||
// would, for a caller such as `quak files COLLECTION`. A caller that asks for
|
||||
// each refresh it needs can also open the library with `refreshOnOpen: false`,
|
||||
// so that `open()` starts none.
|
||||
//
|
||||
// A refresh reports through `onProgress` each collection whose files it has
|
||||
// fetched, so a large account, whose first refresh fetches the files of every
|
||||
@@ -625,6 +628,39 @@ export class Library {
|
||||
return collections;
|
||||
}
|
||||
|
||||
// Bring one collection up to date in the store (issue #217), given its
|
||||
// record from `freshCollections()`. It does for that collection what a
|
||||
// refresh does for a changed one, and fetches no other collection's files.
|
||||
// It sends no request when the stored copy's `updationTime` is as new.
|
||||
// Otherwise it fetches the files changed since the stored copy, or all of
|
||||
// them when none is stored, then commits and saves them. A refresh already
|
||||
// running is waited for first.
|
||||
async refreshCollection(collection: Collection): Promise<void> {
|
||||
if (this.closed) throw new Error("the library is closed");
|
||||
await this.cycle?.catch(() => {});
|
||||
const known = this.store.getCollection(collection.id);
|
||||
if (known && collection.updationTime <= known.updationTime) return;
|
||||
const filePage = await this.client.filesSince({
|
||||
collectionID: collection.id,
|
||||
collectionKey: collection.key,
|
||||
sinceTime: known ? known.updationTime : 0,
|
||||
});
|
||||
// A refresh that ran during the fetch may have stored a copy as new as
|
||||
// `collection` or newer. Committing this result would replace it.
|
||||
const stored = this.store.getCollection(collection.id);
|
||||
if (stored && collection.updationTime <= stored.updationTime) return;
|
||||
// The stored cursor stays where it is, so the next refresh still
|
||||
// fetches the files of every other collection that changed.
|
||||
await this.commit(
|
||||
{
|
||||
collections: [collection],
|
||||
deleted: [],
|
||||
cursor: this.store.collectionsSinceTime,
|
||||
},
|
||||
[{ collectionID: collection.id, page: filePage }],
|
||||
);
|
||||
}
|
||||
|
||||
// Back up every in-scope file to `opts.downloadDirectory`, or else the
|
||||
// library's, each original at its save path, with a durable failure
|
||||
// ledger (issue #51). Takes the lock in that directory first, unless the
|
||||
@@ -766,10 +802,7 @@ export class Library {
|
||||
|
||||
// Fetch every change since the stored cursor, then commit. All network
|
||||
// reads happen before any store mutation, so a fetch that throws leaves the
|
||||
// store untouched and the previous snapshot intact. A collection or file
|
||||
// that does not decrypt is left out instead, as if deleted, with the reason
|
||||
// kept in the store: the cursor moves past it, so no later refresh sees it
|
||||
// again until the server's copy changes.
|
||||
// store untouched and the previous snapshot intact.
|
||||
private async refreshOnce(): Promise<void> {
|
||||
const page = await this.client.collectionsSince({
|
||||
sinceTime: this.store.collectionsSinceTime,
|
||||
@@ -802,6 +835,17 @@ export class Library {
|
||||
});
|
||||
}
|
||||
|
||||
await this.commit(page, filePages);
|
||||
}
|
||||
|
||||
// Commit a collections page and the file pages fetched for its collections
|
||||
// to the store, then save. A collection or file that does not decrypt is
|
||||
// left out, as if deleted, with the reason kept in the store, and no later
|
||||
// refresh sees it again until the server's copy changes.
|
||||
private async commit(
|
||||
page: CollectionsPage,
|
||||
filePages: { collectionID: number; page: FilesPage }[],
|
||||
): Promise<void> {
|
||||
// Network work done; commit to the store and persist only if something
|
||||
// actually changed.
|
||||
let changed = false;
|
||||
|
||||
Reference in New Issue
Block a user