From a012a2e762d5e74815e0616338122f67c74d8e82 Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Fri, 23 Jan 2026 13:47:21 +0000 Subject: [PATCH] wip go 2 --- build.yaml | 6 ++ lib/controllers/client_controller.dart | 124 +++++++--------------- lib/helpers/extensions/gomuks_buffer.dart | 41 +++++++ lib/main.dart | 77 +++++++------- lib/models/client_state.dart | 15 +++ lib/models/full_room.dart | 13 --- lib/models/homeserver.dart | 12 --- lib/models/image_data.dart | 11 -- lib/models/relation_type.dart | 1 - lib/models/session_backup.dart | 17 --- lib/models/space.dart | 20 ---- lib/models/sync_status.dart | 18 ++++ 12 files changed, 156 insertions(+), 199 deletions(-) create mode 100644 build.yaml create mode 100644 lib/helpers/extensions/gomuks_buffer.dart create mode 100644 lib/models/client_state.dart delete mode 100644 lib/models/full_room.dart delete mode 100644 lib/models/homeserver.dart delete mode 100644 lib/models/image_data.dart delete mode 100644 lib/models/relation_type.dart delete mode 100644 lib/models/session_backup.dart delete mode 100644 lib/models/space.dart create mode 100644 lib/models/sync_status.dart diff --git a/build.yaml b/build.yaml new file mode 100644 index 0000000..5d6aeda --- /dev/null +++ b/build.yaml @@ -0,0 +1,6 @@ +targets: + $default: + builders: + json_serializable: + options: + field_rename: snake diff --git a/lib/controllers/client_controller.dart b/lib/controllers/client_controller.dart index 13bc0eb..3d3127b 100644 --- a/lib/controllers/client_controller.dart +++ b/lib/controllers/client_controller.dart @@ -1,37 +1,36 @@ import "dart:convert"; import "dart:ffi"; -import "dart:io"; import "package:ffi/ffi.dart"; import "package:flutter/foundation.dart"; -import "package:matrix/encryption.dart"; -import "package:nexus/controllers/database_controller.dart"; +import "package:nexus/helpers/extensions/gomuks_buffer.dart"; +import "package:nexus/models/client_state.dart"; +import "package:nexus/models/sync_status.dart"; import "package:nexus/src/third_party/gomuks.g.dart"; -import "package:matrix/matrix.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/secure_storage_controller.dart"; -import "package:nexus/models/session_backup.dart"; void gomuksCallback(Pointer command, int requestId, GomuksBuffer data) { - // Convert the C string to Dart - final cmdStr = command.cast().toDartString(); - print("Received event: $cmdStr (requestId=$requestId)"); + try { + final muksEventType = command.cast().toDartString(); + final Map decodedMuksEvent = data.toJson(); - // Optionally inspect 'data' if you need + switch (muksEventType) { + case "client_state": + final clientState = ClientState.fromJson(decodedMuksEvent); + debugPrint("Received event: $clientState"); + case "sync_status": + final syncStatus = SyncStatus.fromJson(decodedMuksEvent); + debugPrint("Received event: $syncStatus"); + } + } catch (error, stackTrace) { + debugPrintStack(stackTrace: stackTrace, label: error.toString()); + } } -class ClientController extends AsyncNotifier { +class ClientController extends Notifier { @override - bool updateShouldNotify( - AsyncValue previous, - AsyncValue next, - ) => - previous.hasValue != next.hasValue || - previous.value?.accessToken != next.value?.accessToken; - static const sessionBackupKey = "sessionBackup"; - - @override - Future build() async { + int build() { final handle = GomuksInit(); + ref.onDispose(() => GomuksDestroy(handle)); GomuksStart( handle, @@ -40,80 +39,29 @@ class ClientController extends AsyncNotifier { ), ); - final client = Client( - "nexus", - logLevel: kReleaseMode ? Level.warning : Level.verbose, - importantStateEvents: {"im.ponies.room_emotes"}, - supportedLoginTypes: {AuthenticationTypes.password}, - verificationMethods: {KeyVerificationMethod.emoji}, - database: await MatrixSdkDatabase.init( - "nexus", - database: await ref.watch(DatabaseController.provider.future), - ), - ); - - final backupJson = await ref - .watch(SecureStorageController.provider.notifier) - .get(sessionBackupKey); - - if (backupJson != null) { - final backup = SessionBackup.fromJson(json.decode(backupJson)); - - await client.init( - waitForFirstSync: false, - newToken: backup.accessToken, - newHomeserver: backup.homeserver, - newUserID: backup.userID, - newDeviceID: backup.deviceID, - newDeviceName: backup.deviceName, - ); - } - - return client; + return handle; } - Future setHomeserver(Uri homeserverUrl) async { - final client = await future; + (int requestId, Map response) sendCommand( + String command, + Map data, + ) { + final responsePtr = calloc(); try { - await client.checkHomeserver(homeserverUrl); - return true; - } catch (_) { - return false; - } - } - - Future login(String username, String password) async { - final client = await future; - try { - final deviceName = "Nexus Client login on ${Platform.localHostname}"; - final details = await MatrixApi(homeserver: client.homeserver).login( - LoginType.mLoginPassword, - initialDeviceDisplayName: deviceName, - identifier: AuthenticationUserIdentifier(user: username), - password: password, + final requestId = GomuksSubmitCommand( + state, + command.toNativeUtf8().cast(), + data.toGomuksBuffer(), + responsePtr, ); - await ref - .watch(SecureStorageController.provider.notifier) - .set( - sessionBackupKey, - json.encode( - SessionBackup( - accessToken: details.accessToken, - homeserver: client.homeserver!, - userID: details.userId, - deviceID: details.deviceId, - deviceName: deviceName, - ).toJson(), - ), - ); - ref.invalidateSelf(asReload: true); - return true; - } catch (_) { - return false; + + return (requestId, responsePtr.ref.toJson()); + } finally { + calloc.free(responsePtr); } } - static final provider = AsyncNotifierProvider( + static final provider = NotifierProvider( ClientController.new, ); } diff --git a/lib/helpers/extensions/gomuks_buffer.dart b/lib/helpers/extensions/gomuks_buffer.dart new file mode 100644 index 0000000..4cd42db --- /dev/null +++ b/lib/helpers/extensions/gomuks_buffer.dart @@ -0,0 +1,41 @@ +import "dart:convert"; +import "dart:ffi"; +import "dart:typed_data"; + +import "package:ffi/ffi.dart"; +import "package:nexus/src/third_party/gomuks.g.dart"; + +extension GomuksBufferX on GomuksBuffer { + /// Safely converts the Go buffer into a Dart `Uint8List` + Uint8List toBytes() { + if (base == nullptr || length <= 0) return Uint8List(0); + return base.asTypedList(length); + } + + /// Decodes the bytes as JSON + Map toJson() { + final bytes = toBytes(); + if (bytes.isEmpty) return {}; + return jsonDecode(utf8.decode(bytes)); + } +} + +extension JsonToGomuksBuffer on Map { + GomuksBuffer toGomuksBuffer() { + final jsonString = json.encode(this); + final bytes = utf8.encode(jsonString); + + final dataPtr = calloc(bytes.length); + dataPtr.asTypedList(bytes.length).setAll(0, bytes); + + final bufPtr = calloc(); + bufPtr.ref.base = dataPtr; + bufPtr.ref.length = bytes.length; + + final bufByValue = bufPtr.ref; + + calloc.free(bufPtr); + + return bufByValue; + } +} diff --git a/lib/main.dart b/lib/main.dart index 9e829a7..4fbc6ef 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,17 +1,11 @@ import "dart:io"; - import "package:flutter/foundation.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:nexus/controllers/client_controller.dart"; import "package:nexus/controllers/shared_prefs_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; import "package:nexus/helpers/extensions/scheme_to_theme.dart"; -import "package:nexus/pages/chat_page.dart"; -import "package:nexus/pages/login_page.dart"; -import "package:nexus/pages/settings_page.dart"; -import "package:nexus/widgets/appbar.dart"; import "package:nexus/widgets/error_dialog.dart"; -import "package:nexus/widgets/loading.dart"; import "package:window_manager/window_manager.dart"; import "package:flutter/material.dart"; import "package:dynamic_system_colors/dynamic_system_colors.dart"; @@ -103,37 +97,46 @@ class App extends ConsumerWidget { builder: (context) => ref .watch(SharedPrefsController.provider) .betterWhen( - data: (_) => ref - .watch(ClientController.provider) - .betterWhen( - data: (client) => - client.accessToken == null ? LoginPage() : ChatPage(), - loading: () => Scaffold( - body: Center( - child: Column( - mainAxisSize: MainAxisSize.min, - spacing: 16, - children: [ - Text( - "Syncing...", - style: Theme.of(context).textTheme.headlineMedium, - ), - Loading(), - ], - ), - ), - appBar: Appbar( - actions: [ - IconButton( - onPressed: () => Navigator.of(context).push( - MaterialPageRoute(builder: (_) => SettingsPage()), - ), - icon: Icon(Icons.settings), - ), - ], - ), - ), - ), + data: (_) { + final response = ref + .watch(ClientController.provider.notifier) + .sendCommand("login", { + "homeserver_url": "federated.nexus", + "username": "quadradical", + "password": "Quadmarad1!", + }); + debugPrint("$response"); + return Placeholder(); + }, + // .betterWhen( + // data: (client) => + // client.accessToken == null ? LoginPage() : ChatPage(), + // loading: () => Scaffold( + // body: Center( + // child: Column( + // mainAxisSize: MainAxisSize.min, + // spacing: 16, + // children: [ + // Text( + // "Syncing...", + // style: Theme.of(context).textTheme.headlineMedium, + // ), + // Loading(), + // ], + // ), + // ), + // appBar: Appbar( + // actions: [ + // IconButton( + // onPressed: () => Navigator.of(context).push( + // MaterialPageRoute(builder: (_) => SettingsPage()), + // ), + // icon: Icon(Icons.settings), + // ), + // ], + // ), + // ), + // ), ), ), ), diff --git a/lib/models/client_state.dart b/lib/models/client_state.dart new file mode 100644 index 0000000..2c1bf05 --- /dev/null +++ b/lib/models/client_state.dart @@ -0,0 +1,15 @@ +import "package:freezed_annotation/freezed_annotation.dart"; +part "client_state.freezed.dart"; +part "client_state.g.dart"; + +@freezed +abstract class ClientState with _$ClientState { + const factory ClientState({ + required bool isInitialized, + required bool isLoggedIn, + required bool isVerified, + }) = _ClientState; + + factory ClientState.fromJson(Map json) => + _$ClientStateFromJson(json); +} diff --git a/lib/models/full_room.dart b/lib/models/full_room.dart deleted file mode 100644 index ee61da6..0000000 --- a/lib/models/full_room.dart +++ /dev/null @@ -1,13 +0,0 @@ -import "package:freezed_annotation/freezed_annotation.dart"; -import "package:matrix/matrix.dart"; -part "full_room.freezed.dart"; - -@freezed -abstract class FullRoom with _$FullRoom { - const FullRoom._(); - const factory FullRoom({ - required Room roomData, - required String title, - required Uri? avatar, - }) = _FullRoom; -} diff --git a/lib/models/homeserver.dart b/lib/models/homeserver.dart deleted file mode 100644 index 903e23d..0000000 --- a/lib/models/homeserver.dart +++ /dev/null @@ -1,12 +0,0 @@ -import "package:freezed_annotation/freezed_annotation.dart"; -part "homeserver.freezed.dart"; - -@freezed -abstract class Homeserver with _$Homeserver { - const factory Homeserver({ - required String name, - required String description, - required Uri url, - required String iconUrl, - }) = _Homeserver; -} diff --git a/lib/models/image_data.dart b/lib/models/image_data.dart deleted file mode 100644 index e5bc57e..0000000 --- a/lib/models/image_data.dart +++ /dev/null @@ -1,11 +0,0 @@ -import "package:freezed_annotation/freezed_annotation.dart"; -part "image_data.freezed.dart"; - -@freezed -abstract class ImageData with _$ImageData { - const factory ImageData({ - required String uri, - required int? height, - required int? width, - }) = _ImageData; -} diff --git a/lib/models/relation_type.dart b/lib/models/relation_type.dart deleted file mode 100644 index 80c5223..0000000 --- a/lib/models/relation_type.dart +++ /dev/null @@ -1 +0,0 @@ -enum RelationType { edit, reply } diff --git a/lib/models/session_backup.dart b/lib/models/session_backup.dart deleted file mode 100644 index 0245c7e..0000000 --- a/lib/models/session_backup.dart +++ /dev/null @@ -1,17 +0,0 @@ -import "package:freezed_annotation/freezed_annotation.dart"; -part "session_backup.freezed.dart"; -part "session_backup.g.dart"; - -@freezed -abstract class SessionBackup with _$SessionBackup { - const factory SessionBackup({ - required String accessToken, - required Uri homeserver, - required String userID, - required String deviceID, - required String deviceName, - }) = _SessionBackup; - - factory SessionBackup.fromJson(Map json) => - _$SessionBackupFromJson(json); -} diff --git a/lib/models/space.dart b/lib/models/space.dart deleted file mode 100644 index d64dcc9..0000000 --- a/lib/models/space.dart +++ /dev/null @@ -1,20 +0,0 @@ -import "package:fast_immutable_collections/fast_immutable_collections.dart"; -import "package:flutter/widgets.dart"; -import "package:freezed_annotation/freezed_annotation.dart"; -import "package:matrix/matrix.dart"; -import "package:nexus/models/full_room.dart"; -part "space.freezed.dart"; - -@freezed -abstract class Space with _$Space { - const Space._(); - const factory Space({ - required String title, - required String id, - required IList children, - required Client client, - Room? roomData, - Uri? avatar, - IconData? icon, - }) = _Space; -} diff --git a/lib/models/sync_status.dart b/lib/models/sync_status.dart new file mode 100644 index 0000000..4ea08e5 --- /dev/null +++ b/lib/models/sync_status.dart @@ -0,0 +1,18 @@ +import "package:freezed_annotation/freezed_annotation.dart"; +part "sync_status.freezed.dart"; +part "sync_status.g.dart"; + +@freezed +abstract class SyncStatus with _$SyncStatus { + const factory SyncStatus({ + required Type type, + required int errorCount, + required int lastSync, + }) = _SyncStatus; + + factory SyncStatus.fromJson(Map json) => + _$SyncStatusFromJson(json); +} + +@JsonEnum(fieldRename: FieldRename.snake) +enum Type { ok, waiting, erroring, permanentlyFailed }