Ask for shareWith, or the server does not send it
Nothing was ever badged as shared, "Stop sharing" never appeared, and the share dialog opened on "not shared with anyone yet" over live shares. The sharing itself was fine. The client simply never learned about it. Stalwart does not return `shareWith` unless a client names it. A `Calendar/get` or `AddressBook/get` with no `properties` comes back without the field at all -- not null, not empty, absent -- confirmed against the live 0.16.19 on a calendar and an address book that really were shared with another account. Omit the list and there is no `shareWith`; name it and the sharee is right there. Both stores fetched everything by asking for nothing, and got less than they would have by asking. They name the properties now. The dialog is the part worth dwelling on. It seeds itself from the `shareWith` it was handed, so it has been showing an empty sharee list on collections that were shared -- the one screen whose whole job is managing sharing, and the one most confidently wrong about it. Someone looking there to see who had access, or to take it away, was told there was nobody. Files never had this: `fileNodeProps` has named the property since file sharing went in, for the same reason and after the same surprise. The two stores that fetched with `ids: null` and no properties are the two that were blind. The mock now omits it the same way. One that hands `shareWith` over unasked lets a client that never asks look correct everywhere except against a real server, which is exactly how this got here. Verified against that mock: sharing a calendar puts the sharee in the store, badges the row, adds "Stop sharing", and the dialog lists them -- while a `Calendar/get` with no properties still comes back without the field, so the mock is now failing the way the server does.
This commit is contained in:
@@ -15,6 +15,37 @@ export interface EventInstance {
|
||||
calendar: Calendar | undefined;
|
||||
}
|
||||
|
||||
/*
|
||||
* Asked for by name, because `shareWith` is not among the properties Stalwart
|
||||
* returns by default.
|
||||
*
|
||||
* A `Calendar/get` with no `properties` comes back without it -- not null, not
|
||||
* empty, absent -- confirmed against 0.16.19 on 2026-08-27 with a calendar that
|
||||
* was genuinely shared: omit the list and there is no `shareWith`; name it and
|
||||
* the sharee is right there. So the client believed nothing was ever shared.
|
||||
* The badge never appeared, "Stop sharing" never appeared, and the share dialog
|
||||
* opened on "not shared with anyone yet" over a live share.
|
||||
*
|
||||
* Files had this right already, for the same reason and after the same
|
||||
* surprise; calendars and address books did not.
|
||||
*/
|
||||
export const CALENDAR_PROPS = [
|
||||
"id",
|
||||
"name",
|
||||
"description",
|
||||
"color",
|
||||
"sortOrder",
|
||||
"isSubscribed",
|
||||
"isVisible",
|
||||
"isDefault",
|
||||
"includeInAvailability",
|
||||
"defaultAlertsWithTime",
|
||||
"defaultAlertsWithoutTime",
|
||||
"timeZone",
|
||||
"shareWith",
|
||||
"myRights",
|
||||
];
|
||||
|
||||
/** A calendar somebody else shared, and the account it lives in. */
|
||||
export interface SharedCalendar {
|
||||
accountId: Id;
|
||||
@@ -131,7 +162,7 @@ export const useCalendar = create<CalendarState>((set, get) => ({
|
||||
const found: SharedCalendar[] = [];
|
||||
for (const [accountId, account] of accounts) {
|
||||
try {
|
||||
const res = await client.call<GetResponse<Calendar>>("Calendar/get", { accountId, ids: null });
|
||||
const res = await client.call<GetResponse<Calendar>>("Calendar/get", { accountId, ids: null, properties: CALENDAR_PROPS });
|
||||
for (const calendar of res.list) found.push({ accountId, accountName: account.name, calendar });
|
||||
} catch {
|
||||
continue;
|
||||
@@ -216,7 +247,7 @@ export const useCalendar = create<CalendarState>((set, get) => ({
|
||||
const accountId = get().accountId;
|
||||
if (!accountId) return;
|
||||
try {
|
||||
const res = await client.call<GetResponse<Calendar>>("Calendar/get", { accountId, ids: null });
|
||||
const res = await client.call<GetResponse<Calendar>>("Calendar/get", { accountId, ids: null, properties: CALENDAR_PROPS });
|
||||
const calendars: Record<Id, Calendar> = {};
|
||||
for (const c of res.list) calendars[c.id] = c;
|
||||
set({ calendars, error: null });
|
||||
|
||||
@@ -14,6 +14,15 @@ export interface Suggestion {
|
||||
photo?: string | null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Asked for by name: `shareWith` is not returned by default.
|
||||
*
|
||||
* An `AddressBook/get` with no `properties` omits it entirely -- confirmed
|
||||
* against 0.16.19 on 2026-08-27 on a book that really was shared. See the note
|
||||
* on CALENDAR_PROPS; both had the same hole and Files did not.
|
||||
*/
|
||||
export const ADDRESS_BOOK_PROPS = ["id", "name", "description", "sortOrder", "isDefault", "isSubscribed", "shareWith", "myRights"];
|
||||
|
||||
/** A book somebody else shared, and the account it lives in. */
|
||||
export interface SharedBook {
|
||||
accountId: Id;
|
||||
@@ -132,7 +141,7 @@ export const useContacts = create<ContactsState>((set, get) => ({
|
||||
const cards: Record<string, ContactCard> = {};
|
||||
for (const [accountId, account] of accounts) {
|
||||
try {
|
||||
const res = await client.call<GetResponse<AddressBook>>("AddressBook/get", { accountId, ids: null });
|
||||
const res = await client.call<GetResponse<AddressBook>>("AddressBook/get", { accountId, ids: null, properties: ADDRESS_BOOK_PROPS });
|
||||
for (const book of res.list) books.push({ accountId, accountName: account.name, book });
|
||||
/*
|
||||
* Cards come only from books the reader has added.
|
||||
@@ -223,7 +232,7 @@ export const useContacts = create<ContactsState>((set, get) => ({
|
||||
const accountId = get().accountId;
|
||||
if (!accountId) return;
|
||||
try {
|
||||
const res = await client.call<GetResponse<AddressBook>>("AddressBook/get", { accountId, ids: null });
|
||||
const res = await client.call<GetResponse<AddressBook>>("AddressBook/get", { accountId, ids: null, properties: ADDRESS_BOOK_PROPS });
|
||||
const books: Record<Id, AddressBook> = {};
|
||||
for (const b of res.list) books[b.id] = b;
|
||||
set({ books, error: null });
|
||||
|
||||
Reference in New Issue
Block a user