Files
Client/lib/widget/media_gallery/gallery_item.dart
T

80 lines
2.3 KiB
Dart

import '../../share_intent/remote_file_ref.dart';
import '../file_viewer/file_kind.dart';
enum GalleryMediaKind { image, video }
/// Single rule for "is this a picture/video": the gallery and the chat bubble
/// both use it, so whatever looks like media in a chat is also in the gallery.
GalleryMediaKind? galleryMediaKindOf(String name, {String? mimeType}) {
final mime = mimeType?.toLowerCase() ?? '';
// Only formats the device decodes itself — image/* also covers PSD, SVG,
// TIFF …, which would hinge on a server preview.
if (_galleryImageMimes.contains(mime)) return GalleryMediaKind.image;
if (mime.startsWith('video/')) return GalleryMediaKind.video;
if (isGalleryImageName(name)) return GalleryMediaKind.image;
if (isGalleryVideoName(name)) return GalleryMediaKind.video;
return null;
}
const Set<String> _galleryImageMimes = {
'image/jpeg',
'image/png',
'image/gif',
'image/webp',
'image/bmp',
'image/x-ms-bmp',
'image/heic',
'image/heif',
'image/avif',
'image/x-icon',
'image/vnd.microsoft.icon',
};
class GalleryItem {
final RemoteFileRef file;
final GalleryMediaKind kind;
/// Talk message id: orders chat media and dedupes overlapping pages.
final int? messageId;
/// Second line in the app bar, e.g. sender and date.
final String? subtitle;
/// Part of the cache key so an overwritten file is not served stale.
final String? eTag;
const GalleryItem({
required this.file,
required this.kind,
this.messageId,
this.subtitle,
this.eTag,
});
String get key => messageId?.toString() ?? file.path;
bool get isVideo => kind == GalleryMediaKind.video;
String get _extension => file.name.split('.').last.toLowerCase();
/// Server previews of a GIF are a single still frame.
bool get needsOriginal => _extension == 'gif';
}
/// Adds [incoming] to [current], dropping duplicates by [GalleryItem.key] and
/// keeping chat media in chronological (message id) order.
List<GalleryItem> mergeGalleryItems(
List<GalleryItem> current,
Iterable<GalleryItem> incoming,
) {
final byKey = {for (final item in current) item.key: item};
for (final item in incoming) {
byKey.putIfAbsent(item.key, () => item);
}
final merged = byKey.values.toList();
if (merged.every((item) => item.messageId != null)) {
merged.sort((a, b) => a.messageId!.compareTo(b.messageId!));
}
return merged;
}