added a swipeable media gallery for chat and folder images and videos with server previews, on-demand originals and zoom, and marked document previews in chat so they no longer pass for photos

This commit is contained in:
MineTec committed 2026-10-28 22:15:57 +01:00
1 parent 952f916231
commit aa176c90e3
22 files changed
+1658 -103

No files matched your search

+6 -2
View File
@@ -140,8 +140,12 @@ class _FilesViewState extends State<_FilesView> {
return ListView.builder(
padding: EdgeInsets.zero,
itemCount: files.length,
itemBuilder: (context, index) =>
FileElement(files[index], widget.path, bloc.refresh),
itemBuilder: (context, index) => FileElement(
files[index],
widget.path,
bloc.refresh,
siblings: files,
),
);
},
),
+41 -4
View File
@@ -17,6 +17,8 @@ import '../../../../widget/confirm_dialog.dart';
import '../../../../widget/demo_restricted.dart';
import '../../../../widget/details_bottom_sheet.dart';
import '../../../../widget/downloads/download_trigger.dart';
import '../../../../widget/media_gallery/gallery_item.dart';
import '../../../../widget/media_gallery/gallery_source.dart';
import '../../../../widget/prompt_dialog.dart';
import '../../talk/widgets/highlighted_linkify.dart';
import '../data/webdav_path.dart';
@@ -33,11 +35,16 @@ class FileElement extends StatefulWidget {
/// highlighted in the tile title. Used by the Files search delegate.
final String? highlight;
/// The listing [file] belongs to, in display order; the media gallery pages
/// through its images and videos. Null opens the gallery on [file] alone.
final List<CacheableFile>? siblings;
const FileElement(
this.file,
this.path,
this.refetch, {
this.highlight,
this.siblings,
super.key,
});
@@ -100,10 +107,7 @@ class _FileElementState extends State<FileElement>
void _onTap() {
if (widget.file.isDirectory) {
AppRoutes.openFolder(
context,
[...widget.path, widget.file.name],
);
AppRoutes.openFolder(context, [...widget.path, widget.file.name]);
return;
}
if (guardDemoAction(context)) return;
@@ -111,12 +115,45 @@ class _FileElementState extends State<FileElement>
confirmCancelDownload();
return;
}
if (galleryMediaKindOf(widget.file.name, mimeType: widget.file.mimeType) !=
null) {
_openGallery();
return;
}
startDownload(
name: widget.file.name,
remoteFile: RemoteFileRef.fromCacheable(widget.file),
);
}
void _openGallery() {
final items = [
for (final file in widget.siblings ?? [widget.file])
if (!file.isDirectory) ?_galleryItem(file),
];
AppRoutes.openMediaGallery(
context,
source: StaticGallerySource(items),
initialKey: widget.file.path,
);
}
static GalleryItem? _galleryItem(CacheableFile file) {
final kind = galleryMediaKindOf(file.name, mimeType: file.mimeType);
if (kind == null) return null;
final size = file.size;
final modified = file.modifiedAt;
return GalleryItem(
file: RemoteFileRef.fromCacheable(file),
kind: kind,
eTag: file.eTag,
subtitle: [
if (size != null) filesize(size),
if (modified != null) modified.formatDateTime(),
].join(' · '),
);
}
void _rename() {
if (guardDemoAction(context)) return;
showPromptDialog(