Add GET_CONTENT picker support, fix account/login bugs, restyle selection UI
Build APK / build (push) Successful in 7m40s

- 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.
This commit is contained in:
2026-09-17 23:36:54 -04:00
parent eee2730fbe
commit 44a23c3632
24 changed files with 1298 additions and 394 deletions
+31 -11
View File
@@ -41,6 +41,15 @@ class SyncedHeaderScaffold extends StatefulWidget {
/// `provider.fetchTrash` for a tab backed by different data.
final Future<void> Function()? onRefresh;
/// When non-null (a tab is mid-selection), this takes over the pinned top
/// bar entirely - replacing the sync-status chip, [actions], and the
/// pull-to-reveal quota panel - instead of appearing as a second app bar
/// further down in [contentSlivers]. Selection is a modal-ish state (you
/// came here to act on specific items), so it reads better as the one
/// thing at the very top than as a strip sandwiched under the normal
/// chrome.
final Widget? selectionBar;
const SyncedHeaderScaffold({
super.key,
required this.scrollController,
@@ -48,6 +57,7 @@ class SyncedHeaderScaffold extends StatefulWidget {
required this.actions,
required this.contentSlivers,
this.onRefresh,
this.selectionBar,
});
@override
@@ -175,22 +185,32 @@ class _SyncedHeaderScaffoldState extends State<SyncedHeaderScaffold> {
slivers: [
SliverAppBar(
pinned: true,
stretch: true,
expandedHeight: _headerLocked ? 190 : kToolbarHeight,
stretch: widget.selectionBar == null,
expandedHeight: widget.selectionBar != null
? kToolbarHeight
: (_headerLocked ? 190 : kToolbarHeight),
collapsedHeight: kToolbarHeight,
backgroundColor: colorScheme.surfaceContainer,
surfaceTintColor: colorScheme.surfaceContainer,
scrolledUnderElevation: 0,
automaticallyImplyLeading: false,
leadingWidth: _headerLocked ? 56 : 160,
leading: leadingWidget,
actions: widget.actions,
flexibleSpace: FlexibleSpaceBar(
background: _SyncedStretchPanel(
provider: provider,
forceVisible: _headerLocked,
),
),
leadingWidth: widget.selectionBar != null
? 0
: (_headerLocked ? 56 : 160),
leading: widget.selectionBar != null ? null : leadingWidget,
titleSpacing: widget.selectionBar != null ? 0 : null,
title: widget.selectionBar,
actions: widget.selectionBar != null
? const []
: widget.actions,
flexibleSpace: widget.selectionBar != null
? null
: FlexibleSpaceBar(
background: _SyncedStretchPanel(
provider: provider,
forceVisible: _headerLocked,
),
),
),
DecoratedSliver(
decoration: BoxDecoration(