Add multi-account support and update project docs

- Add SavedAccount/AccountStore and rework ServerProvider around a list
  of saved accounts: switch/remove/add, swipe-to-cycle on the avatar
  button, per-account browsing prefs (sort/view/filters/cache), and a
  session-generation guard so a stale fetch from an abandoned account
  can't land in the newly active one
- Migrate existing single-account installs into the new schema
  automatically so upgrading users stay logged in
- Settings gains an Accounts section; LoginView gains an "add account"
  mode pushed on top of the main shell instead of replacing it
- Give every Scrollbar in the app a thicker, draggable, Android-style
  thumb via a shared ScrollbarTheme instead of the default hairline
- Splash screen: use the monochrome app icon (tinted per theme) and a
  smaller spinner
- Update CLAUDE.md and .claude/context/*.md to reflect the accumulated
  multi-account, sharing, and UI changes across this session, and add
  a standing instruction to keep these docs current going forward

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-16 11:21:36 -04:00
co-authored by Claude Sonnet 5
parent 5299ac5c2c
commit 1fbdc1f8d0
15 changed files with 1121 additions and 222 deletions
+35
View File
@@ -0,0 +1,35 @@
/// A saved Nextcloud login. The app password itself lives in secure
/// storage under a key derived from [id] - this model only carries the
/// non-secret identity fields that are safe to keep in SharedPreferences.
class SavedAccount {
final String id;
final String serverUrl;
final String username;
const SavedAccount({
required this.id,
required this.serverUrl,
required this.username,
});
/// A deterministic id from the server + username, so re-adding the same
/// account refreshes its stored password instead of creating a duplicate
/// entry in the saved-accounts list.
static String makeId(String serverUrl, String username) {
String slug(String s) =>
s.toLowerCase().replaceAll(RegExp(r'[^a-z0-9]'), '_');
return '${slug(serverUrl)}__${slug(username)}';
}
Map<String, dynamic> toJson() => {
'id': id,
'serverUrl': serverUrl,
'username': username,
};
factory SavedAccount.fromJson(Map<String, dynamic> json) => SavedAccount(
id: json['id'] as String,
serverUrl: json['serverUrl'] as String,
username: json['username'] as String,
);
}