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