Media viewer details panel, Upload FAB size setting, PDF zoom/viewport fixes, avatar menu account switcher; bump to 1.0.2+5
Build App Bundle / build (push) Successful in 6m2s

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
2026-10-01 18:41:51 -04:00
co-authored by Claude Code
parent 0b7df833f6
commit 4611bc2142
22 changed files with 846 additions and 94 deletions
+86 -23
View File
@@ -21,6 +21,7 @@ import '../widgets/noo/overlays/noo_overlay_header.dart';
import '../widgets/noo/overlays/noo_sheet.dart';
import '../widgets/share_sheet.dart';
import '../widgets/viewer/media_action_bar.dart';
import '../widgets/viewer/media_details_panel.dart';
import '../widgets/viewer/media_image_preview.dart';
import '../widgets/viewer/media_pdf_preview.dart';
import '../widgets/viewer/media_text_preview.dart';
@@ -154,6 +155,43 @@ class _FileViewerScreenState extends State<FileViewerScreen> {
/// also drag the gallery to the next item.
bool _isZoomed = false;
final Map<int, Offset> _pointerStart = {};
bool _multiTouch = false;
void _onPointerDown(PointerDownEvent e) {
if (_pointerStart.isNotEmpty) _multiTouch = true;
_pointerStart[e.pointer] = e.position;
}
// Raw pointer events rather than a drag recognizer, so this never competes
// with the PageView / InteractiveViewer gestures in the arena.
void _onPointerUp(PointerUpEvent e) {
final start = _pointerStart.remove(e.pointer);
final wasMulti = _multiTouch;
if (_pointerStart.isEmpty) _multiTouch = false;
if (start == null || wasMulti || _isZoomed || !_isSwipeable) return;
final delta = e.position - start;
if (delta.dy < -80 && delta.dy.abs() > delta.dx.abs() * 1.5) {
_openDetails();
}
}
final _panelKey = GlobalKey<MediaDetailsPanelState>();
void _openDetails() {
if (NooLayout.isDesktop(context)) {
DetailsSheet.show(context, _currentItem);
return;
}
setState(() => _controlsVisible = true);
_panelKey.currentState?.expand();
}
void _onPointerCancel(PointerCancelEvent e) {
_pointerStart.remove(e.pointer);
if (_pointerStart.isEmpty) _multiTouch = false;
}
NextcloudItem get _currentItem => _mediaItems[_currentIndex];
bool get _isOffline => widget.localPathResolver != null;
@@ -394,13 +432,25 @@ class _FileViewerScreenState extends State<FileViewerScreen> {
backgroundColor: stageColor,
body: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => setState(() => _controlsVisible = !_controlsVisible),
onTap: () {
final panel = _panelKey.currentState;
if (panel != null && panel.isExpanded) {
panel.collapse();
return;
}
setState(() => _controlsVisible = !_controlsVisible);
},
child: Stack(
children: [
Positioned.fill(
child: Container(
color: stageColor,
child: _buildBody(context, session),
child: Listener(
onPointerDown: _onPointerDown,
onPointerUp: _onPointerUp,
onPointerCancel: _onPointerCancel,
child: Container(
color: stageColor,
child: _buildBody(context, session),
),
),
),
if (_downloadProgress != null)
@@ -522,25 +572,35 @@ class _FileViewerScreenState extends State<FileViewerScreen> {
offset: _controlsVisible ? Offset.zero : const Offset(0, 1.4),
child: IgnorePointer(
ignoring: !_controlsVisible,
child: MediaActionBar(
isFavorite: _currentItem.isFavorite,
isBusy: _isBusy,
// Favorite/delete/download-to-device all need a live
// server - hidden rather than shown-and-failing while
// browsing an already-local file from the Offline tab.
showServerActions: !_isOffline,
transportControls:
_isSwipeable &&
_currentItem.type == NextcloudItemType.video &&
_videoController != null
? VideoTransportControls(controller: _videoController!)
: null,
onShare: () => ShareSheet.show(context, _currentItem),
onFavorite: () => _toggleFavorite(ops),
onDelete: () => _deleteCurrentItem(ops),
onOpenExternally: () => _openExternally(session),
onDownload: () => _downloadToDevice(session),
onDetails: () => DetailsSheet.show(context, _currentItem),
child: MediaDetailsPanel(
key: _panelKey,
item: _currentItem,
builder: (context, handle, below) => MediaActionBar(
onDetails: NooLayout.isDesktop(context)
? _openDetails
: null,
handle: NooLayout.isDesktop(context) ? null : handle,
below: NooLayout.isDesktop(context) ? null : below,
isFavorite: _currentItem.isFavorite,
isBusy: _isBusy,
// Favorite/delete/download-to-device all need a live
// server - hidden rather than shown-and-failing while
// browsing an already-local file from the Offline tab.
showServerActions: !_isOffline,
transportControls:
_isSwipeable &&
_currentItem.type == NextcloudItemType.video &&
_videoController != null
? VideoTransportControls(
controller: _videoController!,
)
: null,
onShare: () => ShareSheet.show(context, _currentItem),
onFavorite: () => _toggleFavorite(ops),
onDelete: () => _deleteCurrentItem(ops),
onOpenExternally: () => _openExternally(session),
onDownload: () => _downloadToDevice(session),
),
),
),
),
@@ -659,6 +719,9 @@ class _FileViewerScreenState extends State<FileViewerScreen> {
default:
if (_isPdf) {
return MediaPdfPreview(
topInset: _controlsVisible
? MediaQuery.paddingOf(context).top + 60
: 0,
item: widget.item,
session: session,
localPathResolver: widget.localPathResolver,