Skip to content

Commit

Permalink
add support for pinned events
Browse files Browse the repository at this point in the history
  • Loading branch information
Airyzz committed Jan 11, 2025
1 parent 62651ed commit 56e40ce
Show file tree
Hide file tree
Showing 12 changed files with 229 additions and 7 deletions.
2 changes: 2 additions & 0 deletions commet/lib/client/components/component_registry.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:commet/client/matrix/components/emoticon/matrix_space_emoticon_c
import 'package:commet/client/matrix/components/event_search/matrix_event_search_component.dart';
import 'package:commet/client/matrix/components/gif/matrix_gif_component.dart';
import 'package:commet/client/matrix/components/invitation/matrix_invitation_component.dart';
import 'package:commet/client/matrix/components/pinned_messages/matrix_pinned_messages_component.dart';
import 'package:commet/client/matrix/components/push_notifications/matrix_push_notification_component.dart';
import 'package:commet/client/matrix/components/read_receipts/matrix_read_receipt_component.dart';
import 'package:commet/client/matrix/components/threads/matrix_threads_component.dart';
Expand Down Expand Up @@ -42,6 +43,7 @@ class ComponentRegistry {
MatrixGifComponent(client, room),
MatrixReadReceiptComponent(client, room),
MatrixTypingIndicatorsComponent(client, room),
MatrixPinnedMessagesComponent(client, room),
];
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import 'package:commet/client/client.dart';
import 'package:commet/client/components/room_component.dart';

abstract class PinnedMessagesComponent<R extends Client, T extends Room>
implements RoomComponent<R, T> {
List<String> getPinnedMessages();

Future<void> pinMessage(String eventId);

bool get canPinMessages;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import 'package:commet/client/components/pinned_messages/pinned_messages_component.dart';
import 'package:commet/client/matrix/matrix_client.dart';
import 'package:commet/client/matrix/matrix_room.dart';
import 'package:matrix/matrix_api_lite/model/event_types.dart';

class MatrixPinnedMessagesComponent
extends PinnedMessagesComponent<MatrixClient, MatrixRoom> {
@override
MatrixClient client;

@override
MatrixRoom room;

MatrixPinnedMessagesComponent(this.client, this.room);

@override
bool get canPinMessages =>
room.matrixRoom.canChangeStateEvent(EventTypes.RoomPinnedEvents);

@override
List<String> getPinnedMessages() {
return room.matrixRoom.pinnedEventIds.reversed.toList();
}

@override
Future<void> pinMessage(String eventId) async {
var pins = room.matrixRoom.pinnedEventIds.toList();
pins.add(eventId);
await room.matrixRoom.setPinnedEvents(pins);
}
}
3 changes: 3 additions & 0 deletions commet/lib/client/matrix/matrix_room.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import 'package:commet/client/matrix/timeline_events/matrix_timeline_event_emote
import 'package:commet/client/matrix/timeline_events/matrix_timeline_event_encrypted.dart';
import 'package:commet/client/matrix/timeline_events/matrix_timeline_event_membership.dart';
import 'package:commet/client/matrix/timeline_events/matrix_timeline_event_message.dart';
import 'package:commet/client/matrix/timeline_events/matrix_timeline_event_pinned_messages.dart';
import 'package:commet/client/matrix/timeline_events/matrix_timeline_event_redaction.dart';
import 'package:commet/client/matrix/timeline_events/matrix_timeline_event_sticker.dart';
import 'package:commet/client/matrix/timeline_events/matrix_timeline_event_unknown.dart';
Expand Down Expand Up @@ -439,6 +440,8 @@ class MatrixRoom extends Room {
MatrixTimelineEventMembership(event, client: c),
matrix.EventTypes.Redaction =>
MatrixTimelineEventRedaction(event, client: c),
matrix.EventTypes.RoomPinnedEvents =>
MatrixTimelineEventPinnedMessages(event, client: c),
_ => null
};

Expand Down
2 changes: 2 additions & 0 deletions commet/lib/client/matrix/matrix_timeline.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ class MatrixTimeline extends Timeline {
onRemove: onEventRemoved,
eventContextId: contextEventId);

_matrixRoom.postLoad();

// This could maybe make load times realllly slow if we have a ton of stuff in the cache?
// Might be better to only convert as many as we would need to display immediately and then convert the rest on demand
convertAllTimelineEvents();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import 'package:commet/client/matrix/timeline_events/matrix_timeline_event.dart';
import 'package:commet/client/timeline.dart';
import 'package:commet/client/timeline_events/timeline_event_generic.dart';
import 'package:flutter/material.dart';

class MatrixTimelineEventPinnedMessages extends MatrixTimelineEvent
implements TimelineEventGeneric {
MatrixTimelineEventPinnedMessages(super.event, {required super.client});

bool isNewEventPinned() {
if (event.prevContent?.containsKey('pinned') == true) {
var prevList = event.prevContent!['pinned'] as List<dynamic>;
var currList = event.content['pinned'] as List<dynamic>;

return currList.length > prevList.length;
} else {
return true;
}
}

@override
IconData get icon => Icons.push_pin;

@override
String get plainTextBody => getBody();

@override
bool get showSenderAvatar => false;

@override
String getBody({Timeline? timeline}) {
if (isNewEventPinned()) {
return "Message pinned!";
} else {
return "Message unpinned!";
}
}
}
11 changes: 11 additions & 0 deletions commet/lib/ui/molecules/timeline_events/timeline_event_menu.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:commet/client/components/direct_messages/direct_message_component.dart';
import 'package:commet/client/components/emoticon/emoticon_component.dart';
import 'package:commet/client/components/pinned_messages/pinned_messages_component.dart';
import 'package:commet/client/components/push_notification/notification_content.dart';
import 'package:commet/client/components/push_notification/notification_manager.dart';
import 'package:commet/client/timeline.dart';
Expand Down Expand Up @@ -63,6 +64,9 @@ class TimelineEventMenu {

bool canCopy = event is TimelineEventMessage;

var pins = timeline.room.getComponent<PinnedMessagesComponent>();
bool canPin = pins?.canPinMessages == true;

primaryActions = [
if (canEditEvent)
TimelineEventMenuEntry(
Expand Down Expand Up @@ -134,6 +138,13 @@ class TimelineEventMenu {
];

secondaryActions = [
if (canPin)
TimelineEventMenuEntry(
name: "Pin Message",
icon: Icons.push_pin,
action: (context) {
pins!.pinMessage(event.eventId);
}),
if (canCopy)
TimelineEventMenuEntry(
name: CommonStrings.promptCopy,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import 'package:commet/client/components/pinned_messages/pinned_messages_component.dart';
import 'package:commet/client/room.dart';
import 'package:commet/client/timeline_events/timeline_event.dart';
import 'package:commet/ui/molecules/timeline_events/timeline_event_view_single.dart';
import 'package:flutter/material.dart';
import 'package:tiamat/tiamat.dart' as tiamat;

class RoomPinnedMessagesWidget extends StatefulWidget {
const RoomPinnedMessagesWidget(
{required this.room, this.onEventClicked, super.key});
final Room room;
final void Function(String eventId)? onEventClicked;

@override
State<RoomPinnedMessagesWidget> createState() =>
_RoomPinnedMessagesWidgetState();
}

class _RoomPinnedMessagesWidgetState extends State<RoomPinnedMessagesWidget> {
List<TimelineEvent>? events;

@override
void initState() {
super.initState();

loadPinnedMessages();
}

@override
Widget build(BuildContext context) {
if (events == null) {
return const tiamat.Tile(
child: Center(child: CircularProgressIndicator()));
}

if (events!.isEmpty) {
return tiamat.Tile(
child: Center(
child: tiamat.Text.label("No messages have been pinned!")));
}

return tiamat.Tile.low(
child: Column(
children: [
Flexible(
child: ListView.builder(
itemCount: events!.length,
itemBuilder: (context, index) {
var data = events![index];
return Padding(
padding: const EdgeInsets.fromLTRB(4, 2, 4, 2),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Material(
borderRadius: BorderRadius.circular(8),
child: InkWell(
onTap: () =>
widget.onEventClicked?.call(data.eventId),
child: Padding(
padding: const EdgeInsets.all(4.0),
child: TimelineEventViewSingle(
room: widget.room, event: data),
),
)),
),
);
},
),
),
],
),
);
}

void loadPinnedMessages() async {
final comp = widget.room.getComponent<PinnedMessagesComponent>()!;
var eventIds = comp.getPinnedMessages();

var allEvents = await Future.wait<TimelineEvent?>(
eventIds.map((id) => widget.room.getEvent(id)));

setState(() {
events = List<TimelineEvent>.from(allEvents.where((e) => e != null));
});
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:commet/client/client.dart';
import 'package:commet/client/components/event_search/event_search_component.dart';
import 'package:commet/client/components/invitation/invitation_component.dart';
import 'package:commet/client/components/pinned_messages/pinned_messages_component.dart';
import 'package:commet/ui/navigation/adaptive_dialog.dart';
import 'package:commet/ui/organisms/invitation_view/send_invitation.dart';
import 'package:commet/utils/event_bus.dart';
Expand All @@ -16,6 +17,9 @@ class RoomQuickAccessMenu {

final invitation = room.client.getComponent<InvitationComponent>();

final bool supportsPinnedMessages =
room.getComponent<PinnedMessagesComponent>() != null;

actions = [
if (invitation != null)
RoomQuickAccessMenuEntry(
Expand All @@ -24,6 +28,11 @@ class RoomQuickAccessMenu {
builder: (context) => SendInvitationWidget(room, invitation),
title: "Invite"),
icon: Icons.person_add),
if (supportsPinnedMessages)
RoomQuickAccessMenuEntry(
name: "Pinned Messages",
action: (context) => EventBus.openPinnedMessages.add(null),
icon: Icons.push_pin),
if (canSearch)
RoomQuickAccessMenuEntry(
name: "Search",
Expand Down
38 changes: 32 additions & 6 deletions commet/lib/ui/organisms/room_side_panel/room_side_panel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,15 @@ import 'package:commet/ui/atoms/scaled_safe_area.dart';
import 'package:commet/ui/organisms/chat/chat.dart';
import 'package:commet/ui/organisms/room_event_search/room_event_search_widget.dart';
import 'package:commet/ui/organisms/room_members_list/room_members_list.dart';
import 'package:commet/ui/organisms/room_pinned_messages/room_pinned_messages_widget.dart';
import 'package:commet/ui/organisms/room_quick_access_menu/room_quick_access_menu_mobile.dart';
import 'package:commet/ui/pages/main/main_page.dart';
import 'package:commet/utils/event_bus.dart';
import 'package:flutter/material.dart';
import 'package:tiamat/atoms/tile.dart';
import 'package:tiamat/tiamat.dart' as tiamat;

enum SidePanelState {
defaultView,
thread,
search,
}
enum SidePanelState { defaultView, thread, search, pinnedMessages }

class RoomSidePanel extends StatefulWidget {
const RoomSidePanel({required this.state, this.builder, super.key});
Expand All @@ -43,6 +40,7 @@ class _RoomSidePanelState extends State<RoomSidePanel> {
EventBus.openThread.stream.listen(onOpenThreadSignal),
EventBus.closeThread.stream.listen(onCloseThreadSignal),
EventBus.startSearch.stream.listen(onStartSearch),
EventBus.openPinnedMessages.stream.listen(onShowPinnedMessages),
];
super.initState();
}
Expand Down Expand Up @@ -104,6 +102,8 @@ class _RoomSidePanelState extends State<RoomSidePanel> {
return buildThread();
case SidePanelState.search:
return buildSearch();
case SidePanelState.pinnedMessages:
return buildPinnedMessages();
}
}

Expand Down Expand Up @@ -197,7 +197,33 @@ class _RoomSidePanelState extends State<RoomSidePanel> {

void onStartSearch(void event) {
setState(() {
state = SidePanelState.search;
if (state == SidePanelState.search) {
state = SidePanelState.defaultView;
} else {
state = SidePanelState.search;
}
});
}

void onShowPinnedMessages(void event) {
setState(() {
if (state == SidePanelState.pinnedMessages) {
state = SidePanelState.defaultView;
} else {
state = SidePanelState.pinnedMessages;
}
});
}

Widget buildPinnedMessages() {
return SizedBox(
width: Layout.desktop ? 300 : null,
child: RoomPinnedMessagesWidget(
room: widget.state.currentRoom!,
onEventClicked: (eventId) {
EventBus.jumpToEvent.add(eventId);
EventBus.focusTimeline.add(null);
},
));
}
}
3 changes: 3 additions & 0 deletions commet/lib/utils/event_bus.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ class EventBus {

static StreamController<void> startSearch = StreamController.broadcast();

static StreamController<void> openPinnedMessages =
StreamController.broadcast();

static StreamController<String> jumpToEvent = StreamController.broadcast();

static StreamController<void> focusTimeline = StreamController.broadcast();
Expand Down
2 changes: 1 addition & 1 deletion commet/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -925,7 +925,7 @@ packages:
description:
path: "."
ref: HEAD
resolved-ref: "59b86f5b420df8a45671d6c1db08242a9fe206f3"
resolved-ref: e2ba813dfb8541e9309316373dcf74993a34559f
url: "https://github.com/commetchat/matrix-dart-sdk-drift-db.git"
source: git
version: "0.0.1"
Expand Down

0 comments on commit 56e40ce

Please sign in to comment.