From 038b9ea55078ffcc2b3e466cb514f1eefecaab07 Mon Sep 17 00:00:00 2001 From: Juan Castro Date: Tue, 18 Aug 2026 15:24:50 -0400 Subject: [PATCH] docs: say what listShared's total actually counts `total` counts the shares recorded for you; items are filtered after the page is read, so a withdrawn grant leaves the count higher than anything paging will yield. The page description already explained the short-page behaviour, but the field read as an exact count and the example printed it as one. The test pins the gap it describes: two shares, one withdrawn outside the index, one item listed and a total of two. --- .../services/share/ShareService.test.ts | 28 +++++++++++++++++++ src/docs/src/FS/listShared.md | 4 +-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/backend/services/share/ShareService.test.ts b/src/backend/services/share/ShareService.test.ts index f4187a828..a84c9a2e6 100644 --- a/src/backend/services/share/ShareService.test.ts +++ b/src/backend/services/share/ShareService.test.ts @@ -1611,6 +1611,34 @@ describe('ShareService', () => { expect(listed).not.toContain(withdrawn.uuid); }); + // `total` counts rows; items are filtered after the page is read. + it('reports a total that can exceed what paging yields', async () => { + const owner = await makeUser(); + const recipient = await makeUser(); + const withdrawn = await makeFile(owner.user); + const kept = await makeFile(owner.user); + + for (const file of [withdrawn, kept]) { + await share(owner.actor, { + uid: file.uuid, + recipient: { email: recipient.email }, + mode: 'read', + }); + } + await server.services.permission.revokeUserUserPermission( + owner.actor, + recipient.user.username!, + `fs:${withdrawn.uuid}:read`, + ); + + const page = await server.services.share.listSharedWithMe( + recipient.actor, + { includeTotal: true }, + ); + expect(page.items.map((i) => i.entryUid)).toEqual([kept.uuid]); + expect(page.total).toBe(2); + }); + // The owner's view of the same withdrawal. it('stops naming a holder whose grant was withdrawn outside the index', async () => { const owner = await makeUser(); diff --git a/src/docs/src/FS/listShared.md b/src/docs/src/FS/listShared.md index 85d12740b..4e52783d5 100644 --- a/src/docs/src/FS/listShared.md +++ b/src/docs/src/FS/listShared.md @@ -37,7 +37,7 @@ A `Promise` that resolves to an object with: - `items` (Array) - The shares on this page. Each has `uid`, `mode`, `path`, `entryUid`, `isDir`, `name`, `type`, `thumbnail`, `owner`, `issuer`, `holder`, `modified` and `size`. A share row has no directory listing behind it, so `name`, `type` and `thumbnail` are carried on the row itself for rendering. - `cursor` (String) - Pass to the next call to get the following page. **Present only while more pages remain.** -- `total` (Number) - Present only when `includeTotal` was set. +- `total` (Number) - Present only when `includeTotal` was set. An approximation: it counts the shares recorded for you, before the filtering described below, so it can be higher than the number of items paging actually yields. Treat it as a headline figure, not a count to reconcile against. Iterate until `cursor` is absent rather than comparing `items.length` to `limit`. A page can come back short — items you can no longer see are filtered out after the page is read — while more pages still remain. @@ -54,7 +54,7 @@ Items shared with you appear at a **masked path**, `///`, wher