Add better error handling, send messages early and update when delivered
This commit is contained in:
parent
8c7adbc9d3
commit
55ecbc3590
9 changed files with 111 additions and 74 deletions
|
|
@ -9,6 +9,7 @@ import "package:flutter/foundation.dart";
|
||||||
import "package:nexus/controllers/account_data_controller.dart";
|
import "package:nexus/controllers/account_data_controller.dart";
|
||||||
import "package:nexus/controllers/client_state_controller.dart";
|
import "package:nexus/controllers/client_state_controller.dart";
|
||||||
import "package:nexus/controllers/init_complete_controller.dart";
|
import "package:nexus/controllers/init_complete_controller.dart";
|
||||||
|
import "package:nexus/controllers/new_events_controller.dart";
|
||||||
import "package:nexus/controllers/rooms_controller.dart";
|
import "package:nexus/controllers/rooms_controller.dart";
|
||||||
import "package:nexus/controllers/space_edges_controller.dart";
|
import "package:nexus/controllers/space_edges_controller.dart";
|
||||||
import "package:nexus/controllers/sync_status_controller.dart";
|
import "package:nexus/controllers/sync_status_controller.dart";
|
||||||
|
|
@ -74,6 +75,13 @@ class ClientController extends AsyncNotifier<int> {
|
||||||
case "init_complete":
|
case "init_complete":
|
||||||
ref.watch(InitCompleteController.provider.notifier).complete();
|
ref.watch(InitCompleteController.provider.notifier).complete();
|
||||||
break;
|
break;
|
||||||
|
case "send_complete":
|
||||||
|
final event = Event.fromJson(decodedMuksEvent["event"]);
|
||||||
|
|
||||||
|
ref
|
||||||
|
.watch(NewEventsController.provider(event.roomId).notifier)
|
||||||
|
.add(IList([event]));
|
||||||
|
break;
|
||||||
case "sync_complete":
|
case "sync_complete":
|
||||||
final syncData = SyncData.fromJson(decodedMuksEvent);
|
final syncData = SyncData.fromJson(decodedMuksEvent);
|
||||||
final roomProvider = RoomsController.provider;
|
final roomProvider = RoomsController.provider;
|
||||||
|
|
@ -150,8 +158,8 @@ class ClientController extends AsyncNotifier<int> {
|
||||||
Future<void> redactEvent(RedactEventRequest report) =>
|
Future<void> redactEvent(RedactEventRequest report) =>
|
||||||
_sendCommand("redact_event", report.toJson());
|
_sendCommand("redact_event", report.toJson());
|
||||||
|
|
||||||
Future<void> sendMessage(SendMessageRequest request) =>
|
Future<Event> sendMessage(SendMessageRequest request) async =>
|
||||||
_sendCommand("send_message", request.toJson());
|
Event.fromJson(await _sendCommand("send_message", request.toJson()));
|
||||||
|
|
||||||
Future<String?> verify(String recoveryKey) async {
|
Future<String?> verify(String recoveryKey) async {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ class MessageController extends AsyncNotifier<Message?> {
|
||||||
"big": event.localContent?.bigEmoji == true,
|
"big": event.localContent?.bigEmoji == true,
|
||||||
"eventType": type,
|
"eventType": type,
|
||||||
"pmp": event.content["com.beeper.per_message_profile"],
|
"pmp": event.content["com.beeper.per_message_profile"],
|
||||||
|
"error": event.sendError,
|
||||||
"editSource":
|
"editSource":
|
||||||
event.localContent?.editSource ??
|
event.localContent?.editSource ??
|
||||||
newContent?["body"] ??
|
newContent?["body"] ??
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ class RoomChatController extends AsyncNotifier<InMemoryChatController> {
|
||||||
final client = ref.watch(ClientController.provider.notifier);
|
final client = ref.watch(ClientController.provider.notifier);
|
||||||
var room = ref.read(RoomsController.provider)[roomId];
|
var room = ref.read(RoomsController.provider)[roomId];
|
||||||
if (room == null) return InMemoryChatController();
|
if (room == null) return InMemoryChatController();
|
||||||
|
|
||||||
final state = await client.getRoomState(
|
final state = await client.getRoomState(
|
||||||
GetRoomStateRequest(roomId: roomId),
|
GetRoomStateRequest(roomId: roomId),
|
||||||
);
|
);
|
||||||
|
|
@ -78,7 +77,6 @@ class RoomChatController extends AsyncNotifier<InMemoryChatController> {
|
||||||
|
|
||||||
ref.onDispose(
|
ref.onDispose(
|
||||||
ref.listen(NewEventsController.provider(roomId), (_, next) async {
|
ref.listen(NewEventsController.provider(roomId), (_, next) async {
|
||||||
final controller = await future;
|
|
||||||
for (final event in next) {
|
for (final event in next) {
|
||||||
if (event.type == "m.room.redaction") {
|
if (event.type == "m.room.redaction") {
|
||||||
final controller = await future;
|
final controller = await future;
|
||||||
|
|
@ -116,12 +114,8 @@ class RoomChatController extends AsyncNotifier<InMemoryChatController> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (message != null &&
|
if (message != null && ref.mounted) {
|
||||||
!controller.messages.any(
|
await insertMessage(message);
|
||||||
(oldMessage) => oldMessage.id == message.id,
|
|
||||||
) &&
|
|
||||||
ref.mounted) {
|
|
||||||
await controller.insertMessage(message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -152,19 +146,11 @@ class RoomChatController extends AsyncNotifier<InMemoryChatController> {
|
||||||
: controller.updateMessage(oldMessage, message);
|
: controller.updateMessage(oldMessage, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteMessage(Message message, {String? reason}) async {
|
Future<void> deleteMessage(Message message, {String? reason}) => ref
|
||||||
final controller = await future;
|
|
||||||
await controller.removeMessage(message);
|
|
||||||
await ref
|
|
||||||
.watch(ClientController.provider.notifier)
|
.watch(ClientController.provider.notifier)
|
||||||
.redactEvent(
|
.redactEvent(
|
||||||
RedactEventRequest(
|
RedactEventRequest(eventId: message.id, roomId: roomId, reason: reason),
|
||||||
eventId: message.id,
|
|
||||||
roomId: roomId,
|
|
||||||
reason: reason,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> loadOlder([InMemoryChatController? chatController]) async {
|
Future<void> loadOlder([InMemoryChatController? chatController]) async {
|
||||||
final response = await ref
|
final response = await ref
|
||||||
|
|
@ -242,7 +228,8 @@ class RoomChatController extends AsyncNotifier<InMemoryChatController> {
|
||||||
}
|
}
|
||||||
|
|
||||||
final client = ref.watch(ClientController.provider.notifier);
|
final client = ref.watch(ClientController.provider.notifier);
|
||||||
client.sendMessage(
|
final room = ref.read(RoomsController.provider)[roomId];
|
||||||
|
final event = await client.sendMessage(
|
||||||
SendMessageRequest(
|
SendMessageRequest(
|
||||||
roomId: roomId,
|
roomId: roomId,
|
||||||
mentions: Mentions(
|
mentions: Mentions(
|
||||||
|
|
@ -260,6 +247,15 @@ class RoomChatController extends AsyncNotifier<InMemoryChatController> {
|
||||||
: Relation(eventId: relation.id, relationType: relationType),
|
: Relation(eventId: relation.id, relationType: relationType),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
final message = room == null
|
||||||
|
? null
|
||||||
|
: await ref.watch(
|
||||||
|
MessageController.provider(
|
||||||
|
MessageConfig(room: room, event: event),
|
||||||
|
).future,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (message != null) insertMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<chat.User> resolveUser(String id) async {
|
Future<chat.User> resolveUser(String id) async {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ part "message_config.g.dart";
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
abstract class MessageConfig with _$MessageConfig {
|
abstract class MessageConfig with _$MessageConfig {
|
||||||
const MessageConfig._();
|
|
||||||
const factory MessageConfig({
|
const factory MessageConfig({
|
||||||
@Default(false) bool alwaysReturn,
|
@Default(false) bool alwaysReturn,
|
||||||
@Default(false) bool includeEdits,
|
@Default(false) bool includeEdits,
|
||||||
|
|
@ -14,15 +13,6 @@ abstract class MessageConfig with _$MessageConfig {
|
||||||
required Event event,
|
required Event event,
|
||||||
}) = _MessageConfig;
|
}) = _MessageConfig;
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) =>
|
|
||||||
other.runtimeType == runtimeType &&
|
|
||||||
other is MessageConfig &&
|
|
||||||
other.event.eventId == event.eventId;
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType, event.eventId);
|
|
||||||
|
|
||||||
factory MessageConfig.fromJson(Map<String, Object?> json) =>
|
factory MessageConfig.fromJson(Map<String, Object?> json) =>
|
||||||
_$MessageConfigFromJson(json);
|
_$MessageConfigFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import "package:nexus/widgets/chat_page/room_appbar.dart";
|
||||||
import "package:nexus/widgets/chat_page/wrappers/text_message_wrapper.dart";
|
import "package:nexus/widgets/chat_page/wrappers/text_message_wrapper.dart";
|
||||||
import "package:nexus/widgets/chat_page/reply_widget.dart";
|
import "package:nexus/widgets/chat_page/reply_widget.dart";
|
||||||
import "package:nexus/widgets/form_text_input.dart";
|
import "package:nexus/widgets/form_text_input.dart";
|
||||||
// import "package:dynamic_polls/dynamic_polls.dart";
|
import "package:nexus/main.dart";
|
||||||
|
|
||||||
class RoomChat extends HookConsumerWidget {
|
class RoomChat extends HookConsumerWidget {
|
||||||
final bool isDesktop;
|
final bool isDesktop;
|
||||||
|
|
@ -108,11 +108,13 @@ class RoomChat extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
notifier.deleteMessage(
|
Navigator.of(context).pop();
|
||||||
|
await notifier
|
||||||
|
.deleteMessage(
|
||||||
message,
|
message,
|
||||||
reason: deleteReasonController.text,
|
reason: deleteReasonController.text,
|
||||||
);
|
)
|
||||||
Navigator.of(context).pop();
|
.onError(showError);
|
||||||
},
|
},
|
||||||
child: Text("Delete"),
|
child: Text("Delete"),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import "package:flutter/material.dart";
|
||||||
import "package:flutter_chat_core/flutter_chat_core.dart";
|
import "package:flutter_chat_core/flutter_chat_core.dart";
|
||||||
import "package:nexus/widgets/chat_page/lazy_loading/message_avatar.dart";
|
import "package:nexus/widgets/chat_page/lazy_loading/message_avatar.dart";
|
||||||
import "package:nexus/widgets/chat_page/lazy_loading/message_displayname.dart";
|
import "package:nexus/widgets/chat_page/lazy_loading/message_displayname.dart";
|
||||||
|
import "package:timeago/timeago.dart";
|
||||||
|
|
||||||
class MessageWrapper extends StatelessWidget {
|
class MessageWrapper extends StatelessWidget {
|
||||||
final Message message;
|
final Message message;
|
||||||
|
|
@ -10,7 +11,10 @@ class MessageWrapper extends StatelessWidget {
|
||||||
const MessageWrapper(this.message, this.child, this.groupStatus, {super.key});
|
const MessageWrapper(this.message, this.child, this.groupStatus, {super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => ClipRRect(
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
final error = message.metadata?["error"];
|
||||||
|
return ClipRRect(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(12)),
|
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||||
child: AnimatedContainer(
|
child: AnimatedContainer(
|
||||||
padding: message.metadata?["flashing"] == true
|
padding: message.metadata?["flashing"] == true
|
||||||
|
|
@ -33,13 +37,37 @@ class MessageWrapper extends StatelessWidget {
|
||||||
spacing: 4,
|
spacing: 4,
|
||||||
children: [
|
children: [
|
||||||
if (groupStatus?.isFirst != false)
|
if (groupStatus?.isFirst != false)
|
||||||
MessageDisplayname(
|
Row(
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: MessageDisplayname(
|
||||||
message,
|
message,
|
||||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
style: theme.textTheme.titleMedium?.copyWith(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
if (message.deliveredAt != null &&
|
||||||
|
groupStatus?.isFirst == true)
|
||||||
|
Tooltip(
|
||||||
|
message: message.deliveredAt!.toString(),
|
||||||
|
child: Text(
|
||||||
|
format(message.deliveredAt!),
|
||||||
|
style: theme.textTheme.labelSmall?.copyWith(
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
child,
|
child,
|
||||||
|
if (error != null && error != "not sent")
|
||||||
|
Text(
|
||||||
|
error,
|
||||||
|
style: theme.textTheme.labelSmall?.copyWith(
|
||||||
|
color: theme.colorScheme.error,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -47,4 +75,5 @@ class MessageWrapper extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,9 @@ class TextMessageWrapper extends StatelessWidget {
|
||||||
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 12),
|
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 12),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isSentByMe
|
color: isSentByMe
|
||||||
? colorScheme.primaryContainer
|
? (message.id.startsWith("~")
|
||||||
|
? colorScheme.onPrimary
|
||||||
|
: colorScheme.primaryContainer)
|
||||||
: colorScheme.surfaceContainer,
|
: colorScheme.surfaceContainer,
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|
|
||||||
|
|
@ -1357,6 +1357,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.0+1"
|
version: "0.1.0+1"
|
||||||
|
timeago:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: timeago
|
||||||
|
sha256: b05159406a97e1cbb2b9ee4faa9fb096fe0e2dfcd8b08fcd2a00553450d3422e
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.7.1"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ dependencies:
|
||||||
hooks: ^1.0.0
|
hooks: ^1.0.0
|
||||||
code_assets: ^1.0.0
|
code_assets: ^1.0.0
|
||||||
ffigen: ^20.1.1
|
ffigen: ^20.1.1
|
||||||
|
timeago: ^3.7.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner: ^2.4.11
|
build_runner: ^2.4.11
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue