diff --git a/README.md b/README.md index 2562704..c6b5b0d 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ A simple and user-friendly Matrix client made with Flutter and the Matrix Dart S ## Progress -- [ ] Move from the Dart SDK to the Gomuks SDK with bindings: WIP +- [x] Move from the Dart SDK to the Gomuks SDK with Dart bindings: https://git.federated.nexus/Henry-Hiles/nexus/pulls/2 - [ ] Platform Support - [x] Linux - [x] Windows @@ -33,14 +33,17 @@ A simple and user-friendly Matrix client made with Flutter and the Matrix Dart S - [ ] Searching - [ ] Creating (Rooms, Spaces, and DMs) - [x] Joining - - [x] Using alias/id/link + - [x] Using a text/uri/link + - [x] Plain text + - [x] `matrix:` Uri + - [ ] Matrix.to link: I just need to fix my regex, I should do this next. - [ ] From space - [ ] Exploring - [x] Leaving - [x] Subspaces - [x] Messages - [x] Encryption - - [ ] Restoring crypto identity from passphrase/key or verification + - [x] Restoring crypto identity from passphrase/key or verification - [x] Sending - [x] Plain text - [x] HTML/Markdown @@ -105,7 +108,7 @@ A simple and user-friendly Matrix client made with Flutter and the Matrix Dart S - [ ] About - [x] Log Out -## Development +## Build Instructions First, clone and open the repo: @@ -119,13 +122,13 @@ cd nexus #### Linux - With Nix: Either use direnv, or `nix flake develop` -- Without Nix: Install Flutter, Rust, the libsecret dev package for your distro (must be in `PKG_CONFIG_PATH`), and sqlite (must be in `LD_LIBRARY_PATH`). +- Without Nix: Install Flutter, Go, Olm, Git, Clang, and GLibc. #### Windows / MacOS -I don't really know. You will need Flutter and Rust, and otherwise I guess just keep installing stuff until there aren't any errors. +I don't really know. You will need Flutter, Git, Olm, Go, and Visual Studio tools, and otherwise I guess just keep installing stuff until there aren't any errors. I will look into this sometimeTM. -### +### Set up Flutter Get dependencies: @@ -133,6 +136,18 @@ Get dependencies: flutter pub get ``` +Get dependencies: + +```sh +flutter pub get +``` + +Clone Gomuks and generate bindings: + +```sh +scripts/generate.sh +``` + Build generated files, and watch for new changes: ```sh @@ -147,4 +162,4 @@ flutter run ## Community -Come chat in the [Federated Nexus Community](https://matrix.to/#/#space:federated.nexus) for questions or help with developing or using Nexus Client. +Join the [Nexus Client Matrix Room](https://matrix.to/#/#nexus:federated.nexus) for questions or help with developing or using Nexus Client. diff --git a/flake.nix b/flake.nix index c4c5cc3..860d010 100644 --- a/flake.nix +++ b/flake.nix @@ -45,17 +45,11 @@ olm git clang - (flutter.override { extraPkgConfigPackages = [ pkgs.libsecret ]; }) - - (pkgs.writeShellScriptBin "rustup" (builtins.readFile ./nix/fake-rustup.sh)) + flutter ]; env = { - LD_LIBRARY_PATH = "${ - pkgs.lib.makeLibraryPath ([ - pkgs.sqlite - ]) - }:./build/native_assets/linux"; + LD_LIBRARY_PATH = "./build/native_assets/linux"; CPATH = lib.makeSearchPath "include" [ pkgs.glibc.dev ]; }; }; diff --git a/lib/controllers/client_controller.dart b/lib/controllers/client_controller.dart index fd7600f..abfdb72 100644 --- a/lib/controllers/client_controller.dart +++ b/lib/controllers/client_controller.dart @@ -16,6 +16,7 @@ import "package:nexus/models/paginate.dart"; import "package:nexus/models/requests/get_event_request.dart"; import "package:nexus/models/requests/get_related_events_request.dart"; import "package:nexus/models/requests/get_room_state_request.dart"; +import "package:nexus/models/requests/join_room_request.dart"; import "package:nexus/models/requests/login_request.dart"; import "package:nexus/models/profile.dart"; import "package:nexus/models/requests/paginate_request.dart"; @@ -138,6 +139,11 @@ class ClientController extends AsyncNotifier { } } + Future joinRoom(JoinRoomRequest request) async { + final response = await _sendCommand("join_room", request.toJson()); + return response["room_id"]; + } + Future leaveRoom(Room room) async { if (room.metadata == null) return; await _sendCommand("leave_room", {"room_id": room.metadata!.id}); diff --git a/lib/controllers/secure_storage_controller.dart b/lib/controllers/secure_storage_controller.dart deleted file mode 100644 index 4a5781b..0000000 --- a/lib/controllers/secure_storage_controller.dart +++ /dev/null @@ -1,19 +0,0 @@ -import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:flutter_secure_storage/flutter_secure_storage.dart"; - -class SecureStorageController extends Notifier { - @override - FlutterSecureStorage build() => FlutterSecureStorage(); - - Future get(String key) => state.read(key: key); - - Future set(String key, String value) => - state.write(key: key, value: value); - - Future clear() => state.deleteAll(); - - static final provider = - NotifierProvider( - SecureStorageController.new, - ); -} diff --git a/lib/helpers/extensions/join_room_with_snackbars.dart b/lib/helpers/extensions/join_room_with_snackbars.dart index df89740..05b045d 100644 --- a/lib/helpers/extensions/join_room_with_snackbars.dart +++ b/lib/helpers/extensions/join_room_with_snackbars.dart @@ -1,9 +1,12 @@ import "package:collection/collection.dart"; +import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter/material.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:nexus/controllers/client_controller.dart"; import "package:nexus/controllers/key_controller.dart"; import "package:nexus/controllers/spaces_controller.dart"; +import "package:nexus/helpers/extensions/link_to_mention.dart"; +import "package:nexus/models/requests/join_room_request.dart"; extension JoinRoomWithSnackbars on ClientController { Future joinRoomWithSnackBars( @@ -11,77 +14,77 @@ extension JoinRoomWithSnackbars on ClientController { String roomAlias, WidgetRef ref, ) async { - // final parsed = roomAlias.parseIdentifierIntoParts(); - // final alias = parsed?.primaryIdentifier ?? roomAlias; + final roomIdOrAlias = roomAlias.mention ?? roomAlias; - // final scaffoldMessenger = ScaffoldMessenger.of(context); + final scaffoldMessenger = ScaffoldMessenger.of(context); - // final snackbar = scaffoldMessenger.showSnackBar( - // SnackBar( - // content: Text("Joining room $alias."), - // duration: Duration(days: 999), - // ), - // ); + final snackbar = scaffoldMessenger.showSnackBar( + SnackBar( + content: Text("Joining room $roomIdOrAlias."), + duration: Duration(days: 999), + ), + ); - // try { - // final id = await joinRoom(alias, via: parsed?.via.toList()); + try { + final id = await joinRoom( + JoinRoomRequest( + roomIdOrAlias: roomIdOrAlias, + via: IList(Uri.tryParse(roomAlias)?.queryParametersAll["via"] ?? []), + ), + ); - // snackbar.close(); + snackbar.close(); - // scaffoldMessenger.showSnackBar( - // SnackBar( - // content: Text("Room $alias successfully joined."), - // action: SnackBarAction( - // label: "Open", - // onPressed: () async { - // final spaces = await ref.refresh( - // SpacesController.provider.future, - // ); - // final space = spaces.firstWhereOrNull((space) => space.id == id); + scaffoldMessenger.showSnackBar( + SnackBar( + content: Text("Room $roomIdOrAlias successfully joined."), + action: SnackBarAction( + label: "Open", + onPressed: () async { + final spaces = ref.watch(SpacesController.provider); + final space = spaces.firstWhereOrNull((space) => space.id == id); - // await ref - // .watch( - // KeyController.provider(KeyController.spaceKey).notifier, - // ) - // .set( - // space?.id ?? - // spaces - // .firstWhere( - // (space) => - // space.children.firstWhereOrNull( - // (child) => child.roomData.id == id, - // ) != - // null, - // ) - // .id, - // ); + await ref + .watch( + KeyController.provider(KeyController.spaceKey).notifier, + ) + .set( + space?.id ?? + spaces + .firstWhere( + (space) => space.children.any( + (child) => child.metadata?.id == id, + ), + ) + .id, + ); - // if (space == null) { - // await ref - // .watch( - // KeyController.provider(KeyController.roomKey).notifier, - // ) - // .set(id); - // } - // }, - // ), - // ), - // ); - // } catch (error) { - // snackbar.close(); - // if (context.mounted) { - // scaffoldMessenger.showSnackBar( - // SnackBar( - // backgroundColor: Theme.of(context).colorScheme.errorContainer, - // content: Text( - // error.toString(), - // style: TextStyle( - // color: Theme.of(context).colorScheme.onErrorContainer, - // ), - // ), - // ), - // ); - // } - // } + if (space == null) { + await ref + .watch( + KeyController.provider(KeyController.roomKey).notifier, + ) + .set(id); + } + }, + ), + ), + ); + } catch (error) { + snackbar.close(); + if (context.mounted) { + scaffoldMessenger.showSnackBar( + SnackBar( + backgroundColor: Theme.of(context).colorScheme.errorContainer, + content: Text( + error.toString(), + style: TextStyle( + color: Theme.of(context).colorScheme.onErrorContainer, + ), + ), + ), + ); + } + } } } diff --git a/lib/helpers/extensions/link_to_mention.dart b/lib/helpers/extensions/link_to_mention.dart index 33d2bd2..8095132 100644 --- a/lib/helpers/extensions/link_to_mention.dart +++ b/lib/helpers/extensions/link_to_mention.dart @@ -29,7 +29,11 @@ extension LinkToMention on String { if (uri.pathSegments.isNotEmpty) { final identifier = uri.pathSegments.last; if (identifier.isNotEmpty) { - return Uri.decodeComponent(identifier); + return "${switch (uri.pathSegments.firstOrNull) { + "r" || "roomid" => "#", + "u" => "@", + _ => "", + }}${Uri.decodeComponent(identifier)}"; } } } catch (_) {} diff --git a/lib/models/requests/join_room_request.dart b/lib/models/requests/join_room_request.dart new file mode 100644 index 0000000..d6b411e --- /dev/null +++ b/lib/models/requests/join_room_request.dart @@ -0,0 +1,15 @@ +import "package:fast_immutable_collections/fast_immutable_collections.dart"; +import "package:freezed_annotation/freezed_annotation.dart"; +part "join_room_request.freezed.dart"; +part "join_room_request.g.dart"; + +@freezed +abstract class JoinRoomRequest with _$JoinRoomRequest { + const factory JoinRoomRequest({ + required String roomIdOrAlias, + required IList via, + }) = _JoinRoomRequest; + + factory JoinRoomRequest.fromJson(Map json) => + _$JoinRoomRequestFromJson(json); +} diff --git a/lib/pages/settings_page.dart b/lib/pages/settings_page.dart index b348aac..505904c 100644 --- a/lib/pages/settings_page.dart +++ b/lib/pages/settings_page.dart @@ -1,18 +1,11 @@ import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/secure_storage_controller.dart"; class SettingsPage extends ConsumerWidget { const SettingsPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - return Scaffold( - appBar: AppBar(title: Text("Settings")), - body: ElevatedButton( - onPressed: ref.watch(SecureStorageController.provider.notifier).clear, - child: Text("Log out"), - ), - ); + return Placeholder(); } } diff --git a/lib/widgets/chat_page/room_menu.dart b/lib/widgets/chat_page/room_menu.dart index f14b8ca..2687bc8 100644 --- a/lib/widgets/chat_page/room_menu.dart +++ b/lib/widgets/chat_page/room_menu.dart @@ -1,11 +1,8 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter/material.dart"; -import "package:flutter/services.dart"; -import "package:flutter_hooks/flutter_hooks.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:nexus/controllers/client_controller.dart"; import "package:nexus/models/room.dart"; -import "package:nexus/widgets/form_text_input.dart"; class RoomMenu extends ConsumerWidget { final Room room; diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index dffacff..f70fb6e 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -8,7 +8,6 @@ #include #include -#include #include #include #include @@ -21,9 +20,6 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); file_selector_plugin_register_with_registrar(file_selector_linux_registrar); - g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); - flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); g_autoptr(FlPluginRegistrar) screen_retriever_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverLinuxPlugin"); screen_retriever_linux_plugin_register_with_registrar(screen_retriever_linux_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 8b658f4..78dcf40 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -5,7 +5,6 @@ list(APPEND FLUTTER_PLUGIN_LIST dynamic_system_colors file_selector_linux - flutter_secure_storage_linux screen_retriever_linux url_launcher_linux window_manager diff --git a/nix/fake-rustup.sh b/nix/fake-rustup.sh deleted file mode 100644 index 7884c05..0000000 --- a/nix/fake-rustup.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bash -# Fake rustup for nix-managed Rust toolchains - -case "$1" in - run) - if [[ "$2" == "stable" ]]; then - shift 2 - if [[ $# -eq 0 ]]; then - echo "fake rustup: no command given" >&2 - exit 1 - fi - exec "$@" - exit 0 - fi - ;; - - toolchain) - if [[ "$2" == "list" ]]; then - echo "stable (default)" - exit 0 - fi - ;; - - target) - if [[ "$2" == "list" && "$3" == "--toolchain" && "$4" == "stable" && "$5" == "--installed" ]]; then - echo "x86_64-unknown-linux-gnu" - exit 0 - fi - ;; -esac - -echo "fake rustup: the command:" >&2 -echo " rustup $*" >&2 -echo "…is not mocked yet" >&2 -exit 1 \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 222e779..aec6fdc 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -533,54 +533,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.0" - flutter_secure_storage: - dependency: "direct main" - description: - name: flutter_secure_storage - sha256: da922f2aab2d733db7e011a6bcc4a825b844892d4edd6df83ff156b09a9b2e40 - url: "https://pub.dev" - source: hosted - version: "10.0.0" - flutter_secure_storage_darwin: - dependency: transitive - description: - name: flutter_secure_storage_darwin - sha256: "8878c25136a79def1668c75985e8e193d9d7d095453ec28730da0315dc69aee3" - url: "https://pub.dev" - source: hosted - version: "0.2.0" - flutter_secure_storage_linux: - dependency: transitive - description: - name: flutter_secure_storage_linux - sha256: "2b5c76dce569ab752d55a1cee6a2242bcc11fdba927078fb88c503f150767cda" - url: "https://pub.dev" - source: hosted - version: "3.0.0" - flutter_secure_storage_platform_interface: - dependency: transitive - description: - name: flutter_secure_storage_platform_interface - sha256: "8ceea1223bee3c6ac1a22dabd8feefc550e4729b3675de4b5900f55afcb435d6" - url: "https://pub.dev" - source: hosted - version: "2.0.1" - flutter_secure_storage_web: - dependency: transitive - description: - name: flutter_secure_storage_web - sha256: "6a1137df62b84b54261dca582c1c09ea72f4f9a4b2fcee21b025964132d5d0c3" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - flutter_secure_storage_windows: - dependency: transitive - description: - name: flutter_secure_storage_windows - sha256: "3b7c8e068875dfd46719ff57c90d8c459c87f2302ed6b00ff006b3c9fcad1613" - url: "https://pub.dev" - source: hosted - version: "4.1.0" flutter_svg: dependency: "direct main" description: @@ -928,14 +880,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.11.1" - mention_tag_text_field: - dependency: "direct main" - description: - name: mention_tag_text_field - sha256: ba7b9d8003e0f340a65c6dcdb7770f4340f653ae1612a9e31e11d12f7f1dd80f - url: "https://pub.dev" - source: hosted - version: "0.0.9" meta: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 7893653..3c50656 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -59,9 +59,7 @@ dependencies: flutter_svg: ^2.2.2 json_annotation: ^4.9.0 shared_preferences: ^2.5.3 - mention_tag_text_field: ^0.0.9 fluttertagger: ^2.3.1 - flutter_secure_storage: ^10.0.0 dynamic_polls: ^0.0.6 flutter_hooks: ^0.21.3+1 cross_cache: ^1.1.0 diff --git a/src/gomuks/libgomuks.h b/src/gomuks/libgomuks.h new file mode 100644 index 0000000..962d281 --- /dev/null +++ b/src/gomuks/libgomuks.h @@ -0,0 +1,105 @@ +/* Code generated by cmd/cgo; DO NOT EDIT. */ + +/* package go.mau.fi/gomuks/pkg/ffi */ + + +#line 1 "cgo-builtin-export-prolog" + +#include + +#ifndef GO_CGO_EXPORT_PROLOGUE_H +#define GO_CGO_EXPORT_PROLOGUE_H + +#ifndef GO_CGO_GOSTRING_TYPEDEF +typedef struct { const char *p; ptrdiff_t n; } _GoString_; +extern size_t _GoStringLen(_GoString_ s); +extern const char *_GoStringPtr(_GoString_ s); +#endif + +#endif + +/* Start of preamble from import "C" comments. */ + + +#line 9 "ffi.go" + +#include "gomuksffi.h" +#include + +static inline void _gomuks_callEventCallback(EventCallback cb, const char *command, int64_t request_id, GomuksOwnedBuffer data) { + cb(command, request_id, data); +} + +#line 1 "cgo-generated-wrapper" + + +/* End of preamble from import "C" comments. */ + + +/* Start of boilerplate cgo prologue. */ +#line 1 "cgo-gcc-export-header-prolog" + +#ifndef GO_CGO_PROLOGUE_H +#define GO_CGO_PROLOGUE_H + +typedef signed char GoInt8; +typedef unsigned char GoUint8; +typedef short GoInt16; +typedef unsigned short GoUint16; +typedef int GoInt32; +typedef unsigned int GoUint32; +typedef long long GoInt64; +typedef unsigned long long GoUint64; +typedef GoInt64 GoInt; +typedef GoUint64 GoUint; +typedef size_t GoUintptr; +typedef float GoFloat32; +typedef double GoFloat64; +#ifdef _MSC_VER +#if !defined(__cplusplus) || _MSVC_LANG <= 201402L +#include +typedef _Fcomplex GoComplex64; +typedef _Dcomplex GoComplex128; +#else +#include +typedef std::complex GoComplex64; +typedef std::complex GoComplex128; +#endif +#else +typedef float _Complex GoComplex64; +typedef double _Complex GoComplex128; +#endif + +/* + static assertion to make sure the file is being used on architecture + at least with matching size of GoInt. +*/ +typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1]; + +#ifndef GO_CGO_GOSTRING_TYPEDEF +typedef _GoString_ GoString; +#endif +typedef void *GoMap; +typedef void *GoChan; +typedef struct { void *t; void *v; } GoInterface; +typedef struct { void *data; GoInt len; GoInt cap; } GoSlice; + +#endif + +/* End of boilerplate cgo prologue. */ + +#ifdef __cplusplus +extern "C" { +#endif + +extern GomuksHandle GomuksInit(void); +extern int GomuksStart(GomuksHandle handle, EventCallback callback); +extern void GomuksDestroy(GomuksHandle handle); +extern GomuksResponse GomuksSubmitCommand(GomuksHandle handle, char* command, GomuksBorrowedBuffer data); +extern GomuksAccountInfo GomuksGetAccountInfo(GomuksHandle handle); +extern void GomuksFreeAccountInfo(GomuksAccountInfo info); +extern void GomuksFreeBuffer(GomuksOwnedBuffer buf); + +#ifdef __cplusplus +} +#endif diff --git a/src/gomuks/libgomuks.so b/src/gomuks/libgomuks.so new file mode 100644 index 0000000..c4987c5 Binary files /dev/null and b/src/gomuks/libgomuks.so differ diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index b12edca..55fb066 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -8,7 +8,6 @@ #include #include -#include #include #include #include @@ -19,8 +18,6 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("DynamicColorPluginCApi")); FileSelectorWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("FileSelectorWindows")); - FlutterSecureStorageWindowsPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("ScreenRetrieverWindowsPluginCApi")); UrlLauncherWindowsRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 8967b80..9333a2f 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -5,7 +5,6 @@ list(APPEND FLUTTER_PLUGIN_LIST dynamic_system_colors file_selector_windows - flutter_secure_storage_windows screen_retriever_windows url_launcher_windows window_manager