The Row wrapping the pill + search satellite used
crossAxisAlignment.stretch, but Scaffold.bottomNavigationBar gives its
child a loose (unbounded-max) height constraint - stretch on an
unbounded cross axis blows up to fill all available space in
profile/release builds (the debug assertion that would catch it is
stripped there), same class of bug files_controls_row.dart hit earlier
from a different cause. Both children already sized themselves
explicitly, so stretch was never actually needed - wrapping the Row in
a fixed-height SizedBox instead fixes it.
Added a regression test that reproduces this inside a real Scaffold
(the bare SizedBox host the other tests use gives a bounded height and
doesn't trigger it) - confirmed it fails without the fix (height 600,
the full test viewport) and passes with it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
New Settings > Appearance toggle moves Search into the bottom bar and
out of the top bar, so there's only one search entry point on screen.
Tapping it pushes SearchView same as before - it's never highlighted,
since it isn't a real destination.
- Attached: Search becomes the row's last item, styled like any other
tab but permanently idle.
- Floating: Search gets its own separate, always fully round satellite
circle beside the bar instead.
Enabling it costs one regular tab: SettingsController.maxVisibleTabs
is now a dynamic getter (defaultMaxVisibleTabs - 1 while the toggle is
on) instead of the old flat constant, re-enforced immediately if the
user already has all 5 slots filled - the same cleanup path used for
a fresh install or an app update that added a new tab.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Settings > Appearance > "Bottom bar" now switches between the
original edge-to-edge bar and a floating one - inset 16px from both
side edges and clear of the bottom safe area, 28px corners (the same
radius the sheet top/drawer edge use), a 1px line border standing in
for elevation instead of a shadow (product UI stays flat). Persisted
via NooBottomBarStyle, defaulting to the existing attached behavior.
On Android, floating's idle tabs drop their reserved label space -
with no label to leave room for, the icon centers in the whole button
and renders a touch bigger (27 vs 24px) instead of sitting high with a
gap held open under it; the active tab's icon/pill/label position is
unchanged. iOS's tab content is unchanged either way, only the
container.
main.dart sets Scaffold.extendBody while floating so the body scrolls
behind the bar's transparent margin instead of stopping short of it,
matching how the design canvas explored it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Every tab's RefreshIndicator+CustomScrollView was missing an explicit
AlwaysScrollableScrollPhysics, so pull-to-refresh silently couldn't be
triggered once the list was short enough to fit the viewport (empty,
or a single item) - the platform default physics only allows the
overscroll drag RefreshIndicator listens for once content overflows.
- StickyHeaderDelegate (Files'/Photos'/Favorites' pinned controls row)
painted its background from Theme.of(context).colorScheme.surface -
Flutter's own Material 3 scheme, reseeded by the user's accent color
choice - instead of context.nooColors.bg like the rest of the screen,
showing as a stray colored cast behind the row.
- NooSegmentedControl gains onSurface: true wherever one sits inside a
sheet (Files'/Photos' sort and type-filter controls) - it defaulted to
the same surface fill as the sheet itself, so the control had no
visible boundary showing it as a grouped unit.
- Shares tab: removed the per-row "..." overflow menu on every scope
(With you/By you/Links); tapping a row now opens the full Share sheet
for that item instead, fetched via the new
FilesController.fetchItemAtPath (a share only carries enough metadata
for its own row, not what the sheet's header needs).
- fetchShares() now reuses _shareFromJson instead of its own thinner
inline parser, so url/token/permissions/expireDate are actually
populated - a public-link share could never show "Copy link" before.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- SharesController's fetchShares() built NextcloudShare objects through
its own thin inline parser instead of _shareFromJson, so url/token/
permissions/expireDate were always null - a public link share could
never show "Copy link" in the Shares tab. Reuses _shareFromJson.
- Upload/Move/Copy's destination picker hid External storage in its
filter sheet (showStorageScope: false) unlike Files' own controls
row, despite the design doc already calling for full parity.
- Files' and Photos' type filter (All/Files/Folders, All/Photos/Videos)
now render as one consistent style in both filter sheets: every icon
always shown, label only on the selected segment
(NooSegmentedControl's new labelOnlySelected), replacing Files' old
checkmark list and Photos' own always-labelled track.
- Fixes Photos' type filter only visually updating after closing and
reopening the filter sheet - it lived on PhotosView's own State, so
Listenable.merge([photos, files]) never rebuilt the sheet when it
changed; a StatefulBuilder now gives it that trigger.
- Replaces Settings' trailing section jump rail with per-section
collapsible cards (NooGroupedList's new collapsible param, expanded
by default) - one less parallel way to navigate a long screen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- FilesControlsRow (and Photos' own controls row) wrapped its Row in a
SingleChildScrollView, which gives a child unbounded width - silently
breaking the trailing Spacer (a flex child needs bounded width) and
leaving the List/Grid toggle sitting right after the filter chip
instead of pinned to the row's right edge, "not enough controls to
fill the row" or not. Removed the scroll wrapper (two chips and an
icon toggle never need it) and the dead scroll-hint plumbing that
went with it in files_view.dart.
- Photos' All/Photos/Videos chips moved into the filter sheet as a
full-width segmented control (matching Settings' Theme control),
instead of their own row of chips competing for space with the sort
and filter chips.
- Settings' Theme row: the System/Light/Dark segmented control now
sits on its own row below the label instead of squeezed into a
trailing slot beside it, where three icon+label segments were too
cramped to read comfortably - same shape as the sort sheet's
Ascending/Descending control.
- NooBanner (Trash's "Empty trash") and NooSummaryCard (Offline's
"Sync now") both move their action onto its own row below the
description text instead of squeezed onto the same line, where a
longer caption had no room to share with it.
- SharesController now caches each scope (with you / by you, which
also covers links) after its first fetch instead of re-fetching on
every single switch, including switching straight back to a scope
already loaded seconds ago.
- Shares' per-row overflow menu jumped straight to the unshare
confirmation with no menu at all - a link share had no way to copy
its own URL short of opening the full Share sheet. It's a real menu
now: "Copy link" (link shares only) plus remove/unshare.
- Settings gained a trailing jump rail on mobile (one small icon per
section) so a specific section can be reached in one tap instead of
scrolling the whole list by hand; desktop's 2-column grid doesn't
need one.
- The media viewer's PageView now disables its own left/right swipe
while the current photo is zoomed in, so panning around it doesn't
also swipe to the next item.
- The PDF preview no longer sets minScale below 1.0: pdfx's
PdfViewPinch hard-codes an *infinite* pan boundary whenever minScale
< 1 (not an exposed parameter, so there's no way to override it),
which let a page be panned arbitrarily far off-screen without ever
zooming.
showNooSheet's peek detection previously measured content once, right
after the first frame - Details' Versions/Activity tabs fetch their
data asynchronously, so that one-shot measurement only ever saw the
initial loading-spinner height, never the eventual (often long) list,
and the sheet still snapped to full height once data arrived. Replaced
it with a ScrollMetricsNotification-based check that keeps content
capped at the peek height from frame one and upgrades to a draggable
sheet whenever it actually overflows that cap, however and whenever
that happens - fixed for good this time, with a regression test that
mirrors the async-load shape.
Also, in the same pass:
- Upload/Move/Copy's bottom bar meta line now always reads as a count
("Uploading 3 items"), never a filename for the single-item case,
and is centered rather than left-aligned - matches the approved
design and removes a bespoke marquee/chip implementation that only
Upload had.
- Upload/Move/Copy now show the same FilesControlsRow (sort, filter,
List/Grid) Files itself uses, including a folder-grid rendering
path for when List/Grid is set to grid.
- Files' filter chip reads "Filters" instead of "All types" when idle.
- Trash's overflow button - whose only action is "Delete forever" -
is now a direct delete icon (mobile) / "Delete" danger pill
(desktop) instead of a generic "..." menu that hid a single action
behind an extra tap.
- The media viewer's floating chrome (top bar, bottom action bar,
video transport row) now follows the app's light/dark theme instead
of a fixed dark panel - a dark panel read as out of place over a
light-theme stage. Panel tint and icon/text colors come from the
theme's own surface/fg1 tokens (at a higher blur opacity to stay
legible over arbitrary photo/video brightness), and the edge scrim
gradients fade toward the panel's own tint instead of a fixed black.
Mirrored the same change into the design canvas's Main.dc.html/
MainAndroid.dc.html mockups (now with a theme prop) so they match.
Long-content bottom sheets (Details' Versions/Activity tabs with many
rows) opened locked to nearly full screen height with no room left to
adjust. showNooSheet now measures its content once against the
available height: anything that fits comfortably still sizes to
content as before, but content tall enough to need scrolling opens at
a native-style draggable peek instead, which the user can drag up to
reveal more of (and drag back down to dismiss).
The media viewer's stage was pinned to black regardless of theme,
which read as jarringly out of place in light mode - it now follows
the app's own `bg`. The floating top/bottom bars stay a fixed dark
panel either way (they need to read over arbitrary photo/video
brightness, not over the stage), but both had their SafeArea nested
the wrong way around: it wrapped the whole panel instead of just its
content, so the panel's background stopped short of the actual screen
edge - most visibly the bottom bar ending above the gesture area
instead of extending behind it. The background now wraps the SafeArea
instead.
Every sheet and dialog barrier goes solid black instead of dimming the
screen behind it whenever dynamic color ("Match wallpaper") is on -
Material 3's ColorScheme.scrim role is fully opaque black by spec,
meant to be used at reduced opacity by whoever paints it, but
NooColors.fromDynamicScheme mapped it straight into barrierColor.
Use the fixed palette's already-translucent scrim instead, matching
how success/warning/info already fall back to it since they have no
dynamic equivalent either.
Also fix the Details sheet's Info/Versions/Activity segmented control
refetching a tab's data every time it's selected: switching on the
active tab picked which single widget to build, so Flutter tore down
and recreated whichever tab wasn't showing on every switch, discarding
its fetched data. Keep all three mounted in an IndexedStack instead
(the same pattern the app's own main tab bar already uses for this
exact reason), so each tab's state - and its one-shot fetch - survives
switching back and forth.
NooColors (the color source for nearly the whole redesigned UI) only
had two states: the fixed violet palette, or a Material You dynamic
scheme when "Match wallpaper" was on. The 5-swatch custom accent
picker still wrote to SettingsController.seedColor, but nothing read
it back into NooColors - it only reached the underlying Material
ColorScheme, which the Noo-styled UI doesn't use, so every custom
swatch looked identically purple.
Add NooColors.fromSeed, which retints just the accent/accentText/
accentSoft roles from a chosen seed color via ColorScheme.fromSeed
(bg/surface/fg etc. stay the fixed warm-neutral palette, unlike
dynamic color which reshapes everything to match the wallpaper), and
wire it into AppTheme.light/dark. Add a "Default" (Noo violet) swatch
to the picker, and move SettingsController's initial seed color to
that same violet so a fresh install - or any device without dynamic
color - keeps today's look instead of picking up the leftover
Nextcloud-blue default from before seed colors did anything visible.
Trash and Shares built their mobile lists eagerly via NooGroupedList's
own Column instead of a lazy sliver, so an account with hundreds of
items made those tabs laggy - both now use a SliverList/
SliverChildBuilderDelegate instead, matching Files' own lazy list,
while keeping the same one-continuous-card look via per-row corner
rounding and a 1px divider.
FilesControlsRow's sort/filter sheets also had a live-update bug: since
showNooSheet's children are built once up front, tapping a row inside
the open sheet updated the controller but the already-built
checkmarks/toggles stayed stale until the sheet was closed and
reopened. Wrapping each sheet's content in a ListenableBuilder fixes
that.
Also adds NooSelectionBar to styling.md's widget inventory table.
NooSelectionBar's fixed inline slots (3 mobile / 4 desktop, the rest
behind "More") previously just took the first N actions in whatever
order each screen happened to build them in. Give bulk actions a
stable SelectionActionKind identity (independent of their dynamic
icon/label, e.g. favorite vs. unfavorite) so Settings can offer a
drag-to-reorder list - mirroring the existing Tabs section, minus the
pin/hide concept, which doesn't apply here - and each screen applies
that saved priority order via orderSelectionActions before handing
its action list to the bar.
- Bottom bar: labels now sit below each icon instead of beside it, and
the active-tab pill is one shared indicator that slides between tabs
instead of popping in/out per item.
- Upload FAB stays mounted across every tab and collapses to an
icon-only circle off Files/Photos, instead of the Scaffold popping it
fully in/out on every tab switch.
- Tapping the top-bar avatar now opens the accounts sheet instead of
jumping straight to Settings.
- Settings tab reordering only starts a drag from the grip icon now -
previously the whole row claimed vertical drags, so scrolling the
section could trigger an accidental reorder.
The multi-select action bar (Files/Photos/Favorites) hid bulk actions
behind a horizontally-scrolling row with no visible cue that there was
more to find, once there were more than a handful (up to 9: favorite,
share, download, delete, copy, move, rename, sync, details).
Designed the replacement on the linked design canvas first (Selection
action bar boards), then implemented it: NooSelectionBar now shows a
fixed number of actions inline (3 on mobile, 4 on desktop) with the rest
behind a trailing "More" button that opens the same overflow sheet a
file row's own "..." menu already uses. Removed the now-dead
scroll-hint plumbing from the three screens that render it.
The viewer's top/bottom bars still used the pre-rework
FrostedGlassContainer verbatim: a floating inset pill with a Material
colorScheme tint, an outline border and a drop shadow (violating the
design system's no-shadow rule), and theme-derived icon colors that
went near-invisible against the new dark panel in light mode. The
video transport controls were a second, separate floating pill above
the action bar instead of one continuous panel.
- FrostedGlassContainer: drop the shadow/border/ColorScheme tint for a
fixed dark translucent fill matching the approved design canvas: no
shadow anywhere in this screen now.
- file_viewer_screen.dart: top/bottom bars sit flush edge-to-edge
(no floating-pill inset) with a gradient scrim behind each, and use
fixed white (not theme-derived) text/icon color, since this chrome
always sits over a black media stage regardless of app theme.
- ViewerIconButton: same fix - fixed white by default instead of
colors.fg1.
- MediaActionBar: takes an optional transportControls slot so video's
transport row renders inside the same panel as the action row,
above a thin divider, instead of as a separate floating pill.
- MediaVideoPreview: renders only the video surface now; reports its
controller up via onController (only when active, so a neighboring
PageView page can't steal the transport row) instead of owning a
floating panel. VideoTransportControls is the extracted, chrome-less
transport row content, rendered by the parent via MediaActionBar's
new slot.
Verified live on device: photo and video viewing, favorite/delete
coloring, and the merged transport+action panel all match the
approved canvas (https://claude.ai/artifact/3AGPqqMdkLSC2ypCh2CQs4).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replaces the Material 3 screens with the Noo design system: warm
neutrals, one violet accent, pill controls, Schibsted Grotesk/
Instrument Sans, Lucide icons, no gradients/shadows.
- New lib/widgets/noo/ component kit (core, lists, files, media, nav,
overlays) plus lib/theme/design_tokens.dart for the color/type/space/
radius/motion tokens.
- Rebuilt the app shell (top/bottom bars, drawer, desktop sidebar and
toolbar), every tab (Files/Offline, Photos, Favorites, Recent,
Activity, Trash, Shares), Settings, the lock and login screens, the
media viewer, search, the details/share sheets, and the share-upload
and move/copy destination pickers.
- Added a design canvas (linked from DESIGN_SYSTEM.md) covering the
screens the spec didn't already describe, with matching Android and
iOS chrome; wrote up the approved recipes into DESIGN_SYSTEM.md §4.
- Removed now-dead legacy widgets (media_grid_tile, swipeable_item,
sync_status_badge, selectable_thumbnail) and updated
architecture.md/styling.md/standards.md to describe the new
structure and component/testing conventions.
- Added widget tests for the noo/ component kit.
This is a UI-only rework: no provider/model/service behavior changed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Offline tab is now FilesView(offline: true) over a FolderBrowser interface
(FilesController / OfflineController) sharing controls, tiles, thumbnails
- Synced files follow the Files Cache rule (default: refresh every 15 min):
per-account WorkManager jobs, in-app timer, resume and pull triggers;
root-etag shortcut skips full walks when nothing changed
- Single sync at a time (shared lock); logging out stops that account's sync
- Sync safety: PROPFIND failures skip the path instead of deleting local files
- Mirror empty folders and remove deleted ones; drop synced paths deleted on
the server; missing local files are re-downloaded
- Notifications: silent per-account sync notifications, "Background sync
notifications" setting, audible upload/download completion
- Files refresh in place (no spinner flash); retry after network returns
- Fix sync badges not updating (shared native status stream), "Sync off" on
launch (eager providers), Files Cache section moved under Device Sync
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Folders now show the synced status badge themselves, not just their
nested files - device-sync state never tracked folders individually, so
syncStatusFor derives a folder's badge from whether its path falls
within sync scope instead.
- Turning sync off for a path now deletes its local mirror and clears its
native sync-state entries (SyncEngine.removeLocalSync), instead of just
stopping future updates and leaving the downloaded copy behind.
- "Sync to device" in Files' selection toolbar now works over the whole
selection at once instead of being gated to a single selected item.
- The sync header's expanded panel shows real folder/item counts instead
of a static "Synced" label that stayed accurate-sounding even before
anything had actually synced.
- Confirmation SnackBars on starting/stopping sync from both the
selection toolbar and Settings' Device Sync card.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Consolidates duplicated GET/PUT/notification-channel logic across
DownloadService/ShareUploadService/SyncEngine into shared Kotlin helpers,
gives upload/download real batch queueing instead of dropping a second
concurrent batch, and dedupes repeated Dart channel-argument boilerplate.
Replaces the 2300+ line ServerProvider god object with ten focused
ChangeNotifiers (SessionController, SettingsController, FilesController,
PhotosController, FavoritesController, TrashController, SharesController,
RecentController, SyncStatusController, PickController) plus ItemOperations,
a plain coordinator for cross-domain item mutations - fixing the coupling
where device-sync status, per-tab data, and global UI prefs all lived in
one object. Updates every view/widget call site accordingly and refreshes
the architecture/server/standards/styling docs to match.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- New native sync engine (SyncEngine/SyncWorker/ConflictResolveWorker,
WorkManager-based) that mirrors selected Nextcloud folders or files to
app-private local storage, periodically and on-demand ("Sync now"),
with never-auto-resolved conflict notifications.
- Single files (not just folders) can now be marked "Sync to device".
- Live sync status (syncing/synced/conflicts) pushes from native to Dart
over a new EventChannel; the persistent header chip/panel now reflects
device-sync status instead of the WebDAV-refresh loading state, with a
cloud_off/cloud_sync/cloud_done/cloud_alert icon set and an expandable
conflicts list with in-app "Keep local"/"Use server" resolution.
Per-item cloud_done/sync badges show on Files tiles.
- Share/Download short-circuit to the local copy for already-synced
files instead of a fresh network fetch.
- Settings gained a Device Sync section (per-folder list, "Sync
everything", Wi-Fi-only toggle, manual "Sync now").
- Fixed two release-only bugs found via on-device testing: Android's
HttpURLConnection silently rejects the PROPFIND method (switched to
OkHttp), and R8 was stripping WorkManager's reflection-instantiated
internals (broadened proguard-rules.pro to keep androidx.work.**
wholesale rather than chasing individual classes).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Request POST_NOTIFICATIONS once on first app launch, with a
plain-language rationale dialog shown before the OS prompt
(permission_handler), since upload/download progress relies on it.
- ShareUploadView no longer shows "More tabs" - there's nowhere useful
for it to go mid-upload.
- FloatingBottomNavBar's pill scrolls horizontally instead of
overflowing when the visible tab set doesn't fit.
- Settings account card: drop the separate server-address row and its
globe icon, showing the host inline with the quota percentage/
"Unlimited storage" text instead (left-aligned, same size).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- WebDAV MOVE/COPY-backed copy/move for files and folders, with a
destination picker (independent nav state so it doesn't disturb the
Files tab's browsing position) and a conflict-resolution sheet
(overwrite all / keep both / decide per item).
- Downloads now use the same foreground-service + notification approach
as uploads, surviving app closure.
- Favorites is now its own tab (account-wide, via a WebDAV SEARCH query)
instead of a Files-tab filter toggle, which couldn't represent
favorited items outside the currently browsed folder correctly.
- Share sheet restyled to match the app's design language, plus a
"share file directly" action via the OS share sheet; details sheet
tab icons resized to match the bottom nav.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Let Noo be picked as a source (Files + Photos) by other apps' native
file/photo pickers via Android's GET_CONTENT intent, mirroring the
existing "Share to Noo" plumbing: MainActivity.kt/PickIntentService/
PickRequest, ServerProvider.pickRequest/confirmPick/cancelPick, and
pick-mode tap handling in FilesView/PhotosView. While picking, the
bottom nav and MoreTabsButton are restricted to Files/Photos only.
- Fix two account bugs: testConnection() failures no longer delete a
still-valid stored app password (only an actual 401 does), and
switchAccount() now retries a nominally-active-but-logged-out account
instead of no-op'ing - both were silently locking users out of "Continue
as" with no way back in except removing and re-adding the account
(now exposed via login_view.dart's inline remove button).
- Fix the login WebView: clear cookies before every load so "Add Account"
can't silently reuse an existing session, and push it as a
non-animated route to avoid a known WebView-as-PlatformView black-screen-
on-pop issue under Impeller.
- Redesign the Files/Photos selection toolbar to take over the top app bar
entirely (SyncedHeaderScaffold.selectionBar) instead of sitting as a
second bar under the controls row, with a horizontally-scrollable,
uniformly-colored actions row and the same one-shot scroll-hint nudge
the controls row uses.
- Redesign ShareUploadView to share the same SyncedHeaderScaffold chrome
as every tab, with the uploading-file summary (marqueed via the new
shared MarqueeTitle widget) and upload button grouped into one
rounded/elevated bottom sheet.
- Restyle ShareSheet to open on the same DetailsHeader every other
per-item sheet uses, and fix a couple of small design-language drifts
(hardcoded text style, a non-rounded icon).
- Settings' account card: icon buttons with tooltips instead of text
buttons for refresh/logout/remove, divider removed.
getItemIcon/getIconColor/ItemThumbnail move out of files_view.dart into
widgets/item_icon.dart so other screens (the share-upload destination
picker, in a follow-up commit) can reuse the same file/folder icon
treatment instead of re-deriving it.
The Files controls bar's "this scrolls" hint is now a one-shot nudge
(scrolls right then back once on first appearance) instead of a
persistent chevron badge or edge fade - both prior attempts blended into
the row's sparse icon content too easily to actually register.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
App lock (local_auth) gates the app behind the OS's biometric/device
credential prompt via a new lock screen, layered on top of the existing
Nextcloud session rather than replacing it.
Multi-account support lets users save several Nextcloud logins and switch
between them (AccountStore, per-account browsing prefs, avatar swipe-to-
cycle); logout() now keeps an account's saved credentials/prefs intact so
it can be resumed from a "Continue as..." list, while removeAccount()
stays the only destructive path.
The account settings screen is redesigned around a single profile card
(avatar/name/email, server host, storage quota with groups) with
Refresh Cache/Logout/Remove actions, replacing the old separate "Server
Connection Info" section.
Also fixes activity-tab timestamps: the OCS Activity API reports time as
an ISO 8601 `datetime` string, not the numeric `timestamp` field the code
incorrectly assumed, which made every activity render as Jan 1 1970.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Revert the scrollbar-thumb feature entirely (per-view Scrollbar
wrappers and the ScrollbarThemeData override) - back to plain
scrollables with no visible thumb
- Settings: move the logout button into the profile card (next to the
name/email), and move the ONLINE/OFFLINE badge + "Connected to..."
text out of the top row into a combined line at the bottom of the
card, instead of split between the AppBar and the top of the screen
- Update architecture.md/styling.md to drop the stale scrollbar-theme
documentation
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- 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>
- Accept Android's "Share to..." intent (ACTION_SEND/SEND_MULTIPLE) to
upload a shared file, with a new destination-folder picker screen
- Unlink Files/Photos sort settings and make Files remember sort
per folder (like per-folder view settings), instead of one shared
global sort field for both tabs
- Media viewer: show the file name (marquee-scrolling if too long) in
the top bar next to a resized back button, slide the top bar out of
view instead of fading it, and unify icon color/contrast across the
top bar, bottom action bar, and video transport controls
- Add a scrollbar thumb to every scrollable list in the app (all tab
views via SyncedHeaderScaffold, search results, settings, the new
upload folder picker, share sheet, and the Details sheet's tabs)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Remove peek details sheet from media viewer; scope Activity tab to
the current file via client-side filtering instead of unreliable
server-side query params
- Add file rename action (WebDAV MOVE); fix search result taps to
switch to Files tab and rebuild breadcrumbs from an absolute path
- Add swipe-to-delete confirmation dialog; fix a crash from disposing
the rename dialog's TextEditingController too early
- Rework pull-to-sync gesture so sync and lock-open thresholds fire
independently within one gesture, raise thresholds, and add a
classic Material refresh spinner
- Add "tap active tab to scroll to top" behavior with a settings toggle
- Align selection toolbar padding with the sort/filter controls row
- Replace app icon assets (adaptive icon + splash screen) with
updated IconKitchen export
- Replace the seek bar style dropdown with a Default/Wavy/Slim/Squiggly
preset picker, each rendered with a live, perpetually-animated
preview via a new shared SeekBarPainter/SeekBarPreview
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Major additions since the initial rebuild:
- New tabs: Trash, Shares, Recent, configurable tab visibility/order/default
- Nextcloud sharing: search users/teams, internal link, email shares,
public links, "others with access" - in its own Share bottom sheet
- Details bottom sheet (Info/Versions/Activity) with a persistent peek
panel in the media viewer, reachable from selection, swipe, and the
media viewer
- Files/Photos: tap-and-hold multi-select with a sticky selection toolbar,
swipe actions (favorite/delete/share), sticky sort/filter controls,
list/grid views, breadcrumb chips, whole-account Photos view
- Media viewer: pinch/double-tap zoom on images, predictive-back support,
fixed PDF zoom-out floor
- Settings: Material You theming incl. AMOLED black, configurable bottom
bar tabs, swipe actions, media seek bar style, and folder-listing cache
policy (never/periodic/manual) to cut down on refetching
- Login flow moved to an in-app Chrome Custom Tab with retry-tolerant
polling
- Replaced ad hoc widgets with native Material 3 equivalents (toggle
IconButtons, SliverAppBar selection toolbar, SearchBar, drag handles)
- Rebrand: app renamed to Noo throughout, package id -> dev.ayushya.noo,
new adaptive launcher icon
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replaces the starter template with Login Flow v2 auth, WebDAV-backed
Files/Photos/Activity tabs, a swipeable media viewer with PDF/text/video
preview, Material You dynamic theming, a frosted-glass floating nav bar,
and a rebranded app icon/identity (com.example -> family.nasa).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>