129 lines
3.7 KiB
Dart
129 lines
3.7 KiB
Dart
import '../../../../api/marianumcloud/talk/chat/get_chat_response.dart';
|
|
import '../../../../api/marianumcloud/talk/get_shared_items/get_shared_items.dart';
|
|
import '../../../../extensions/date_time.dart';
|
|
import '../../../../session/session_manager.dart';
|
|
import '../../../../share_intent/remote_file_ref.dart';
|
|
import '../../../../widget/media_gallery/gallery_item.dart';
|
|
import '../../../../widget/media_gallery/gallery_source.dart';
|
|
import '../details/shared_items_view.dart';
|
|
|
|
GalleryItem? galleryItemFromTalk(GetChatResponseObject message) {
|
|
final file = message.messageParameters?['file'];
|
|
if (file == null || file.path == null) return null;
|
|
final kind = galleryMediaKindOf(file.name, mimeType: file.mimetype);
|
|
if (kind == null) return null;
|
|
return GalleryItem(
|
|
file: RemoteFileRef.fromTalk(file),
|
|
kind: kind,
|
|
messageId: message.id,
|
|
subtitle:
|
|
'${message.actorDisplayName} · ${message.sentAt.formatDateTime()}',
|
|
);
|
|
}
|
|
|
|
/// All images and videos of a chat, via Talk's shared-items `media` stream.
|
|
/// Opens on the tapped message and fills in around it in the background.
|
|
class TalkGallerySource extends GallerySource {
|
|
/// The share endpoint only pages backwards from the newest item, so reaching
|
|
/// an old tapped image means walking down to it; give up beyond this.
|
|
static const _maxCatchUpPages = 25;
|
|
|
|
final String token;
|
|
final GalleryItem initial;
|
|
final int _epoch = SessionManager().sessionEpoch;
|
|
|
|
List<GalleryItem> _items;
|
|
int? _cursor;
|
|
bool _hasOlder = false;
|
|
bool _loading = false;
|
|
bool _disposed = false;
|
|
|
|
TalkGallerySource({required this.token, required this.initial})
|
|
: _items = [initial] {
|
|
_catchUp();
|
|
}
|
|
|
|
@override
|
|
List<GalleryItem> get items => _items;
|
|
|
|
@override
|
|
bool get hasOlder => _hasOlder && !_loading;
|
|
|
|
bool get _stale => _disposed || !SessionManager().isCurrentSession(_epoch);
|
|
|
|
Future<SharedItemsPage> _fetch(int? cursor) async => buildSharedItemsPage(
|
|
await GetSharedItems(
|
|
token,
|
|
objectType: 'media',
|
|
limit: sharedItemsPageSize,
|
|
lastKnownMessageId: cursor,
|
|
).run(),
|
|
cursor,
|
|
);
|
|
|
|
Iterable<GalleryItem> _toItems(SharedItemsPage page) =>
|
|
page.items.map(galleryItemFromTalk).nonNulls;
|
|
|
|
Future<void> _catchUp() async {
|
|
final initialId = initial.messageId!;
|
|
_loading = true;
|
|
try {
|
|
final collected = <GalleryItem>[];
|
|
int? cursor;
|
|
var hasMore = true;
|
|
var reached = false;
|
|
for (var pages = 0; hasMore && pages < _maxCatchUpPages; pages++) {
|
|
final page = await _fetch(cursor);
|
|
if (_stale) return;
|
|
collected.addAll(_toItems(page));
|
|
cursor = page.lastKnownMessageId;
|
|
hasMore = page.hasMore;
|
|
if (page.items.any((item) => item.id <= initialId)) {
|
|
reached = true;
|
|
break;
|
|
}
|
|
}
|
|
if (reached || !hasMore) {
|
|
_items = mergeGalleryItems(_items, collected);
|
|
_cursor = cursor;
|
|
_hasOlder = hasMore;
|
|
} else {
|
|
// Too far down: skip the newer media and only page older ones.
|
|
_cursor = initialId;
|
|
_hasOlder = true;
|
|
}
|
|
} on Object {
|
|
if (_stale) return;
|
|
_cursor = initialId;
|
|
_hasOlder = true;
|
|
} finally {
|
|
_loading = false;
|
|
}
|
|
notifyListeners();
|
|
}
|
|
|
|
@override
|
|
Future<void> loadOlder() async {
|
|
if (!hasOlder) return;
|
|
_loading = true;
|
|
try {
|
|
final page = await _fetch(_cursor);
|
|
if (_stale) return;
|
|
_items = mergeGalleryItems(_items, _toItems(page));
|
|
_cursor = page.lastKnownMessageId;
|
|
_hasOlder = page.hasMore;
|
|
} on Object {
|
|
// Retried on the next page change.
|
|
} finally {
|
|
_loading = false;
|
|
}
|
|
if (!_stale) notifyListeners();
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_disposed = true;
|
|
super.dispose();
|
|
}
|
|
}
|