chore: Display stories seenByUsers

This commit is contained in:
Christian Pauly 2021-12-26 09:59:34 +01:00
parent 518111e84f
commit 80bd291805
3 changed files with 88 additions and 6 deletions

View File

@ -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<StoryPage> {
final List<Event> events = [];
Timeline? timeline;
Event? get currentEvent => index < events.length ? events[index] : null;
List<User> 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<StoryPage> {
});
}
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<VideoPlayerController> loadVideoController(Event event) async {
@ -148,7 +210,7 @@ class StoryPageController extends State<StoryPage> {
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();

View File

@ -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),
),
),
),
)),
],
),
);

View File

@ -69,20 +69,19 @@ extension RoomStatusExtension on Room {
}
List<User> getSeenByUsers(
Timeline timeline,
List<Event> filteredEvents,
Set<String> unfolded,
) {
Timeline timeline, List<Event> filteredEvents, Set<String> unfolded,
{String? eventId}) {
if (timeline.events.isEmpty) return [];
final filteredEvents = timeline.getFilteredEvents(unfolded: unfolded);
if (filteredEvents.isEmpty) return [];
eventId ??= filteredEvents.first.eventId;
final lastReceipts = <User>{};
// 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;
}
}