diff --git a/lib/pages/story/story_page.dart b/lib/pages/story/story_page.dart index fd58a383..00ba84dd 100644 --- a/lib/pages/story/story_page.dart +++ b/lib/pages/story/story_page.dart @@ -5,6 +5,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:matrix/matrix.dart'; import 'package:path_provider/path_provider.dart'; import 'package:video_player/video_player.dart'; @@ -13,6 +14,8 @@ import 'package:vrouter/vrouter.dart'; import 'package:fluffychat/pages/story/story_view.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions.dart/client_stories_extension.dart'; import 'package:fluffychat/utils/platform_infos.dart'; +import 'package:fluffychat/utils/room_status_extension.dart'; +import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/matrix.dart'; class StoryPage extends StatefulWidget { @@ -31,8 +34,60 @@ class StoryPageController extends State { final List events = []; + Timeline? timeline; + Event? get currentEvent => index < events.length ? events[index] : null; + List get currentSeenByUsers { + final timeline = this.timeline; + final currentEvent = this.currentEvent; + if (timeline == null || currentEvent == null) return []; + return Matrix.of(context).client.getRoomById(roomId)?.getSeenByUsers( + timeline, + events, + {}, + eventId: currentEvent.eventId, + ) ?? + []; + } + + void displaySeenByUsers() => showModalBottomSheet( + context: context, + builder: (context) => Scaffold( + appBar: AppBar( + title: Text(seenByUsersTitle), + ), + body: ListView.builder( + itemCount: currentSeenByUsers.length, + itemBuilder: (context, i) => ListTile( + leading: Avatar( + mxContent: currentSeenByUsers[i].avatarUrl, + name: currentSeenByUsers[i].calcDisplayname(), + ), + title: Text(currentSeenByUsers[i].calcDisplayname()), + ), + ), + ), + ); + + String get seenByUsersTitle { + final seenByUsers = currentSeenByUsers; + if (seenByUsers.isEmpty) return ''; + if (seenByUsers.length == 1) { + return L10n.of(context)!.seenByUser(seenByUsers.single.calcDisplayname()); + } + if (seenByUsers.length == 2) { + return L10n.of(context)!.seenByUserAndUser( + seenByUsers.first.calcDisplayname(), + seenByUsers.last.calcDisplayname(), + ); + } + return L10n.of(context)!.seenByUserAndCountOthers( + seenByUsers.single.calcDisplayname(), + seenByUsers.length - 1, + ); + } + final TextEditingController replyController = TextEditingController(); static const Duration _step = Duration(milliseconds: 50); @@ -56,6 +111,13 @@ class StoryPageController extends State { }); } + bool get isOwnStory { + final client = Matrix.of(context).client; + final room = client.getRoomById(roomId); + if (room == null) return false; + return room.ownPowerLevel >= 100; + } + String get roomId => VRouter.of(context).pathParameters['roomid'] ?? ''; Future loadVideoController(Event event) async { @@ -148,7 +210,7 @@ class StoryPageController extends State { await room.join(); await joinedFuture; } - final timeline = await room.getTimeline(); + final timeline = this.timeline = await room.getTimeline(); timeline.requestKeys(); var events = timeline.events.where((e) => e.type == EventTypes.Message).toList(); diff --git a/lib/pages/story/story_view.dart b/lib/pages/story/story_view.dart index 1c179c8a..79f97033 100644 --- a/lib/pages/story/story_view.dart +++ b/lib/pages/story/story_view.dart @@ -225,6 +225,27 @@ class StoryView extends StatelessWidget { ), ), ), + if (controller.isOwnStory && + controller.currentSeenByUsers.isNotEmpty) + Positioned( + bottom: 16, + left: 16, + right: 16, + child: SafeArea( + child: Center( + child: OutlinedButton.icon( + onPressed: controller.displaySeenByUsers, + icon: const Icon( + Icons.visibility_outlined, + color: Colors.white70, + ), + label: Text( + controller.seenByUsersTitle, + style: const TextStyle(color: Colors.white70), + ), + ), + ), + )), ], ), ); diff --git a/lib/utils/room_status_extension.dart b/lib/utils/room_status_extension.dart index 59cd9a30..e9bfde1a 100644 --- a/lib/utils/room_status_extension.dart +++ b/lib/utils/room_status_extension.dart @@ -69,20 +69,19 @@ extension RoomStatusExtension on Room { } List getSeenByUsers( - Timeline timeline, - List filteredEvents, - Set unfolded, - ) { + Timeline timeline, List filteredEvents, Set unfolded, + {String? eventId}) { if (timeline.events.isEmpty) return []; final filteredEvents = timeline.getFilteredEvents(unfolded: unfolded); if (filteredEvents.isEmpty) return []; + eventId ??= filteredEvents.first.eventId; final lastReceipts = {}; // now we iterate the timeline events until we hit the first rendered event for (final event in timeline.events) { lastReceipts.addAll(event.receipts.map((r) => r.user)); - if (event.eventId == filteredEvents.first.eventId) { + if (event.eventId == eventId) { break; } }