forked from Nexus/nexus
Compare commits
59 commits
4c69831c54
...
a1401f20ea
| Author | SHA1 | Date | |
|---|---|---|---|
|
a1401f20ea |
|||
|
f8d6dcead5 |
|||
|
02c79a6d7c |
|||
|
11aef9fc5a |
|||
|
2f39949a2e |
|||
|
166295fdb5 |
|||
|
607cd54e02 |
|||
|
67b96ae731 |
|||
|
ed81b4afa1 |
|||
|
345fa3b5ff |
|||
|
f50fb6ab09 |
|||
|
5601fb27c0 |
|||
|
0d1f7c1819 |
|||
|
4bbf694479 |
|||
|
dd9b9fdc62 |
|||
|
8b7f88cc0b |
|||
|
70793a2f77 |
|||
|
b2c763deef |
|||
|
5c66d35017 |
|||
|
a25f9d2e73 |
|||
|
4a3b7e9a14 |
|||
|
6974e5cc06 |
|||
|
42c32b1b1c |
|||
|
87466f9d05 |
|||
|
ea72654887 |
|||
|
f1af130a63 |
|||
|
e7b772ef66 |
|||
|
28dfe9e981 |
|||
|
d4c98a0cfb |
|||
|
01772b567a |
|||
|
f9927d1eb3 |
|||
|
0d44d10e05 |
|||
|
11ecec5ab3 |
|||
|
b407bbfdee |
|||
|
04b7ab8e2e |
|||
|
0cae2692bc |
|||
|
b387f0755a |
|||
|
840f2fe464 |
|||
|
e5062683e8 |
|||
|
ffe879680d |
|||
|
32dfba178a |
|||
|
fe845e6cd6 |
|||
|
d3e6340b28 |
|||
|
b6e7bb82da |
|||
|
eb503ba647 |
|||
|
cda971a335 |
|||
| e4f666b824 | |||
| cf2150466e | |||
| 23bbbb533e | |||
|
db9fc597f3 |
|||
|
6839f0bdae |
|||
|
6f4ad046b0 |
|||
|
4494705ef9 |
|||
|
237886971c |
|||
|
95a4e03f00 |
|||
|
9054b6b357 |
|||
|
8b056d8ed1 |
|||
|
edbc647a06 |
|||
| 91516c2f20 |
64 changed files with 948 additions and 465 deletions
39
.github/workflows/android.yml
vendored
Normal file
39
.github/workflows/android.yml
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
name: "Build APK"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
tags: ["*"]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-apk:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Lix GHA Installer Action
|
||||
uses: samueldr/lix-gha-installer-action@v2026-02-22
|
||||
with:
|
||||
extra_nix_config: experimental-features = nix-command flakes flake-self-attrs
|
||||
|
||||
- name: Decode keystore
|
||||
run: echo "$KEYSTORE_CONTENT" | base64 --decode > keystore.jks
|
||||
env:
|
||||
KEYSTORE_CONTENT: ${{ secrets.KEYSTORE_CONTENT }}
|
||||
|
||||
- name: Build app
|
||||
run: nix develop --command bash -c "flutter pub get && dart scripts/generate.dart && flutter pub run build_runner build && flutter build apk --release"
|
||||
env:
|
||||
KEYSTORE_PATH: ../../keystore.jks
|
||||
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
|
||||
|
||||
- name: Upload installer artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: APK
|
||||
path: build/app/outputs/flutter-apk/app-release.apk
|
||||
37
.github/workflows/flatpak.yml
vendored
Normal file
37
.github/workflows/flatpak.yml
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
name: "Build Flatpaks"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
tags: ["*"]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-flatpak:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- arch: x86_64
|
||||
runner: ubuntu-latest
|
||||
- arch: aarch64
|
||||
runner: ubuntu-24.04-arm
|
||||
runs-on: ${{ matrix.runner }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Lix GHA Installer Action
|
||||
uses: samueldr/lix-gha-installer-action@v2026-02-22
|
||||
with:
|
||||
extra_nix_config: experimental-features = nix-command flakes flake-self-attrs
|
||||
|
||||
- name: Build app
|
||||
run: nix build .#flatpak
|
||||
|
||||
- name: Upload installer artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: flatpak-${{ matrix.arch }}
|
||||
path: result/nexus.federated.Nexus.flatpak
|
||||
56
.github/workflows/windows.yml
vendored
56
.github/workflows/windows.yml
vendored
|
|
@ -1,46 +1,50 @@
|
|||
name: "Build Windows Version"
|
||||
name: "Build EXE"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
tags: ["*"]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-windows:
|
||||
runs-on: "windows-latest"
|
||||
build-exe:
|
||||
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"
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
targets: "x86_64-pc-windows-msvc"
|
||||
submodules: recursive
|
||||
|
||||
- name: "Install Flutter dependencies"
|
||||
run: flutter pub get
|
||||
- name: Set up Flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
flutter-version: 3.41.5
|
||||
|
||||
- name: "Run build_runner & build Windows EXE"
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v6
|
||||
|
||||
- name: Build with Flutter
|
||||
run: |
|
||||
flutter pub run build_runner build --delete-conflicting-outputs
|
||||
flutter pub get
|
||||
dart scripts/generate.dart
|
||||
flutter pub run build_runner build
|
||||
flutter build windows --release
|
||||
|
||||
- name: "Upload exe zip"
|
||||
uses: "actions/upload-artifact@v4"
|
||||
- name: Upload exe zip
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: "windows-portable"
|
||||
path: "build/windows/x64/runner/Release/"
|
||||
name: windows-portable
|
||||
path: build/windows/x64/runner/Release/
|
||||
|
||||
- name: "Install Inno Setup"
|
||||
- name: Install Inno Setup
|
||||
run: choco install innosetup -y
|
||||
|
||||
- name: "Build Inno Setup installer"
|
||||
- name: Build Inno Setup installer
|
||||
run: iscc windows/installer.iss
|
||||
|
||||
- name: "Upload installer artifact"
|
||||
uses: "actions/upload-artifact@v4"
|
||||
- name: Upload installer artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: "windows-installer"
|
||||
path: "windows/dist/Nexus-Setup.exe"
|
||||
name: windows-installer
|
||||
path: windows/dist/Nexus-Setup.exe
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -36,7 +36,9 @@ key.properties
|
|||
# Generated Files
|
||||
*.g.dart
|
||||
*.freezed.dart
|
||||
src/
|
||||
|
||||
# Devel Password
|
||||
password.txt
|
||||
|
||||
# Nix
|
||||
/result
|
||||
4
.gitmodules
vendored
Normal file
4
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
[submodule "gomuks"]
|
||||
path = gomuks
|
||||
url = https://github.com/zachatrocity/gomuks
|
||||
branch = init-root-dir
|
||||
22
README.md
22
README.md
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
## Description
|
||||
|
||||
A simple and user-friendly Matrix client made with Flutter and the Matrix Dart SDK.
|
||||
A simple and user-friendly Matrix client made with Flutter and a Gomuks backend.
|
||||
|
||||
## Screenshots
|
||||
|
||||
|
|
@ -17,8 +17,8 @@ A simple and user-friendly Matrix client made with Flutter and the Matrix Dart S
|
|||
|
||||
- [ ] New logo
|
||||
- [ ] Make context menus appear as bottom sheets on mobile
|
||||
- [x] Move from the Dart SDK to the Gomuks SDK with Dart bindings: https://git.federated.nexus/Henry-Hiles/nexus/pulls/2
|
||||
- [ ] Allow using remote gomuks over websocket
|
||||
- [x] Move from the Dart SDK to the Gomuks Backend with Dart bindings: https://git.federated.nexus/Henry-Hiles/nexus/pulls/2
|
||||
- [ ] Allow using remote Gomuks over websocket
|
||||
- [ ] Platform Support
|
||||
- [x] Linux
|
||||
- [x] Windows
|
||||
|
|
@ -54,6 +54,7 @@ A simple and user-friendly Matrix client made with Flutter and the Matrix Dart S
|
|||
- [x] HTML/Markdown
|
||||
- [x] Replies
|
||||
- [x] Choose ping on/off
|
||||
- [ ] Per message profiles
|
||||
- [ ] Attachments
|
||||
- [ ] Commands with [MSC4391](https://github.com/matrix-org/matrix-spec-proposals/pull/4391)
|
||||
- [x] Mentions
|
||||
|
|
@ -64,6 +65,7 @@ A simple and user-friendly Matrix client made with Flutter and the Matrix Dart S
|
|||
- [ ] GIFs using Gomuks' GIF proxies
|
||||
- [x] Recieving
|
||||
- [x] Plain text
|
||||
- [x] Per message profiles
|
||||
- [x] HTML
|
||||
- [x] Replies
|
||||
- [x] Viewing
|
||||
|
|
@ -128,7 +130,7 @@ A simple and user-friendly Matrix client made with Flutter and the Matrix Dart S
|
|||
First, clone and open the repo:
|
||||
|
||||
```sh
|
||||
git clone https://git.federated.nexus/Henry-Hiles/nexus
|
||||
git clone --recurse-submodules https://git.federated.nexus/Henry-Hiles/nexus
|
||||
cd nexus
|
||||
```
|
||||
|
||||
|
|
@ -136,12 +138,12 @@ cd nexus
|
|||
|
||||
#### Linux
|
||||
|
||||
- With Nix: Either use direnv, or `nix flake develop`
|
||||
- With Nix: Either use direnv and `direnv allow`, or `nix flake develop`
|
||||
- Without Nix: Install Flutter, Go, Olm, Git, Clang, and GLibc.
|
||||
|
||||
#### Windows / MacOS
|
||||
|
||||
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.
|
||||
I don't really know. You will need Flutter, Git, 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
|
||||
|
||||
|
|
@ -151,13 +153,7 @@ Get dependencies:
|
|||
flutter pub get
|
||||
```
|
||||
|
||||
Get dependencies:
|
||||
|
||||
```sh
|
||||
flutter pub get
|
||||
```
|
||||
|
||||
Clone Gomuks and generate bindings:
|
||||
Generate Gomuks bindings:
|
||||
|
||||
```sh
|
||||
scripts/generate.sh
|
||||
|
|
|
|||
|
|
@ -39,6 +39,11 @@ android {
|
|||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
// do we want to update.. eventually?
|
||||
jvmTarget = "17"
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "nexus.federated.Nexus"
|
||||
minSdk = 29
|
||||
|
|
@ -50,7 +55,8 @@ android {
|
|||
signingConfigs {
|
||||
release {
|
||||
keyAlias "key"
|
||||
storeFile keystoreProperties['path'] ? file(keystoreProperties['path']) : file(System.getenv("KEYSTORE_PATH"))
|
||||
def storePath = keystoreProperties['path'] ?: System.getenv("KEYSTORE_PATH")
|
||||
storeFile storePath ? file(storePath) : null
|
||||
keyPassword keystoreProperties['password'] ?: System.getenv("KEYSTORE_PASSWORD")
|
||||
storePassword keystoreProperties['password'] ?: System.getenv("KEYSTORE_PASSWORD")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
android:label="Nexus"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/nexus_round"
|
||||
android:roundIcon="@mipmap/ic_launcher"
|
||||
android:allowBackup="false"
|
||||
android:fullBackupContent="false">
|
||||
<activity
|
||||
|
|
|
|||
BIN
assets/background.png
Normal file
BIN
assets/background.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
62
assets/background.svg
Normal file
62
assets/background.svg
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="100mm"
|
||||
height="100mm"
|
||||
viewBox="0 0 100 100"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
|
||||
sodipodi:docname="background.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:zoom="1.0847363"
|
||||
inkscape:cx="57.156749"
|
||||
inkscape:cy="214.33781"
|
||||
inkscape:window-width="1904"
|
||||
inkscape:window-height="971"
|
||||
inkscape:window-x="35"
|
||||
inkscape:window-y="32"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="layer1" /><defs
|
||||
id="defs1"><linearGradient
|
||||
id="linearGradient10"
|
||||
inkscape:collect="always"><stop
|
||||
style="stop-color:#c7a312;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop10" /><stop
|
||||
style="stop-color:#26a0b3;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop11" /></linearGradient><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient10"
|
||||
id="linearGradient11"
|
||||
x1="20.031296"
|
||||
y1="32.697563"
|
||||
x2="90.709213"
|
||||
y2="66.3423"
|
||||
gradientUnits="userSpaceOnUse" /></defs><g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"><rect
|
||||
style="fill:url(#linearGradient11);fill-opacity:1;stroke:none;stroke-width:7.99999;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
|
||||
id="rect10"
|
||||
width="100"
|
||||
height="100"
|
||||
x="0"
|
||||
y="0"
|
||||
ry="28.294127" /></g></svg>
|
||||
|
After Width: | Height: | Size: 2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 45 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.4 MiB |
85
flake.lock
generated
85
flake.lock
generated
|
|
@ -18,17 +18,54 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1767640445,
|
||||
"narHash": "sha256-UWYqmD7JFBEDBHWYcqE6s6c77pWdcU/i+bwD6XxMb8A=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "9f0c42f8bc7151b8e7e5840fb3bd454ad850d8c5",
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix2flatpak": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1774604963,
|
||||
"narHash": "sha256-MtAW1FIdirSlUAAO7s1u9auv5y3I6t3uJ+GeEbqiqxI=",
|
||||
"owner": "neobrain",
|
||||
"repo": "nix2flatpak",
|
||||
"rev": "3e04657fbcb49956ac301410b071a7f0b2ad5988",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "neobrain",
|
||||
"repo": "nix2flatpak",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1773389992,
|
||||
"narHash": "sha256-wvfdLLWJ2I9oEpDd9PfMA8osfIZicoQ5MT1jIwNs9Tk=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "c06b4ae3d6599a672a6210b7021d699c351eebda",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
|
|
@ -49,10 +86,42 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1767640445,
|
||||
"narHash": "sha256-UWYqmD7JFBEDBHWYcqE6s6c77pWdcU/i+bwD6XxMb8A=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "9f0c42f8bc7151b8e7e5840fb3bd454ad850d8c5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-parts": "flake-parts",
|
||||
"nixpkgs": "nixpkgs"
|
||||
"nix2flatpak": "nix2flatpak",
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
44
flake.nix
44
flake.nix
|
|
@ -2,8 +2,10 @@
|
|||
description = "Nexus Flutter Flake";
|
||||
|
||||
inputs = {
|
||||
self.submodules = true;
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||
nix2flatpak.url = "github:neobrain/nix2flatpak";
|
||||
};
|
||||
|
||||
outputs =
|
||||
|
|
@ -33,36 +35,42 @@
|
|||
_module.args.pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
config = {
|
||||
permittedInsecurePackages = [ "olm-3.2.16" ];
|
||||
android_sdk.accept_license = true;
|
||||
allowUnfree = true;
|
||||
};
|
||||
};
|
||||
|
||||
devShells =
|
||||
packages =
|
||||
let
|
||||
packages = with pkgs; [
|
||||
go
|
||||
olm
|
||||
git
|
||||
];
|
||||
|
||||
env = {
|
||||
LIBCLANG_PATH = lib.makeLibraryPath [ pkgs.libclang ];
|
||||
LD_LIBRARY_PATH = "./build/native_assets/linux:${lib.makeLibraryPath [ pkgs.zlib ]}";
|
||||
CPATH = lib.makeSearchPath "include" [ pkgs.glibc.dev ];
|
||||
default = pkgs.callPackage ./linux/nix/pkg {
|
||||
src = self;
|
||||
};
|
||||
in
|
||||
{
|
||||
default = pkgs.mkShell {
|
||||
inherit env;
|
||||
packages = packages ++ [
|
||||
pkgs.flutter
|
||||
];
|
||||
inherit default;
|
||||
|
||||
flatpak = inputs.nix2flatpak.lib.${system}.mkFlatpak {
|
||||
appName = "Nexus";
|
||||
developer = "QuadRadical";
|
||||
appId = "nexus.federated.Nexus";
|
||||
package = default;
|
||||
runtime = "org.gnome.Platform/49";
|
||||
permissions = {
|
||||
share = [ "network" ];
|
||||
sockets = [
|
||||
"fallback-x11"
|
||||
"wayland"
|
||||
];
|
||||
devices = [ "dri" ];
|
||||
};
|
||||
};
|
||||
|
||||
nix = pkgs.mkShell { inherit packages env; };
|
||||
gomuks = pkgs.callPackage ./linux/nix/pkg/gomuks.nix {
|
||||
src = self;
|
||||
};
|
||||
};
|
||||
|
||||
devShells.default = pkgs.callPackage ./linux/nix/devshell.nix { };
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
1
gomuks
Submodule
1
gomuks
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit daa0ba028e7d89ba9fc7580fc8099348e6145cb3
|
||||
105
hook/build.dart
105
hook/build.dart
|
|
@ -3,11 +3,12 @@ import "package:hooks/hooks.dart";
|
|||
import "package:code_assets/code_assets.dart";
|
||||
|
||||
Future<void> main(List<String> args) => build(args, (input, output) async {
|
||||
final buildDir = input.packageRoot.resolve("src/");
|
||||
if (await File(buildDir.resolve("lock").toFilePath()).exists()) return;
|
||||
final codeConfig = input.config.code;
|
||||
final targetOS = codeConfig.targetOS;
|
||||
final targetArch = codeConfig.targetArchitecture;
|
||||
|
||||
final targetOS = input.config.code.targetOS;
|
||||
String libFileName;
|
||||
Map<String, String> env = {};
|
||||
switch (targetOS) {
|
||||
case OS.linux:
|
||||
libFileName = "libgomuks.so";
|
||||
|
|
@ -17,24 +18,60 @@ Future<void> main(List<String> args) => build(args, (input, output) async {
|
|||
break;
|
||||
case OS.windows:
|
||||
libFileName = "libgomuks.dll";
|
||||
env = {"GOCACHE": r"C:\Users\runneradmin\AppData\Local\go-build"};
|
||||
break;
|
||||
case OS.android:
|
||||
libFileName = "libgomuks.so";
|
||||
|
||||
final targetNdkApi = codeConfig.android.targetNdkApi;
|
||||
|
||||
final ndkHome =
|
||||
Platform.environment["ANDROID_NDK_HOME"] ??
|
||||
Platform.environment["ANDROID_NDK_ROOT"] ??
|
||||
Platform.environment["NDK_HOME"] ??
|
||||
await _findNdkFromSdk();
|
||||
if (ndkHome == null) {
|
||||
throw Exception(
|
||||
"Could not find Android NDK. Set ANDROID_NDK_HOME or install via sdkmanager.",
|
||||
);
|
||||
}
|
||||
|
||||
final hostTag = _ndkHostTag();
|
||||
final (goArch, ccTriple) = _androidArch(targetArch);
|
||||
final cc =
|
||||
"$ndkHome/toolchains/llvm/prebuilt/$hostTag/bin/$ccTriple$targetNdkApi-clang";
|
||||
|
||||
env = {"CGO_ENABLED": "1", "GOOS": "android", "GOARCH": goArch, "CC": cc};
|
||||
break;
|
||||
default:
|
||||
throw UnsupportedError("Unsupported OS: $targetOS");
|
||||
}
|
||||
|
||||
final gomuksBuildDir = buildDir.resolve("gomuks/");
|
||||
final libFile = gomuksBuildDir.resolve(libFileName);
|
||||
var libFile = input.packageRoot.resolve(libFileName);
|
||||
final gomuksBuildDir = input.packageRoot.resolve("gomuks/");
|
||||
|
||||
print("Building Gomuks shared library $libFileName from source...");
|
||||
final result = await Process.run("go", [
|
||||
"build",
|
||||
"-o",
|
||||
libFile.path,
|
||||
"-buildmode=c-shared",
|
||||
], workingDirectory: gomuksBuildDir.resolve("source/pkg/ffi/").toFilePath());
|
||||
if (!(await File.fromUri(libFile).exists())) {
|
||||
final buildDir = input.packageRoot.resolve("build/");
|
||||
libFile = buildDir.resolve("${targetArch.name}/$libFileName");
|
||||
|
||||
if (result.exitCode != 0) {
|
||||
throw Exception("Failed to build Gomuks shared library\n${result.stderr}");
|
||||
// goheif/dav1d supported on Android would need to fix upstream
|
||||
final tags = targetOS == OS.android ? "goolm,noheic" : "goolm";
|
||||
|
||||
print(
|
||||
"Building Gomuks shared library $libFileName (${targetOS.name}/${targetArch.name}) from source...",
|
||||
);
|
||||
final result = await Process.run(
|
||||
"go",
|
||||
["build", "-tags", tags, "-o", libFile.path, "-buildmode=c-shared"],
|
||||
workingDirectory: gomuksBuildDir.resolve("pkg/ffi/").toFilePath(),
|
||||
environment: env.isNotEmpty ? env : null,
|
||||
);
|
||||
|
||||
if (result.exitCode != 0) {
|
||||
throw Exception(
|
||||
"Failed to build Gomuks shared library\n${result.stderr}",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final generatedFile = "src/third_party/gomuks.g.dart";
|
||||
|
|
@ -52,3 +89,43 @@ Future<void> main(List<String> args) => build(args, (input, output) async {
|
|||
..dependencies.add(gomuksBuildDir);
|
||||
print("Done!");
|
||||
});
|
||||
|
||||
Future<String?> _findNdkFromSdk() async {
|
||||
// pretty sure this wont be needed with nix, i'll get this removed
|
||||
final androidHome =
|
||||
Platform.environment["ANDROID_HOME"] ??
|
||||
Platform.environment["ANDROID_SDK_ROOT"];
|
||||
if (androidHome == null) return null;
|
||||
final ndkDir = Directory("$androidHome/ndk");
|
||||
if (!await ndkDir.exists()) return null;
|
||||
final versions = await ndkDir.list().toList();
|
||||
if (versions.isEmpty) return null;
|
||||
versions.sort((a, b) => a.path.compareTo(b.path));
|
||||
return versions.last.path;
|
||||
}
|
||||
|
||||
String _ndkHostTag() {
|
||||
if (Platform.isMacOS) {
|
||||
return "darwin-x86_64";
|
||||
} else if (Platform.isLinux) {
|
||||
return "linux-x86_64";
|
||||
} else if (Platform.isWindows) {
|
||||
return "windows-x86_64";
|
||||
}
|
||||
throw UnsupportedError("Unsupported host platform for Android NDK");
|
||||
}
|
||||
|
||||
(String goArch, String ccTriple) _androidArch(Architecture arch) {
|
||||
switch (arch) {
|
||||
case Architecture.arm64:
|
||||
return ("arm64", "aarch64-linux-android");
|
||||
case Architecture.arm:
|
||||
return ("arm", "armv7a-linux-androideabi");
|
||||
case Architecture.x64:
|
||||
return ("amd64", "x86_64-linux-android");
|
||||
case Architecture.ia32:
|
||||
return ("386", "i686-linux-android");
|
||||
default:
|
||||
throw UnsupportedError("Unsupported Android architecture: $arch");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -387,7 +387,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = nexus.federated.nexus;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = nexus.federated.Nexus;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
|
|
@ -519,7 +519,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = nexus.federated.nexus;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = nexus.federated.Nexus;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
|
|
@ -545,7 +545,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = nexus.federated.nexus;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = nexus.federated.Nexus;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
|
|
|
|||
44
lib/controllers/author_controller.dart
Normal file
44
lib/controllers/author_controller.dart
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import "dart:async";
|
||||
import "package:collection/collection.dart";
|
||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:nexus/controllers/members_controller.dart";
|
||||
import "package:nexus/models/configs/author_config.dart";
|
||||
import "package:nexus/models/membership.dart";
|
||||
|
||||
class AuthorController extends AsyncNotifier<Membership> {
|
||||
final AuthorConfig config;
|
||||
AuthorController(this.config);
|
||||
|
||||
@override
|
||||
Future<Membership> build() async {
|
||||
var member = await ref.watch(
|
||||
MembersController.provider(config.room).selectAsync(
|
||||
(value) => value.firstWhereOrNull(
|
||||
(membership) => membership.userId == config.message.authorId,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final pmp = config.message.metadata?["pmp"] == null
|
||||
? null
|
||||
: Membership.fromContent(
|
||||
IMap(config.message.metadata?["pmp"]),
|
||||
config.message.authorId,
|
||||
);
|
||||
|
||||
return Membership(
|
||||
avatarUrl: pmp?.avatarUrl ?? member?.avatarUrl,
|
||||
displayName:
|
||||
pmp?.displayName ??
|
||||
member?.displayName ??
|
||||
config.message.authorId.substring(1).split(":").first,
|
||||
userId: config.message.authorId,
|
||||
);
|
||||
}
|
||||
|
||||
static final provider = AsyncNotifierProvider.family
|
||||
.autoDispose<AuthorController, Membership, AuthorConfig>(
|
||||
AuthorController.new,
|
||||
);
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import "dart:developer";
|
||||
import "dart:ffi";
|
||||
import "dart:io";
|
||||
import "dart:isolate";
|
||||
import "package:collection/collection.dart";
|
||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||
|
|
@ -31,11 +32,20 @@ import "package:nexus/models/sync_data.dart";
|
|||
import "package:nexus/models/sync_status.dart";
|
||||
import "package:nexus/src/third_party/gomuks.g.dart";
|
||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:path_provider/path_provider.dart";
|
||||
|
||||
class ClientController extends AsyncNotifier<int> {
|
||||
@override
|
||||
Future<int> build() async {
|
||||
final handle = await Isolate.run(GomuksInit);
|
||||
final Pointer<Char> root;
|
||||
if (Platform.isAndroid) {
|
||||
final dir = await getApplicationSupportDirectory();
|
||||
root = "${dir.path}/gomuks".toNativeUtf8().cast();
|
||||
} else {
|
||||
root = nullptr.cast();
|
||||
}
|
||||
|
||||
final handle = GomuksInit(root);
|
||||
|
||||
final callable =
|
||||
NativeCallable<
|
||||
|
|
@ -143,12 +153,12 @@ class ClientController extends AsyncNotifier<int> {
|
|||
Future<void> sendMessage(SendMessageRequest request) =>
|
||||
_sendCommand("send_message", request.toJson());
|
||||
|
||||
Future<bool> verify(String recoveryKey) async {
|
||||
Future<String?> verify(String recoveryKey) async {
|
||||
try {
|
||||
await _sendCommand("verify", {"recovery_key": recoveryKey});
|
||||
return true;
|
||||
return null;
|
||||
} catch (error) {
|
||||
return false;
|
||||
return error.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -218,12 +228,12 @@ class ClientController extends AsyncNotifier<int> {
|
|||
});
|
||||
}
|
||||
|
||||
Future<bool> login(LoginRequest login) async {
|
||||
Future<String?> login(LoginRequest login) async {
|
||||
try {
|
||||
await _sendCommand("login", login.toJson());
|
||||
return true;
|
||||
return null;
|
||||
} catch (error) {
|
||||
return false;
|
||||
return error.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +1,39 @@
|
|||
import "package:collection/collection.dart";
|
||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:nexus/models/event.dart";
|
||||
import "package:nexus/controllers/client_controller.dart";
|
||||
import "package:nexus/models/membership.dart";
|
||||
import "package:nexus/models/requests/get_room_state_request.dart";
|
||||
import "package:nexus/models/room.dart";
|
||||
|
||||
class MembersController extends Notifier<IList<Event>> {
|
||||
class MembersController extends AsyncNotifier<IList<Membership>> {
|
||||
final Room room;
|
||||
MembersController(this.room);
|
||||
|
||||
@override
|
||||
IList<Event> build() => (room.state["m.room.member"]?.values ?? [])
|
||||
.map(
|
||||
(eventRowId) =>
|
||||
room.events.firstWhereOrNull((event) => event.rowId == eventRowId),
|
||||
)
|
||||
.nonNulls
|
||||
.where((member) => member.content["membership"] == "join")
|
||||
.toIList();
|
||||
Future<IList<Membership>> build() async {
|
||||
if (room.metadata == null) return const IList.empty();
|
||||
|
||||
static final provider = NotifierProvider.family
|
||||
.autoDispose<MembersController, IList<Event>, Room>(
|
||||
final state = await ref
|
||||
.watch(ClientController.provider.notifier)
|
||||
.getRoomState(
|
||||
GetRoomStateRequest(
|
||||
roomId: room.metadata!.id,
|
||||
fetchMembers: room.metadata!.hasMemberList == false,
|
||||
includeMembers: true,
|
||||
),
|
||||
);
|
||||
|
||||
return state.nonNulls
|
||||
.where((member) => member.content["membership"] == "join")
|
||||
.map(
|
||||
(membership) =>
|
||||
Membership.fromContent(membership.content, membership.stateKey!),
|
||||
)
|
||||
.toIList();
|
||||
}
|
||||
|
||||
static final provider =
|
||||
AsyncNotifierProvider.family<MembersController, IList<Membership>, Room>(
|
||||
MembersController.new,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,8 @@ import "package:collection/collection.dart";
|
|||
import "package:flutter_chat_core/flutter_chat_core.dart";
|
||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:nexus/controllers/client_state_controller.dart";
|
||||
import "package:nexus/controllers/members_controller.dart";
|
||||
import "package:nexus/helpers/extensions/mxc_to_https.dart";
|
||||
import "package:nexus/models/message_config.dart";
|
||||
import "package:nexus/models/configs/message_config.dart";
|
||||
|
||||
class MessageController extends AsyncNotifier<Message?> {
|
||||
final MessageConfig config;
|
||||
|
|
@ -27,12 +26,6 @@ class MessageController extends AsyncNotifier<Message?> {
|
|||
|
||||
if (!ref.mounted) return null;
|
||||
|
||||
final members = ref.read(MembersController.provider(config.room));
|
||||
final author = members.firstWhereOrNull(
|
||||
(member) => member.stateKey == event.authorId,
|
||||
);
|
||||
if (!ref.mounted) return null;
|
||||
|
||||
final content = (event.decrypted ?? event.content);
|
||||
final type = (config.event.decryptedType ?? config.event.type);
|
||||
final newContent = content["m.new_content"] as Map?;
|
||||
|
|
@ -52,14 +45,11 @@ class MessageController extends AsyncNotifier<Message?> {
|
|||
"timelineId": event.timelineRowId,
|
||||
"big": event.localContent?.bigEmoji == true,
|
||||
"eventType": type,
|
||||
"avatarUrl": author?.content["avatar_url"],
|
||||
"pmp": event.content["com.beeper.per_message_profile"],
|
||||
"editSource":
|
||||
event.localContent?.editSource ??
|
||||
newContent?["body"] ??
|
||||
content["body"],
|
||||
"displayName": author?.content["displayname"]?.isNotEmpty == true
|
||||
? author?.content["displayname"]
|
||||
: event.authorId.substring(1).split(":")[0],
|
||||
"txnId": config.event.transactionId,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
|||
import "package:flutter_chat_core/flutter_chat_core.dart";
|
||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:nexus/controllers/message_controller.dart";
|
||||
import "package:nexus/models/message_config.dart";
|
||||
import "package:nexus/models/messages_config.dart";
|
||||
import "package:nexus/models/configs/message_config.dart";
|
||||
import "package:nexus/models/configs/messages_config.dart";
|
||||
|
||||
class MessagesController extends AsyncNotifier<IList<Message>> {
|
||||
final MessagesConfig config;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import "dart:async";
|
||||
|
||||
import "package:collection/collection.dart";
|
||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||
import "package:flutter_chat_core/flutter_chat_core.dart";
|
||||
|
|
@ -11,8 +10,8 @@ import "package:nexus/controllers/message_controller.dart";
|
|||
import "package:nexus/controllers/messages_controller.dart";
|
||||
import "package:nexus/controllers/new_events_controller.dart";
|
||||
import "package:nexus/controllers/rooms_controller.dart";
|
||||
import "package:nexus/models/message_config.dart";
|
||||
import "package:nexus/models/messages_config.dart";
|
||||
import "package:nexus/models/configs/messages_config.dart";
|
||||
import "package:nexus/models/configs/message_config.dart";
|
||||
import "package:nexus/models/requests/get_room_state_request.dart";
|
||||
import "package:nexus/models/requests/paginate_request.dart";
|
||||
import "package:nexus/models/requests/redact_event_request.dart";
|
||||
|
|
@ -31,11 +30,7 @@ class RoomChatController extends AsyncNotifier<InMemoryChatController> {
|
|||
if (room == null) return InMemoryChatController();
|
||||
|
||||
final state = await client.getRoomState(
|
||||
GetRoomStateRequest(
|
||||
roomId: roomId,
|
||||
fetchMembers: room.metadata?.hasMemberList == false,
|
||||
includeMembers: true,
|
||||
),
|
||||
GetRoomStateRequest(roomId: roomId),
|
||||
);
|
||||
|
||||
ref
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ class RoomsController extends Notifier<IMap<String, Room>> {
|
|||
return acc.add(
|
||||
roomId,
|
||||
existing?.copyWith(
|
||||
hasMore: incoming.hasMore,
|
||||
metadata: incoming.metadata ?? existing.metadata,
|
||||
events: events!,
|
||||
state: incoming.state.entries.fold(
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ extension JoinRoomWithSnackbars on ClientController {
|
|||
WidgetRef ref,
|
||||
) async {
|
||||
final roomIdOrAlias = roomAlias.mention ?? roomAlias;
|
||||
// TODO: Parse vias properly
|
||||
|
||||
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ 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";
|
||||
import "package:window_size/window_size.dart";
|
||||
|
||||
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||
|
||||
|
|
@ -59,14 +58,11 @@ void showError(Object error, [StackTrace? stackTrace]) {
|
|||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
await windowManager.ensureInitialized();
|
||||
await windowManager.waitUntilReadyToShow(
|
||||
WindowOptions(titleBarStyle: TitleBarStyle.hidden),
|
||||
);
|
||||
|
||||
if (Platform.isLinux) {
|
||||
setWindowMinSize(const Size.square(500));
|
||||
} else {
|
||||
if (Platform.isLinux || Platform.isMacOS || Platform.isWindows) {
|
||||
await windowManager.ensureInitialized();
|
||||
await windowManager.waitUntilReadyToShow(
|
||||
WindowOptions(titleBarStyle: TitleBarStyle.hidden),
|
||||
);
|
||||
await windowManager.setMinimumSize(Size.square(500));
|
||||
}
|
||||
|
||||
|
|
|
|||
14
lib/models/configs/author_config.dart
Normal file
14
lib/models/configs/author_config.dart
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import "package:flutter_chat_core/flutter_chat_core.dart";
|
||||
import "package:freezed_annotation/freezed_annotation.dart";
|
||||
import "package:nexus/models/room.dart";
|
||||
part "author_config.freezed.dart";
|
||||
part "author_config.g.dart";
|
||||
|
||||
@freezed
|
||||
abstract class AuthorConfig with _$AuthorConfig {
|
||||
const factory AuthorConfig({required Message message, required Room room}) =
|
||||
_AuthorConfig;
|
||||
|
||||
factory AuthorConfig.fromJson(Map<String, Object?> json) =>
|
||||
_$AuthorConfigFromJson(json);
|
||||
}
|
||||
22
lib/models/membership.dart
Normal file
22
lib/models/membership.dart
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||
import "package:freezed_annotation/freezed_annotation.dart";
|
||||
part "membership.freezed.dart";
|
||||
|
||||
@freezed
|
||||
abstract class Membership with _$Membership {
|
||||
const Membership._();
|
||||
const factory Membership({
|
||||
required Uri? avatarUrl,
|
||||
required String displayName,
|
||||
required String userId,
|
||||
}) = _Membership;
|
||||
|
||||
factory Membership.fromContent(
|
||||
IMap<String, dynamic> content,
|
||||
String userId,
|
||||
) => Membership(
|
||||
avatarUrl: Uri.tryParse(content["avatar_url"] ?? ""),
|
||||
userId: userId,
|
||||
displayName: content["displayname"] ?? userId.substring(1).split(":").first,
|
||||
);
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ part "get_room_state_request.g.dart";
|
|||
abstract class GetRoomStateRequest with _$GetRoomStateRequest {
|
||||
const factory GetRoomStateRequest({
|
||||
required String roomId,
|
||||
required bool fetchMembers,
|
||||
@Default(false) bool fetchMembers,
|
||||
@Default(false) bool includeMembers,
|
||||
}) = _GetRoomStateRequest;
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class ChatPage extends ConsumerWidget {
|
|||
body: Builder(
|
||||
builder: (context) => Row(
|
||||
children: [
|
||||
if (isDesktop) Sidebar(),
|
||||
if (isDesktop) Sidebar(isDesktop: isDesktop),
|
||||
Expanded(
|
||||
child: RoomChat(
|
||||
isDesktop: isDesktop,
|
||||
|
|
@ -26,7 +26,7 @@ class ChatPage extends ConsumerWidget {
|
|||
],
|
||||
),
|
||||
),
|
||||
drawer: isDesktop ? null : Sidebar(),
|
||||
drawer: isDesktop ? null : Sidebar(isDesktop: isDesktop),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ class LoginPage extends HookConsumerWidget {
|
|||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
isLoading.value = true;
|
||||
final succeeded = await client.login(
|
||||
final error = await client.login(
|
||||
LoginRequest(
|
||||
username: username.text,
|
||||
password: password.text,
|
||||
|
|
@ -183,11 +183,11 @@ class LoginPage extends HookConsumerWidget {
|
|||
),
|
||||
);
|
||||
|
||||
if (!succeeded && context.mounted) {
|
||||
if (error != null && context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
"Login failed. Is your password right?",
|
||||
"Login failed. Is your password right?\nError: $error",
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.onErrorContainer,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import "package:flutter/material.dart";
|
|||
import "package:flutter_hooks/flutter_hooks.dart";
|
||||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||
import "package:nexus/controllers/client_controller.dart";
|
||||
import "package:nexus/widgets/appbar.dart";
|
||||
import "package:nexus/widgets/form_text_input.dart";
|
||||
|
||||
class VerifyPage extends HookConsumerWidget {
|
||||
|
|
@ -11,72 +12,75 @@ class VerifyPage extends HookConsumerWidget {
|
|||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final passphraseController = useTextEditingController();
|
||||
final isVerifying = useState(false);
|
||||
return AlertDialog(
|
||||
title: Text("Verify"),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Enter your recovery key or passphrase below to unlock encrypted messages.\nYour passphrase is usually not the same as your password.",
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
FormTextInput(
|
||||
required: false,
|
||||
autofocus: true,
|
||||
capitalize: true,
|
||||
controller: passphraseController,
|
||||
obscure: true,
|
||||
title: "Recovery Key or Passphrase",
|
||||
return Scaffold(
|
||||
appBar: Appbar(),
|
||||
body: AlertDialog(
|
||||
title: Text("Verify"),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Enter your recovery key or passphrase below to unlock encrypted messages.\nYour passphrase is usually not the same as your password.",
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
FormTextInput(
|
||||
required: false,
|
||||
autofocus: true,
|
||||
capitalize: true,
|
||||
controller: passphraseController,
|
||||
obscure: true,
|
||||
title: "Recovery Key or Passphrase",
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: isVerifying.value
|
||||
? null
|
||||
: () async {
|
||||
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||
final snackbar = scaffoldMessenger.showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
"Attempting to verify with recovery key...",
|
||||
),
|
||||
duration: Duration(days: 999),
|
||||
),
|
||||
);
|
||||
|
||||
isVerifying.value = true;
|
||||
|
||||
final error = await ref
|
||||
.watch(ClientController.provider.notifier)
|
||||
.verify(passphraseController.text);
|
||||
|
||||
snackbar.close();
|
||||
if (error != null) {
|
||||
isVerifying.value = false;
|
||||
if (context.mounted) {
|
||||
scaffoldMessenger.showSnackBar(
|
||||
SnackBar(
|
||||
backgroundColor: Theme.of(
|
||||
context,
|
||||
).colorScheme.errorContainer,
|
||||
content: Text(
|
||||
"Verification failed. Is your passphrase correct?\nError: $error",
|
||||
style: TextStyle(
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.onErrorContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Text("Verify"),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: isVerifying.value
|
||||
? null
|
||||
: () async {
|
||||
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||
final snackbar = scaffoldMessenger.showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
"Attempting to verify with recovery key...",
|
||||
),
|
||||
duration: Duration(days: 999),
|
||||
),
|
||||
);
|
||||
|
||||
isVerifying.value = true;
|
||||
|
||||
final success = await ref
|
||||
.watch(ClientController.provider.notifier)
|
||||
.verify(passphraseController.text);
|
||||
|
||||
snackbar.close();
|
||||
if (!success) {
|
||||
isVerifying.value = false;
|
||||
if (context.mounted) {
|
||||
scaffoldMessenger.showSnackBar(
|
||||
SnackBar(
|
||||
backgroundColor: Theme.of(
|
||||
context,
|
||||
).colorScheme.errorContainer,
|
||||
content: Text(
|
||||
"Verification failed. Is your passphrase correct?",
|
||||
style: TextStyle(
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.onErrorContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Text("Verify"),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,15 +35,14 @@ class Appbar extends StatelessWidget implements PreferredSizeWidget {
|
|||
}
|
||||
|
||||
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,
|
||||
title: IgnorePointer(child: title),
|
||||
flexibleSpace: GestureDetector(onDoubleTap: maximize),
|
||||
actions: [
|
||||
...actions,
|
||||
if (!(Platform.isAndroid || Platform.isIOS)) ...[
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import "dart:io";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter/services.dart";
|
||||
import "package:flutter_chat_core/flutter_chat_core.dart";
|
||||
|
|
@ -8,8 +7,8 @@ import "package:hooks_riverpod/hooks_riverpod.dart";
|
|||
import "package:nexus/controllers/room_chat_controller.dart";
|
||||
import "package:nexus/models/relation_type.dart";
|
||||
import "package:nexus/models/room.dart";
|
||||
import "package:nexus/widgets/chat_page/mention_overlay.dart";
|
||||
import "package:nexus/widgets/chat_page/relation_preview.dart";
|
||||
import "package:nexus/widgets/chat_page/composer/mention_overlay.dart";
|
||||
import "package:nexus/widgets/chat_page/composer/relation_preview.dart";
|
||||
|
||||
class ChatBox extends HookConsumerWidget {
|
||||
final Message? relatedMessage;
|
||||
|
|
@ -55,20 +54,15 @@ class ChatBox extends HookConsumerWidget {
|
|||
|
||||
final node = useFocusNode(
|
||||
onKeyEvent: (_, event) {
|
||||
if (event is KeyDownEvent && !Platform.isAndroid && !Platform.isIOS) {
|
||||
if (event.logicalKey == LogicalKeyboardKey.enter &&
|
||||
!HardwareKeyboard.instance.isShiftPressed) {
|
||||
send();
|
||||
return KeyEventResult.handled;
|
||||
} else if (event.logicalKey == LogicalKeyboardKey.escape) {
|
||||
onDismiss();
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
if (event is KeyDownEvent &&
|
||||
event.logicalKey == LogicalKeyboardKey.escape) {
|
||||
onDismiss();
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
|
||||
return KeyEventResult.ignored;
|
||||
},
|
||||
)..requestFocus();
|
||||
);
|
||||
|
||||
final style = TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
|
|
@ -86,10 +80,11 @@ class ChatBox extends HookConsumerWidget {
|
|||
child: Column(
|
||||
children: [
|
||||
RelationPreview(
|
||||
relatedMessage,
|
||||
room: room,
|
||||
shouldMention: shouldMention.value,
|
||||
toggleShouldMention: () =>
|
||||
shouldMention.value = !shouldMention.value,
|
||||
relatedMessage: relatedMessage,
|
||||
relationType: relationType,
|
||||
onDismiss: onDismiss,
|
||||
),
|
||||
|
|
@ -155,7 +150,9 @@ class ChatBox extends HookConsumerWidget {
|
|||
),
|
||||
controller: controller.value,
|
||||
key: key,
|
||||
autofocus: true,
|
||||
// TODO: Setting for send on enter on / off
|
||||
onFieldSubmitted: (_) => send(),
|
||||
textInputAction: TextInputAction.done,
|
||||
focusNode: node,
|
||||
),
|
||||
),
|
||||
|
|
@ -2,6 +2,7 @@ import "package:flutter/material.dart";
|
|||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||
import "package:nexus/controllers/members_controller.dart";
|
||||
import "package:nexus/controllers/rooms_controller.dart";
|
||||
import "package:nexus/helpers/extensions/better_when.dart";
|
||||
import "package:nexus/models/room.dart";
|
||||
import "package:nexus/widgets/avatar_or_hash.dart";
|
||||
import "package:nexus/widgets/loading.dart";
|
||||
|
|
@ -31,55 +32,47 @@ class MentionOverlay extends ConsumerWidget {
|
|||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
padding: EdgeInsets.all(8),
|
||||
child: switch (triggerCharacter) {
|
||||
"@" => Consumer(
|
||||
builder: (_, ref, _) {
|
||||
final members = ref.watch(MembersController.provider(room));
|
||||
return ListView(
|
||||
children:
|
||||
(query.isEmpty
|
||||
? members
|
||||
: members.where(
|
||||
(member) =>
|
||||
member.stateKey?.toLowerCase().contains(
|
||||
query.toLowerCase(),
|
||||
) ==
|
||||
true ||
|
||||
(member.content["displayname"] as String?)
|
||||
?.toLowerCase()
|
||||
.contains(query.toLowerCase()) ==
|
||||
true,
|
||||
))
|
||||
.map(
|
||||
(member) => ListTile(
|
||||
leading: AvatarOrHash(
|
||||
Uri.tryParse(
|
||||
member.content["avatar_url"] ?? "",
|
||||
),
|
||||
member.content["displayname"] ?? "",
|
||||
),
|
||||
title: Text(
|
||||
member.content["displayname"] as String? ??
|
||||
member.stateKey ??
|
||||
"Unknown User",
|
||||
),
|
||||
subtitle: member.stateKey != null
|
||||
? Text(member.stateKey!)
|
||||
: null,
|
||||
onTap: () => addTag(
|
||||
id: "[@${member.content["displayname"]}](https://matrix.to/#/${member.stateKey})",
|
||||
name:
|
||||
member.stateKey
|
||||
?.substring(1)
|
||||
"@" =>
|
||||
ref
|
||||
.watch(MembersController.provider(room))
|
||||
.betterWhen(
|
||||
data: (members) => ListView(
|
||||
children:
|
||||
(query.isEmpty
|
||||
? members
|
||||
: members.where(
|
||||
(member) =>
|
||||
member.userId.toLowerCase().contains(
|
||||
query.toLowerCase(),
|
||||
) ==
|
||||
true ||
|
||||
member.displayName
|
||||
.toLowerCase()
|
||||
.contains(
|
||||
query.toLowerCase(),
|
||||
) ==
|
||||
true,
|
||||
))
|
||||
.map(
|
||||
(member) => ListTile(
|
||||
leading: AvatarOrHash(
|
||||
member.avatarUrl,
|
||||
member.displayName,
|
||||
),
|
||||
title: Text(member.displayName),
|
||||
subtitle: Text(member.userId),
|
||||
onTap: () => addTag(
|
||||
id: "[@${member.displayName}](https://matrix.to/#/${member.userId})",
|
||||
name: member.userId
|
||||
.substring(1)
|
||||
.split(":")
|
||||
.first ??
|
||||
"Unknown User",
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
},
|
||||
),
|
||||
.first,
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
"#" => ListView(
|
||||
children:
|
||||
(query.isEmpty
|
||||
|
|
@ -2,7 +2,9 @@ import "package:flutter/material.dart";
|
|||
import "package:flutter_chat_core/flutter_chat_core.dart";
|
||||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||
import "package:nexus/models/relation_type.dart";
|
||||
import "package:nexus/widgets/avatar_or_hash.dart";
|
||||
import "package:nexus/models/room.dart";
|
||||
import "package:nexus/widgets/chat_page/lazy_loading/message_avatar.dart";
|
||||
import "package:nexus/widgets/chat_page/lazy_loading/message_displayname.dart";
|
||||
|
||||
class RelationPreview extends ConsumerWidget {
|
||||
final Message? relatedMessage;
|
||||
|
|
@ -10,8 +12,11 @@ class RelationPreview extends ConsumerWidget {
|
|||
final VoidCallback onDismiss;
|
||||
final bool shouldMention;
|
||||
final VoidCallback toggleShouldMention;
|
||||
const RelationPreview({
|
||||
required this.relatedMessage,
|
||||
final Room room;
|
||||
|
||||
const RelationPreview(
|
||||
this.relatedMessage, {
|
||||
required this.room,
|
||||
required this.relationType,
|
||||
required this.onDismiss,
|
||||
required this.shouldMention,
|
||||
|
|
@ -36,14 +41,10 @@ class RelationPreview extends ConsumerWidget {
|
|||
"Editing message:",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
AvatarOrHash(
|
||||
Uri.tryParse(relatedMessage?.metadata?["avatarUrl"] ?? ""),
|
||||
relatedMessage?.metadata?["displayName"]?.toString() ?? "",
|
||||
height: 16,
|
||||
),
|
||||
Text(
|
||||
relatedMessage!.metadata?["displayName"] ??
|
||||
relatedMessage!.authorId,
|
||||
MessageAvatar(relatedMessage!, room),
|
||||
MessageDisplayname(
|
||||
relatedMessage!,
|
||||
room,
|
||||
style: theme.textTheme.labelMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
|
|
@ -22,6 +22,10 @@ class Html extends ConsumerWidget {
|
|||
html,
|
||||
textStyle: textStyle,
|
||||
customWidgetBuilder: (element) {
|
||||
if (element.attributes.keys.contains("data-mx-profile-fallback")) {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
|
||||
if (element.attributes.keys.contains("data-mx-spoiler")) {
|
||||
return InlineCustomWidget(child: SpoilerText(text: element.text));
|
||||
}
|
||||
|
|
|
|||
30
lib/widgets/chat_page/lazy_loading/message_avatar.dart
Normal file
30
lib/widgets/chat_page/lazy_loading/message_avatar.dart
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import "package:flutter/widgets.dart";
|
||||
import "package:flutter_chat_core/flutter_chat_core.dart";
|
||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:nexus/controllers/author_controller.dart";
|
||||
import "package:nexus/helpers/extensions/better_when.dart";
|
||||
import "package:nexus/models/configs/author_config.dart";
|
||||
import "package:nexus/models/room.dart";
|
||||
import "package:nexus/widgets/avatar_or_hash.dart";
|
||||
|
||||
class MessageAvatar extends ConsumerWidget {
|
||||
final Message message;
|
||||
final Room room;
|
||||
final double height;
|
||||
const MessageAvatar(this.message, this.room, {this.height = 16, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) => ref
|
||||
.watch(
|
||||
AuthorController.provider(AuthorConfig(room: room, message: message)),
|
||||
)
|
||||
.betterWhen(
|
||||
data: (membership) => AvatarOrHash(
|
||||
membership.avatarUrl,
|
||||
membership.displayName,
|
||||
height: height,
|
||||
),
|
||||
loading: () =>
|
||||
AvatarOrHash(null, message.authorId.substring(1), height: height),
|
||||
);
|
||||
}
|
||||
28
lib/widgets/chat_page/lazy_loading/message_displayname.dart
Normal file
28
lib/widgets/chat_page/lazy_loading/message_displayname.dart
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import "package:flutter/widgets.dart";
|
||||
import "package:flutter_chat_core/flutter_chat_core.dart";
|
||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:nexus/controllers/author_controller.dart";
|
||||
import "package:nexus/helpers/extensions/better_when.dart";
|
||||
import "package:nexus/models/configs/author_config.dart";
|
||||
import "package:nexus/models/room.dart";
|
||||
|
||||
class MessageDisplayname extends ConsumerWidget {
|
||||
final Message message;
|
||||
final Room room;
|
||||
final TextStyle? style;
|
||||
const MessageDisplayname(this.message, this.room, {this.style, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) => ref
|
||||
.watch(
|
||||
AuthorController.provider(AuthorConfig(room: room, message: message)),
|
||||
)
|
||||
.betterWhen(
|
||||
data: (membership) => Text(
|
||||
"${membership.displayName} ${message.metadata?["pmp"] == null ? "" : "(via ${message.authorId})"}",
|
||||
style: style,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
loading: () => Text(""),
|
||||
);
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||
import "package:nexus/controllers/members_controller.dart";
|
||||
import "package:nexus/helpers/extensions/better_when.dart";
|
||||
import "package:nexus/models/room.dart";
|
||||
import "package:nexus/widgets/avatar_or_hash.dart";
|
||||
|
||||
|
|
@ -10,15 +11,17 @@ class MemberList extends ConsumerWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final members = ref.watch(MembersController.provider(room));
|
||||
final membersProvider = ref.watch(MembersController.provider(room));
|
||||
return Drawer(
|
||||
shape: Border(),
|
||||
child: ListView(
|
||||
child: Column(
|
||||
children: [
|
||||
AppBar(
|
||||
scrolledUnderElevation: 0,
|
||||
leading: Icon(Icons.people),
|
||||
title: Text("Members (${members.length})"),
|
||||
title: Text(
|
||||
"Members ${membersProvider.when(data: (members) => "${members.length}", error: (_, _) => "", loading: () => "")}",
|
||||
),
|
||||
actionsPadding: EdgeInsets.only(right: 4),
|
||||
actions: [
|
||||
if (Scaffold.of(context).hasEndDrawer)
|
||||
|
|
@ -29,24 +32,32 @@ class MemberList extends ConsumerWidget {
|
|||
),
|
||||
],
|
||||
),
|
||||
...members.map(
|
||||
(member) => ListTile(
|
||||
onTap: () => showDialog(
|
||||
context: context,
|
||||
builder: (context) =>
|
||||
Dialog(child: Text("TODO: Open member popover")),
|
||||
),
|
||||
leading: AvatarOrHash(
|
||||
Uri.tryParse(member.content["avatar_url"] ?? ""),
|
||||
member.content["displayname"].toString(),
|
||||
),
|
||||
title: Text(
|
||||
member.content["displayname"].toString(),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
subtitle: Text(
|
||||
member.stateKey ?? "Unknown User",
|
||||
overflow: TextOverflow.ellipsis,
|
||||
membersProvider.betterWhen(
|
||||
data: (members) => Expanded(
|
||||
child: ListView(
|
||||
children: members
|
||||
.map(
|
||||
(member) => ListTile(
|
||||
onTap: () => showDialog(
|
||||
context: context,
|
||||
builder: (context) =>
|
||||
Dialog(child: Text("TODO: Open member popover")),
|
||||
),
|
||||
leading: AvatarOrHash(
|
||||
member.avatarUrl,
|
||||
member.displayName,
|
||||
),
|
||||
title: Text(
|
||||
member.displayName,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
subtitle: Text(
|
||||
member.userId,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
import "dart:math";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_chat_core/flutter_chat_core.dart";
|
||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:nexus/controllers/event_controller.dart";
|
||||
import "package:nexus/controllers/message_controller.dart";
|
||||
import "package:nexus/helpers/extensions/better_when.dart";
|
||||
import "package:nexus/models/message_config.dart";
|
||||
import "package:nexus/models/configs/message_config.dart";
|
||||
import "package:nexus/models/requests/get_event_request.dart";
|
||||
import "package:nexus/models/room.dart";
|
||||
import "package:nexus/widgets/avatar_or_hash.dart";
|
||||
import "package:nexus/widgets/chat_page/html/quoted.dart";
|
||||
import "package:nexus/widgets/chat_page/lazy_loading/message_avatar.dart";
|
||||
import "package:nexus/widgets/chat_page/lazy_loading/message_displayname.dart";
|
||||
|
||||
typedef OnTapReply = void Function(Message message)?;
|
||||
|
||||
|
|
@ -61,73 +61,28 @@ class ReplyWidget extends ConsumerWidget {
|
|||
return SizedBox.shrink();
|
||||
}
|
||||
|
||||
final smallerText =
|
||||
message is TextMessage &&
|
||||
replyMessage.metadata?["body"] != null
|
||||
? replyMessage.metadata!["body"].substring(
|
||||
0,
|
||||
min(
|
||||
max(
|
||||
max(
|
||||
(message as TextMessage)
|
||||
.text
|
||||
.length -
|
||||
(replyMessage
|
||||
.metadata?["displayName"]
|
||||
as String)
|
||||
.length -
|
||||
5,
|
||||
message
|
||||
.metadata?["displayName"]
|
||||
.length,
|
||||
),
|
||||
5,
|
||||
),
|
||||
replyMessage.metadata!["body"].length,
|
||||
),
|
||||
)
|
||||
: null;
|
||||
final replyText =
|
||||
(smallerText == null ||
|
||||
smallerText.length ==
|
||||
replyMessage
|
||||
.metadata!["body"]
|
||||
.length)
|
||||
? replyMessage.metadata!["body"]
|
||||
: "$smallerText...";
|
||||
|
||||
return InkWell(
|
||||
onTap: () => onTapReply?.call(replyMessage),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
spacing: 8,
|
||||
children: [
|
||||
AvatarOrHash(
|
||||
Uri.tryParse(
|
||||
replyMessage.metadata?["avatarUrl"] ??
|
||||
"",
|
||||
),
|
||||
replyMessage.metadata?["displayName"] ??
|
||||
"",
|
||||
height: 16,
|
||||
),
|
||||
MessageAvatar(replyMessage, room),
|
||||
Flexible(
|
||||
child: Text(
|
||||
replyMessage
|
||||
.metadata?["displayName"] ??
|
||||
replyMessage.authorId,
|
||||
child: MessageDisplayname(
|
||||
replyMessage,
|
||||
room,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.labelMedium
|
||||
?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Text(
|
||||
replyText,
|
||||
replyMessage.metadata!["body"],
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(
|
||||
context,
|
||||
|
|
|
|||
|
|
@ -13,15 +13,14 @@ import "package:nexus/helpers/extensions/better_when.dart";
|
|||
import "package:nexus/helpers/extensions/show_context_menu.dart";
|
||||
import "package:nexus/models/relation_type.dart";
|
||||
import "package:nexus/models/requests/report_request.dart";
|
||||
import "package:nexus/widgets/chat_page/chat_box.dart";
|
||||
import "package:nexus/widgets/chat_page/composer/chat_box.dart";
|
||||
import "package:nexus/widgets/chat_page/image_message.dart";
|
||||
import "package:nexus/widgets/chat_page/member_list.dart";
|
||||
import "package:nexus/widgets/chat_page/message_wrapper.dart";
|
||||
import "package:nexus/widgets/chat_page/wrappers/message_wrapper.dart";
|
||||
import "package:nexus/widgets/chat_page/room_appbar.dart";
|
||||
import "package:nexus/widgets/chat_page/text_message_wrapper.dart";
|
||||
import "package:nexus/widgets/chat_page/wrappers/text_message_wrapper.dart";
|
||||
import "package:nexus/widgets/chat_page/reply_widget.dart";
|
||||
import "package:nexus/widgets/form_text_input.dart";
|
||||
import "package:nexus/widgets/loading.dart";
|
||||
// import "package:dynamic_polls/dynamic_polls.dart";
|
||||
|
||||
class RoomChat extends HookConsumerWidget {
|
||||
|
|
@ -233,7 +232,7 @@ class RoomChat extends HookConsumerWidget {
|
|||
children: getMessageOptions(message),
|
||||
),
|
||||
builders: Builders(
|
||||
loadMoreBuilder: (_) => Loading(),
|
||||
loadMoreBuilder: (_) => SizedBox.shrink(),
|
||||
|
||||
chatAnimatedListBuilder: (_, itemBuilder) =>
|
||||
ChatAnimatedList(
|
||||
|
|
@ -320,6 +319,7 @@ class RoomChat extends HookConsumerWidget {
|
|||
),
|
||||
),
|
||||
groupStatus,
|
||||
room,
|
||||
),
|
||||
|
||||
systemMessageBuilder:
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ import "package:nexus/widgets/chat_page/room_menu.dart";
|
|||
import "package:nexus/widgets/form_text_input.dart";
|
||||
|
||||
class Sidebar extends HookConsumerWidget {
|
||||
const Sidebar({super.key});
|
||||
final bool isDesktop;
|
||||
const Sidebar({required this.isDesktop, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
|
|
@ -220,9 +221,12 @@ class Sidebar extends HookConsumerWidget {
|
|||
),
|
||||
)
|
||||
.toList(),
|
||||
onDestinationSelected: (value) => selectedRoomIdNotifier.set(
|
||||
selectedSpace.children[value].metadata?.id,
|
||||
),
|
||||
onDestinationSelected: (value) {
|
||||
selectedRoomIdNotifier.set(
|
||||
selectedSpace.children[value].metadata?.id,
|
||||
);
|
||||
if (!isDesktop) Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,12 +1,21 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:flutter_chat_core/flutter_chat_core.dart";
|
||||
import "package:nexus/widgets/avatar_or_hash.dart";
|
||||
import "package:nexus/models/room.dart";
|
||||
import "package:nexus/widgets/chat_page/lazy_loading/message_avatar.dart";
|
||||
import "package:nexus/widgets/chat_page/lazy_loading/message_displayname.dart";
|
||||
|
||||
class MessageWrapper extends StatelessWidget {
|
||||
final Message message;
|
||||
final Widget child;
|
||||
final Room room;
|
||||
final MessageGroupStatus? groupStatus;
|
||||
const MessageWrapper(this.message, this.child, this.groupStatus, {super.key});
|
||||
const MessageWrapper(
|
||||
this.message,
|
||||
this.child,
|
||||
this.groupStatus,
|
||||
this.room, {
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => ClipRRect(
|
||||
|
|
@ -24,11 +33,7 @@ class MessageWrapper extends StatelessWidget {
|
|||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
groupStatus?.isFirst != false
|
||||
? AvatarOrHash(
|
||||
Uri.parse(message.metadata?["avatarUrl"] ?? ""),
|
||||
height: 40,
|
||||
message.metadata?["displayName"] ?? "",
|
||||
)
|
||||
? MessageAvatar(message, room, height: 40)
|
||||
: SizedBox(width: 40),
|
||||
Expanded(
|
||||
child: Column(
|
||||
|
|
@ -36,9 +41,9 @@ class MessageWrapper extends StatelessWidget {
|
|||
spacing: 4,
|
||||
children: [
|
||||
if (groupStatus?.isFirst != false)
|
||||
Text(
|
||||
message.metadata?["displayName"] ?? message.authorId,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
MessageDisplayname(
|
||||
message,
|
||||
room,
|
||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
|
|
@ -3,7 +3,7 @@ import "package:flutter_chat_core/flutter_chat_core.dart";
|
|||
import "package:flutter_link_previewer/flutter_link_previewer.dart";
|
||||
import "package:nexus/models/room.dart";
|
||||
import "package:nexus/widgets/chat_page/html/html.dart";
|
||||
import "package:nexus/widgets/chat_page/message_wrapper.dart";
|
||||
import "package:nexus/widgets/chat_page/wrappers/message_wrapper.dart";
|
||||
import "package:nexus/widgets/chat_page/reply_widget.dart";
|
||||
|
||||
class TextMessageWrapper extends StatelessWidget {
|
||||
|
|
@ -109,6 +109,7 @@ class TextMessageWrapper extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
groupStatus,
|
||||
room,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ project(runner LANGUAGES CXX)
|
|||
set(BINARY_NAME "nexus")
|
||||
# The unique GTK application identifier for this application. See:
|
||||
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
|
||||
set(APPLICATION_ID "nexus.federated.nexus")
|
||||
set(APPLICATION_ID "nexus.federated.Nexus")
|
||||
|
||||
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
|
||||
# versions of CMake.
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
#include <screen_retriever_linux/screen_retriever_linux_plugin.h>
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
#include <window_manager/window_manager_plugin.h>
|
||||
#include <window_size/window_size_plugin.h>
|
||||
|
||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) dynamic_system_colors_registrar =
|
||||
|
|
@ -29,7 +28,4 @@ void fl_register_plugins(FlPluginRegistry* registry) {
|
|||
g_autoptr(FlPluginRegistrar) window_manager_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin");
|
||||
window_manager_plugin_register_with_registrar(window_manager_registrar);
|
||||
g_autoptr(FlPluginRegistrar) window_size_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "WindowSizePlugin");
|
||||
window_size_plugin_register_with_registrar(window_size_registrar);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
|||
screen_retriever_linux
|
||||
url_launcher_linux
|
||||
window_manager
|
||||
window_size
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|
|
|
|||
9
linux/nexus.federated.Nexus.desktop
Normal file
9
linux/nexus.federated.Nexus.desktop
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[Desktop Entry]
|
||||
Name=Nexus
|
||||
GenericName=Matrix Client
|
||||
Comment=A simple and user-friendly Matrix client
|
||||
Exec=nexus
|
||||
Icon=nexus
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Chat;Network;InstantMessaging;
|
||||
41
linux/nix/devshell.nix
Normal file
41
linux/nix/devshell.nix
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
{ pkgs, lib }:
|
||||
let
|
||||
android = pkgs.androidenv.composeAndroidPackages {
|
||||
toolsVersion = "26.1.1";
|
||||
platformToolsVersion = "36.0.1";
|
||||
buildToolsVersions = [
|
||||
"35.0.0"
|
||||
"36.0.0"
|
||||
];
|
||||
cmakeVersions = [ "3.22.1" ];
|
||||
platformVersions = [ "36" ];
|
||||
abiVersions = [
|
||||
"armeabi-v7a"
|
||||
"arm64-v8a"
|
||||
];
|
||||
includeNDK = true;
|
||||
ndkVersions = [ "28.2.13676358" ];
|
||||
};
|
||||
in
|
||||
pkgs.mkShell {
|
||||
packages = with pkgs; [
|
||||
go
|
||||
git
|
||||
jdk17
|
||||
flutter
|
||||
android.platform-tools
|
||||
];
|
||||
|
||||
env = rec {
|
||||
LIBCLANG_PATH = lib.makeLibraryPath [ pkgs.libclang ];
|
||||
LD_LIBRARY_PATH = "./build/native_assets/linux:${lib.makeLibraryPath [ pkgs.zlib ]}";
|
||||
CPATH = lib.makeSearchPath "include" [ pkgs.glibc.dev ];
|
||||
|
||||
ANDROID_HOME = "${android.androidsdk}/libexec/android-sdk";
|
||||
ANDROID_SDK_ROOT = ANDROID_HOME;
|
||||
JAVA_HOME = pkgs.jdk17;
|
||||
|
||||
TOOLS = "${ANDROID_HOME}/build-tools/${"36.0.0"}";
|
||||
GRADLE_OPTS = "-Dorg.gradle.project.android.aapt2FromMavenOverride=${TOOLS}/aapt2";
|
||||
};
|
||||
}
|
||||
44
linux/nix/pkg/default.nix
Normal file
44
linux/nix/pkg/default.nix
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
lib,
|
||||
callPackage,
|
||||
libclang,
|
||||
flutter,
|
||||
src,
|
||||
}:
|
||||
|
||||
flutter.buildFlutterApplication {
|
||||
pname = "nexus";
|
||||
version = "0.1.0";
|
||||
inherit src;
|
||||
|
||||
preBuild = ''
|
||||
cp ${callPackage ./gomuks.nix { inherit src; }}/lib/* .
|
||||
packageRunCustom nexus generate source/scripts test
|
||||
packageRun build_runner build
|
||||
'';
|
||||
|
||||
env.LIBCLANG_PATH = lib.makeLibraryPath [ libclang ];
|
||||
|
||||
autoPubspecLock = src + "/pubspec.lock";
|
||||
|
||||
gitHashes = {
|
||||
window_size = "sha256-XelNtp7tpZ91QCEcvewVphNUtgQX7xrp5QP0oFo6DgM=";
|
||||
dynamic_system_colors = "sha256-es6rjMK1drkqZBKYUP77yw/q5+0uLwWOEDOXRawy3Dc=";
|
||||
flutter_chat_ui = "sha256-4fuag7lRH5cMBFD3fUzj2K541JwXLoz8HF/4OMr3uhk=";
|
||||
flutter_link_previewer = "sha256-4fuag7lRH5cMBFD3fUzj2K541JwXLoz8HF/4OMr3uhk=";
|
||||
};
|
||||
|
||||
postInstall = ''
|
||||
install -D assets/icon.svg $out/share/icons/hicolor/scalable/apps/nexus.svg
|
||||
install -Dm755 linux/nexus.federated.Nexus.desktop -t $out/share/applications
|
||||
wrapProgram $out/bin/nexus \
|
||||
--suffix LD_LIBRARY_PATH : $out/app/nexus/lib
|
||||
'';
|
||||
|
||||
meta = {
|
||||
description = "A simple and user-friendly Matrix client";
|
||||
mainProgram = "nexus";
|
||||
platforms = lib.platforms.linux;
|
||||
maintainers = with lib.maintainers; [ quadradical ];
|
||||
};
|
||||
}
|
||||
31
linux/nix/pkg/gomuks.nix
Normal file
31
linux/nix/pkg/gomuks.nix
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
src,
|
||||
buildGoModule,
|
||||
}:
|
||||
|
||||
buildGoModule (finalAttrs: {
|
||||
pname = "gomuks-ffi";
|
||||
version = "submodule";
|
||||
|
||||
doCheck = false;
|
||||
|
||||
src = "${src}/gomuks";
|
||||
|
||||
vendorHash = "sha256-zBDfBZqUoHIfZ0AajZEvSBbskjpFB7yIsomt0KYDo7Y=";
|
||||
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
|
||||
go build -buildmode=c-shared -o libgomuks.so -tags goolm,noheic ./pkg/ffi
|
||||
|
||||
runHook postBuild
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
|
||||
install -Dm0644 libgomuks.so -t $out/lib
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
})
|
||||
|
|
@ -43,6 +43,7 @@ static void my_application_activate(GApplication* application) {
|
|||
}
|
||||
}
|
||||
#endif
|
||||
gtk_widget_set_size_request(GTK_WIDGET(window), 500, 500);
|
||||
if (use_header_bar) {
|
||||
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
|
||||
gtk_widget_show(GTK_WIDGET(header_bar));
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
androidenv,
|
||||
}:
|
||||
androidenv.composeAndroidPackages {
|
||||
toolsVersion = "26.1.1";
|
||||
platformToolsVersion = "36.0.1";
|
||||
buildToolsVersions = [
|
||||
"35.0.0"
|
||||
"36.0.0"
|
||||
];
|
||||
cmakeVersions = [ "3.22.1" ];
|
||||
platformVersions = [ "36" ];
|
||||
abiVersions = [
|
||||
"armeabi-v7a"
|
||||
"arm64-v8a"
|
||||
];
|
||||
includeNDK = true;
|
||||
ndkVersions = [ "27.0.12077973" ];
|
||||
|
||||
}
|
||||
42
pubspec.lock
42
pubspec.lock
|
|
@ -29,10 +29,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: analyzer_buffer
|
||||
sha256: aba2f75e63b3135fd1efaa8b6abefe1aa6e41b6bd9806221620fa48f98156033
|
||||
sha256: "5fcd06b0715ebeee99f03e3f437b3412249969d8d12b191ea8a1d76e42a4e4a1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.11"
|
||||
version: "0.3.1"
|
||||
analyzer_plugin:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -348,10 +348,11 @@ packages:
|
|||
dynamic_system_colors:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dynamic_system_colors
|
||||
sha256: "43794e658fa88cbdec9f397dd1afd2eb69b6c9717e99b93b16ba37c3aa3b3a8c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
path: "."
|
||||
ref: HEAD
|
||||
resolved-ref: "3b61760d5e0ac1229eefde5b61247947eede4110"
|
||||
url: "https://github.com/hasali19/flutter_dynamic_system_colors"
|
||||
source: git
|
||||
version: "1.8.0"
|
||||
encrypt:
|
||||
dependency: transitive
|
||||
|
|
@ -521,10 +522,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_riverpod
|
||||
sha256: "38ec6c303e2c83ee84512f5fc2a82ae311531021938e63d7137eccc107bf3c02"
|
||||
sha256: "4e166be88e1dbbaa34a280bdb744aeae73b7ef25fdf8db7a3bb776760a3648e2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
version: "3.3.1"
|
||||
flutter_svg:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -643,10 +644,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: hooks_riverpod
|
||||
sha256: b880efcd17757af0aa242e5dceac2fb781a014c22a32435a5daa8f17e9d5d8a9
|
||||
sha256: "08527ec06aaef75e4b78694e045ef0cd8346594eaf9cc18b0f866398b07b93b1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
version: "3.3.1"
|
||||
html:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1051,26 +1052,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: riverpod
|
||||
sha256: "16ff608d21e8ea64364f2b7c049c94a02ab81668f78845862b6e88b71dd4935a"
|
||||
sha256: "8c22216be8ad3ef2b44af3a329693558c98eca7b8bd4ef495c92db0bba279f83"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
version: "3.2.1"
|
||||
riverpod_analyzer_utils:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: riverpod_analyzer_utils
|
||||
sha256: "947b05d04c52a546a2ac6b19ef2a54b08520ff6bdf9f23d67957a4c8df1c3bc0"
|
||||
sha256: e55bc08c084a424e1bbdc303fe8ea75daafe4269b68fd0e0f6f1678413715b66
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0-dev.8"
|
||||
version: "1.0.0-dev.9"
|
||||
riverpod_lint:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: riverpod_lint
|
||||
sha256: "4d2eb0d19bbe7e3323bd0ce4553b2e6170d161a13914bfdd85a3612329edcb43"
|
||||
sha256: "64e8debf5b719a37d48b9785dd595d34133fdcd84b8fd07157a621c54ab2156f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
version: "3.1.3"
|
||||
rxdart:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1548,15 +1549,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.1"
|
||||
window_size:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "plugins/window_size"
|
||||
ref: HEAD
|
||||
resolved-ref: eb3964990cf19629c89ff8cb4a37640c7b3d5601
|
||||
url: "https://github.com/google/flutter-desktop-embedding"
|
||||
source: git
|
||||
version: "0.1.0"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
16
pubspec.yaml
16
pubspec.yaml
|
|
@ -21,8 +21,8 @@ dependencies:
|
|||
sdk: flutter
|
||||
flutter_localizations:
|
||||
sdk: flutter
|
||||
flutter_riverpod: ^3.0.3
|
||||
hooks_riverpod: ^3.0.3
|
||||
flutter_riverpod: ^3.3.1
|
||||
hooks_riverpod: ^3.3.1
|
||||
intl: ^0.20.1
|
||||
fast_immutable_collections: ^11.0.0
|
||||
path_provider: ^2.1.3
|
||||
|
|
@ -31,13 +31,11 @@ dependencies:
|
|||
image_picker: ^1.1.2
|
||||
file_picker: ^10.3.3
|
||||
path: ^1.9.0
|
||||
dynamic_system_colors: ^1.8.0
|
||||
dynamic_system_colors:
|
||||
git:
|
||||
url: https://github.com/hasali19/flutter_dynamic_system_colors
|
||||
collection: ^1.19.1
|
||||
window_manager: ^0.5.1
|
||||
window_size:
|
||||
git:
|
||||
url: https://github.com/google/flutter-desktop-embedding
|
||||
path: plugins/window_size
|
||||
flutter_chat_core: ^2.0.0
|
||||
flyer_chat_image_message: ^2.2.2
|
||||
flyer_chat_system_message: ^2.1.13
|
||||
|
|
@ -69,7 +67,7 @@ dev_dependencies:
|
|||
custom_lint: ^0.8.0
|
||||
flutter_lints: ^6.0.0
|
||||
freezed: ^3.2.3
|
||||
riverpod_lint: ^3.0.3
|
||||
riverpod_lint: ^3.1.3
|
||||
flutter_launcher_icons: ^0.14.1
|
||||
json_serializable: ^6.11.1
|
||||
|
||||
|
|
@ -77,7 +75,7 @@ flutter_launcher_icons:
|
|||
ios: true
|
||||
android: true
|
||||
image_path: assets/icon.png
|
||||
adaptive_icon_background: "#000000"
|
||||
adaptive_icon_background: assets/background.png
|
||||
adaptive_icon_foreground: assets/foreground.png
|
||||
remove_alpha_ios: true
|
||||
windows:
|
||||
|
|
|
|||
|
|
@ -3,26 +3,7 @@ import "package:ffigen/ffigen.dart";
|
|||
import "package:path/path.dart";
|
||||
|
||||
void main(List<String> args) async {
|
||||
final repoDir = Directory.fromUri(
|
||||
Platform.script.resolve("../src/gomuks/source"),
|
||||
);
|
||||
if (await repoDir.exists()) await repoDir.delete(recursive: true);
|
||||
await repoDir.create(recursive: true);
|
||||
|
||||
print("Cloning Gomuks repository...");
|
||||
final cloneResult = await Process.run("git", [
|
||||
"clone",
|
||||
"--depth",
|
||||
"1",
|
||||
"https://mau.dev/gomuks/gomuks",
|
||||
repoDir.path,
|
||||
]);
|
||||
|
||||
if (cloneResult.exitCode != 0) {
|
||||
throw Exception(
|
||||
"Failed to clone Gomuks repository: \n${cloneResult.stderr}",
|
||||
);
|
||||
}
|
||||
final repoDir = Directory.fromUri(Platform.script.resolve("../gomuks"));
|
||||
|
||||
print("Generating FFI Bindings...");
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
pushd "$(dirname "$(readlink -f "$0")")"/.. > /dev/null || exit
|
||||
|
||||
mkdir -p build
|
||||
touch build/lock
|
||||
dart scripts/generate.dart
|
||||
rm build/lock
|
||||
|
||||
popd > /dev/null || exit
|
||||
|
|
@ -11,7 +11,6 @@
|
|||
#include <screen_retriever_windows/screen_retriever_windows_plugin_c_api.h>
|
||||
#include <url_launcher_windows/url_launcher_windows.h>
|
||||
#include <window_manager/window_manager_plugin.h>
|
||||
#include <window_size/window_size_plugin.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
DynamicColorPluginCApiRegisterWithRegistrar(
|
||||
|
|
@ -24,6 +23,4 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
|||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||
WindowManagerPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("WindowManagerPlugin"));
|
||||
WindowSizePluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("WindowSizePlugin"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
|||
screen_retriever_windows
|
||||
url_launcher_windows
|
||||
window_manager
|
||||
window_size
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|
|
|
|||
|
|
@ -89,11 +89,11 @@ BEGIN
|
|||
BEGIN
|
||||
BLOCK "040904e4"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "nexus.federated.nexus" "\0"
|
||||
VALUE "CompanyName", "nexus.federated.Nexus" "\0"
|
||||
VALUE "FileDescription", "nexus" "\0"
|
||||
VALUE "FileVersion", VERSION_AS_STRING "\0"
|
||||
VALUE "InternalName", "nexus" "\0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2025 nexus.federated.nexus. All rights reserved." "\0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2025 nexus.federated.Nexus. All rights reserved." "\0"
|
||||
VALUE "OriginalFilename", "nexus.exe" "\0"
|
||||
VALUE "ProductName", "nexus" "\0"
|
||||
VALUE "ProductVersion", VERSION_AS_STRING "\0"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue