Compare commits

..

50 commits

Author SHA1 Message Date
b9ac38e7df
add polls to readme 2026-01-10 12:18:16 -05:00
5ba3680111
Add blurhash support 2026-01-10 11:55:41 -05:00
a1c7349b3b
use a widget for edited 2026-01-10 10:21:28 -05:00
396a0f4c4f
i cant get old encryption keys to load :/ 2026-01-10 10:17:57 -05:00
8ba9f170cc
fix build 2026-01-09 09:42:29 -05:00
d0cb57e41f
grab installer from windows 2026-01-09 09:29:46 -05:00
db2e88662e
use iss from windows 2026-01-08 22:11:58 -05:00
e64bea33c9
add icon 2026-01-08 21:35:09 -05:00
43b11ff475
remove some un-needed code 2026-01-08 21:07:09 -05:00
3012bebe93
trim text before checking if empty 2026-01-08 21:00:38 -05:00
125c78d9d2
Fix login issue 2026-01-08 20:55:21 -05:00
1583d50805
fix regex 2026-01-08 20:15:25 -05:00
baf26d0ec9
Add maximize support and some other stuff 2026-01-08 20:11:10 -05:00
0d80c93bc7
add windows support 2026-01-08 19:01:57 -05:00
78a3bd63dd
rename windows action 2026-01-08 18:53:08 -05:00
e3d051767c
use flutter secure storage not simple secure storage 2026-01-08 18:51:20 -05:00
8844964374
fix 2026-01-08 13:24:56 -05:00
2d382fa63d
upload portable too 2026-01-08 13:24:08 -05:00
ed10ec8c09
auto dispose members controller (is there a reason i didnt do this before?) 2026-01-08 12:29:49 -05:00
9efd82a52a
upload specifically the exe 2026-01-08 12:14:18 -05:00
f2cdc03798
update build instructions 2026-01-08 12:07:30 -05:00
a5aee2a701
Add windows to readme again 2026-01-08 12:06:34 -05:00
02f3a31db1
update readme 2026-01-08 12:05:53 -05:00
a3b1bc8e0e
Fix installer 2026-01-08 11:51:55 -05:00
9c99aabf29
add installer 2026-01-08 11:38:29 -05:00
c048aee833
remove clipboard stuff 2026-01-08 10:02:26 -05:00
0c118c5b67
upload the right file 2026-01-07 16:35:55 -05:00
c44c1635e1
add ls's 2026-01-07 16:24:23 -05:00
060d154665
make badges larger 2026-01-07 15:56:50 -05:00
257ebdd5b5
bump clipboard 2026-01-07 15:27:00 -05:00
6298758686
Update readme 2026-01-07 14:13:42 -05:00
0b5641e398
use system font 2026-01-07 13:54:43 -05:00
3783554940
ignore dead code for line 2026-01-07 13:42:58 -05:00
fd34669c5f
downgrade clipboard 2026-01-07 09:24:43 -05:00
03f3634d9d
bump clipboard 2026-01-06 20:08:41 -05:00
be860e5d7d
fix build 2026-01-06 19:52:32 -05:00
94d40d2416
add workflow 2026-01-06 19:52:02 -05:00
2dcd81a5c4
upgrade clipboard 2026-01-06 18:06:12 -05:00
68fba2c412
bump 2026-01-06 17:52:57 -05:00
3fd0d5f461
Use timelines, encryption now works, except not fetching keys from old devices 2026-01-06 16:31:51 -05:00
1539235d7d
Partially working encryption 2026-01-06 16:11:21 -05:00
c4516c312a
update build instructions 2026-01-06 14:45:59 -05:00
168546ae45
init vod properly 2026-01-06 13:59:16 -05:00
0f563455ff
Mark as read button 2026-01-06 13:39:51 -05:00
e8a8bb3b4f
Fix warnings 2026-01-06 13:27:11 -05:00
45ae72ed2a
Disable unsupported messages even on debug builds
We could make a setting for this, maybe!
2026-01-06 12:23:57 -05:00
cb5846600b
fix some bugs i think ? 2026-01-06 12:08:34 -05:00
50a6eb7c92
mark edits as complete in readme 2026-01-06 11:24:20 -05:00
a429ef9b25
only allow editing text messages 2026-01-06 11:23:31 -05:00
f9f4a4b48e
working edits 2026-01-06 11:20:17 -05:00
31 changed files with 401 additions and 280 deletions

46
.github/workflows/windows.yml vendored Normal file
View file

@ -0,0 +1,46 @@
name: "Build Windows Version"
on:
workflow_dispatch:
jobs:
build-windows:
runs-on: "windows-latest"
steps:
- name: "Checkout repository"
uses: "actions/checkout@v4"
- name: "Set up Flutter"
uses: "subosito/flutter-action@v2"
- name: "Set up Rust"
uses: "dtolnay/rust-toolchain@stable"
with:
targets: "x86_64-pc-windows-msvc"
- name: "Install Flutter dependencies"
run: flutter pub get
- name: "Run build_runner & build Windows EXE"
run: |
flutter pub run build_runner build --delete-conflicting-outputs
flutter build windows --release
- name: "Upload exe zip"
uses: "actions/upload-artifact@v4"
with:
name: "windows-portable"
path: "build/windows/x64/runner/Release/"
- name: "Install Inno Setup"
run: choco install innosetup -y
- name: "Build Inno Setup installer"
run: iscc windows/installer.iss
- name: "Upload installer artifact"
uses: "actions/upload-artifact@v4"
with:
name: "windows-installer"
path: "windows/dist/Nexus-Setup.exe"

View file

@ -1,3 +1,9 @@
{ {
"cSpell.words": ["Appbar", "Displayname", "prefs"] "cSpell.words": [
"Appbar",
"Displayname",
"Homeserver",
"prefs",
"vodozemac"
]
} }

View file

@ -17,7 +17,7 @@ A simple and user-friendly Matrix client made with Flutter and the Matrix Dart S
- [ ] Platform Support - [ ] Platform Support
- [x] Linux - [x] Linux
- [x] Windows (untested, if you are interested in helping to test, open an issue) - [x] Windows
- [ ] MacOS - [ ] MacOS
- [ ] Android - [ ] Android
- [ ] iOS - [ ] iOS
@ -28,7 +28,7 @@ A simple and user-friendly Matrix client made with Flutter and the Matrix Dart S
- [x] Rooms / Spaces - [x] Rooms / Spaces
- [x] Displaying and choosing - [x] Displaying and choosing
- [x] Reading, showing unread - [x] Reading, showing unread
- [ ] Mark as read button on rooms and spaces - [x] Mark as read button on rooms and spaces
- [ ] Searching - [ ] Searching
- [ ] Creating (Rooms, Spaces, and DMs) - [ ] Creating (Rooms, Spaces, and DMs)
- [ ] Joining - [ ] Joining
@ -38,6 +38,8 @@ A simple and user-friendly Matrix client made with Flutter and the Matrix Dart S
- [x] Leaving - [x] Leaving
- [x] Subspaces - [x] Subspaces
- [x] Messages - [x] Messages
- [x] Encryption
- [ ] Restoring crypto identity from passphrase/key or verification
- [x] Sending - [x] Sending
- [x] Plain text - [x] Plain text
- [x] HTML/Markdown - [x] HTML/Markdown
@ -48,7 +50,6 @@ A simple and user-friendly Matrix client made with Flutter and the Matrix Dart S
- [x] Rooms - [x] Rooms
- [ ] Custom emojis/stickers - [ ] Custom emojis/stickers
- [ ] GIFs, maybe through Tenor or something - [ ] GIFs, maybe through Tenor or something
- [ ] Encrypted messages
- [x] Recieving - [x] Recieving
- [x] Plain text - [x] Plain text
- [x] HTML - [x] HTML
@ -57,8 +58,10 @@ A simple and user-friendly Matrix client made with Flutter and the Matrix Dart S
- [ ] Jump to original message - [ ] Jump to original message
- [x] Edits - [x] Edits
- [x] Attachments - [x] Attachments
- [x] Blurhashing
- [ ] Downloading attachments - [ ] Downloading attachments
- [ ] Opening attachments in their own view - [ ] Opening attachments in their own view
- [ ] Polls
- [x] Mentions - [x] Mentions
- [x] Users - [x] Users
- [x] Rooms - [x] Rooms
@ -66,11 +69,10 @@ A simple and user-friendly Matrix client made with Flutter and the Matrix Dart S
- [x] Matrix URIs - [x] Matrix URIs
- [x] Matrix.to links - [x] Matrix.to links
- [x] Custom emojis/stickers - [x] Custom emojis/stickers
- [ ] Encrypted messages
- [x] History loading - [x] History loading
- [x] Backwards - [x] Backwards
- [ ] Forwards - [ ] Forwards
- [ ] Editing - [x] Editing
- [x] Deleting - [x] Deleting
- [ ] Reactions: Waiting on https://github.com/flyerhq/flutter_chat_ui/pull/838 - [ ] Reactions: Waiting on https://github.com/flyerhq/flutter_chat_ui/pull/838
- [ ] Pins - [ ] Pins
@ -87,6 +89,7 @@ A simple and user-friendly Matrix client made with Flutter and the Matrix Dart S
- [ ] Spam filtering - [ ] Spam filtering
- [ ] Settings - [ ] Settings
- [ ] Light/Dark mode - [ ] Light/Dark mode
- [ ] Show media by default
- [ ] Dynamic Theming - [ ] Dynamic Theming
- [ ] Devices - [ ] Devices
- [ ] Viewing devices - [ ] Viewing devices
@ -103,18 +106,43 @@ A simple and user-friendly Matrix client made with Flutter and the Matrix Dart S
## Development ## Development
Fork and clone the project, then: First, clone and open the repo:
```sh
git clone https://git.federated.nexus/Henry-Hiles/nexus
cd nexus
```
### Prerequisites
#### Linux
- With Nix: Either use direnv, or `nix flake develop` - 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, Rust, the libsecret dev package for your distro (must be in `PKG_CONFIG_PATH`), and sqlite (must be in `LD_LIBRARY_PATH`).
#### 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.
###
Get dependencies:
```sh
flutter pub get
```
Build generated files, and watch for new changes: Build generated files, and watch for new changes:
```sh ```sh
flutter pub run build_runner watch --delete-conflicting-outputs flutter pub run build_runner watch --delete-conflicting-outputs
``` ```
Run `flutter run` to run the app. Run the app:
```sh
flutter run
```
## Community ## Community

18
flake.lock generated
View file

@ -5,11 +5,11 @@
"nixpkgs-lib": "nixpkgs-lib" "nixpkgs-lib": "nixpkgs-lib"
}, },
"locked": { "locked": {
"lastModified": 1759362264, "lastModified": 1767609335,
"narHash": "sha256-wfG0S7pltlYyZTM+qqlhJ7GMw2fTF4mLKCIVhLii/4M=", "narHash": "sha256-feveD98mQpptwrAEggBQKJTYbvwwglSbOv53uCfH9PY=",
"owner": "hercules-ci", "owner": "hercules-ci",
"repo": "flake-parts", "repo": "flake-parts",
"rev": "758cf7296bee11f1706a574c77d072b8a7baa881", "rev": "250481aafeb741edfe23d29195671c19b36b6dca",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -20,11 +20,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1759381078, "lastModified": 1767640445,
"narHash": "sha256-gTrEEp5gEspIcCOx9PD8kMaF1iEmfBcTbO0Jag2QhQs=", "narHash": "sha256-UWYqmD7JFBEDBHWYcqE6s6c77pWdcU/i+bwD6XxMb8A=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "7df7ff7d8e00218376575f0acdcc5d66741351ee", "rev": "9f0c42f8bc7151b8e7e5840fb3bd454ad850d8c5",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -36,11 +36,11 @@
}, },
"nixpkgs-lib": { "nixpkgs-lib": {
"locked": { "locked": {
"lastModified": 1754788789, "lastModified": 1765674936,
"narHash": "sha256-x2rJ+Ovzq0sCMpgfgGaaqgBSwY+LST+WbZ6TytnT9Rk=", "narHash": "sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo=",
"owner": "nix-community", "owner": "nix-community",
"repo": "nixpkgs.lib", "repo": "nixpkgs.lib",
"rev": "a73b9c743612e4244d865a2fdee11865283c04e6", "rev": "2075416fcb47225d9b68ac469a5c4801a9c4dd85",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -1,8 +1,10 @@
import "dart:convert"; import "dart:convert";
import "dart:io"; import "dart:io";
import "package:flutter/foundation.dart"; import "package:flutter/foundation.dart";
import "package:matrix/encryption.dart";
import "package:nexus/controllers/database_controller.dart"; import "package:nexus/controllers/database_controller.dart";
import "package:flutter_vodozemac/flutter_vodozemac.dart"; import "package:vodozemac/vodozemac.dart" as vod;
import "package:flutter_vodozemac/flutter_vodozemac.dart" as fl_vod;
import "package:matrix/matrix.dart"; import "package:matrix/matrix.dart";
import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/secure_storage_controller.dart"; import "package:nexus/controllers/secure_storage_controller.dart";
@ -13,23 +15,27 @@ class ClientController extends AsyncNotifier<Client> {
bool updateShouldNotify( bool updateShouldNotify(
AsyncValue<Client> previous, AsyncValue<Client> previous,
AsyncValue<Client> next, AsyncValue<Client> next,
) => previous.hasValue != next.hasValue; ) =>
previous.hasValue != next.hasValue ||
previous.value?.accessToken != next.value?.accessToken;
static const sessionBackupKey = "sessionBackup"; static const sessionBackupKey = "sessionBackup";
@override @override
Future<Client> build() async { Future<Client> build() async {
if (!vod.isInitialized()) fl_vod.init();
final client = Client( final client = Client(
"nexus", "nexus",
logLevel: kReleaseMode ? Level.warning : Level.verbose, logLevel: kReleaseMode ? Level.warning : Level.verbose,
importantStateEvents: {"im.ponies.room_emotes"}, importantStateEvents: {"im.ponies.room_emotes"},
supportedLoginTypes: {AuthenticationTypes.password}, supportedLoginTypes: {AuthenticationTypes.password},
verificationMethods: {KeyVerificationMethod.emoji},
database: await MatrixSdkDatabase.init( database: await MatrixSdkDatabase.init(
"nexus", "nexus",
database: await ref.watch(DatabaseController.provider.future), database: await ref.watch(DatabaseController.provider.future),
), ),
nativeImplementations: NativeImplementationsIsolate( nativeImplementations: NativeImplementationsIsolate(
compute, compute,
vodozemacInit: init, vodozemacInit: fl_vod.init,
), ),
); );

View file

@ -1,30 +1,18 @@
import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:matrix/matrix.dart"; import "package:matrix/matrix.dart";
import "package:nexus/controllers/from_controller.dart";
class EventsController extends AsyncNotifier<GetRoomEventsResponse> { class EventsController extends AsyncNotifier<Timeline> {
EventsController(this.room); EventsController(this.room);
final Room room; final Room room;
@override @override
Future<GetRoomEventsResponse> build({String? from}) async { Future<Timeline> build({String? from}) => room.getTimeline();
final response = await room.client.getRoomEvents(
room.id, Future<void> prev() async {
Direction.b, final timeline = await future;
from: from, await timeline.requestHistory();
limit: 32,
);
if (ref.mounted) {
ref.watch(FromController.provider(room).notifier).set(response.end);
}
return response;
} }
Future<GetRoomEventsResponse> prev() async =>
build(from: ref.read(FromController.provider(room)));
static final provider = AsyncNotifierProvider.autoDispose static final provider = AsyncNotifierProvider.autoDispose
.family<EventsController, GetRoomEventsResponse, Room>( .family<EventsController, Timeline, Room>(EventsController.new);
EventsController.new,
);
} }

View file

@ -1,15 +0,0 @@
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:matrix/matrix.dart";
class FromController extends Notifier<String?> {
FromController(_);
@override
String? build() => null;
void set(String? value) => state = value;
static final provider =
NotifierProvider.family<FromController, String?, Room>(
FromController.new,
);
}

View file

@ -15,8 +15,8 @@ class MembersController extends AsyncNotifier<IList<MatrixEvent>> {
[], [],
); );
static final provider = static final provider = AsyncNotifierProvider.family
AsyncNotifierProvider.family<MembersController, IList<MatrixEvent>, Room>( .autoDispose<MembersController, IList<MatrixEvent>, Room>(
MembersController.new, MembersController.new,
); );
} }

View file

@ -40,7 +40,16 @@ class RoomChatController extends AsyncNotifier<ChatController> {
if (oldMessage == null || message == null) return; if (oldMessage == null || message == null) return;
return await updateMessage( return await updateMessage(
oldMessage, oldMessage,
message.copyWith(id: oldMessage.id), message.copyWith(
id: oldMessage.id,
replyToMessageId: oldMessage.replyToMessageId,
metadata: {
...(oldMessage.metadata ?? {}),
...((message.metadata ?? {}).filterMap(
(key, value) => value == null ? null : MapEntry(key, value),
)),
},
),
); );
} }
if (message != null) { if (message != null) {
@ -51,7 +60,7 @@ class RoomChatController extends AsyncNotifier<ChatController> {
); );
return InMemoryChatController( return InMemoryChatController(
messages: await response.chunk.toMessages(room), messages: await response.events.toMessages(room),
); );
} }
@ -76,14 +85,22 @@ class RoomChatController extends AsyncNotifier<ChatController> {
} }
Future<void> loadOlder() async { Future<void> loadOlder() async {
final currentEvents = await future;
await ref.watch(EventsController.provider(room).notifier).prev();
final newEvents = await ref.watch(EventsController.provider(room).future);
final controller = await future; final controller = await future;
final response = await ref await controller.insertAllMessages(
.watch(EventsController.provider(room).notifier) await newEvents.events
.prev(); .where(
(event) => !currentEvents.messages.any(
final messages = await response.chunk.toMessages(room); (existingEvent) => existingEvent.id == event.eventId,
),
await controller.insertAllMessages(messages, index: 0); )
.toList()
.toMessages(room),
index: 0,
);
ref.notifyListeners(); ref.notifyListeners();
} }

View file

@ -1,26 +1,19 @@
import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:simple_secure_storage/simple_secure_storage.dart"; import "package:flutter_secure_storage/flutter_secure_storage.dart";
class SecureStorageController extends AsyncNotifier<void> { class SecureStorageController extends Notifier<FlutterSecureStorage> {
@override @override
Future<void> build() => SimpleSecureStorage.initialize(); FlutterSecureStorage build() => FlutterSecureStorage();
Future<String?> get(String key) async { Future<String?> get(String key) => state.read(key: key);
await future;
return SimpleSecureStorage.read(key);
}
Future<void> set(String key, String value) async { Future<void> set(String key, String value) =>
await future; state.write(key: key, value: value);
return SimpleSecureStorage.write(key, value);
}
Future<void> clear() async { Future<void> clear() => state.deleteAll();
await future;
return SimpleSecureStorage.clear();
}
static final provider = AsyncNotifierProvider<SecureStorageController, void>( static final provider =
SecureStorageController.new, NotifierProvider<SecureStorageController, FlutterSecureStorage>(
); SecureStorageController.new,
);
} }

View file

@ -64,7 +64,7 @@ class SpacesController extends AsyncNotifier<IList<Space>> {
avatar: space.avatar, avatar: space.avatar,
id: space.roomData.id, id: space.roomData.id,
roomData: space.roomData, roomData: space.roomData,
children: IList(await space.roomData.getAllChildren(client)), children: IList(await space.roomData.getAllChildren()),
), ),
), ),
)), )),

View file

@ -1,5 +1,4 @@
import "package:collection/collection.dart"; import "package:collection/collection.dart";
import "package:flutter/foundation.dart";
import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:flutter_chat_core/flutter_chat_core.dart";
import "package:matrix/matrix.dart"; import "package:matrix/matrix.dart";
@ -9,7 +8,6 @@ extension EventToMessage on Event {
bool includeEdits = false, bool includeEdits = false,
}) async { }) async {
final replyId = inReplyToEventId(); final replyId = inReplyToEventId();
final newEvent = (unsigned?["m.relations"] as Map?)?["m.replace"]; final newEvent = (unsigned?["m.relations"] as Map?)?["m.replace"];
final event = newEvent == null ? this : Event.fromJson(newEvent, room); final event = newEvent == null ? this : Event.fromJson(newEvent, room);
@ -82,6 +80,7 @@ extension EventToMessage on Event {
source: (await getAttachmentUri()).toString(), source: (await getAttachmentUri()).toString(),
replyToMessageId: replyId, replyToMessageId: replyId,
deliveredAt: originServerTs, deliveredAt: originServerTs,
blurhash: (event.content["info"] as Map?)?["xyz.amorgan.blurhash"],
), ),
MessageTypes.Audio => Message.audio( MessageTypes.Audio => Message.audio(
metadata: metadata, metadata: metadata,
@ -121,7 +120,9 @@ extension EventToMessage on Event {
), ),
EventTypes.Redaction => null, EventTypes.Redaction => null,
_ => _ =>
kDebugMode // Turn this on for debugging purposes
false
// ignore: dead_code
? Message.unsupported( ? Message.unsupported(
metadata: metadata, metadata: metadata,
id: eventId, id: eventId,

View file

@ -5,7 +5,7 @@ import "package:nexus/helpers/extensions/get_full_room.dart";
import "package:nexus/models/full_room.dart"; import "package:nexus/models/full_room.dart";
extension RoomToChildren on Room { extension RoomToChildren on Room {
Future<IList<FullRoom>> getAllChildren(Client client) async { Future<IList<FullRoom>> getAllChildren() async {
final direct = await Future.wait( final direct = await Future.wait(
spaceChildren spaceChildren
.map( .map(
@ -19,7 +19,7 @@ extension RoomToChildren on Room {
return (await Future.wait( return (await Future.wait(
direct.map( direct.map(
(child) async => child.roomData.isSpace (child) async => child.roomData.isSpace
? await child.roomData.getAllChildren(client) ? await child.roomData.getAllChildren()
: [child], : [child],
), ),
)).expand((list) => list).toIList(); )).expand((list) => list).toIList();

View file

@ -7,6 +7,10 @@ extension SchemeToTheme on ColorScheme {
titleSpacing: 0, titleSpacing: 0,
backgroundColor: surfaceContainerLow, backgroundColor: surfaceContainerLow,
), ),
textTheme: ThemeData(
fontFamilyFallback: ["sans"],
brightness: brightness,
).textTheme,
inputDecorationTheme: const InputDecorationTheme( inputDecorationTheme: const InputDecorationTheme(
border: OutlineInputBorder(), border: OutlineInputBorder(),
), ),

View file

@ -1,3 +1,5 @@
import "dart:io";
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:nexus/controllers/client_controller.dart"; import "package:nexus/controllers/client_controller.dart";
@ -34,6 +36,7 @@ New Value: ${newValue is AsyncData ? newValue.value : newValue}
void showError(Object error, [StackTrace? stackTrace]) { void showError(Object error, [StackTrace? stackTrace]) {
if (error.toString().contains("DioException")) return; if (error.toString().contains("DioException")) return;
if (error.toString().contains("UTF-16")) return; if (error.toString().contains("UTF-16")) return;
if (error.toString().contains("HTTP request failed")) return;
if (error.toString().contains("Invalid image data")) return; if (error.toString().contains("Invalid image data")) return;
debugPrintStack(stackTrace: stackTrace, label: error.toString()); debugPrintStack(stackTrace: stackTrace, label: error.toString());
@ -57,11 +60,15 @@ void main() async {
WindowOptions(titleBarStyle: TitleBarStyle.hidden), WindowOptions(titleBarStyle: TitleBarStyle.hidden),
); );
if (Platform.isLinux) {
setWindowMinSize(const Size.square(500));
} else {
await windowManager.setMinimumSize(Size.square(500));
}
FlutterError.onError = (FlutterErrorDetails details) => FlutterError.onError = (FlutterErrorDetails details) =>
showError(details.exception.toString(), details.stack); showError(details.exception.toString(), details.stack);
setWindowMinSize(const Size.square(500));
runApp( runApp(
ProviderScope( ProviderScope(
observers: [ observers: [

View file

@ -1,5 +1,6 @@
import "dart:io"; import "dart:io";
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:window_manager/window_manager.dart";
class Appbar extends StatelessWidget implements PreferredSizeWidget { class Appbar extends StatelessWidget implements PreferredSizeWidget {
final Widget? leading; final Widget? leading;
@ -7,6 +8,7 @@ class Appbar extends StatelessWidget implements PreferredSizeWidget {
final Color? backgroundColor; final Color? backgroundColor;
final double? scrolledUnderElevation; final double? scrolledUnderElevation;
final List<Widget> actions; final List<Widget> actions;
const Appbar({ const Appbar({
super.key, super.key,
this.title, this.title,
@ -17,19 +19,42 @@ class Appbar extends StatelessWidget implements PreferredSizeWidget {
}); });
@override @override
Size get preferredSize => AppBar().preferredSize; Size get preferredSize => const Size.fromHeight(kToolbarHeight);
@override @override
AppBar build(BuildContext context) => AppBar( Widget build(BuildContext context) {
leading: leading, Future<void> maximize() async {
backgroundColor: backgroundColor, final isMaximized = await windowManager.isMaximized();
scrolledUnderElevation: scrolledUnderElevation,
actionsPadding: EdgeInsets.symmetric(horizontal: 8), if (isMaximized) {
title: title, return windowManager.unmaximize();
actions: [ }
...actions,
if (!(Platform.isAndroid || Platform.isIOS)) return windowManager.maximize();
IconButton(onPressed: () => exit(0), icon: Icon(Icons.close)), }
],
); return GestureDetector(
behavior: HitTestBehavior.translucent,
onDoubleTap: maximize,
onPanStart: (_) => windowManager.startDragging(),
child: AppBar(
leading: leading,
backgroundColor: backgroundColor,
scrolledUnderElevation: scrolledUnderElevation,
actionsPadding: const EdgeInsets.symmetric(horizontal: 8),
title: title,
actions: [
...actions,
if (!(Platform.isAndroid || Platform.isIOS)) ...[
if (!Platform.isLinux)
IconButton(
onPressed: maximize,
icon: const Icon(Icons.fullscreen),
),
IconButton(onPressed: () => exit(0), icon: const Icon(Icons.close)),
],
],
),
);
}
} }

View file

@ -30,8 +30,8 @@ class AvatarOrHash extends StatelessWidget {
child: Center( child: Center(
child: Badge( child: Badge(
isLabelVisible: hasBadge, isLabelVisible: hasBadge,
smallSize: 8, smallSize: 12,
backgroundColor: Theme.of(context).colorScheme.onPrimaryContainer, backgroundColor: Theme.of(context).colorScheme.primary,
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(4)), borderRadius: BorderRadius.all(Radius.circular(4)),
child: SizedBox( child: SizedBox(

View file

@ -35,12 +35,17 @@ class ChatBox extends HookConsumerWidget {
relatedMessage is TextMessage && relatedMessage is TextMessage &&
controller.value.text.isEmpty) { controller.value.text.isEmpty) {
final text = (relatedMessage as TextMessage).text; final text = (relatedMessage as TextMessage).text;
controller.value.text = relatedMessage?.replyToMessageId == null final splitText = relatedMessage?.replyToMessageId == null
? text ? text
: text.split("\n\n").sublist(1).join("\n\n"); : text.split("\n\n").sublist(1).join("\n\n");
final notEmpty = splitText.isEmpty ? text : splitText;
controller.value.text = notEmpty.startsWith("* ")
? notEmpty.substring(2)
: notEmpty;
} }
void send() { void send() {
if (controller.value.text.trim().isEmpty) return;
ref ref
.watch(RoomChatController.provider(room).notifier) .watch(RoomChatController.provider(room).notifier)
.send( .send(
@ -119,11 +124,7 @@ class ChatBox extends HookConsumerWidget {
triggerCharacter.value = newTriggerCharacter; triggerCharacter.value = newTriggerCharacter;
query.value = newQuery; query.value = newQuery;
}, },
triggerCharacterAndStyles: { triggerCharacterAndStyles: {"@": style, "#": style},
"@": style,
"#": style,
":": style,
},
builder: (context, key) => TextFormField( builder: (context, key) => TextFormField(
enabled: room.canSendDefaultMessages, enabled: room.canSendDefaultMessages,
maxLines: 12, maxLines: 12,

View file

@ -142,8 +142,6 @@ class Html extends ConsumerWidget {
"data-mx-bg-color" => MapEntry("background-color", value), "data-mx-bg-color" => MapEntry("background-color", value),
"edited" => MapEntry("display", "block"),
_ => null, _ => null,
}, },
) )

View file

@ -106,7 +106,7 @@ class MentionOverlay extends ConsumerWidget {
title: Text(room.title), title: Text(room.title),
subtitle: room.roomData.topic.isEmpty subtitle: room.roomData.topic.isEmpty
? null ? null
: Text(room.roomData.topic), : Text(room.roomData.topic, maxLines: 1),
onTap: () => addTag( onTap: () => addTag(
id: "[#${room.roomData.getLocalizedDisplayname()}](https://matrix.to/#/${room.roomData.id})", id: "[#${room.roomData.getLocalizedDisplayname()}](https://matrix.to/#/${room.roomData.id})",
name: name:

View file

@ -36,7 +36,7 @@ class RoomAppbar extends StatelessWidget implements PreferredSizeWidget {
title: Column( title: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text(room.title, overflow: TextOverflow.ellipsis), Text(room.title, overflow: TextOverflow.ellipsis, maxLines: 1),
if (room.roomData.topic.isNotEmpty) if (room.roomData.topic.isNotEmpty)
Text( Text(
room.roomData.topic, room.roomData.topic,

View file

@ -67,7 +67,9 @@ class RoomChat extends HookConsumerWidget {
title: Text("Reply"), title: Text("Reply"),
), ),
), ),
if (message.authorId == room.roomData.client.userID) // Should check if is state event (has state_key), if so, don't show edit option
if (message is TextMessage &&
message.authorId == room.roomData.client.userID)
PopupMenuItem( PopupMenuItem(
onTap: () { onTap: () {
replyToMessage.value = message; replyToMessage.value = message;
@ -199,12 +201,15 @@ class RoomChat extends HookConsumerWidget {
( (
context, context,
message, { message, {
required details,
required index, required index,
}) => context.showContextMenu( TapUpDetails? details,
globalPosition: details.globalPosition, }) => details?.globalPosition == null
children: getMessageOptions(message), ? null
), : context.showContextMenu(
globalPosition:
details!.globalPosition,
children: getMessageOptions(message),
),
onMessageLongPress: onMessageLongPress:
( (
context, context,
@ -239,22 +244,41 @@ class RoomChat extends HookConsumerWidget {
required bool isSentByMe, required bool isSentByMe,
MessageGroupStatus? groupStatus, MessageGroupStatus? groupStatus,
}) => FlyerChatTextMessage( }) => FlyerChatTextMessage(
customWidget: Html( customWidget: Column(
(message.metadata?["formatted"] crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Html(
(message.metadata?["formatted"]
as String) as String)
.replaceAllMapped( .replaceAllMapped(
RegExp( RegExp(
regexLink, "(<a\\b[^>]*>.*?<\\/a>)|(\\bhttps?:\\/\\/[^\\s<]+)",
caseSensitive: false, caseSensitive: false,
), ),
(m) => (m) {
"<a href=\"${m.group(0)!}\">${m.group(0)!}</a>", // If it's already an <a> tag, leave it unchanged
if (m.group(1) !=
null) {
return m.group(1)!;
}
// Otherwise, wrap the bare URL
final url = m.group(2)!;
return "<a href=\"$url\">$url</a>";
},
) )
.replaceAll("\n", "<br/>") + .replaceAll("\n", "<br/>"),
((message.editedAt != null) client: room.roomData.client,
? "<sub edited>(edited)</sub>" ),
: ""), if (message.editedAt != null)
client: room.roomData.client, Text(
"(edited)",
style: theme
.textTheme
.labelSmall,
),
],
), ),
topWidget: TopWidget( topWidget: TopWidget(
message, message,

View file

@ -1,7 +1,8 @@
import "package:clipboard/clipboard.dart";
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:flutter/services.dart";
import "package:flutter_hooks/flutter_hooks.dart"; import "package:flutter_hooks/flutter_hooks.dart";
import "package:matrix/matrix.dart"; import "package:matrix/matrix.dart";
import "package:nexus/helpers/extensions/room_to_children.dart";
import "package:nexus/widgets/form_text_input.dart"; import "package:nexus/widgets/form_text_input.dart";
class RoomMenu extends StatelessWidget { class RoomMenu extends StatelessWidget {
@ -12,15 +13,31 @@ class RoomMenu extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final danger = Theme.of(context).colorScheme.error; final danger = Theme.of(context).colorScheme.error;
void markRead(String roomId) async {
for (final child in await room.getAllChildren()) {
await child.roomData.setReadMarker(
child.roomData.lastEvent?.eventId,
mRead: child.roomData.lastEvent?.eventId,
);
}
}
return PopupMenuButton( return PopupMenuButton(
itemBuilder: (_) => [ itemBuilder: (_) => [
PopupMenuItem( PopupMenuItem(
onTap: () async { onTap: () async {
final link = await room.matrixToInviteLink(); final link = await room.matrixToInviteLink();
await FlutterClipboard.copy(link.toString()); await Clipboard.setData(ClipboardData(text: link.toString()));
}, },
child: ListTile(leading: Icon(Icons.link), title: Text("Copy Link")), child: ListTile(leading: Icon(Icons.link), title: Text("Copy Link")),
), ),
PopupMenuItem(
onTap: () => markRead(room.id),
child: ListTile(
leading: Icon(Icons.check),
title: Text("Mark as Read"),
),
),
PopupMenuItem( PopupMenuItem(
onTap: () => showDialog( onTap: () => showDialog(
context: context, context: context,

View file

@ -8,10 +8,9 @@
#include <dynamic_system_colors/dynamic_color_plugin.h> #include <dynamic_system_colors/dynamic_color_plugin.h>
#include <file_selector_linux/file_selector_plugin.h> #include <file_selector_linux/file_selector_plugin.h>
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
#include <screen_retriever_linux/screen_retriever_linux_plugin.h> #include <screen_retriever_linux/screen_retriever_linux_plugin.h>
#include <simple_secure_storage_linux/simple_secure_storage_linux_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h> #include <url_launcher_linux/url_launcher_plugin.h>
#include <webcrypto/webcrypto_plugin.h>
#include <window_manager/window_manager_plugin.h> #include <window_manager/window_manager_plugin.h>
#include <window_size/window_size_plugin.h> #include <window_size/window_size_plugin.h>
@ -22,18 +21,15 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar); 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 = g_autoptr(FlPluginRegistrar) screen_retriever_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverLinuxPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverLinuxPlugin");
screen_retriever_linux_plugin_register_with_registrar(screen_retriever_linux_registrar); screen_retriever_linux_plugin_register_with_registrar(screen_retriever_linux_registrar);
g_autoptr(FlPluginRegistrar) simple_secure_storage_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "SimpleSecureStorageLinuxPlugin");
simple_secure_storage_linux_plugin_register_with_registrar(simple_secure_storage_linux_registrar);
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
g_autoptr(FlPluginRegistrar) webcrypto_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "WebcryptoPlugin");
webcrypto_plugin_register_with_registrar(webcrypto_registrar);
g_autoptr(FlPluginRegistrar) window_manager_registrar = g_autoptr(FlPluginRegistrar) window_manager_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin");
window_manager_plugin_register_with_registrar(window_manager_registrar); window_manager_plugin_register_with_registrar(window_manager_registrar);

View file

@ -5,10 +5,9 @@
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
dynamic_system_colors dynamic_system_colors
file_selector_linux file_selector_linux
flutter_secure_storage_linux
screen_retriever_linux screen_retriever_linux
simple_secure_storage_linux
url_launcher_linux url_launcher_linux
webcrypto
window_manager window_manager
window_size window_size
) )

View file

@ -193,14 +193,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.4.2" version: "0.4.2"
clipboard:
dependency: "direct main"
description:
name: clipboard
sha256: "619f4e9e946cfd637ac994f49af356bb590ab88b0c4aded03204ee566fd69d9e"
url: "https://pub.dev"
source: hosted
version: "3.0.8"
clock: clock:
dependency: transitive dependency: transitive
description: description:
@ -213,10 +205,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: code_builder name: code_builder
sha256: "11654819532ba94c34de52ff5feb52bd81cba1de00ef2ed622fd50295f9d4243" sha256: "6a6cab2ba4680d6423f34a9b972a4c9a94ebe1b62ecec4e1a1f2cba91fd1319d"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.11.0" version: "4.11.1"
collection: collection:
dependency: "direct main" dependency: "direct main"
description: description:
@ -301,10 +293,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: custom_lint_visitor name: custom_lint_visitor
sha256: "91f2a81e9f0abb4b9f3bb529f78b6227ce6050300d1ae5b1e2c69c66c7a566d8" sha256: e466d17856197cf9bce7ca03804d784fddab809db7bda787f3d2799ac89faadd
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.0+8.4.0" version: "1.0.0+9.0.0"
dart_style: dart_style:
dependency: transitive dependency: transitive
description: description:
@ -373,10 +365,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: ffi name: ffi
sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" sha256: d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.4" version: "2.1.5"
file: file:
dependency: transitive dependency: transitive
description: description:
@ -451,10 +443,10 @@ packages:
description: description:
path: "packages/flutter_chat_ui" path: "packages/flutter_chat_ui"
ref: HEAD ref: HEAD
resolved-ref: "6cfbadbf364251dd3c6a986e20c9d97636ad3412" resolved-ref: "03be67c8c81c8f637672ee03dd8f082d2c223627"
url: "https://github.com/Henry-Hiles/flutter_chat_ui" url: "https://github.com/Henry-Hiles/flutter_chat_ui"
source: git source: git
version: "2.9.1" version: "2.11.1"
flutter_hooks: flutter_hooks:
dependency: "direct main" dependency: "direct main"
description: description:
@ -476,10 +468,10 @@ packages:
description: description:
path: "packages/flutter_link_previewer" path: "packages/flutter_link_previewer"
ref: HEAD ref: HEAD
resolved-ref: "6cfbadbf364251dd3c6a986e20c9d97636ad3412" resolved-ref: "03be67c8c81c8f637672ee03dd8f082d2c223627"
url: "https://github.com/Henry-Hiles/flutter_chat_ui" url: "https://github.com/Henry-Hiles/flutter_chat_ui"
source: git source: git
version: "4.1.2" version: "4.2.0"
flutter_lints: flutter_lints:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -525,6 +517,54 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.11.1" version: "2.11.1"
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: flutter_svg:
dependency: "direct main" dependency: "direct main"
description: description:
@ -596,18 +636,18 @@ packages:
description: description:
path: "packages/flyer_chat_text_message" path: "packages/flyer_chat_text_message"
ref: HEAD ref: HEAD
resolved-ref: "6cfbadbf364251dd3c6a986e20c9d97636ad3412" resolved-ref: "03be67c8c81c8f637672ee03dd8f082d2c223627"
url: "https://github.com/Henry-Hiles/flutter_chat_ui" url: "https://github.com/Henry-Hiles/flutter_chat_ui"
source: git source: git
version: "2.5.2" version: "2.6.0"
freezed: freezed:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: freezed name: freezed
sha256: "13065f10e135263a4f5a4391b79a8efc5fb8106f8dd555a9e49b750b45393d77" sha256: "03dd9b7423ff0e31b7e01b2204593e5e1ac5ee553b6ea9d8184dff4a26b9fb07"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.2.3" version: "3.2.4"
freezed_annotation: freezed_annotation:
dependency: "direct main" dependency: "direct main"
description: description:
@ -700,10 +740,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: idb_shim name: idb_shim
sha256: "071f3b05032fa62e60ca15db9939f8afbaf403b37e67747ac88f858c3e999228" sha256: b26b2ad126be411d0072d1dfc4d97ebe02121a863e4eadc635b511b9bc138489
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.6.7+1" version: "2.7.1+2"
image: image:
dependency: transitive dependency: transitive
description: description:
@ -816,14 +856,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.11.3" version: "6.11.3"
just_throttle_it:
dependency: transitive
description:
name: just_throttle_it
sha256: af2d0c1e5c7f4e0bef79a55edf3d74c180908253f89203467bc432730f5fac5b
url: "https://pub.dev"
source: hosted
version: "3.0.1"
leak_tracker: leak_tracker:
dependency: transitive dependency: transitive
description: description:
@ -908,10 +940,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.16.0" version: "1.17.0"
mime: mime:
dependency: transitive dependency: transitive
description: description:
@ -1164,10 +1196,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: scrollview_observer name: scrollview_observer
sha256: c2f713509f18f88f637b2084b47a90c91fb1ef066d5d82d2cf3194d8509dc6ab sha256: "6e40ced415145c449a691d892157a3b854b751f024aed20d9aebda04c21444a3"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.26.2" version: "1.26.3"
sdp_transform: sdp_transform:
dependency: transitive dependency: transitive
description: description:
@ -1180,18 +1212,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: sembast name: sembast
sha256: c8063c3146c3c8d5f5b04230de7682c768440a575fbda2634f14d22f263197c3 sha256: "139cf71496105de32e7a08a4e3a1ead0f81c4a616ec9703ed07e8f0d10cdd505"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.8.5+2" version: "3.8.6"
sembast_web:
dependency: transitive
description:
name: sembast_web
sha256: "0362c7c241ad6546d3e27b4cfffaae505e5a9661e238dbcdd176756cc960fe7a"
url: "https://pub.dev"
source: hosted
version: "2.4.2"
shared_preferences: shared_preferences:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1280,62 +1304,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.0" version: "3.0.0"
simple_secure_storage:
dependency: "direct main"
description:
name: simple_secure_storage
sha256: ca823a355bb7bb0e9b969876508e7d3a5dc0d1fb2dcb681c85b6e315f1e876e9
url: "https://pub.dev"
source: hosted
version: "0.3.7"
simple_secure_storage_android:
dependency: transitive
description:
name: simple_secure_storage_android
sha256: "50fb27267755843af039da116d0e545f313ae329ef8838101880802259e0f741"
url: "https://pub.dev"
source: hosted
version: "0.3.1"
simple_secure_storage_darwin:
dependency: transitive
description:
name: simple_secure_storage_darwin
sha256: "8bd2ffcc62b478957ce20046bb96618b91a11e74af5d9fe2b4b229117bad18a7"
url: "https://pub.dev"
source: hosted
version: "0.2.2"
simple_secure_storage_linux:
dependency: transitive
description:
name: simple_secure_storage_linux
sha256: a7b7dccfaf496c27f882c26634ac083f2f545c0a4ca0818534c6261205a83686
url: "https://pub.dev"
source: hosted
version: "0.2.5"
simple_secure_storage_platform_interface:
dependency: transitive
description:
name: simple_secure_storage_platform_interface
sha256: "04fd4ce4c2b97c01a12eba46f51e3075a793d11f13340d06a64eb9b45a463ca5"
url: "https://pub.dev"
source: hosted
version: "0.2.3"
simple_secure_storage_web:
dependency: transitive
description:
name: simple_secure_storage_web
sha256: "63a3474a9931ab2587e01d22e7e95c0b7cc31338c0fafed5db9d1d798d1d3e0e"
url: "https://pub.dev"
source: hosted
version: "0.2.3"
simple_secure_storage_windows:
dependency: transitive
description:
name: simple_secure_storage_windows
sha256: cf31d2a97c26cf854aeb3c9774cd253f6600fb3fdfc6d807d480afae678cef10
url: "https://pub.dev"
source: hosted
version: "0.3.2"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
@ -1401,10 +1369,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: sqflite_common_ffi name: sqflite_common_ffi
sha256: "9faa2fedc5385ef238ce772589f7718c24cdddd27419b609bb9c6f703ea27988" sha256: "8d7b8749a516cbf6e9057f9b480b716ad14fc4f3d3873ca6938919cc626d9025"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.6" version: "2.3.7+1"
sqlite3: sqlite3:
dependency: transitive dependency: transitive
description: description:
@ -1473,26 +1441,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test name: test
sha256: "65e29d831719be0591f7b3b1a32a3cda258ec98c58c7b25f7b84241bc31215bb" sha256: "75906bf273541b676716d1ca7627a17e4c4070a3a16272b7a3dc7da3b9f3f6b7"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.26.2" version: "1.26.3"
test_api: test_api:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.6" version: "0.7.7"
test_core: test_core:
dependency: transitive dependency: transitive
description: description:
name: test_core name: test_core
sha256: "80bf5a02b60af04b09e14f6fe68b921aad119493e26e490deaca5993fef1b05a" sha256: "0cc24b5ff94b38d2ae73e1eb43cc302b77964fbf67abad1e296025b78deb53d0"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.11" version: "0.6.12"
thumbhash: thumbhash:
dependency: transitive dependency: transitive
description: description:
@ -1640,19 +1608,20 @@ packages:
vodozemac: vodozemac:
dependency: "direct main" dependency: "direct main"
description: description:
name: vodozemac path: dart
sha256: "39144e20740807731871c9248d811ed5a037b21d0aa9ffcfa630954de74139d9" ref: "krille/use-specced-olm-session-config"
url: "https://pub.dev" resolved-ref: "8770e0555b1bb692e3e1a43a7726b27eae285b20"
source: hosted url: "https://github.com/famedly/dart-vodozemac"
source: git
version: "0.4.0" version: "0.4.0"
watcher: watcher:
dependency: transitive dependency: transitive
description: description:
name: watcher name: watcher
sha256: f52385d4f73589977c80797e60fe51014f7f2b957b5e9a62c3f6ada439889249 sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.0" version: "1.2.1"
web: web:
dependency: transitive dependency: transitive
description: description:
@ -1677,14 +1646,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.3" version: "3.0.3"
webcrypto:
dependency: transitive
description:
name: webcrypto
sha256: "6b43001c4110856ff7fa5e5e65e7b2d44bec1d8b54a4d84d5fa2c7622267c5c1"
url: "https://pub.dev"
source: hosted
version: "0.6.0"
webkit_inspection_protocol: webkit_inspection_protocol:
dependency: transitive dependency: transitive
description: description:
@ -1759,5 +1720,5 @@ packages:
source: hosted source: hosted
version: "2.2.3" version: "2.2.3"
sdks: sdks:
dart: ">=3.9.2 <4.0.0" dart: ">=3.10.0 <4.0.0"
flutter: ">=3.35.0" flutter: ">=3.35.0"

View file

@ -12,6 +12,11 @@ environment:
sdk: "^3.9.2" sdk: "^3.9.2"
dependency_overrides: dependency_overrides:
vodozemac:
git:
url: https://github.com/famedly/dart-vodozemac
ref: krille/use-specced-olm-session-config
path: dart
analyzer: ^8.4.0 analyzer: ^8.4.0
source_gen: ^4.0.2 source_gen: ^4.0.2
@ -60,13 +65,12 @@ dependencies:
flutter_vodozemac: ^0.4.1 flutter_vodozemac: ^0.4.1
flutter_widget_from_html_core: ^0.17.0 flutter_widget_from_html_core: ^0.17.0
flutter_svg: ^2.2.2 flutter_svg: ^2.2.2
simple_secure_storage: ^0.3.6
json_annotation: ^4.9.0 json_annotation: ^4.9.0
vodozemac: ^0.4.0 vodozemac: ^0.4.0
clipboard: ^3.0.8
shared_preferences: ^2.5.3 shared_preferences: ^2.5.3
mention_tag_text_field: ^0.0.9 mention_tag_text_field: ^0.0.9
fluttertagger: ^2.3.1 fluttertagger: ^2.3.1
flutter_secure_storage: ^10.0.0
dev_dependencies: dev_dependencies:
build_runner: ^2.4.11 build_runner: ^2.4.11
@ -83,4 +87,6 @@ flutter_launcher_icons:
image_path: assets/icon.png image_path: assets/icon.png
adaptive_icon_background: "#000000" adaptive_icon_background: "#000000"
adaptive_icon_foreground: assets/foreground.png adaptive_icon_foreground: assets/foreground.png
remove_alpha_ios: true remove_alpha_ios: true
windows:
generate: true

View file

@ -8,10 +8,9 @@
#include <dynamic_system_colors/dynamic_color_plugin_c_api.h> #include <dynamic_system_colors/dynamic_color_plugin_c_api.h>
#include <file_selector_windows/file_selector_windows.h> #include <file_selector_windows/file_selector_windows.h>
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <screen_retriever_windows/screen_retriever_windows_plugin_c_api.h> #include <screen_retriever_windows/screen_retriever_windows_plugin_c_api.h>
#include <simple_secure_storage_windows/simple_secure_storage_windows_plugin_c_api.h>
#include <url_launcher_windows/url_launcher_windows.h> #include <url_launcher_windows/url_launcher_windows.h>
#include <webcrypto/webcrypto_plugin.h>
#include <window_manager/window_manager_plugin.h> #include <window_manager/window_manager_plugin.h>
#include <window_size/window_size_plugin.h> #include <window_size/window_size_plugin.h>
@ -20,14 +19,12 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("DynamicColorPluginCApi")); registry->GetRegistrarForPlugin("DynamicColorPluginCApi"));
FileSelectorWindowsRegisterWithRegistrar( FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows")); registry->GetRegistrarForPlugin("FileSelectorWindows"));
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar( ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ScreenRetrieverWindowsPluginCApi")); registry->GetRegistrarForPlugin("ScreenRetrieverWindowsPluginCApi"));
SimpleSecureStorageWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("SimpleSecureStorageWindowsPluginCApi"));
UrlLauncherWindowsRegisterWithRegistrar( UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows")); registry->GetRegistrarForPlugin("UrlLauncherWindows"));
WebcryptoPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("WebcryptoPlugin"));
WindowManagerPluginRegisterWithRegistrar( WindowManagerPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("WindowManagerPlugin")); registry->GetRegistrarForPlugin("WindowManagerPlugin"));
WindowSizePluginRegisterWithRegistrar( WindowSizePluginRegisterWithRegistrar(

View file

@ -5,10 +5,9 @@
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
dynamic_system_colors dynamic_system_colors
file_selector_windows file_selector_windows
flutter_secure_storage_windows
screen_retriever_windows screen_retriever_windows
simple_secure_storage_windows
url_launcher_windows url_launcher_windows
webcrypto
window_manager window_manager
window_size window_size
) )

17
windows/installer.iss Normal file
View file

@ -0,0 +1,17 @@
[Setup]
AppName=Nexus
AppVersion=1.0.0
DefaultDirName={pf}\Nexus
DefaultGroupName=Nexus
OutputDir=dist
OutputBaseFilename=Nexus-Setup
Compression=lzma
SolidCompression=yes
ArchitecturesInstallIn64BitMode=x64
[Files]
Source: "..\build\windows\x64\runner\Release\*"; DestDir: "{app}"; Flags: recursesubdirs ignoreversion
[Icons]
Name: "{group}\Nexus"; Filename: "{app}\nexus.exe"
Name: "{commondesktop}\Nexus"; Filename: "{app}\nexus.exe"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Before After
Before After