mirror of
https://gitlab.com/famedly/fluffychat.git
synced 2024-12-25 06:52:35 +01:00
Merge branch 'krille/new-state-collapsing' into 'main'
change: Remove previous collapse solution See merge request famedly/fluffychat!390
This commit is contained in:
commit
73cff455c0
@ -16,9 +16,10 @@ import 'state_message.dart';
|
||||
class Message extends StatelessWidget {
|
||||
final Event event;
|
||||
final Event nextEvent;
|
||||
final Function(Event) onSelect;
|
||||
final Function(Event) onAvatarTab;
|
||||
final Function(String) scrollToEventId;
|
||||
final void Function(Event) onSelect;
|
||||
final void Function(Event) onAvatarTab;
|
||||
final void Function(String) scrollToEventId;
|
||||
final void Function(String) unfold;
|
||||
final bool longPressSelect;
|
||||
final bool selected;
|
||||
final Timeline timeline;
|
||||
@ -29,6 +30,7 @@ class Message extends StatelessWidget {
|
||||
this.onSelect,
|
||||
this.onAvatarTab,
|
||||
this.scrollToEventId,
|
||||
@required this.unfold,
|
||||
this.selected,
|
||||
this.timeline});
|
||||
|
||||
@ -38,15 +40,9 @@ class Message extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (event.type == EventTypes.RoomCreate) {
|
||||
return InkWell(
|
||||
onTap: () => onSelect(event),
|
||||
child: StateMessage(event),
|
||||
);
|
||||
}
|
||||
if (![EventTypes.Message, EventTypes.Sticker, EventTypes.Encrypted]
|
||||
.contains(event.type)) {
|
||||
return StateMessage(event);
|
||||
return StateMessage(event, unfold: unfold);
|
||||
}
|
||||
|
||||
var client = Matrix.of(context).client;
|
||||
|
@ -7,31 +7,55 @@ import '../../app_config.dart';
|
||||
|
||||
class StateMessage extends StatelessWidget {
|
||||
final Event event;
|
||||
const StateMessage(this.event);
|
||||
final void Function(String) unfold;
|
||||
const StateMessage(this.event, {@required this.unfold});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 8.0,
|
||||
right: 8.0,
|
||||
bottom: 8.0,
|
||||
),
|
||||
child: Center(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).secondaryHeaderColor,
|
||||
borderRadius: BorderRadius.circular(7),
|
||||
),
|
||||
child: Text(
|
||||
event.getLocalizedBody(MatrixLocals(L10n.of(context))),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.bodyText1.fontSize *
|
||||
AppConfig.fontSizeFactor,
|
||||
color: Theme.of(context).textTheme.bodyText2.color,
|
||||
decoration: event.redacted ? TextDecoration.lineThrough : null,
|
||||
if (event.unsigned['im.fluffychat.collapsed_state_event'] == true) {
|
||||
return Container();
|
||||
}
|
||||
final int counter =
|
||||
event.unsigned['im.fluffychat.collapsed_state_event_count'] ?? 0;
|
||||
return InkWell(
|
||||
onTap: counter != 0 ? () => unfold(event.eventId) : null,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 8.0,
|
||||
right: 8.0,
|
||||
bottom: 8.0,
|
||||
),
|
||||
child: Center(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).secondaryHeaderColor,
|
||||
borderRadius: BorderRadius.circular(7),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
event.getLocalizedBody(MatrixLocals(L10n.of(context))),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.bodyText1.fontSize *
|
||||
AppConfig.fontSizeFactor,
|
||||
color: Theme.of(context).textTheme.bodyText2.color,
|
||||
decoration:
|
||||
event.redacted ? TextDecoration.lineThrough : null,
|
||||
),
|
||||
),
|
||||
if (counter != 0)
|
||||
Text(
|
||||
counter == 1
|
||||
? L10n.of(context).oneMoreEvent
|
||||
: L10n.of(context).xMoreEvents(counter.toString()),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -513,6 +513,18 @@
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"oneMoreEvent": "1 more event",
|
||||
"@oneMoreEvent": {
|
||||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"xMoreEvents": "{count} more events",
|
||||
"@xMoreEvents": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"count": {}
|
||||
}
|
||||
},
|
||||
"createdTheChat": "{username} created the chat",
|
||||
"@createdTheChat": {
|
||||
"type": "text",
|
||||
|
@ -3,7 +3,7 @@ import 'package:famedlysdk/famedlysdk.dart';
|
||||
import '../app_config.dart';
|
||||
|
||||
extension FilteredTimelineExtension on Timeline {
|
||||
List<Event> getFilteredEvents({bool collapseRoomCreate = true}) {
|
||||
List<Event> getFilteredEvents({Set<String> unfolded = const {}}) {
|
||||
final filteredEvents = events
|
||||
.where((e) =>
|
||||
// always filter out edit and reaction relationships
|
||||
@ -19,23 +19,35 @@ extension FilteredTimelineExtension on Timeline {
|
||||
// if we enabled to hide all unknown events, don't show those
|
||||
(!AppConfig.hideUnknownEvents || e.isEventTypeKnown) &&
|
||||
// remove state events that we don't want to render
|
||||
(!{EventTypes.Message, EventTypes.Sticker, EventTypes.Encrypted}
|
||||
.contains(e.type) ||
|
||||
!AppConfig.hideAllStateEvents))
|
||||
(e.isState || !AppConfig.hideAllStateEvents))
|
||||
.toList();
|
||||
|
||||
// Hide state events from the room creater right after the room created event
|
||||
if (collapseRoomCreate &&
|
||||
filteredEvents[filteredEvents.length - 1].type ==
|
||||
EventTypes.RoomCreate) {
|
||||
while (filteredEvents.length >= 3 &&
|
||||
filteredEvents[filteredEvents.length - 2].senderId ==
|
||||
filteredEvents[filteredEvents.length - 1].senderId &&
|
||||
![EventTypes.Message, EventTypes.Sticker, EventTypes.Encrypted]
|
||||
.contains(filteredEvents[filteredEvents.length - 2].type)) {
|
||||
filteredEvents.removeAt(filteredEvents.length - 2);
|
||||
// Fold state events
|
||||
var counter = 0;
|
||||
for (var i = filteredEvents.length - 1; i >= 0; i--) {
|
||||
if (!filteredEvents[i].isState) continue;
|
||||
if (i > 0 &&
|
||||
filteredEvents[i - 1].isState &&
|
||||
!unfolded.contains(filteredEvents[i - 1].eventId)) {
|
||||
counter++;
|
||||
filteredEvents[i].unsigned['im.fluffychat.collapsed_state_event'] =
|
||||
true;
|
||||
} else {
|
||||
filteredEvents[i].unsigned['im.fluffychat.collapsed_state_event'] =
|
||||
false;
|
||||
filteredEvents[i]
|
||||
.unsigned['im.fluffychat.collapsed_state_event_count'] = counter;
|
||||
counter = 0;
|
||||
}
|
||||
}
|
||||
return filteredEvents;
|
||||
}
|
||||
}
|
||||
|
||||
extension IsStateExtension on Event {
|
||||
bool get isState => !{
|
||||
EventTypes.Message,
|
||||
EventTypes.Sticker,
|
||||
EventTypes.Encrypted
|
||||
}.contains(type);
|
||||
}
|
||||
|
@ -63,11 +63,14 @@ extension RoomStatusExtension on Room {
|
||||
}
|
||||
|
||||
String getLocalizedSeenByText(
|
||||
BuildContext context, Timeline timeline, List<Event> filteredEvents) {
|
||||
BuildContext context,
|
||||
Timeline timeline,
|
||||
List<Event> filteredEvents,
|
||||
Set<String> unfolded,
|
||||
) {
|
||||
var seenByText = '';
|
||||
if (timeline.events.isNotEmpty) {
|
||||
final filteredEvents =
|
||||
timeline.getFilteredEvents(collapseRoomCreate: false);
|
||||
final filteredEvents = timeline.getFilteredEvents(unfolded: unfolded);
|
||||
final lastReceipts = <User>{};
|
||||
// now we iterate the timeline events until we hit the first rendered event
|
||||
for (final event in timeline.events) {
|
||||
|
@ -72,7 +72,7 @@ class _ChatState extends State<Chat> {
|
||||
|
||||
List<Event> filteredEvents;
|
||||
|
||||
bool _collapseRoomCreate = true;
|
||||
final Set<String> _unfolded = {};
|
||||
|
||||
Event replyEvent;
|
||||
|
||||
@ -146,12 +146,22 @@ class _ChatState extends State<Chat> {
|
||||
if (!mounted) return;
|
||||
setState(
|
||||
() {
|
||||
filteredEvents =
|
||||
timeline.getFilteredEvents(collapseRoomCreate: _collapseRoomCreate);
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
Future<bool> getTimeline(BuildContext context) async {
|
||||
if (timeline == null) {
|
||||
timeline = await room.getTimeline(onUpdate: updateView);
|
||||
@ -712,9 +722,7 @@ class _ChatState extends State<Chat> {
|
||||
thisEventsKeyMap[filteredEvents[i].eventId] = i;
|
||||
}
|
||||
|
||||
return ListView.custom(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: max(
|
||||
final horizontalPadding = max(
|
||||
0,
|
||||
(MediaQuery.of(context).size.width -
|
||||
FluffyThemes.columnWidth *
|
||||
@ -724,7 +732,14 @@ class _ChatState extends State<Chat> {
|
||||
null
|
||||
? 4.5
|
||||
: 3.5)) /
|
||||
2),
|
||||
2)
|
||||
.toDouble();
|
||||
|
||||
return ListView.custom(
|
||||
padding: EdgeInsets.only(
|
||||
top: 16,
|
||||
left: horizontalPadding,
|
||||
right: horizontalPadding,
|
||||
),
|
||||
reverse: true,
|
||||
controller: _scrollController,
|
||||
@ -759,9 +774,11 @@ class _ChatState extends State<Chat> {
|
||||
builder: (_, __) {
|
||||
final seenByText =
|
||||
room.getLocalizedSeenByText(
|
||||
context,
|
||||
timeline,
|
||||
filteredEvents);
|
||||
context,
|
||||
timeline,
|
||||
filteredEvents,
|
||||
_unfolded,
|
||||
);
|
||||
return AnimatedContainer(
|
||||
height: seenByText.isEmpty ? 0 : 24,
|
||||
duration: seenByText.isEmpty
|
||||
@ -830,13 +847,8 @@ class _ChatState extends State<Chat> {
|
||||
'${event.senderId} ',
|
||||
),
|
||||
),
|
||||
unfold: _unfold,
|
||||
onSelect: (Event event) {
|
||||
if (event.type ==
|
||||
EventTypes.RoomCreate) {
|
||||
return setState(() =>
|
||||
_collapseRoomCreate =
|
||||
false);
|
||||
}
|
||||
if (!event.redacted) {
|
||||
if (selectedEvents
|
||||
.contains(event)) {
|
||||
|
Loading…
Reference in New Issue
Block a user