parent
721349ca21
commit
ccca02a0bc
6 changed files with 146 additions and 143 deletions
2
gomuks
2
gomuks
|
|
@ -1 +1 @@
|
||||||
Subproject commit b11eedca121a7081d3ee48165ba363e722d18d87
|
Subproject commit 480938f9e01e7eae2cf249657efae03df76599d6
|
||||||
|
|
@ -6,17 +6,9 @@ import "dart:math";
|
||||||
|
|
||||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||||
import "package:ffi/ffi.dart";
|
import "package:ffi/ffi.dart";
|
||||||
import "package:flutter/foundation.dart";
|
import "package:flutter/cupertino.dart";
|
||||||
import "package:intl/intl.dart";
|
import "package:intl/intl.dart";
|
||||||
import "package:nexus/controllers/account_data.dart";
|
|
||||||
import "package:nexus/controllers/client_state.dart";
|
|
||||||
import "package:nexus/controllers/init_complete.dart";
|
|
||||||
import "package:nexus/controllers/rooms.dart";
|
|
||||||
import "package:nexus/controllers/space_edges.dart";
|
|
||||||
import "package:nexus/controllers/sync_status.dart";
|
|
||||||
import "package:nexus/controllers/top_level_spaces.dart";
|
|
||||||
import "package:nexus/helpers/extensions/gomuks_buffer.dart";
|
import "package:nexus/helpers/extensions/gomuks_buffer.dart";
|
||||||
import "package:nexus/main.dart";
|
|
||||||
import "package:nexus/models/capabilities.dart";
|
import "package:nexus/models/capabilities.dart";
|
||||||
import "package:nexus/models/content/message.dart";
|
import "package:nexus/models/content/message.dart";
|
||||||
import "package:nexus/models/event.dart";
|
import "package:nexus/models/event.dart";
|
||||||
|
|
@ -46,9 +38,9 @@ import "package:nexus/models/requests/set_membership.dart";
|
||||||
import "package:nexus/models/requests/set_state.dart";
|
import "package:nexus/models/requests/set_state.dart";
|
||||||
import "package:nexus/models/requests/upload_media.dart";
|
import "package:nexus/models/requests/upload_media.dart";
|
||||||
import "package:nexus/models/room.dart";
|
import "package:nexus/models/room.dart";
|
||||||
|
import "package:nexus/models/room_metadata.dart";
|
||||||
import "package:nexus/models/room_summary.dart";
|
import "package:nexus/models/room_summary.dart";
|
||||||
import "package:nexus/models/spec_versions_response.dart";
|
import "package:nexus/models/spec_versions_response.dart";
|
||||||
import "package:nexus/models/sync_data.dart";
|
|
||||||
import "package:nexus/src/third_party/gomuks.g.dart";
|
import "package:nexus/src/third_party/gomuks.g.dart";
|
||||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||||
import "package:path_provider/path_provider.dart";
|
import "package:path_provider/path_provider.dart";
|
||||||
|
|
@ -56,6 +48,7 @@ import "package:path_provider/path_provider.dart";
|
||||||
class ClientController extends AsyncNotifier<int> {
|
class ClientController extends AsyncNotifier<int> {
|
||||||
@override
|
@override
|
||||||
Future<int> build() async {
|
Future<int> build() async {
|
||||||
|
debugPrint("Setting Gomuks env...");
|
||||||
if (Platform.isAndroid || Platform.isIOS) {
|
if (Platform.isAndroid || Platform.isIOS) {
|
||||||
final env = {
|
final env = {
|
||||||
"GOMUKS_ROOT": (await getApplicationSupportDirectory()).path,
|
"GOMUKS_ROOT": (await getApplicationSupportDirectory()).path,
|
||||||
|
|
@ -70,6 +63,8 @@ class ClientController extends AsyncNotifier<int> {
|
||||||
..free(valuePtr);
|
..free(valuePtr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debugPrint("Initializing Gomuks...");
|
||||||
final handle = await Isolate.run(() {
|
final handle = await Isolate.run(() {
|
||||||
final bufferPointer = GomuksConfig(
|
final bufferPointer = GomuksConfig(
|
||||||
matrix: .new(
|
matrix: .new(
|
||||||
|
|
@ -85,99 +80,9 @@ class ClientController extends AsyncNotifier<int> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
final callable =
|
|
||||||
NativeCallable<
|
|
||||||
Void Function(Pointer<Char>, Int64, GomuksOwnedBuffer)
|
|
||||||
>.listener((
|
|
||||||
Pointer<Char> command,
|
|
||||||
int requestId,
|
|
||||||
GomuksOwnedBuffer data,
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
final muksEventType = command.cast<Utf8>().toDartString();
|
|
||||||
debugPrint("Handling $muksEventType...");
|
|
||||||
final decodedMuksEvent = data.toJson();
|
|
||||||
|
|
||||||
switch (muksEventType) {
|
|
||||||
case "client_state":
|
|
||||||
ref
|
|
||||||
.watch(ClientStateController.provider.notifier)
|
|
||||||
.set(.fromJson(decodedMuksEvent));
|
|
||||||
break;
|
|
||||||
case "sync_status":
|
|
||||||
ref
|
|
||||||
.watch(SyncStatusController.provider.notifier)
|
|
||||||
.set(.fromJson(decodedMuksEvent));
|
|
||||||
break;
|
|
||||||
case "init_complete":
|
|
||||||
ref.watch(InitCompleteController.provider.notifier).complete();
|
|
||||||
break;
|
|
||||||
case "send_complete":
|
|
||||||
final event = Event.fromJson(decodedMuksEvent["event"]);
|
|
||||||
ref
|
|
||||||
.watch(RoomsController.provider.notifier)
|
|
||||||
.update(
|
|
||||||
.new({
|
|
||||||
event.roomId: .new(events: .new({event.rowId: event})),
|
|
||||||
}),
|
|
||||||
.new(),
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case "sync_complete":
|
|
||||||
final syncData = SyncData.fromJson(decodedMuksEvent);
|
|
||||||
final roomProvider = RoomsController.provider;
|
|
||||||
final accountDataProvider = AccountDataController.provider;
|
|
||||||
|
|
||||||
if (syncData.clearState) {
|
|
||||||
ref.invalidate(roomProvider);
|
|
||||||
ref.invalidate(accountDataProvider);
|
|
||||||
}
|
|
||||||
|
|
||||||
ref
|
|
||||||
.watch(roomProvider.notifier)
|
|
||||||
.update(syncData.rooms, syncData.leftRooms);
|
|
||||||
ref
|
|
||||||
.watch(accountDataProvider.notifier)
|
|
||||||
.update(syncData.accountData);
|
|
||||||
|
|
||||||
if (syncData.topLevelSpaces != null) {
|
|
||||||
ref
|
|
||||||
.watch(TopLevelSpacesController.provider.notifier)
|
|
||||||
.set(syncData.topLevelSpaces!);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (syncData.spaceEdges != null) {
|
|
||||||
ref
|
|
||||||
.watch(SpaceEdgesController.provider.notifier)
|
|
||||||
.set(syncData.spaceEdges!);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ref
|
|
||||||
// .watch(SyncStatusController.provider.notifier)
|
|
||||||
// .set(SyncStatus.fromJson(decodedMuksEvent));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
debugPrint("Unhandled event: $muksEventType");
|
|
||||||
}
|
|
||||||
debugPrint("Finished handling $muksEventType...");
|
|
||||||
} catch (error, stackTrace) {
|
|
||||||
if (kDebugMode) {
|
|
||||||
debugPrintStack(stackTrace: stackTrace, label: error.toString());
|
|
||||||
rethrow;
|
|
||||||
} else {
|
|
||||||
showError(error, stackTrace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ref.onDispose(() => GomuksDestroy(handle));
|
ref.onDispose(() => GomuksDestroy(handle));
|
||||||
ref.onDispose(callable.close);
|
|
||||||
|
|
||||||
final errorCode = GomuksStart(handle, callable.nativeFunction);
|
return handle;
|
||||||
|
|
||||||
if (errorCode == 0) return handle;
|
|
||||||
throw Exception("GomuksStart returned error code $errorCode");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<dynamic> callGomuksMethod(
|
Future<dynamic> callGomuksMethod(
|
||||||
|
|
@ -201,13 +106,16 @@ class ClientController extends AsyncNotifier<int> {
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Event?> handlePush(Map<String, dynamic> data) async {
|
Future<(Event, RoomMetadata)> handlePush(Map<String, dynamic> data) async {
|
||||||
final response = await callGomuksMethod(
|
final response = await callGomuksMethod(
|
||||||
data,
|
data,
|
||||||
(handle, data) async => GomuksHandlePush(handle, data),
|
(handle, data) async => GomuksHandlePush(handle, data),
|
||||||
);
|
);
|
||||||
|
|
||||||
return response == null ? null : .fromJson(response);
|
return (
|
||||||
|
Event.fromJson(response["event"]),
|
||||||
|
RoomMetadata.fromJson(response["room"]),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
dynamic _sendCommand(
|
dynamic _sendCommand(
|
||||||
|
|
@ -270,11 +178,6 @@ class ClientController extends AsyncNotifier<int> {
|
||||||
await _sendCommand("leave_room", {"room_id": room.metadata!.id});
|
await _sendCommand("leave_room", {"room_id": room.metadata!.id});
|
||||||
}
|
}
|
||||||
|
|
||||||
// (await _sendCommand("get_event_context", {
|
|
||||||
// "room_id": request.roomId,
|
|
||||||
// "event_id": r"$OqZT4NuTj0J1-771IOEEWRI4XdumRNu6ighlvO3K3gc",
|
|
||||||
// }));
|
|
||||||
|
|
||||||
Future<IList<Event>> getRoomState(GetRoomStateRequest request) async {
|
Future<IList<Event>> getRoomState(GetRoomStateRequest request) async {
|
||||||
Future<List?> getState(GetRoomStateRequest request) async =>
|
Future<List?> getState(GetRoomStateRequest request) async =>
|
||||||
(await _sendCommand("get_room_state", request.toJson())) as List?;
|
(await _sendCommand("get_room_state", request.toJson())) as List?;
|
||||||
|
|
|
||||||
124
lib/controllers/gomuks_listener.dart
Normal file
124
lib/controllers/gomuks_listener.dart
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
import "dart:async";
|
||||||
|
import "dart:ffi";
|
||||||
|
|
||||||
|
import "package:flutter/foundation.dart";
|
||||||
|
import "package:nexus/controllers/account_data.dart";
|
||||||
|
import "package:nexus/controllers/client.dart";
|
||||||
|
import "package:nexus/controllers/client_state.dart";
|
||||||
|
import "package:nexus/controllers/init_complete.dart";
|
||||||
|
import "package:nexus/controllers/rooms.dart";
|
||||||
|
import "package:nexus/controllers/space_edges.dart";
|
||||||
|
import "package:nexus/controllers/sync_status.dart";
|
||||||
|
import "package:nexus/controllers/top_level_spaces.dart";
|
||||||
|
import "package:nexus/helpers/extensions/gomuks_buffer.dart";
|
||||||
|
import "package:nexus/main.dart";
|
||||||
|
import "package:ffi/ffi.dart";
|
||||||
|
import "package:nexus/models/event.dart";
|
||||||
|
import "package:nexus/models/sync_data.dart";
|
||||||
|
import "package:nexus/src/third_party/gomuks.g.dart";
|
||||||
|
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||||
|
|
||||||
|
class GomuksListenerController extends AsyncNotifier<void> {
|
||||||
|
@override
|
||||||
|
Future<void> build() async {
|
||||||
|
debugPrint("Starting gomuks...");
|
||||||
|
final handle = await ref.watch(ClientController.provider.future);
|
||||||
|
|
||||||
|
final callable =
|
||||||
|
NativeCallable<
|
||||||
|
Void Function(Pointer<Char>, Int64, GomuksOwnedBuffer)
|
||||||
|
>.listener((
|
||||||
|
Pointer<Char> command,
|
||||||
|
int requestId,
|
||||||
|
GomuksOwnedBuffer data,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
final muksEventType = command.cast<Utf8>().toDartString();
|
||||||
|
debugPrint("Handling $muksEventType...");
|
||||||
|
final decodedMuksEvent = data.toJson();
|
||||||
|
|
||||||
|
switch (muksEventType) {
|
||||||
|
case "client_state":
|
||||||
|
ref
|
||||||
|
.watch(ClientStateController.provider.notifier)
|
||||||
|
.set(.fromJson(decodedMuksEvent));
|
||||||
|
break;
|
||||||
|
case "sync_status":
|
||||||
|
ref
|
||||||
|
.watch(SyncStatusController.provider.notifier)
|
||||||
|
.set(.fromJson(decodedMuksEvent));
|
||||||
|
break;
|
||||||
|
case "init_complete":
|
||||||
|
ref.watch(InitCompleteController.provider.notifier).complete();
|
||||||
|
break;
|
||||||
|
case "send_complete":
|
||||||
|
final event = Event.fromJson(decodedMuksEvent["event"]);
|
||||||
|
ref
|
||||||
|
.watch(RoomsController.provider.notifier)
|
||||||
|
.update(
|
||||||
|
.new({
|
||||||
|
event.roomId: .new(events: .new({event.rowId: event})),
|
||||||
|
}),
|
||||||
|
.new(),
|
||||||
|
);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "sync_complete":
|
||||||
|
final syncData = SyncData.fromJson(decodedMuksEvent);
|
||||||
|
final roomProvider = RoomsController.provider;
|
||||||
|
final accountDataProvider = AccountDataController.provider;
|
||||||
|
|
||||||
|
if (syncData.clearState) {
|
||||||
|
ref.invalidate(roomProvider);
|
||||||
|
ref.invalidate(accountDataProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
ref
|
||||||
|
.watch(roomProvider.notifier)
|
||||||
|
.update(syncData.rooms, syncData.leftRooms);
|
||||||
|
ref
|
||||||
|
.watch(accountDataProvider.notifier)
|
||||||
|
.update(syncData.accountData);
|
||||||
|
|
||||||
|
if (syncData.topLevelSpaces != null) {
|
||||||
|
ref
|
||||||
|
.watch(TopLevelSpacesController.provider.notifier)
|
||||||
|
.set(syncData.topLevelSpaces!);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (syncData.spaceEdges != null) {
|
||||||
|
ref
|
||||||
|
.watch(SpaceEdgesController.provider.notifier)
|
||||||
|
.set(syncData.spaceEdges!);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ref
|
||||||
|
// .watch(SyncStatusController.provider.notifier)
|
||||||
|
// .set(SyncStatus.fromJson(decodedMuksEvent));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
debugPrint("Unhandled event: $muksEventType");
|
||||||
|
}
|
||||||
|
debugPrint("Finished handling $muksEventType...");
|
||||||
|
} catch (error, stackTrace) {
|
||||||
|
if (kDebugMode) {
|
||||||
|
debugPrintStack(stackTrace: stackTrace, label: error.toString());
|
||||||
|
rethrow;
|
||||||
|
} else {
|
||||||
|
showError(error, stackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ref.onDispose(callable.close);
|
||||||
|
|
||||||
|
final errorCode = GomuksStart(handle, callable.nativeFunction);
|
||||||
|
if (errorCode != 0) {
|
||||||
|
throw Exception("GomuksStart returned error code $errorCode");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static final provider = AsyncNotifierProvider<GomuksListenerController, void>(
|
||||||
|
GomuksListenerController.new,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -8,7 +8,6 @@ import "package:intl/intl.dart";
|
||||||
import "package:nexus/controllers/key.dart";
|
import "package:nexus/controllers/key.dart";
|
||||||
import "package:nexus/controllers/notification.dart";
|
import "package:nexus/controllers/notification.dart";
|
||||||
import "package:nexus/controllers/push_key.dart";
|
import "package:nexus/controllers/push_key.dart";
|
||||||
import "package:nexus/controllers/rooms.dart";
|
|
||||||
import "package:nexus/main.dart";
|
import "package:nexus/main.dart";
|
||||||
import "package:nexus/controllers/client.dart";
|
import "package:nexus/controllers/client.dart";
|
||||||
import "package:nexus/controllers/client_state.dart";
|
import "package:nexus/controllers/client_state.dart";
|
||||||
|
|
@ -63,12 +62,11 @@ class UnifiedPushController extends AsyncNotifier<bool> {
|
||||||
"Failed to decrypt notification. Try toggling off and on UnifiedPush in settings.",
|
"Failed to decrypt notification. Try toggling off and on UnifiedPush in settings.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
final event = await ref
|
final (event, roomMetadata) = await ref
|
||||||
.read(ClientController.provider.notifier)
|
.read(ClientController.provider.notifier)
|
||||||
.handlePush(json.decode(String.fromCharCodes(message.content)));
|
.handlePush(json.decode(String.fromCharCodes(message.content)));
|
||||||
|
|
||||||
if (event == null ||
|
if (event.unreadType?.shouldNotify() != true ||
|
||||||
event.unreadType?.shouldNotify() != true ||
|
|
||||||
(!isInBackground &&
|
(!isInBackground &&
|
||||||
await windowManager.isFocused().onError((_, _) => true) &&
|
await windowManager.isFocused().onError((_, _) => true) &&
|
||||||
await ref.read(
|
await ref.read(
|
||||||
|
|
@ -79,41 +77,19 @@ class UnifiedPushController extends AsyncNotifier<bool> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final provider = RoomsController.provider.select(
|
final icon = roomMetadata.avatar == null
|
||||||
(rooms) => rooms[event.roomId],
|
|
||||||
);
|
|
||||||
|
|
||||||
if (ref.read(provider)?.metadata == null) {
|
|
||||||
final completer = Completer<void>();
|
|
||||||
|
|
||||||
final subscription = ref.listen(provider, (previous, next) {
|
|
||||||
if (next?.metadata != null && !completer.isCompleted) {
|
|
||||||
completer.complete();
|
|
||||||
}
|
|
||||||
}, fireImmediately: true);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await completer.future.timeout(.new(seconds: 10));
|
|
||||||
} on TimeoutException {
|
|
||||||
// metadata didn't show up in time
|
|
||||||
} finally {
|
|
||||||
subscription.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final room = ref.read(provider);
|
|
||||||
final avatar = room?.metadata?.avatar;
|
|
||||||
final icon = avatar == null
|
|
||||||
? null
|
? null
|
||||||
: await ref
|
: await ref
|
||||||
.read(ClientController.provider.notifier)
|
.read(ClientController.provider.notifier)
|
||||||
.downloadMedia(.new(mxc: avatar, isAvatar: true));
|
.downloadMedia(
|
||||||
|
.new(mxc: roomMetadata.avatar!, isAvatar: true),
|
||||||
|
);
|
||||||
|
|
||||||
await ref
|
await ref
|
||||||
.read(NotificationController.provider.notifier)
|
.read(NotificationController.provider.notifier)
|
||||||
.send(
|
.send(
|
||||||
id: event.eventId.hashCode & 0x7fffffff,
|
id: event.eventId.hashCode & 0x7fffffff,
|
||||||
title: room?.metadata?.name ?? "New Event",
|
title: roomMetadata.name ?? "New Event",
|
||||||
icon: icon,
|
icon: icon,
|
||||||
payload: event.eventId,
|
payload: event.eventId,
|
||||||
body: switch (event.content) {
|
body: switch (event.content) {
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ import "package:dynamic_color/dynamic_color.dart";
|
||||||
import "package:flutter/foundation.dart";
|
import "package:flutter/foundation.dart";
|
||||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||||
import "package:media_kit/media_kit.dart";
|
import "package:media_kit/media_kit.dart";
|
||||||
import "package:nexus/controllers/client.dart";
|
|
||||||
import "package:nexus/controllers/client_state.dart";
|
import "package:nexus/controllers/client_state.dart";
|
||||||
|
import "package:nexus/controllers/gomuks_listener.dart";
|
||||||
import "package:nexus/controllers/key.dart";
|
import "package:nexus/controllers/key.dart";
|
||||||
import "package:nexus/controllers/member_list_opened.dart";
|
import "package:nexus/controllers/member_list_opened.dart";
|
||||||
import "package:nexus/controllers/multi_provider.dart";
|
import "package:nexus/controllers/multi_provider.dart";
|
||||||
|
|
@ -152,7 +152,7 @@ class const App({super.key}) extends StatelessWidget {
|
||||||
builder: (_, ref, _) => switch (ref.watch(
|
builder: (_, ref, _) => switch (ref.watch(
|
||||||
MultiProviderController.provider(
|
MultiProviderController.provider(
|
||||||
.new([
|
.new([
|
||||||
ClientController.provider,
|
GomuksListenerController.provider,
|
||||||
NotificationController.provider,
|
NotificationController.provider,
|
||||||
UnifiedPushController.provider,
|
UnifiedPushController.provider,
|
||||||
MemberListOpenedController.provider,
|
MemberListOpenedController.provider,
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ buildGoModule (
|
||||||
|
|
||||||
src = "${src}/gomuks";
|
src = "${src}/gomuks";
|
||||||
|
|
||||||
vendorHash = "sha256-e3+bUe7IURUUaLy905a3xlRgKbkHpVfMuGlZCNSxOyU=";
|
vendorHash = "sha256-rLLDbNrYf5HqG3Y6zihRi1v+GqGeNAtZUL+8QHdCz7w=";
|
||||||
|
|
||||||
buildPhase = ''
|
buildPhase = ''
|
||||||
runHook preBuild
|
runHook preBuild
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue