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.
50 lines
1.4 KiB
Dart
50 lines
1.4 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:marquee/marquee.dart';
|
|
|
|
/// A plain, single-line ellipsized [Text] for content that fits, or an
|
|
/// auto-scrolling [Marquee] for text too long for the available width -
|
|
/// measured once via [TextPainter] rather than always marqueeing, so short
|
|
/// text just sits still like normal.
|
|
class MarqueeTitle extends StatelessWidget {
|
|
final String text;
|
|
final TextStyle? style;
|
|
|
|
const MarqueeTitle({super.key, required this.text, required this.style});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return LayoutBuilder(
|
|
builder: (context, constraints) {
|
|
final painter = TextPainter(
|
|
text: TextSpan(text: text, style: style),
|
|
maxLines: 1,
|
|
textDirection: Directionality.of(context),
|
|
)..layout(maxWidth: double.infinity);
|
|
|
|
if (painter.width <= constraints.maxWidth) {
|
|
return Text(
|
|
text,
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: style,
|
|
);
|
|
}
|
|
|
|
return SizedBox(
|
|
height: painter.height,
|
|
child: Marquee(
|
|
text: text,
|
|
style: style,
|
|
blankSpace: 48,
|
|
velocity: 30,
|
|
startPadding: 0,
|
|
pauseAfterRound: const Duration(seconds: 1),
|
|
fadingEdgeStartFraction: 0.1,
|
|
fadingEdgeEndFraction: 0.15,
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|