design: Hide unimportant state events instead of folding
This commit is contained in:
parent
361f8385a9
commit
b642f89738
13 changed files with 125 additions and 161 deletions
|
|
@ -28,7 +28,6 @@ import 'package:fluffychat/utils/platform_infos.dart';
|
|||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import '../../utils/account_bundles.dart';
|
||||
import '../../utils/localized_exception_extension.dart';
|
||||
import '../../utils/matrix_sdk_extensions.dart/filtered_timeline_extension.dart';
|
||||
import '../../utils/matrix_sdk_extensions.dart/matrix_file_extension.dart';
|
||||
import 'send_file_dialog.dart';
|
||||
import 'send_location_dialog.dart';
|
||||
|
|
@ -111,8 +110,6 @@ class ChatController extends State<Chat> {
|
|||
|
||||
List<Event> selectedEvents = [];
|
||||
|
||||
late List<Event> filteredEvents;
|
||||
|
||||
final Set<String> unfolded = {};
|
||||
|
||||
Event? replyEvent;
|
||||
|
|
@ -184,22 +181,7 @@ class ChatController extends State<Chat> {
|
|||
|
||||
void updateView() {
|
||||
if (!mounted) return;
|
||||
setState(
|
||||
() {
|
||||
filteredEvents = timeline!.getFilteredEvents(unfolded: unfolded);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void unfold(String eventId) {
|
||||
var i = filteredEvents.indexWhere((e) => e.eventId == eventId);
|
||||
setState(() {
|
||||
while (i < filteredEvents.length - 1 && filteredEvents[i].isState) {
|
||||
unfolded.add(filteredEvents[i].eventId);
|
||||
i++;
|
||||
}
|
||||
filteredEvents = timeline!.getFilteredEvents(unfolded: unfolded);
|
||||
});
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
Future<bool> getTimeline() async {
|
||||
|
|
@ -225,7 +207,6 @@ class ChatController extends State<Chat> {
|
|||
}
|
||||
});
|
||||
}
|
||||
filteredEvents = timeline!.getFilteredEvents(unfolded: unfolded);
|
||||
timeline!.requestKeys(onlineKeyBackupOnly: false);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -656,7 +637,7 @@ class ChatController extends State<Chat> {
|
|||
}
|
||||
|
||||
void scrollToEventId(String eventId) async {
|
||||
var eventIndex = filteredEvents.indexWhere((e) => e.eventId == eventId);
|
||||
var eventIndex = timeline!.events.indexWhere((e) => e.eventId == eventId);
|
||||
if (eventIndex == -1) {
|
||||
// event id not found...maybe we can fetch it?
|
||||
// the try...finally is here to start and close the loading dialog reliably
|
||||
|
|
@ -693,7 +674,7 @@ class ChatController extends State<Chat> {
|
|||
rethrow;
|
||||
}
|
||||
eventIndex =
|
||||
filteredEvents.indexWhere((e) => e.eventId == eventId);
|
||||
timeline!.events.indexWhere((e) => e.eventId == eventId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import 'package:fluffychat/pages/chat/events/message.dart';
|
|||
import 'package:fluffychat/pages/chat/seen_by_row.dart';
|
||||
import 'package:fluffychat/pages/chat/typing_indicators.dart';
|
||||
import 'package:fluffychat/pages/user_bottom_sheet/user_bottom_sheet.dart';
|
||||
import 'package:fluffychat/utils/matrix_sdk_extensions.dart/filtered_timeline_extension.dart';
|
||||
import 'package:fluffychat/utils/platform_infos.dart';
|
||||
|
||||
class ChatEventList extends StatelessWidget {
|
||||
|
|
@ -26,9 +27,10 @@ class ChatEventList extends StatelessWidget {
|
|||
// create a map of eventId --> index to greatly improve performance of
|
||||
// ListView's findChildIndexCallback
|
||||
final thisEventsKeyMap = <String, int>{};
|
||||
for (var i = 0; i < controller.filteredEvents.length; i++) {
|
||||
thisEventsKeyMap[controller.filteredEvents[i].eventId] = i;
|
||||
for (var i = 0; i < controller.timeline!.events.length; i++) {
|
||||
thisEventsKeyMap[controller.timeline!.events[i].eventId] = i;
|
||||
}
|
||||
|
||||
return ListView.custom(
|
||||
padding: EdgeInsets.only(
|
||||
top: 16,
|
||||
|
|
@ -55,7 +57,7 @@ class ChatEventList extends StatelessWidget {
|
|||
}
|
||||
|
||||
// Request history button or progress indicator:
|
||||
if (i == controller.filteredEvents.length + 1) {
|
||||
if (i == controller.timeline!.events.length + 1) {
|
||||
if (controller.timeline!.isRequestingHistory) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator.adaptive(strokeWidth: 2),
|
||||
|
|
@ -76,37 +78,40 @@ class ChatEventList extends StatelessWidget {
|
|||
}
|
||||
|
||||
// The message at this index:
|
||||
final event = controller.timeline!.events[i - 1];
|
||||
|
||||
return AutoScrollTag(
|
||||
key: ValueKey(controller.filteredEvents[i - 1].eventId),
|
||||
key: ValueKey(event.eventId),
|
||||
index: i - 1,
|
||||
controller: controller.scrollController,
|
||||
child: Message(controller.filteredEvents[i - 1],
|
||||
onSwipe: (direction) => controller.replyAction(
|
||||
replyTo: controller.filteredEvents[i - 1]),
|
||||
onInfoTab: controller.showEventInfo,
|
||||
onAvatarTab: (Event event) => showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (c) => UserBottomSheet(
|
||||
user: event.senderFromMemoryOrFallback,
|
||||
outerContext: context,
|
||||
onMention: () => controller.sendController.text +=
|
||||
'${event.senderFromMemoryOrFallback.mention} ',
|
||||
),
|
||||
),
|
||||
unfold: controller.unfold,
|
||||
onSelect: controller.onSelectMessage,
|
||||
scrollToEventId: (String eventId) =>
|
||||
controller.scrollToEventId(eventId),
|
||||
longPressSelect: controller.selectedEvents.isEmpty,
|
||||
selected: controller.selectedEvents.any((e) =>
|
||||
e.eventId == controller.filteredEvents[i - 1].eventId),
|
||||
timeline: controller.timeline!,
|
||||
nextEvent: i < controller.filteredEvents.length
|
||||
? controller.filteredEvents[i]
|
||||
: null),
|
||||
child: event.isVisibleInGui
|
||||
? Message(event,
|
||||
onSwipe: (direction) =>
|
||||
controller.replyAction(replyTo: event),
|
||||
onInfoTab: controller.showEventInfo,
|
||||
onAvatarTab: (Event event) => showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (c) => UserBottomSheet(
|
||||
user: event.senderFromMemoryOrFallback,
|
||||
outerContext: context,
|
||||
onMention: () => controller.sendController.text +=
|
||||
'${event.senderFromMemoryOrFallback.mention} ',
|
||||
),
|
||||
),
|
||||
onSelect: controller.onSelectMessage,
|
||||
scrollToEventId: (String eventId) =>
|
||||
controller.scrollToEventId(eventId),
|
||||
longPressSelect: controller.selectedEvents.isEmpty,
|
||||
selected: controller.selectedEvents
|
||||
.any((e) => e.eventId == event.eventId),
|
||||
timeline: controller.timeline!,
|
||||
nextEvent: i < controller.timeline!.events.length
|
||||
? controller.timeline!.events[i]
|
||||
: null)
|
||||
: Container(),
|
||||
);
|
||||
},
|
||||
childCount: controller.filteredEvents.length + 2,
|
||||
childCount: controller.timeline!.events.length + 2,
|
||||
findChildIndexCallback: (key) =>
|
||||
controller.findChildIndexCallback(key, thisEventsKeyMap),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ class Message extends StatelessWidget {
|
|||
final void Function(Event)? onAvatarTab;
|
||||
final void Function(Event)? onInfoTab;
|
||||
final void Function(String)? scrollToEventId;
|
||||
final void Function(String) unfold;
|
||||
final void Function(SwipeDirection) onSwipe;
|
||||
final bool longPressSelect;
|
||||
final bool selected;
|
||||
|
|
@ -36,7 +35,6 @@ class Message extends StatelessWidget {
|
|||
this.onAvatarTab,
|
||||
this.scrollToEventId,
|
||||
required this.onSwipe,
|
||||
required this.unfold,
|
||||
this.selected = false,
|
||||
required this.timeline,
|
||||
Key? key})
|
||||
|
|
@ -57,7 +55,7 @@ class Message extends StatelessWidget {
|
|||
if (event.type.startsWith('m.call.')) {
|
||||
return Container();
|
||||
}
|
||||
return StateMessage(event, unfold: unfold);
|
||||
return StateMessage(event);
|
||||
}
|
||||
|
||||
if (event.type == EventTypes.Message &&
|
||||
|
|
|
|||
|
|
@ -8,9 +8,7 @@ import '../../../config/app_config.dart';
|
|||
|
||||
class StateMessage extends StatelessWidget {
|
||||
final Event event;
|
||||
final void Function(String) unfold;
|
||||
const StateMessage(this.event, {required this.unfold, Key? key})
|
||||
: super(key: key);
|
||||
const StateMessage(this.event, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
@ -25,48 +23,43 @@ class StateMessage extends StatelessWidget {
|
|||
vertical: 4.0,
|
||||
),
|
||||
child: Center(
|
||||
child: InkWell(
|
||||
onTap: counter != 0 ? () => unfold(event.eventId) : null,
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).brightness == Brightness.light
|
||||
? Colors.white
|
||||
: Colors.grey.shade900,
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
FutureBuilder<String>(
|
||||
future: event
|
||||
.calcLocalizedBody(MatrixLocals(L10n.of(context)!)),
|
||||
builder: (context, snapshot) {
|
||||
return Text(
|
||||
snapshot.data ??
|
||||
event.calcLocalizedBodyFallback(
|
||||
MatrixLocals(L10n.of(context)!)),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 14 * AppConfig.fontSizeFactor,
|
||||
color: Theme.of(context).textTheme.bodyText2!.color,
|
||||
decoration: event.redacted
|
||||
? TextDecoration.lineThrough
|
||||
: null,
|
||||
),
|
||||
);
|
||||
}),
|
||||
if (counter != 0)
|
||||
Text(
|
||||
L10n.of(context)!.moreEvents(counter),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14 * AppConfig.fontSizeFactor,
|
||||
),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).brightness == Brightness.light
|
||||
? Colors.white
|
||||
: Colors.grey.shade900,
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
FutureBuilder<String>(
|
||||
future:
|
||||
event.calcLocalizedBody(MatrixLocals(L10n.of(context)!)),
|
||||
builder: (context, snapshot) {
|
||||
return Text(
|
||||
snapshot.data ??
|
||||
event.calcLocalizedBodyFallback(
|
||||
MatrixLocals(L10n.of(context)!)),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 14 * AppConfig.fontSizeFactor,
|
||||
color: Theme.of(context).textTheme.bodyText2!.color,
|
||||
decoration:
|
||||
event.redacted ? TextDecoration.lineThrough : null,
|
||||
),
|
||||
);
|
||||
}),
|
||||
if (counter != 0)
|
||||
Text(
|
||||
L10n.of(context)!.moreEvents(counter),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14 * AppConfig.fontSizeFactor,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -12,11 +12,7 @@ class SeenByRow extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final seenByUsers = controller.room!.getSeenByUsers(
|
||||
controller.timeline!,
|
||||
controller.filteredEvents,
|
||||
controller.unfolded,
|
||||
);
|
||||
final seenByUsers = controller.room!.getSeenByUsers(controller.timeline!);
|
||||
const maxAvatars = 7;
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
|
|
@ -28,8 +24,8 @@ class SeenByRow extends StatelessWidget {
|
|||
duration: seenByUsers.isEmpty
|
||||
? const Duration(milliseconds: 0)
|
||||
: const Duration(milliseconds: 300),
|
||||
alignment: controller.filteredEvents.isNotEmpty &&
|
||||
controller.filteredEvents.first.senderId ==
|
||||
alignment: controller.timeline!.events.isNotEmpty &&
|
||||
controller.timeline!.events.first.senderId ==
|
||||
Matrix.of(context).client.userID
|
||||
? Alignment.topRight
|
||||
: Alignment.topLeft,
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ class TypingIndicators extends StatelessWidget {
|
|||
height: typingUsers.isEmpty ? 0 : Avatar.defaultSize + bottomPadding,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.bounceInOut,
|
||||
alignment: controller.filteredEvents.isNotEmpty &&
|
||||
controller.filteredEvents.first.senderId ==
|
||||
alignment: controller.timeline!.events.isNotEmpty &&
|
||||
controller.timeline!.events.first.senderId ==
|
||||
Matrix.of(context).client.userID
|
||||
? Alignment.topRight
|
||||
: Alignment.topLeft,
|
||||
|
|
|
|||
|
|
@ -45,6 +45,12 @@ class SettingsChatView extends StatelessWidget {
|
|||
storeKey: SettingKeys.hideUnknownEvents,
|
||||
defaultValue: AppConfig.hideUnknownEvents,
|
||||
),
|
||||
SettingsSwitchListTile.adaptive(
|
||||
title: L10n.of(context)!.hideUnimportantStateEvents,
|
||||
onChanged: (b) => AppConfig.hideUnimportantStateEvents = b,
|
||||
storeKey: SettingKeys.hideUnimportantStateEvents,
|
||||
defaultValue: AppConfig.hideUnimportantStateEvents,
|
||||
),
|
||||
if (PlatformInfos.isMobile)
|
||||
SettingsSwitchListTile.adaptive(
|
||||
title: L10n.of(context)!.autoplayImages,
|
||||
|
|
|
|||
|
|
@ -110,8 +110,6 @@ class StoryPageController extends State<StoryPage> {
|
|||
if (timeline == null || currentEvent == null) return [];
|
||||
return Matrix.of(context).client.getRoomById(roomId)?.getSeenByUsers(
|
||||
timeline,
|
||||
events,
|
||||
{},
|
||||
eventId: currentEvent.eventId,
|
||||
) ??
|
||||
[];
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue