Seven test modules each built a migrated in-memory store by hand: open, migrate,
and (in sharing) wrap it in a Db. local::memory_conn() is that, and
open_in_memory uses it too. Each module's db() is now one line, and the schema,
Connection and Mutex imports it needed are gone.
DRY pass #2, batch 2, F9 (#5372).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Db::conn is documented as the one way to take the lock, yet five production and
test sites reached past it with db.0.lock(): the startup summary, the desktop's
trash sweep and config_get, and tests in sharing and update. All five now call
conn().
DRY pass #2, batch 2, F8 (#5372).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Five places read the server address and token straight from sync_state:
sharing, autosync, sync_unlink, update's download token, and the ffi. Reading it
raw is how Android came to send its sealed token to the share routes (#5381).
state::credentials(conn, seal) now holds that read. With a seal it opens the token
(open_token), and without one (the desktop keeps it plain) it returns it as stored.
Every site calls it.
DRY pass #2, batch 1, F1 (#5372).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Android has stored its device token sealed ("sealed:…") since 8592b83, and
core's sharing calls read the token from the store themselves. The ffi opened
it in credentials() and then threw the result away, so every Share-sheet
request went out as `Bearer sealed:…` and the server refused it.
The sharing functions now take the server address and token from the caller.
The ffi passes what credentials() opened; the desktop, which stores its token
plain, reads it through sharing::stored_link. A new ffi test serves one request
on a loopback port and checks the bearer token that arrives (#5381).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the audit (#5179, core and desktop half).
- Every `db.0.lock().map_err(|e| e.to_string())?` (about 50 sites in core and
the desktop) is now `db.conn()?`. The few sites that deliberately handle
a poisoned lock differently, and the tests, keep their own spelling.
- push::Change derives Default, so its four constructors name only the
fields they set.
- store: list_notes, reminders, titles and search share notes_where (ids
from a query, each loaded through load_note). Labels share
LABEL_SELECT/label_row, and saved filters share
SAVED_FILTER_SELECT/saved_filter_row.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Share dialog lists people and groups in one picker and shows a group share
as its name and member count. Settings gains a Groups section for the admin:
create, rename, delete, and add or remove people.
The core client reads the directory's groups and group shares (ShareTarget:
a member or a group); the desktop command takes user_id or group_id.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The core pulls with shares from a server offering them (protocol 6): a note
says how it is held (owner, edit, view) and who shared it, and a revoked note
leaves the device. The first such pull starts the feed over once, so notes
shared before this build arrive. The store refuses what a share doesn't allow
(view: everything; edit: anything but the text), push sends only the text of
someone else's note, and their notes stay out of trash, reminders and
reordering. Unlinking drops them.
The Share dialog's calls go to the linked server over the device token, as
Tauri commands and through the FFI. The desktop now offers Share and "Shared
with me"; unlinked, the dialog says sharing needs a server.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>