Compare commits

..
Author SHA1 Message Date
e300f4efc9
small fixups 2026-09-26 21:11:11 -04:00
bab699658f
pop sidebar before pushing notif page 2026-09-26 20:32:44 -04:00
1c1f585362
add JumpToEventController to allow jumping to events from the NotificationsPage when it has been pushed from the NotificationController 2026-09-26 19:37:47 -04:00
c90a1c41b0
working jumps from notif page
Doesn't work from notif page pushed from notif controller
2026-09-26 19:13:30 -04:00
9f070fcdba
reorganize pages into being under widgets 2026-09-26 18:06:51 -04:00
d712bb3193
tweak scroll behavior 2026-09-26 14:52:24 -04:00
3217c17ea0
don't depend on SuperSliverList for scrolls 2026-09-26 13:58:30 -04:00
031fbe326c
change highlight timing 2026-09-26 13:42:31 -04:00
0149aa2f84
fixup jumping to pins 2026-09-26 13:30:39 -04:00
a1efdb0f46
check off jumping on readme 2026-09-26 13:15:48 -04:00
389b65ae61 add support for jumping to out-of-context events (#95)
Fixes #84.

Reviewed-on: Nexus/nexus#95
2026-09-26 13:03:16 -04:00
3429a3ed98
add twim image 2026-09-25 10:20:53 -04:00
7ab92f76bf
fix for janky scrolling when tapping reply with SuperSliverView
I don't like this but it does work.
2026-09-24 22:46:48 -04:00
aecfac9533
free base before buffer 2026-09-24 22:20:31 -04:00
c7b89b8e56
small cleanups 2026-09-24 20:59:18 -04:00
721727d48c
small cleanups 2026-09-24 20:50:28 -04:00
004c3bcd0d
remove outdated error ignores
I believe some of these are fixed, if not they should be noisy so I notice them and can re-add
2026-09-24 20:38:30 -04:00
943cd85e22
treewide: ref.watch(ClientController.provider.notifier) -> ref.read 2026-09-24 20:35:20 -04:00
09904536e7
small fixups 2026-09-24 20:31:22 -04:00
856072e0b0
fixup! refactor chatview to have two sliver lists 2026-09-24 20:22:30 -04:00
110caff170
refactor chatview to have two sliver lists
Co-authored-by: Erwan Leboucher <erwanleboucher@gmail.com>
2026-09-24 20:02:23 -04:00
eeb2e2f54b
extract scroll helpers into their own hook 2026-09-24 18:10:30 -04:00
529a2e35c4
make buildEventOptions an extension 2026-09-24 14:41:13 -04:00
6ad4657d76
refactor chatview 2026-09-24 14:24:41 -04:00
38fd19ad1a
move isInBackground in main 2026-09-24 14:08:54 -04:00
0636366359
fixup errors caused by bump 2026-09-24 11:56:46 -04:00
efe9262a97
tweak timeout 2026-09-24 11:49:19 -04:00
2763a0fdf5
bump deps 2026-09-24 11:47:20 -04:00
a5da44fdea
more restrictive timeout on notification init 2026-09-24 11:42:57 -04:00
ccca02a0bc
Separate out GomuksListenerController from ClientController
Fixes #89
2026-09-24 11:03:16 -04:00
721349ca21
explicit listener for ClientStateController in notifs 2026-09-23 11:22:47 -04:00
5d994bf2bb
wait for metadata to come in on notif's rooms before sending them 2026-09-23 11:17:02 -04:00
a90bf00f18
dispose notif handler 2026-09-23 10:30:12 -04:00
f2efbbeb9f
fixup select 2026-09-22 20:48:59 -04:00
ed761d6071
gate selectable text behind mouse connected 2026-09-22 20:40:55 -04:00
8f35ea2435
Add "Copy Text" context menu option 2026-09-22 20:18:16 -04:00
023e75e063
always make events selectable (on mobile the long press overrides it) 2026-09-22 20:11:18 -04:00
4d0a4605d6
type contextMenuCallback 2026-09-22 19:57:23 -04:00
faa7292001
add codeblock copy button 2026-09-22 19:19:21 -04:00
027033ad9e
allow copying messages when on desktop without mobile mode on 2026-09-22 19:13:32 -04:00
ece3d16318
use dot shorthands in about dialog 2026-09-22 18:48:47 -04:00
5738239da2
fix name of ShowAboutDialog extension 2026-09-22 18:31:38 -04:00
a2791a6717
various small fixups 2026-09-22 15:53:37 -04:00
f910d87a23
don't require login for UP controller to finish init 2026-09-22 15:33:55 -04:00
f4142859ba
fallback isFocused to true 2026-09-22 15:05:57 -04:00
775c77194b
run in background properly on android 2026-09-22 15:02:30 -04:00
41806be9bf
More platform-independent approach to managing registered state 2026-09-22 14:34:08 -04:00
6e47594a91
fix race condition on startup 2026-09-22 11:36:29 -04:00
386b3ad1cf
inline the mention fetch 2026-09-22 10:37:49 -04:00
61f2aa7f2a
update gomuks vendorHash 2026-09-22 10:02:56 -04:00
0c660a98a7
just grab events out of get_mentions for now 2026-09-22 09:57:10 -04:00
09534a6775
use GomuksConfig 2026-09-22 09:53:00 -04:00
0cd815dc79
init ClientController before other providers 2026-09-21 23:03:57 -04:00
800ed0636d
bump nix2flatpak 2026-09-21 22:18:24 -04:00
e4cf2a837c
fixup login 2026-09-21 21:41:18 -04:00
11dca9bf14
seperate URI handler desktop file 2026-09-21 21:20:16 -04:00
436b8642b3
read not watch MultiProviderController providers
Fixes random loading animations
2026-09-21 19:27:10 -04:00
269d97be8f
bump nix2flatpak 2026-09-21 18:53:49 -04:00
43f70f35a3
bump nix2flatpak 2026-09-21 18:00:42 -04:00
92b148f5b6
gate registration behind not flatpak 2026-09-21 17:54:03 -04:00
00a42b5f1a
fixup flatpak build 2026-09-21 17:20:43 -04:00
700b4e720f
open notifications page on notif tap
please please please let me never touch c++ again i hate it

also the c++ got reformatted here but i dont really care
2026-09-21 16:56:12 -04:00
2a11bcb7e8
update hashes 2026-09-21 15:44:10 -04:00
fea39e4191
use xdg_desktop_portal over flutter_local_notifications on linux 2026-09-21 15:43:01 -04:00
deaf564c36
use portals for linux notifs 2026-09-21 14:16:29 -04:00
c0d14ac30e
don't write service by default 2026-09-20 17:27:45 -04:00
1a6a5c36ea
link to module from readme 2026-09-20 17:21:26 -04:00
35ef82accd
fixup module 2026-09-20 17:18:39 -04:00
f40dd22277
use mkEnableOption for enableNotifications 2026-09-20 17:14:56 -04:00
1cfea5a819
add nix module 2026-09-20 17:13:22 -04:00
b3023d8147
don't auto create dbus service 2026-09-20 17:00:10 -04:00
e8fb3a8523
use SharedPreferencesAsync to avoid race condition in Sharedprefs
This was so painful to debug...
2026-09-20 15:45:32 -04:00
0d1ace3fd0
add more logs for notifs 2026-09-20 13:41:22 -04:00
3d690c26d8
hopefully fixup UP on linux when backgrounded 2026-09-20 13:27:47 -04:00
3fee789c90
fixup flatpak perms 2026-09-20 00:07:47 -04:00
d7c64fea80
potentially fix notifs on flatpak 2026-09-20 00:04:50 -04:00
87e7e093f7
Revert "don't invalidate UP controller"
This reverts commit 73c4a7d07f.
2026-09-20 00:02:49 -04:00
8b7e29ae33
Set device name to "Nexus on $platform"
Fixes #86.
2026-09-19 22:41:13 -04:00
673c5bd894
fix memory leaks
Oops! They were pretty slow, but still adds up. This might fix the bug that was stopping Nexus from working on GraphineOS.
2026-09-19 21:13:03 -04:00
650c62fb99
update gomuks hash 2026-09-19 18:45:05 -04:00
4f3b77f9f3
set env for gomuks
Fixes #62
2026-09-19 18:41:09 -04:00
85bb653980
fix gomuks init on android / ios 2026-09-18 15:39:45 -04:00
a9d2ccbcd1
bump gradle 2026-09-18 13:02:40 -04:00
73c4a7d07f
don't invalidate UP controller 2026-09-18 10:57:16 -04:00
57f9498aa3
better wrapping behavior for timestamps on state events 2026-09-18 09:58:49 -04:00
cfab803fa0
show timestamps for state events 2026-09-17 23:21:33 -04:00
f23a35384d
nicer / less intrusive mention style 2026-09-17 14:27:51 -04:00
7f7fb451e6
Bump emoji picker to 1.1.1
Better performance now.
2026-09-17 12:09:16 -04:00
5d62e66cd9
remove duplicate "Windows EXE" entry in "Try it out" section of README
Thanks to @julian45 for spotting this!
2026-09-16 22:11:43 -04:00
009ba504e3
fixup logic for not init-ing UP
An XOR was never the right choice for this
2026-09-16 22:07:00 -04:00
30b922d390
enlarge emojis on emoji overlay 2026-09-16 22:03:42 -04:00
63183e0824
don't init UP on platforms other than Linux or Android 2026-09-16 21:59:55 -04:00
163947fc90
add emoji overlay 2026-09-16 16:52:58 -04:00
11457470a0
move around args to overlays 2026-09-16 16:30:15 -04:00
dca0b7dd7d
refactor overlay system 2026-09-16 16:27:10 -04:00
e5dc270f83
bump emoji picker 2026-09-16 16:27:07 -04:00
810a0b102b
add support for setting recent emoji 2026-09-16 15:46:51 -04:00
ec1603b828
add support for showing recent emoji 2026-09-16 15:18:41 -04:00
5acdbb5be0 Better emoji picker (#73)
Adds a better, custom, emoji picker.

Reviewed-on: Nexus/nexus#73
2026-09-16 14:47:46 -04:00
e21c9e8c22
make highlights work better for Notifications page 2026-09-12 09:52:01 -04:00
ea687471bc
Make TODO on notifications page more useful 2026-09-11 22:42:43 -04:00
8c440447f2
check off Notifications page in readme 2026-09-11 22:42:17 -04:00
8ebec15324
hide label whe not selected for filter 2026-09-11 22:41:38 -04:00
cd8d551e26
take notif filter out of card 2026-09-11 22:39:18 -04:00
118e5a5826
update readme for platform support to be more clear 2026-09-11 22:35:13 -04:00
6ca9b2a948
add notification page filter 2026-09-11 22:34:43 -04:00
23e01c7a0d
Notifications page
Doesn't yet allow for jumping to events
2026-09-11 22:03:42 -04:00
9e7fcdbef7
sticky -> clientSticky to avoid conflicts with sticky events 2026-09-11 19:50:29 -04:00
9507d69458
move windows warning in readme 2026-09-11 19:29:47 -04:00
13d90df226
remove un-needed overrides 2026-09-11 19:28:10 -04:00
e925f9657c
add needed permissions for UnifiedPush to nexus flatpak 2026-09-10 17:14:41 -04:00
b8882d6e48
mark off notification support as added, add notifications page todo 2026-09-10 17:06:14 -04:00
ff9bf3bec4 Push Notifications (#66)
Fixes #35.

Things to do:
- [x] Register with local distributor
- [x] Register with homeserver
- [x] Recieve messages
- [x] Show notifications
- [ ] Notifications page (could come later)

Reviewed-on: Nexus/nexus#66
2026-09-10 15:37:05 -04:00
6e42902322
run GomuksInit on an Isolate
Fixes #70
2026-09-10 15:32:30 -04:00
c105a6035c
use dot shorthands where possible in generate script 2026-09-10 15:21:53 -04:00
3d7ef9b32f
actually generate bindings
Yes `ffigen`, I did actually want you to generate the bindings, not a file consisting only of comments. Crazy, right?
2026-09-10 15:20:56 -04:00
51ef66174a
fix generate script to work with new ffigen 2026-09-10 15:15:12 -04:00
1efb68ebe0
bump deps, remove unwanted and now un-needed analyzer rule
Fixes #69
2026-09-10 15:09:47 -04:00
0a667fda0b
update gomuks vendorHash 2026-09-09 15:44:25 -04:00
da352cd035
cap max length of embeds 2026-09-09 15:37:41 -04:00
0201f71e5d
allow banning left users 2026-09-09 15:26:04 -04:00
4314c88d5c
fallback to empty profile if fetch fails 2026-09-09 15:25:10 -04:00
88cdcf9863
use compat bridge for settings sidebar 2026-08-23 14:02:25 -04:00
e2f25749e0
fix fetching spec versions early 2026-08-23 14:00:11 -04:00
47a3d9d92f
Add comment about why MaterialUiCompatibilityBridge is needed for HtmlWidget
[skip ci]
2026-08-22 15:19:46 -04:00
a680c5cd0c
update navigation_rail_m3e hash 2026-08-22 15:15:39 -04:00
0887421f3e
put bridge for link colors 2026-08-22 15:14:41 -04:00
a090d88a23
add theming workaround for navigation_rail_m3e 2026-08-22 15:12:35 -04:00
32693fb3b5
fix path in nix build 2026-08-22 14:26:28 -04:00
4e1f1b64bc
run APK build on x86_64 2026-08-22 14:18:12 -04:00
20f1c784f6
optimize gomuks build size 2026-08-22 14:16:35 -04:00
9b79304edc
try to optimize APK size 2026-08-22 14:05:31 -04:00
b797b8de9c
fix icons not rendering 2026-08-22 13:51:44 -04:00
56bb75866c
bump JDK version 2026-08-22 13:50:00 -04:00
3baea40669
skip broken android lints 2026-08-22 13:24:26 -04:00
2e72ecd559
fix navigation_rail_m3e hash
Fixes nix build
2026-08-22 13:06:33 -04:00
077044ec19 Move to Flutter 3.47, use the improvements that come with this (#64)
Reviewed-on: Nexus/nexus#64
2026-08-22 13:04:33 -04:00
01b13b4764
set min version of macos to 13.0 2026-08-12 10:46:32 -04:00
050e42cc6f
comp for older macos 2026-08-12 10:34:36 -04:00
67cee201b6
add TODO comment to LinkToMention extension 2026-08-11 20:52:14 -04:00
2d7b067c93
use mobile icon for macOS
this looks better in the macos launcher squircles
2026-08-11 20:39:04 -04:00
a9993013f5
add rooms to mention chips
Needs handling for event links, plus clickability
2026-08-11 20:34:43 -04:00
9c488e8981
only upload .dmgs 2026-08-11 20:08:59 -04:00
5c16f42d20
pin upload-artifact to a commit 2026-08-11 19:58:15 -04:00
092fc616e6
fix paths in macos CI 2026-08-11 19:56:26 -04:00
6cbf78be6b
change step names for macos
[skip ci]
2026-08-11 19:20:08 -04:00
68f4bb0c03
update macos status in readme 2026-08-11 19:18:09 -04:00
9e24f4cb31
add DMG builds 2026-08-11 19:12:56 -04:00
43d2e94d62
make loadOlder properly guard 2026-08-11 10:39:45 -04:00
62fe37c6ae
use mobile icons on iOS 2026-08-11 10:17:24 -04:00
b9dfbd3ca2
update MacOS icons 2026-08-11 10:16:18 -04:00
620440e2a1
Allow logging into homeservers without discovery working 2026-08-10 22:50:13 -04:00
724171384c
change readme wording
[skip ci]
2026-08-08 20:49:23 -04:00
1bd677eb23
add MacOS support to readme 2026-08-08 20:36:48 -04:00
41658c3a9d
change output artifact name for macos ci 2026-08-08 20:33:41 -04:00
b011cc2166
reformat build hook file
[skip ci]
2026-08-08 20:31:09 -04:00
75ad6cbd02
set SDKROOT for macos build 2026-08-08 20:20:02 -04:00
6c1d3d8894
debug macos 2026-08-08 20:08:49 -04:00
842ab0a82c
don't use xcrun clang on macos 2026-08-08 19:57:03 -04:00
a0e5d2fb9e
make macos-related actions not use nix 2026-08-08 19:48:56 -04:00
a2cdc3df68
set CC manually macos 2026-08-08 19:08:20 -04:00
07f8a35b74
bump deps, remove hack for fallback theme that is no longer needed 2026-08-08 19:05:04 -04:00
85b9007c47
bump flake 2026-08-08 18:53:12 -04:00
5bbd51c4bc
fix macos permission issues 2026-08-08 18:52:49 -04:00
21a0e0db70
correctly undo lipo changes 2026-08-08 18:03:57 -04:00
a5f42fe56e
add workaround for lipo issues with Flutter from Nix on MacOS 2026-08-08 17:56:01 -04:00
185c1d2714
undo lipo changes 2026-08-08 17:53:58 -04:00
7eab09585b
Revert "move lipo wrapper"
This reverts commit 56dbd91c96.
2026-08-08 17:51:00 -04:00
c84f0e7e93
dismiss keyboard on scroll of room chat 2026-08-08 17:02:31 -04:00
f7847d587c
only enable autofocus on desktop platforms 2026-08-08 16:57:51 -04:00
56dbd91c96
move lipo wrapper 2026-08-08 16:49:25 -04:00
7bb4d87638 feat: Add iOS platform support (#60)
Co-authored-by: Henry-Hiles <henry@henryhiles.com>
Reviewed-on: Nexus/nexus#60
2026-08-08 16:35:56 -04:00
bf31f5510a
make room dialog scrollable 2026-08-08 16:21:58 -04:00
bd185824f9
set loading when waiting for token exchange 2026-08-08 16:18:54 -04:00
2afd5ae258
put account data keys in model 2026-08-08 16:14:11 -04:00
76f9175857
update gomuks vendorHash 2026-08-08 13:27:10 -04:00
e32941f062
remove old extraEnv for macos
No longer needed.
2026-08-08 13:22:51 -04:00
cf89604ff5
update gomuks
Fixes error when fetching versions after login
2026-08-08 13:21:29 -04:00
293 changed files with 7503 additions and 3803 deletions

View file

@ -27,7 +27,7 @@ jobs:
KEYSTORE_CONTENT: ${{ secrets.KEYSTORE_CONTENT }} KEYSTORE_CONTENT: ${{ secrets.KEYSTORE_CONTENT }}
- name: Build app - 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" run: nix develop --command bash -c "flutter pub get && dart scripts/generate.dart && flutter pub run build_runner build && flutter build apk --target-platform android-arm64"
env: env:
KEYSTORE_PATH: ../../keystore.jks KEYSTORE_PATH: ../../keystore.jks
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}

48
.github/workflows/ios.yml vendored Normal file
View file

@ -0,0 +1,48 @@
name: "Build iOS App"
on:
push:
branches: ["main"]
tags: ["*"]
workflow_dispatch:
jobs:
build-app:
runs-on: macos-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
submodules: recursive
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: 3.47.4
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: gomuks/go.mod
- name: Build app
run: |
flutter pub get
dart scripts/generate.dart
flutter pub run build_runner build
flutter build ios --release --no-codesign
# Unsigned IPA: SideStore/AltStore re-sign this on-device with the
# user's own Apple ID at install time, so no certificate is needed here.
- name: Package unsigned IPA
run: |
mkdir Payload
cp -r build/ios/iphoneos/Runner.app Payload/
zip -r Nexus.ipa Payload
- name: Upload IPA artifact
uses: actions/upload-artifact@v6
with:
name: Nexus.ipa
path: Nexus.ipa

View file

@ -1,10 +1,9 @@
name: "Build MacOS App" name: "Build MacOS App"
on: on:
# TODO: Uncomment this once the MacOS workflow actually works push:
# push: branches: ["main"]
# branches: ["main"] tags: ["*"]
# tags: ["*"]
workflow_dispatch: workflow_dispatch:
jobs: jobs:
@ -17,16 +16,32 @@ jobs:
with: with:
submodules: recursive submodules: recursive
- name: Lix GHA Installer Action - name: Set up Flutter
uses: samueldr/lix-gha-installer-action@v2026-02-22 uses: subosito/flutter-action@v2
with: with:
extra_nix_config: experimental-features = nix-command flakes flake-self-attrs flutter-version: 3.47.4
- name: Build app - name: Set up Go
run: nix develop --command bash -c "flutter pub get && dart scripts/generate.dart && flutter pub run build_runner build && flutter build macos --release" uses: actions/setup-go@v6
with:
go-version-file: gomuks/go.mod
- name: Upload installer artifact - name: Build App
run: |
flutter pub get
dart scripts/generate.dart
flutter pub run build_runner build
flutter build macos --release
- name: Create DMG
id: create-dmg
uses: L-Super/create-dmg-actions@28511e988b13ca34096d439159a66d468b8e724f
with:
dmg_name: nexus
src_dir: build/macos/Build/Products/Release/Nexus.app
- name: Upload DMG
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v6
with: with:
name: App name: nexus.dmg
path: build/macos/Build/Products/Release/Nexus.app path: ${{ steps.create-dmg.outputs.dmg_path }}

View file

@ -19,8 +19,7 @@ jobs:
- name: Set up Flutter - name: Set up Flutter
uses: subosito/flutter-action@v2 uses: subosito/flutter-action@v2
with: with:
flutter-version: 3.44.4 flutter-version: 3.47.4
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v6 uses: actions/setup-go@v6
with: with:

1
.gitmodules vendored
View file

@ -1,4 +1,3 @@
[submodule "gomuks"] [submodule "gomuks"]
path = gomuks path = gomuks
url = https://github.com/gomuks/gomuks url = https://github.com/gomuks/gomuks
branch = main

View file

@ -4,7 +4,7 @@
# This file should be version controlled and should not be manually edited. # This file should be version controlled and should not be manually edited.
version: version:
revision: "nixpkgs000000000000000000000000000000000" revision: "6b182d2c7585eba26d4edce0f97630effd256c33"
channel: "stable" channel: "stable"
project_type: app project_type: app
@ -13,11 +13,11 @@ project_type: app
migration: migration:
platforms: platforms:
- platform: root - platform: root
create_revision: nixpkgs000000000000000000000000000000000 create_revision: 6b182d2c7585eba26d4edce0f97630effd256c33
base_revision: nixpkgs000000000000000000000000000000000 base_revision: 6b182d2c7585eba26d4edce0f97630effd256c33
- platform: macos - platform: ios
create_revision: nixpkgs000000000000000000000000000000000 create_revision: 6b182d2c7585eba26d4edce0f97630effd256c33
base_revision: nixpkgs000000000000000000000000000000000 base_revision: 6b182d2c7585eba26d4edce0f97630effd256c33
# User provided section # User provided section

17
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,17 @@
{
"configurations": [
{
"name": "Flutter",
"type": "dart",
"request": "launch",
"program": "lib/main.dart"
},
{
"name": "Flutter (Headless)",
"type": "dart",
"request": "launch",
"program": "lib/main.dart",
"env": { "FLUTTER_HEADLESS": "1" }
}
]
}

View file

@ -11,6 +11,8 @@
"muks", "muks",
"prefs", "prefs",
"unban", "unban",
"unredact" "unifiedpush",
"unredact",
"webpush"
] ]
} }

View file

@ -21,7 +21,7 @@ See [Effective Dart: Style](https://dart.dev/effective-dart/style) for general r
Controllers live in `lib/controllers/` and provide a source that exposes data and logic via Riverpod providers, allowing other parts of the code to watch state changes with ref.watch (`ref.watch(MyController.provider)`), access the current value with ref.read (`ref.read(MyController.provider)`), and run helper methods on those classes using the notifier: Controllers live in `lib/controllers/` and provide a source that exposes data and logic via Riverpod providers, allowing other parts of the code to watch state changes with ref.watch (`ref.watch(MyController.provider)`), access the current value with ref.read (`ref.read(MyController.provider)`), and run helper methods on those classes using the notifier:
```dart ```dart
ref.watch(MyController.provider.notifier).helperMethod() ref.read(MyController.provider.notifier).helperMethod()
``` ```
We use an object oriented style for controllers, where `provider` is a static member on the controller class. E.g. We use an object oriented style for controllers, where `provider` is a static member on the controller class. E.g.

View file

@ -15,12 +15,17 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend.
## Progress ## Progress
- [ ] Platform Support - [x] Platform Support
- [x] Linux - [x] Linux
- [x] Windows - [x] Windows
- Currently has some bugs, due to us not having any developers on Windows. If you want to fix these, get in contact with us though the [Nexus Client Matrix room](https://matrix.to/#/#nexus:federated.nexus).
- [x] Android - [x] Android
- [ ] MacOS ([#50](https://git.federated.nexus/Nexus/nexus/issues/50)) - [x] MacOS
- [ ] iOS - [x] Unsigned .ipa
- [ ] App Store
- [x] iOS
- [x] Unsigned .ipa
- [ ] App Store
- [ ] Web (may not be possible) - [ ] Web (may not be possible)
- [x] Login (via OAuth) - [x] Login (via OAuth)
- [x] Rooms / Spaces - [x] Rooms / Spaces
@ -50,10 +55,10 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend.
- [x] Per message profiles - [x] Per message profiles
- [x] Attachments - [x] Attachments
- [ ] Commands with [MSC4391](https://github.com/matrix-org/matrix-spec-proposals/pull/4391) - [ ] Commands with [MSC4391](https://github.com/matrix-org/matrix-spec-proposals/pull/4391)
- [x] Mentions - [x] Tags
- [x] Users - [x] Users
- [x] Rooms - [x] Rooms
- [ ] Inline emoji picker (Putting this here since it'll be implemented the same way as mentions) - [x] Emojis
- [ ] Custom emojis/stickers - [ ] Custom emojis/stickers
- [ ] GIFs using Gomuks' GIF proxies - [ ] GIFs using Gomuks' GIF proxies
- [x] Receiving - [x] Receiving
@ -63,9 +68,7 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend.
- [x] URL Previews - [x] URL Previews
- [x] Replies - [x] Replies
- [x] Viewing - [x] Viewing
- [ ] Jump to original message - [x] Jump to original message
- [x] In loaded timeline
- [ ] Out of loaded timeline
- [x] Edits - [x] Edits
- [x] Attachments - [x] Attachments
- [x] Unencrypted - [x] Unencrypted
@ -105,7 +108,8 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend.
- [x] Member list - [x] Member list
- [x] Sort by power level - [x] Sort by power level
- [ ] Colors based off of power level - [ ] Colors based off of power level
- [ ] Notifications using UnifiedPush ([#35](https://git.federated.nexus/Nexus/nexus/issues/35)) - [x] Notifications using UnifiedPush ([#35](https://git.federated.nexus/Nexus/nexus/issues/35))
- [x] Notifications page
- [ ] Group calls using [MSC4195](https://github.com/matrix-org/matrix-spec-proposals/pull/4195) - [ ] Group calls using [MSC4195](https://github.com/matrix-org/matrix-spec-proposals/pull/4195)
- [ ] Invites - [ ] Invites
- [x] Settings - [x] Settings
@ -115,10 +119,13 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend.
If you want to try out Nexus, grab one of the following artifacts from CI: If you want to try out Nexus, grab one of the following artifacts from CI:
- [Android APK](https://nightly.link/Henry-Hiles/nexus/workflows/android/main/APK.zip) - [Android APK](https://nightly.link/Henry-Hiles/nexus/workflows/android/main/APK.zip)
- [Unsigned iOS IPA](https://nightly.link/Henry-Hiles/nexus/workflows/ios/main/Nexus.ipa.zip)
- [Unsigned MacOS DMG](https://nightly.link/Henry-Hiles/nexus/workflows/macos/main/nexus.dmg.zip)
- [Windows EXE](https://nightly.link/Henry-Hiles/nexus/workflows/windows/main/windows-installer.zip) - [Windows EXE](https://nightly.link/Henry-Hiles/nexus/workflows/windows/main/windows-installer.zip)
- Flatpak - Flatpak
- [AArch64/Arm64](https://nightly.link/Henry-Hiles/nexus/workflows/flatpak/main/flatpak-aarch64.zip) - [AArch64/Arm64](https://nightly.link/Henry-Hiles/nexus/workflows/flatpak/main/flatpak-aarch64.zip)
- [x86_64/AMD64](https://nightly.link/Henry-Hiles/nexus/workflows/flatpak/main/flatpak-x86_64.zip) - [x86_64/AMD64](https://nightly.link/Henry-Hiles/nexus/workflows/flatpak/main/flatpak-x86_64.zip)
- [NixOS Module](linux/nix/module.nix)
## Build it yourself ## Build it yourself
@ -126,8 +133,8 @@ If you want to try out Nexus, grab one of the following artifacts from CI:
#### Linux #### Linux
- With Nix: Either use direnv and `direnv allow`, or `nix flake develop` - On NixOS: Either use direnv and `direnv allow`, or `nix flake develop`
- Without Nix: Install Flutter, Go, Git, Libclang, Libass, MPV, and Glibc. Do not use any Snap packages, they cause various compilation issues. - On other distros: Install Flutter, Go, Git, Libclang, Libass, MPV, and Glibc. Do not use any Snap packages, they cause various compilation issues.
#### Windows #### Windows
@ -187,6 +194,12 @@ dart scripts/generate.dart
> export CPATH="$(clang -v 2>&1 | grep "Selected GCC installation" | rev | cut -d' ' -f1 | rev)/include" > export CPATH="$(clang -v 2>&1 | grep "Selected GCC installation" | rev | cut -d' ' -f1 | rev)/include"
> ``` > ```
Build webcrypto:
```sh
flutter pub run webcrypto:setup
```
Build generated files, and watch for new changes: Build generated files, and watch for new changes:
```sh ```sh

22
altstore-source.json Normal file
View file

@ -0,0 +1,22 @@
{
"name": "Nexus",
"identifier": "nexus.federated.nexus.source",
"subtitle": "Nexus for iOS",
"description": "Nexus iOS builds for AltStore and SideStore.",
"iconURL": "https://git.federated.nexus/Nexus/nexus/raw/branch/main/assets/icon.png",
"website": "https://git.federated.nexus/Nexus/nexus",
"apps": [
{
"name": "Nexus",
"bundleIdentifier": "nexus.federated.nexus",
"developerName": "Nexus",
"subtitle": "A Matrix client",
"localizedDescription": "Nexus for iOS. Unsigned builds, resigned at install by AltStore/SideStore.",
"iconURL": "https://git.federated.nexus/Nexus/nexus/raw/branch/main/assets/icon.png",
"category": "social",
"screenshots": [],
"versions": []
}
],
"news": []
}

View file

@ -6,6 +6,12 @@ analyzer:
- "build/**" - "build/**"
- "**/*.g.dart" - "**/*.g.dart"
- "**/*.freezed.dart" - "**/*.freezed.dart"
- android/**
- ios/**
- web/**
- windows/**
- macos/**
- linux/**
include: package:flutter_lints/flutter.yaml include: package:flutter_lints/flutter.yaml
linter: linter:
rules: rules:

View file

@ -1,77 +0,0 @@
plugins {
id "com.android.application"
id "kotlin-android"
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id "dev.flutter.flutter-gradle-plugin"
}
def localProperties = new Properties()
def localPropertiesFile = rootProject.file("local.properties")
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader("UTF-8") { reader ->
localProperties.load(reader)
}
}
def flutterVersionCode = localProperties.getProperty("flutter.versionCode")
if (flutterVersionCode == null) {
flutterVersionCode = "1"
}
def flutterVersionName = localProperties.getProperty("flutter.versionName")
if (flutterVersionName == null) {
flutterVersionName = "1.0"
}
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
android {
namespace = "nexus.federated.nexus"
ndkVersion = flutter.ndkVersion
compileSdk = 34
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
defaultConfig {
applicationId = "nexus.federated.nexus"
minSdk = 29
targetSdkVersion flutter.targetSdkVersion
versionCode = flutterVersionCode.toInteger()
versionName = flutterVersionName
}
signingConfigs {
release {
keyAlias "key"
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")
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
debug {
applicationIdSuffix = ".debug"
}
}
}
flutter {
source = "../.."
}

View file

@ -0,0 +1,84 @@
import java.util.Properties
plugins {
id("com.android.application")
id("dev.flutter.flutter-gradle-plugin")
}
val keystoreProperties = Properties()
val keystorePropertiesFile = rootProject.file("key.properties")
if (keystorePropertiesFile.exists()) {
keystorePropertiesFile.inputStream().use {
keystoreProperties.load(it)
}
}
android {
namespace = "nexus.federated.nexus"
compileSdk = 36
ndkVersion = flutter.ndkVersion
compileOptions {
isCoreLibraryDesugaringEnabled = true
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
defaultConfig {
applicationId = "nexus.federated.nexus"
minSdk = 29
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
multiDexEnabled = true
}
signingConfigs {
create("release") {
keyAlias = "key"
val storePath =
keystoreProperties["path"]?.toString()
?: System.getenv("KEYSTORE_PATH")
storeFile = storePath?.let { file(it) }
keyPassword =
keystoreProperties["password"]?.toString()
?: System.getenv("KEYSTORE_PASSWORD")
storePassword =
keystoreProperties["password"]?.toString()
?: System.getenv("KEYSTORE_PASSWORD")
}
}
buildTypes {
release {
signingConfig = signingConfigs.getByName("release")
}
debug {
applicationIdSuffix = ".debug"
}
}
}
dependencies {
implementation("org.unifiedpush.android:embedded-fcm-distributor:3.1.0")
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
}
kotlin {
compilerOptions {
jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_21
}
}
flutter {
source = "../.."
}

View file

@ -1,26 +0,0 @@
allprojects {
repositories {
google()
mavenCentral()
}
}
rootProject.buildDir = "../build"
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
afterEvaluate {project ->
if (project.hasProperty("android")) {
android {
compileSdkVersion 36
buildToolsVersion '33.0.1'
}
}
}
project.evaluationDependsOn(":app")
}
tasks.register("clean", Delete) {
delete rootProject.buildDir
}

25
android/build.gradle.kts Normal file
View file

@ -0,0 +1,25 @@
allprojects {
repositories {
google()
mavenCentral()
}
}
val newBuildDir: Directory =
rootProject.layout.buildDirectory
.dir("../../build")
.get()
rootProject.layout.buildDirectory.value(newBuildDir)
subprojects {
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
project.layout.buildDirectory.value(newSubprojectBuildDir)
}
subprojects {
project.evaluationDependsOn(":app")
}
tasks.register<Delete>("clean") {
delete(rootProject.layout.buildDirectory)
}

View file

@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.0-all.zip

View file

@ -1,25 +0,0 @@
pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}()
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "8.13.0" apply false
id "org.jetbrains.kotlin.android" version "2.2.20" apply false
}
include ":app"

View file

@ -0,0 +1,26 @@
pluginManagement {
val flutterSdkPath =
run {
val properties = java.util.Properties()
file("local.properties").inputStream().use { properties.load(it) }
val flutterSdkPath = properties.getProperty("flutter.sdk")
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
flutterSdkPath
}
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "9.3.1" apply false
id("org.jetbrains.kotlin.android") version "2.4.10" apply false
}
include(":app")

View file

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Before After
Before After

BIN
assets/twim/notif.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

29
flake.lock generated
View file

@ -5,11 +5,11 @@
"nixpkgs-lib": "nixpkgs-lib" "nixpkgs-lib": "nixpkgs-lib"
}, },
"locked": { "locked": {
"lastModified": 1782949081, "lastModified": 1788450739,
"narHash": "sha256-vp6Y/Grm98ESt6ceOkWiHWyZRDV3J1RID4w+6NWK9yA=", "narHash": "sha256-glZLQlzIn1fXH6PazR2iUmTo7kzzyYSshrWhLS9TqCU=",
"owner": "hercules-ci", "owner": "hercules-ci",
"repo": "flake-parts", "repo": "flake-parts",
"rev": "17c9d6cdfc60c64f4ee8d306f9bc0b4ccb51481e", "rev": "31729ca8cbdb4fa927b34e5f4353e6a83f39e993",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -42,15 +42,16 @@
"nixpkgs": "nixpkgs" "nixpkgs": "nixpkgs"
}, },
"locked": { "locked": {
"lastModified": 1774860670, "lastModified": 1790043059,
"narHash": "sha256-YjJkQrvxrErXtfDi3obUn6rNmkA+CIAZ3f5NgL5xuYE=", "narHash": "sha256-7ID4HL4IrrlalCsxMASkcAr+v0VHTNFp6+sZ+dpvOV8=",
"owner": "neobrain", "owner": "Henry-Hiles",
"repo": "nix2flatpak", "repo": "nix2flatpak",
"rev": "61d68e21e3fbc2d57590051f48736bea271f4aba", "rev": "290dd282e555cab988c021c2be5f2361e58c4d51",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "neobrain", "owner": "Henry-Hiles",
"ref": "quad/nexus",
"repo": "nix2flatpak", "repo": "nix2flatpak",
"type": "github" "type": "github"
} }
@ -73,11 +74,11 @@
}, },
"nixpkgs-lib": { "nixpkgs-lib": {
"locked": { "locked": {
"lastModified": 1782614948, "lastModified": 1788057806,
"narHash": "sha256-ePjCwr1sNm9NYUqywL7QfK3JnlS015msC+eBu2zKlp8=", "narHash": "sha256-DTQSMxzDWmT0zhguthvegnVkn7CFqGCv4IHCzk5ZUpM=",
"owner": "nix-community", "owner": "nix-community",
"repo": "nixpkgs.lib", "repo": "nixpkgs.lib",
"rev": "db3f255737b94216eb71cce308e2912cf6bc2d7c", "rev": "596e2e3940e09b2abbeb03f75fa1828c57fcd72c",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -88,11 +89,11 @@
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1784497964, "lastModified": 1790185690,
"narHash": "sha256-vlHUuqAcbcH2RKmHbPiuQzbv1pnzzavXnI62RD0bqCU=", "narHash": "sha256-xJ+X4hBtOcAFGBOe5nAMyMUeF9foJBmIOu3NjBqBycU=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "241313f4e8e508cb9b13278c2b0fa25b9ca27163", "rev": "4975466d324710c576dc11ad614684e6bd8cad8e",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -5,7 +5,7 @@
self.submodules = true; self.submodules = true;
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
flake-parts.url = "github:hercules-ci/flake-parts"; flake-parts.url = "github:hercules-ci/flake-parts";
nix2flatpak.url = "github:neobrain/nix2flatpak"; nix2flatpak.url = "github:Henry-Hiles/nix2flatpak/quad/nexus";
}; };
outputs = outputs =
@ -16,62 +16,12 @@
... ...
}@inputs: }@inputs:
flake-parts.lib.mkFlake { inherit inputs; } { flake-parts.lib.mkFlake { inherit inputs; } {
imports = [ ./linux/nix ];
systems = [ systems = [
"x86_64-linux" "x86_64-linux"
"aarch64-linux" "aarch64-linux"
"aarch64-darwin" "aarch64-darwin"
"x86_64-darwin" "x86_64-darwin"
]; ];
perSystem =
{
lib,
pkgs,
system,
...
}:
{
_module.args.pkgs = import nixpkgs {
inherit system;
config = {
android_sdk.accept_license = true;
allowUnfree = true;
};
};
packages =
let
default = pkgs.callPackage ./linux/nix/pkg {
src = self;
};
in
{
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 = [
"pulseaudio"
"fallback-x11"
"wayland"
];
devices = [ "dri" ];
};
};
gomuks = pkgs.callPackage ./linux/nix/pkg/gomuks.nix {
src = self;
};
};
devShells.default = pkgs.callPackage ./linux/nix/devshell.nix { };
};
}; };
} }

2
gomuks

@ -1 +1 @@
Subproject commit 2d566a2b585e2dba882714f7ca741a52afb36237 Subproject commit 480938f9e01e7eae2cf249657efae03df76599d6

View file

@ -1,4 +1,5 @@
import "dart:io"; import "dart:io";
import "package:collection/collection.dart"; import "package:collection/collection.dart";
import "package:hooks/hooks.dart"; import "package:hooks/hooks.dart";
import "package:code_assets/code_assets.dart"; import "package:code_assets/code_assets.dart";
@ -13,22 +14,46 @@ Future<void> main(List<String> args) => build(args, (input, output) async {
String libFileName; String libFileName;
Map<String, String> extraEnv = {}; Map<String, String> extraEnv = {};
IOSSdk? iosSdk;
String? iosSdkPath;
String? iosTargetTriple;
switch (targetOS) { switch (targetOS) {
case OS.linux: case OS.linux:
libFileName = "libgomuks.so"; libFileName = "libgomuks.so";
break; break;
case OS.iOS:
// Go only supports buildmode=c-archive for ios, not c-shared, so the
// archive built below gets linked into a dylib via a separate clang
// invocation before being registered as a normal asset like macOS.
libFileName = "libgomuks.dylib";
final iosConfig = codeConfig.iOS;
iosSdk = iosConfig.targetSdk;
final minVersion = iosConfig.targetVersion;
iosSdkPath = await getXCodeTool(sdkType: iosSdk.type);
final archTriple = switch (targetArch) {
Architecture.arm64 => "arm64",
Architecture.x64 => "x86_64",
_ => throw UnsupportedError(
"Unsupported iOS architecture: $targetArch",
),
};
iosTargetTriple = iosSdk == IOSSdk.iPhoneSimulator
? "$archTriple-apple-ios$minVersion.0-simulator"
: "$archTriple-apple-ios$minVersion.0";
extraEnv = {
"GOOS": "ios",
"CC": await getXCodeTool(sdkType: iosSdk.type, findTool: "clang"),
"CGO_CFLAGS": "-isysroot $iosSdkPath -target $iosTargetTriple",
"CGO_LDFLAGS": "-isysroot $iosSdkPath -target $iosTargetTriple",
};
break;
case OS.macOS: case OS.macOS:
libFileName = "libgomuks.dylib"; libFileName = "libgomuks.dylib";
extraEnv = { extraEnv = {
"SDKROOT": await getXCodeSDK(), "SDKROOT": await getXCodeTool(),
"CGO_ENABLED": "1", "MACOSX_DEPLOYMENT_TARGET": codeConfig.macOS.targetVersion.toString(),
"GOARCH": switch (targetArch) {
Architecture.arm64 => "arm64",
Architecture.x64 => "amd64",
_ => throw UnsupportedError(
"Unsupported macOS architecture: $targetArch",
),
},
}; };
break; break;
case OS.windows: case OS.windows:
@ -96,15 +121,28 @@ Future<void> main(List<String> args) => build(args, (input, output) async {
final tags = [ final tags = [
"sqlite_fts5", "sqlite_fts5",
"goolm", "goolm",
// goheif/dav1d is not supported on Android, would need to be fixed upstream // goheif/dav1d is not supported on Android or iOS, would need to be fixed upstream
if (targetOS == OS.android) "noheic", if (targetOS == OS.android || targetOS == OS.iOS) "noheic",
].join(","); ].join(",");
final archiveFile = targetOS == OS.iOS
? buildDir.resolve("${targetArch.name}/libgomuks.a")
: libFile;
print( print(
"Building Gomuks shared library $libFileName (${targetOS.name}/${targetArch.name}) to ${libFile.path}...", "Building Gomuks shared library $libFileName (${targetOS.name}/${targetArch.name}) to ${archiveFile.path}...",
); );
final result = await Process.run( final result = await Process.run(
"go", "go",
["build", "-tags", tags, "-o", libFile.path, "-buildmode=c-shared"], [
"build",
"-trimpath",
"-ldflags=-s -w",
"-tags",
tags,
"-o",
archiveFile.path,
"-buildmode=${targetOS == OS.iOS ? "c-archive" : "c-shared"}",
],
workingDirectory: gomuksBuildDir.resolve("pkg/ffi/").toFilePath(), workingDirectory: gomuksBuildDir.resolve("pkg/ffi/").toFilePath(),
environment: { environment: {
"CGO_ENABLED": "1", "CGO_ENABLED": "1",
@ -124,6 +162,35 @@ Future<void> main(List<String> args) => build(args, (input, output) async {
"Failed to build Gomuks shared library\n${result.stderr}", "Failed to build Gomuks shared library\n${result.stderr}",
); );
} }
if (targetOS == OS.iOS) {
print("Linking $archiveFile into $libFile...");
final linkResult = await Process.run("xcrun", [
"--sdk",
iosSdk!.type,
"clang",
"-dynamiclib",
"-isysroot",
iosSdkPath!,
"-target",
iosTargetTriple!,
"-install_name",
"@rpath/libgomuks.dylib",
"-framework",
"Security",
"-framework",
"CoreFoundation",
"-framework",
"SystemConfiguration",
"-force_load",
archiveFile.path,
"-o",
libFile.path,
]);
if (linkResult.exitCode != 0) {
throw Exception("Failed to link Gomuks dylib\n${linkResult.stderr}");
}
}
} }
final generatedFile = "src/third_party/gomuks.g.dart"; final generatedFile = "src/third_party/gomuks.g.dart";

34
ios/.gitignore vendored Normal file
View file

@ -0,0 +1,34 @@
**/dgph
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3

View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
</dict>
</plist>

View file

@ -0,0 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"

View file

@ -0,0 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"

43
ios/Podfile Normal file
View file

@ -0,0 +1,43 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '13.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
target 'RunnerTests' do
inherit! :search_paths
end
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end

28
ios/Podfile.lock Normal file
View file

@ -0,0 +1,28 @@
PODS:
- Flutter (1.0.0)
- media_kit_libs_ios_video (1.0.4):
- Flutter
- media_kit_video (0.0.1):
- Flutter
DEPENDENCIES:
- Flutter (from `Flutter`)
- media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`)
- media_kit_video (from `.symlinks/plugins/media_kit_video/ios`)
EXTERNAL SOURCES:
Flutter:
:path: Flutter
media_kit_libs_ios_video:
:path: ".symlinks/plugins/media_kit_libs_ios_video/ios"
media_kit_video:
:path: ".symlinks/plugins/media_kit_video/ios"
SPEC CHECKSUMS:
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
media_kit_libs_ios_video: 5a18affdb97d1f5d466dc79988b13eff6c5e2854
media_kit_video: 1746e198cb697d1ffb734b1d05ec429d1fcd1474
PODFILE CHECKSUM: 3c63482e143d1b91d2d2560aee9fb04ecc74ac7e
COCOAPODS: 1.17.0

View file

@ -0,0 +1,759 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
2ADFEE6C745FFB2100CC9AFD /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8156A45F510382D558AED93A /* Pods_RunnerTests.framework */; };
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
7884E8682EC3CC0700C636F2 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */; };
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; };
88D55A5F1C7625122EBEE29D /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 82F9A3759658343C81A0EA4C /* Pods_Runner.framework */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
proxyType = 1;
remoteGlobalIDString = 97C146ED1CF9000F007C117D;
remoteInfo = Runner;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
22DE4310DAFBE211CDF700B1 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
363A89F6FA09BCCBDED440CE /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
63516CF65A79A5DDDE64B0D1 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
8156A45F510382D558AED93A /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
82F9A3759658343C81A0EA4C /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
AEB96D8976BD46D6860C7041 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
B8B599FDB72E4713FB7B5A44 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
FE5E4162842555A83F8A7FD7 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
8C0A36760DA8B873D8F6DDCE /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
2ADFEE6C745FFB2100CC9AFD /* Pods_RunnerTests.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */,
88D55A5F1C7625122EBEE29D /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
1CFFAE23B502BCCEB2B191A3 /* Pods */ = {
isa = PBXGroup;
children = (
AEB96D8976BD46D6860C7041 /* Pods-Runner.debug.xcconfig */,
363A89F6FA09BCCBDED440CE /* Pods-Runner.release.xcconfig */,
22DE4310DAFBE211CDF700B1 /* Pods-Runner.profile.xcconfig */,
B8B599FDB72E4713FB7B5A44 /* Pods-RunnerTests.debug.xcconfig */,
FE5E4162842555A83F8A7FD7 /* Pods-RunnerTests.release.xcconfig */,
63516CF65A79A5DDDE64B0D1 /* Pods-RunnerTests.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
331C8082294A63A400263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
331C807B294A618700263BE5 /* RunnerTests.swift */,
);
path = RunnerTests;
sourceTree = "<group>";
};
594EB1F65336CF9C7958F6FA /* Frameworks */ = {
isa = PBXGroup;
children = (
82F9A3759658343C81A0EA4C /* Pods_Runner.framework */,
8156A45F510382D558AED93A /* Pods_RunnerTests.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */,
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */,
1CFFAE23B502BCCEB2B191A3 /* Pods */,
594EB1F65336CF9C7958F6FA /* Frameworks */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
331C8081294A63A400263BE5 /* RunnerTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
331C8080294A63A400263BE5 /* RunnerTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
EE82EC529EB6C0F1FA480659 /* [CP] Check Pods Manifest.lock */,
331C807D294A63A400263BE5 /* Sources */,
331C807F294A63A400263BE5 /* Resources */,
8C0A36760DA8B873D8F6DDCE /* Frameworks */,
);
buildRules = (
);
dependencies = (
331C8086294A63A400263BE5 /* PBXTargetDependency */,
);
name = RunnerTests;
productName = RunnerTests;
productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
0830C6758DA3ECA81FB12386 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
1D165FDFB9D0D939B041966D /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
packageProductDependencies = (
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */,
);
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
331C8080294A63A400263BE5 = {
CreatedOnToolsVersion = 14.0;
TestTargetID = 97C146ED1CF9000F007C117D;
};
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
packageReferences = (
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */,
);
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
331C8080294A63A400263BE5 /* RunnerTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
331C807F294A63A400263BE5 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
0830C6758DA3ECA81FB12386 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
1D165FDFB9D0D939B041966D /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
EE82EC529EB6C0F1FA480659 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
331C807D294A63A400263BE5 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
7884E8682EC3CC0700C636F2 /* SceneDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 97C146ED1CF9000F007C117D /* Runner */;
targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = PK66NJM372;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = nexus.federated.nexus;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
331C8088294A63A400263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = B8B599FDB72E4713FB7B5A44 /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = nexus.federated.nexus.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Debug;
};
331C8089294A63A400263BE5 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = FE5E4162842555A83F8A7FD7 /* Pods-RunnerTests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = nexus.federated.nexus.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Release;
};
331C808A294A63A400263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 63516CF65A79A5DDDE64B0D1 /* Pods-RunnerTests.profile.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = nexus.federated.nexus.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = PK66NJM372;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = nexus.federated.nexus;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = PK66NJM372;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = nexus.federated.nexus;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
331C8088294A63A400263BE5 /* Debug */,
331C8089294A63A400263BE5 /* Release */,
331C808A294A63A400263BE5 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
/* Begin XCLocalSwiftPackageReference section */
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = {
isa = XCLocalSwiftPackageReference;
relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage;
};
/* End XCLocalSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = {
isa = XCSwiftPackageProductDependency;
productName = FlutterGeneratedPluginSwiftPackage;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View file

@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<PreActions>
<ExecutionAction
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
<ActionContent
title = "Run Prepare Flutter Framework Script"
scriptText = "/bin/sh &quot;$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh&quot; prepare&#10;">
<EnvironmentBuildable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</EnvironmentBuildable>
</ActionContent>
</ExecutionAction>
</PreActions>
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "331C8080294A63A400263BE5"
BuildableName = "RunnerTests.xctest"
BlueprintName = "RunnerTests"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
enableGPUValidationMode = "1"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View file

@ -0,0 +1,17 @@
import Flutter
import UIKit
@main
@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) {
GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry)
}
}

View file

@ -0,0 +1 @@
{"images":[{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@3x.png","scale":"3x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@1x.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@1x.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"Icon-App-83.5x83.5@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"Icon-App-1024x1024@1x.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}}

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 846 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

View file

@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

View file

@ -0,0 +1,5 @@
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

79
ios/Runner/Info.plist Normal file
View file

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<false/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Nexus</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>nexus</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>nexus.federated.nexus</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneConfigurationName</key>
<string>flutter</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View file

@ -0,0 +1 @@
#import "GeneratedPluginRegistrant.h"

View file

@ -0,0 +1,6 @@
import Flutter
import UIKit
class SceneDelegate: FlutterSceneDelegate {
}

View file

@ -0,0 +1,12 @@
import Flutter
import UIKit
import XCTest
class RunnerTests: XCTestCase {
func testExample() {
// If you add code to the Runner application, consider adding tests here.
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
}
}

View file

@ -6,10 +6,8 @@ import "package:nexus/models/content/content.dart";
import "package:nexus/models/content/message.dart"; import "package:nexus/models/content/message.dart";
import "package:path/path.dart"; import "package:path/path.dart";
class AttachmentController extends Notifier<(String, MessageContent?)?> { class AttachmentController(final String roomId)
final String roomId; extends Notifier<(String, MessageContent?)?> {
AttachmentController(this.roomId);
@override @override
Null build() => null; Null build() => null;

View file

@ -4,10 +4,8 @@ import "package:nexus/controllers/client_id.dart";
import "package:nexus/models/oauth_auth_code_response.dart"; import "package:nexus/models/oauth_auth_code_response.dart";
import "package:nexus/models/requests/oauth/get_auth_url.dart"; import "package:nexus/models/requests/oauth/get_auth_url.dart";
class AuthUrlController extends AsyncNotifier<OAuthAuthCodeResponse> { class AuthUrlController(final Uri homeserver)
final Uri homeserver; extends AsyncNotifier<OAuthAuthCodeResponse> {
AuthUrlController(this.homeserver);
@override @override
Future<OAuthAuthCodeResponse> build() async => ref Future<OAuthAuthCodeResponse> build() async => ref
.watch(ClientController.provider.notifier) .watch(ClientController.provider.notifier)

View file

@ -1,19 +1,17 @@
import "dart:async"; import "dart:async";
import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/user.dart"; import "package:nexus/controllers/user.dart";
import "package:nexus/models/content/membership.dart"; import "package:nexus/models/content/membership.dart";
import "package:nexus/models/event.dart"; import "package:nexus/models/event.dart";
class AuthorController extends AsyncNotifier<MembershipContent> { class AuthorController(final Event event)
final Event event; extends AsyncNotifier<MembershipContent> {
AuthorController(this.event);
@override @override
Future<MembershipContent> build() async { Future<MembershipContent> build() async {
final member = await ref.watch( final member = await ref.watch(
UserController.provider( UserController.provider(.new(roomId: event.roomId, userId: event.sender))
.new(roomId: event.roomId, userId: event.sender), .future,
).future,
); );
return .new( return .new(

View file

@ -1,26 +1,28 @@
import "dart:async";
import "dart:ffi"; import "dart:ffi";
import "dart:io"; import "dart:io";
import "dart:isolate"; import "dart:isolate";
import "dart:math"; import "dart:math";
import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:ffi/ffi.dart"; import "package:ffi/ffi.dart";
import "package:flutter/foundation.dart"; import "package:flutter/cupertino.dart";
import "package:nexus/controllers/account_data.dart"; import "package:intl/intl.dart";
import "package:nexus/controllers/client_state.dart";
import "package:nexus/controllers/init_complete.dart";
import "package:nexus/controllers/rooms.dart";
import "package:nexus/controllers/space_edges.dart";
import "package:nexus/controllers/sync_status.dart";
import "package:nexus/controllers/top_level_spaces.dart";
import "package:nexus/helpers/extensions/gomuks_buffer.dart"; import "package:nexus/helpers/extensions/gomuks_buffer.dart";
import "package:nexus/main.dart"; import "package:nexus/models/capabilities.dart";
import "package:nexus/models/content/message.dart"; import "package:nexus/models/content/message.dart";
import "package:nexus/models/event.dart"; import "package:nexus/models/event.dart";
import "package:nexus/models/event_context.dart";
import "package:nexus/models/gomuks_config.dart";
import "package:nexus/models/oauth_auth_code_response.dart"; import "package:nexus/models/oauth_auth_code_response.dart";
import "package:nexus/models/open_graph_data.dart"; import "package:nexus/models/open_graph_data.dart";
import "package:nexus/models/paginate.dart"; import "package:nexus/models/paginate.dart";
import "package:nexus/models/paginate_manual.dart";
import "package:nexus/models/requests/deregister_pusher.dart";
import "package:nexus/models/requests/download_media.dart"; import "package:nexus/models/requests/download_media.dart";
import "package:nexus/models/requests/get_event.dart"; import "package:nexus/models/requests/get_event.dart";
import "package:nexus/models/requests/get_event_context.dart";
import "package:nexus/models/requests/get_mentions.dart";
import "package:nexus/models/requests/get_related_events.dart"; import "package:nexus/models/requests/get_related_events.dart";
import "package:nexus/models/requests/get_room_state.dart"; import "package:nexus/models/requests/get_room_state.dart";
import "package:nexus/models/requests/join_room.dart"; import "package:nexus/models/requests/join_room.dart";
@ -29,7 +31,9 @@ import "package:nexus/models/requests/oauth/exchange_token.dart";
import "package:nexus/models/requests/oauth/get_auth_url.dart"; import "package:nexus/models/requests/oauth/get_auth_url.dart";
import "package:nexus/models/requests/oauth/register_client.dart"; import "package:nexus/models/requests/oauth/register_client.dart";
import "package:nexus/models/requests/paginate.dart"; import "package:nexus/models/requests/paginate.dart";
import "package:nexus/models/requests/paginate_manual.dart";
import "package:nexus/models/requests/redact_event.dart"; import "package:nexus/models/requests/redact_event.dart";
import "package:nexus/models/requests/register_pusher.dart";
import "package:nexus/models/requests/report.dart"; import "package:nexus/models/requests/report.dart";
import "package:nexus/models/requests/send_event.dart"; import "package:nexus/models/requests/send_event.dart";
import "package:nexus/models/requests/send_message.dart"; import "package:nexus/models/requests/send_message.dart";
@ -38,8 +42,9 @@ import "package:nexus/models/requests/set_membership.dart";
import "package:nexus/models/requests/set_state.dart"; import "package:nexus/models/requests/set_state.dart";
import "package:nexus/models/requests/upload_media.dart"; import "package:nexus/models/requests/upload_media.dart";
import "package:nexus/models/room.dart"; import "package:nexus/models/room.dart";
import "package:nexus/models/room_metadata.dart";
import "package:nexus/models/room_summary.dart";
import "package:nexus/models/spec_versions_response.dart"; import "package:nexus/models/spec_versions_response.dart";
import "package:nexus/models/sync_data.dart";
import "package:nexus/src/third_party/gomuks.g.dart"; import "package:nexus/src/third_party/gomuks.g.dart";
import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:path_provider/path_provider.dart"; import "package:path_provider/path_provider.dart";
@ -47,134 +52,99 @@ import "package:path_provider/path_provider.dart";
class ClientController extends AsyncNotifier<int> { class ClientController extends AsyncNotifier<int> {
@override @override
Future<int> build() async { Future<int> build() async {
final Pointer<Char> root; debugPrint("Setting Gomuks env...");
if (Platform.isAndroid) { if (Platform.isAndroid || Platform.isIOS) {
final dir = await getApplicationSupportDirectory(); final env = {
root = "${dir.path}/gomuks".toNativeUtf8().cast(); "GOMUKS_ROOT": (await getApplicationSupportDirectory()).path,
} else { "GOMUKS_CACHE_HOME": (await getApplicationCacheDirectory()).path,
root = nullptr.cast(); };
} for (final MapEntry(:key, :value) in env.entries) {
final keyPtr = key.toNativeUtf8().cast<Char>();
final handle = GomuksInit(root); final valuePtr = value.toNativeUtf8().cast<Char>();
final callable =
NativeCallable<
Void Function(Pointer<Char>, Int64, GomuksOwnedBuffer)
>.listener((
Pointer<Char> command,
int requestId,
GomuksOwnedBuffer data,
) {
try { try {
final muksEventType = command.cast<Utf8>().toDartString(); GomuksSetEnv(keyPtr, valuePtr);
debugPrint("Handling $muksEventType..."); } finally {
final decodedMuksEvent = data.toJson(); calloc
..free(keyPtr)
switch (muksEventType) { ..free(valuePtr);
case "client_state": }
ref }
.watch(ClientStateController.provider.notifier)
.set(.fromJson(decodedMuksEvent));
break;
case "sync_status":
ref
.watch(SyncStatusController.provider.notifier)
.set(.fromJson(decodedMuksEvent));
break;
case "init_complete":
ref.watch(InitCompleteController.provider.notifier).complete();
break;
case "send_complete":
final event = Event.fromJson(decodedMuksEvent["event"]);
ref
.watch(RoomsController.provider.notifier)
.update(
.new({
event.roomId: .new(events: .new({event.rowId: event})),
}),
.new(),
);
break;
case "sync_complete":
final syncData = SyncData.fromJson(decodedMuksEvent);
final roomProvider = RoomsController.provider;
final accountDataProvider = AccountDataController.provider;
if (syncData.clearState) {
ref.invalidate(roomProvider);
ref.invalidate(accountDataProvider);
} }
ref debugPrint("Initializing Gomuks...");
.watch(roomProvider.notifier) final handle = await Isolate.run(() {
.update(syncData.rooms, syncData.leftRooms); final bufferPointer = GomuksConfig(
ref matrix: .new(
.watch(accountDataProvider.notifier) initialDeviceDisplayName:
.update(syncData.accountData); "Nexus on ${toBeginningOfSentenceCase(Platform.operatingSystem)}",
),
).toJson().toGomuksBufferPtr();
if (syncData.topLevelSpaces != null) { try {
ref return GomuksInit(bufferPointer.ref);
.watch(TopLevelSpacesController.provider.notifier) } finally {
.set(syncData.topLevelSpaces!); calloc
} ..free(bufferPointer.ref.base)
..free(bufferPointer);
if (syncData.spaceEdges != null) {
ref
.watch(SpaceEdgesController.provider.notifier)
.set(syncData.spaceEdges!);
}
// ref
// .watch(SyncStatusController.provider.notifier)
// .set(SyncStatus.fromJson(decodedMuksEvent));
break;
default:
debugPrint("Unhandled event: $muksEventType");
}
debugPrint("Finished handling $muksEventType...");
} catch (error, stackTrace) {
if (kDebugMode) {
debugPrintStack(stackTrace: stackTrace, label: error.toString());
rethrow;
} else {
showError(error, stackTrace);
}
} }
}); });
ref.onDispose(() => GomuksDestroy(handle)); ref.onDispose(() => GomuksDestroy(handle));
ref.onDispose(callable.close);
final errorCode = GomuksStart(handle, callable.nativeFunction); return handle;
if (errorCode == 0) return handle;
throw Exception("GomuksStart returned error code $errorCode");
} }
Future<dynamic> _sendCommand( Future<dynamic> callGomuksMethod(
String command, [ Map<String, dynamic> data,
Map<String, dynamic> data = const {}, FutureOr<GomuksResponse> Function(int handle, GomuksBorrowedBuffer data)
]) async { callback,
) async {
final bufferPointer = data.toGomuksBufferPtr(); final bufferPointer = data.toGomuksBufferPtr();
try {
final handle = await future; final handle = await future;
final response = await Isolate.run( final response = await Isolate.run(
() => GomuksSubmitCommand( () => callback(handle, bufferPointer.ref),
handle,
command.toNativeUtf8().cast<Char>(),
bufferPointer.ref,
),
); );
calloc.free(bufferPointer);
final json = response.buf.toJson(); final json = response.buf.toJson();
if (response.command.cast<Utf8>().toDartString() == "error") { if (response.command.cast<Utf8>().toDartString() == "error") {
throw json; throw json;
} }
return json; return json;
} finally {
calloc
..free(bufferPointer.ref.base)
..free(bufferPointer);
} }
}
Future<(Event, RoomMetadata)> handlePush(Map<String, dynamic> data) async {
final response = await callGomuksMethod(
data,
(handle, data) async => GomuksHandlePush(handle, data),
);
return (
Event.fromJson(response["event"]),
RoomMetadata.fromJson(response["room"]),
);
}
dynamic _sendCommand(
String command, [
Map<String, dynamic> data = const {},
]) => callGomuksMethod(data, (handle, data) {
final commandPointer = command.toNativeUtf8().cast<Char>();
try {
return GomuksSubmitCommand(handle, commandPointer, data);
} finally {
calloc.free(commandPointer);
}
});
Future<void> redactEvent(RedactEventRequest report) => Future<void> redactEvent(RedactEventRequest report) =>
_sendCommand("redact_event", report.toJson()); _sendCommand("redact_event", report.toJson());
@ -213,21 +183,17 @@ class ClientController extends AsyncNotifier<int> {
} }
} }
Future<String> joinRoom(JoinRoomRequest request) async { Future<String> joinRoom(JoinRoomRequest request) async =>
final response = await _sendCommand("join_room", request.toJson()); (await _sendCommand("join_room", request.toJson()))["room_id"];
return response["room_id"];
} Future<RoomSummary> getRoomSummary(JoinRoomRequest request) async =>
.fromJson(await _sendCommand("get_room_summary", request.toJson()));
Future<void> leaveRoom(Room room) async { Future<void> leaveRoom(Room room) async {
if (room.metadata == null) return; if (room.metadata == null) return;
await _sendCommand("leave_room", {"room_id": room.metadata!.id}); await _sendCommand("leave_room", {"room_id": room.metadata!.id});
} }
// (await _sendCommand("get_event_context", {
// "room_id": request.roomId,
// "event_id": r"$OqZT4NuTj0J1-771IOEEWRI4XdumRNu6ighlvO3K3gc",
// }));
Future<IList<Event>> getRoomState(GetRoomStateRequest request) async { Future<IList<Event>> getRoomState(GetRoomStateRequest request) async {
Future<List?> getState(GetRoomStateRequest request) async => Future<List?> getState(GetRoomStateRequest request) async =>
(await _sendCommand("get_room_state", request.toJson())) as List?; (await _sendCommand("get_room_state", request.toJson())) as List?;
@ -248,6 +214,12 @@ class ClientController extends AsyncNotifier<int> {
return .new(response?.map((event) => .fromJson(event))); return .new(response?.map((event) => .fromJson(event)));
} }
Future<IList<Event>> getMentions(GetMentionsRequest request) async => .new(
// TODO: Handle `related_events`
((await _sendCommand("get_mentions", request.toJson()))["events"] as List)
.map((event) => .fromJson(event)),
);
Future<Event?> getEvent(GetEventRequest request) async { Future<Event?> getEvent(GetEventRequest request) async {
final json = await _sendCommand("get_event", request.toJson()); final json = await _sendCommand("get_event", request.toJson());
return json == null ? null : .fromJson(json); return json == null ? null : .fromJson(json);
@ -259,8 +231,19 @@ class ClientController extends AsyncNotifier<int> {
Future<Paginate> paginate(PaginateRequest request) async => Future<Paginate> paginate(PaginateRequest request) async =>
.fromJson(await _sendCommand("paginate", request.toJson())); .fromJson(await _sendCommand("paginate", request.toJson()));
Future<ProfileResponse> getProfile(String userId) async => Future<PaginateManual> paginateManual(PaginateManualRequest request) async =>
.fromJson(await _sendCommand("get_profile", {"user_id": userId})); .fromJson(await _sendCommand("paginate_manual", request.toJson()));
Future<EventContext> getEventContext(GetEventContextRequest request) async =>
.fromJson(await _sendCommand("get_event_context", request.toJson()));
Future<ProfileResponse> getProfile(String userId) async {
try {
return .fromJson(await _sendCommand("get_profile", {"user_id": userId}));
} catch (_) {
return ProfileResponse(profile: .new(id: userId));
}
}
Future<void> reportEvent(ReportRequest request) => Future<void> reportEvent(ReportRequest request) =>
_sendCommand("report_event", request.toJson()); _sendCommand("report_event", request.toJson());
@ -271,6 +254,15 @@ class ClientController extends AsyncNotifier<int> {
Future<void> setAccountData(SetAccountDataRequest request) => Future<void> setAccountData(SetAccountDataRequest request) =>
_sendCommand("set_account_data", request.toJson()); _sendCommand("set_account_data", request.toJson());
Future<void> registerPusher(RegisterPusherRequest request) =>
_sendCommand("register_homeserver_push", request.toJson());
Future<void> deregisterPusher(DeregisterPusherRequest request) =>
_sendCommand("register_homeserver_push", {
...request.toJson(),
"kind": null,
});
Future<MessageContent> uploadMedia(UploadMediaRequest request) async => Future<MessageContent> uploadMedia(UploadMediaRequest request) async =>
.fromJson(await _sendCommand("upload_media", request.toJson())); .fromJson(await _sendCommand("upload_media", request.toJson()));
@ -280,9 +272,11 @@ class ClientController extends AsyncNotifier<int> {
Future<void> logout() => _sendCommand("logout"); Future<void> logout() => _sendCommand("logout");
Future<void> markRead(Room room) async { Future<void> markRead(Room room) async {
if (room.timeline.isEmpty || room.metadata == null) return;
final eventRowId = room.timeline[room.timeline.keys.reduce(max)]; final eventRowId = room.timeline[room.timeline.keys.reduce(max)];
final event = eventRowId == null ? null : room.events[eventRowId]; final event = eventRowId == null ? null : room.events[eventRowId];
if (event == null || room.metadata == null) return;
if (event == null) return;
await _sendCommand("mark_read", { await _sendCommand("mark_read", {
"room_id": room.metadata!.id, "room_id": room.metadata!.id,
@ -308,10 +302,14 @@ class ClientController extends AsyncNotifier<int> {
Future<SpecVersionsResponse> getSpecVersions() async => Future<SpecVersionsResponse> getSpecVersions() async =>
.fromJson(await _sendCommand("get_versions")); .fromJson(await _sendCommand("get_versions"));
Future<Capabilities> getCapabilities() async => Capabilities.fromJson(
(await _sendCommand("get_capabilities"))["capabilities"],
);
Future<Uri?> discoverHomeserver(Uri homeserver) async { Future<Uri?> discoverHomeserver(Uri homeserver) async {
try { try {
final response = await _sendCommand("discover_homeserver", { final response = await _sendCommand("discover_homeserver", {
"user_id": "@fake-user:${homeserver.host}", "user_id": "@fake-user:${homeserver.authority}",
}); });
return Uri.parse(response["m.homeserver"]?["base_url"]); return Uri.parse(response["m.homeserver"]?["base_url"]);
} catch (error) { } catch (error) {

View file

@ -1,10 +1,7 @@
import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/client.dart"; import "package:nexus/controllers/client.dart";
class ClientIdController extends AsyncNotifier<String> { class ClientIdController(final Uri homeserver) extends AsyncNotifier<String> {
final Uri homeserver;
ClientIdController(this.homeserver);
@override @override
Future<String> build() => ref Future<String> build() => ref
.watch(ClientController.provider.notifier) .watch(ClientController.provider.notifier)

View file

@ -1,84 +0,0 @@
import "dart:convert";
import "package:emoji_text_field/models/emoji_category.dart";
import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:flutter/material.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:http/http.dart";
import "package:nexus/models/emoji.dart";
typedef EmojiTuple = (IMap<String, EmojiCategory>, IMap<String, List<String>>);
class EmojiController extends AsyncNotifier<EmojiTuple> {
@override
Future<EmojiTuple> build() async {
final response = await get(
.https("github.com", "github/gemoji/raw/refs/heads/master/db/emoji.json"),
);
if (response.statusCode != 200) {
throw Exception("Failed to load emoji data");
}
final data = json.decode(response.body);
final entries = (data as List)
.cast<Map<String, dynamic>>()
.map(Emoji.fromJson)
.toIList();
final categoryMap = entries.fold<IMap<String, IList<String>>>(
.new(),
(acc, entry) => acc.update(
entry.category,
(list) => list.add(entry.emoji),
ifAbsent: () => .new([entry.emoji]),
),
);
final keywordMap = entries.fold<IMap<String, IList<String>>>(
.new(),
(acc, entry) => acc.add(
entry.emoji,
.new([...entry.tags, ...entry.aliases, entry.description]),
),
);
final customCategories = IMap.fromEntries(
categoryMap.entries.map(
(entry) => MapEntry(
entry.key,
EmojiCategory(
name: entry.key,
icon: switch (entry.key) {
"Smileys & Emotion" => Icons.emoji_emotions,
"People & Body" => Icons.emoji_people,
"Animals & Nature" => Icons.emoji_nature,
"Food & Drink" => Icons.emoji_food_beverage,
"Travel & Places" => Icons.travel_explore,
"Activities" => Icons.sports_soccer,
"Objects" => Icons.emoji_objects,
"Symbols" => Icons.emoji_symbols,
"Flags" => Icons.emoji_flags,
_ => Icons.category,
},
emojis: entry.value.toList(growable: false),
),
),
),
);
final customKeywords = IMap(
.fromEntries(
keywordMap.entries.map(
(e) => .new(e.key, e.value.toList(growable: false)),
),
),
);
return (customCategories, customKeywords);
}
static final provider = AsyncNotifierProvider<EmojiController, EmojiTuple>(
EmojiController.new,
);
}

View file

@ -5,10 +5,8 @@ import "package:nexus/controllers/rooms.dart";
import "package:nexus/models/event.dart"; import "package:nexus/models/event.dart";
import "package:nexus/models/requests/get_event.dart"; import "package:nexus/models/requests/get_event.dart";
class EventController extends AsyncNotifier<Event?> { class EventController(final GetEventRequest request)
final GetEventRequest request; extends AsyncNotifier<Event?> {
EventController(this.request);
@override @override
Future<Event?> build() async { Future<Event?> build() async {
final room = ref.watch( final room = ref.watch(

View file

@ -0,0 +1,124 @@
import "dart:async";
import "dart:ffi";
import "package:flutter/foundation.dart";
import "package:nexus/controllers/account_data.dart";
import "package:nexus/controllers/client.dart";
import "package:nexus/controllers/client_state.dart";
import "package:nexus/controllers/init_complete.dart";
import "package:nexus/controllers/rooms.dart";
import "package:nexus/controllers/space_edges.dart";
import "package:nexus/controllers/sync_status.dart";
import "package:nexus/controllers/top_level_spaces.dart";
import "package:nexus/helpers/extensions/gomuks_buffer.dart";
import "package:nexus/main.dart";
import "package:ffi/ffi.dart";
import "package:nexus/models/event.dart";
import "package:nexus/models/sync_data.dart";
import "package:nexus/src/third_party/gomuks.g.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
class GomuksListenerController extends AsyncNotifier<void> {
@override
Future<void> build() async {
debugPrint("Starting gomuks...");
final handle = await ref.watch(ClientController.provider.future);
final callable =
NativeCallable<
Void Function(Pointer<Char>, Int64, GomuksOwnedBuffer)
>.listener((
Pointer<Char> command,
int requestId,
GomuksOwnedBuffer data,
) {
try {
final muksEventType = command.cast<Utf8>().toDartString();
debugPrint("Handling $muksEventType...");
final decodedMuksEvent = data.toJson();
switch (muksEventType) {
case "client_state":
ref
.watch(ClientStateController.provider.notifier)
.set(.fromJson(decodedMuksEvent));
break;
case "sync_status":
ref
.watch(SyncStatusController.provider.notifier)
.set(.fromJson(decodedMuksEvent));
break;
case "init_complete":
ref.watch(InitCompleteController.provider.notifier).complete();
break;
case "send_complete":
final event = Event.fromJson(decodedMuksEvent["event"]);
ref
.watch(RoomsController.provider.notifier)
.update(
.new({
event.roomId: .new(events: .new({event.rowId: event})),
}),
.new(),
);
break;
case "sync_complete":
final syncData = SyncData.fromJson(decodedMuksEvent);
final roomProvider = RoomsController.provider;
final accountDataProvider = AccountDataController.provider;
if (syncData.clearState) {
ref.invalidate(roomProvider);
ref.invalidate(accountDataProvider);
}
ref
.watch(roomProvider.notifier)
.update(syncData.rooms, syncData.leftRooms);
ref
.watch(accountDataProvider.notifier)
.update(syncData.accountData);
if (syncData.topLevelSpaces != null) {
ref
.watch(TopLevelSpacesController.provider.notifier)
.set(syncData.topLevelSpaces!);
}
if (syncData.spaceEdges != null) {
ref
.watch(SpaceEdgesController.provider.notifier)
.set(syncData.spaceEdges!);
}
// ref
// .watch(SyncStatusController.provider.notifier)
// .set(SyncStatus.fromJson(decodedMuksEvent));
break;
default:
debugPrint("Unhandled event: $muksEventType");
}
debugPrint("Finished handling $muksEventType...");
} catch (error, stackTrace) {
if (kDebugMode) {
debugPrintStack(stackTrace: stackTrace, label: error.toString());
rethrow;
} else {
showError(error, stackTrace);
}
}
});
ref.onDispose(callable.close);
final errorCode = GomuksStart(handle, callable.nativeFunction);
if (errorCode != 0) {
throw Exception("GomuksStart returned error code $errorCode");
}
}
static final provider = AsyncNotifierProvider<GomuksListenerController, void>(
GomuksListenerController.new,
);
}

View file

@ -0,0 +1,16 @@
import "package:flutter_riverpod/flutter_riverpod.dart";
class JumpToEventController(String? _) extends Notifier<String?> {
@override
String? build() => null;
void set(String? eventId) => state = eventId;
@override
bool updateShouldNotify(_, _) => true;
static final provider = NotifierProvider.family
.autoDispose<JumpToEventController, String?, String?>(
JumpToEventController.new,
);
}

View file

@ -1,30 +1,28 @@
import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/shared_prefs.dart"; import "package:nexus/controllers/shared_prefs.dart";
class KeyController extends Notifier<String?> { class KeyController(final String key) extends AsyncNotifier<String?> {
final String key;
KeyController(this.key);
static const String spaceKey = "space"; static const String spaceKey = "space";
static const String roomKey = "room"; static const String roomKey = "room";
static const String pushKeyKey = "pushKey";
@override @override
String? build() => Future<String?> build() =>
ref.watch(SharedPrefsController.provider).requireValue.getString(key); ref.watch(SharedPrefsController.provider).getString(key);
Future<void> set(String? value) async { Future<void> set(String? value) async {
final prefs = ref.watch(SharedPrefsController.provider).requireValue; final prefs = ref.watch(SharedPrefsController.provider);
state = value; state = .data(value);
if (value == null) { if (value == null) {
prefs.remove(key); await prefs.remove(key);
} else { } else {
prefs.setString(key, value); await prefs.setString(key, value);
} }
} }
static final provider = static final provider =
NotifierProvider.family<KeyController, String?, String>( AsyncNotifierProvider.family<KeyController, String?, String>(
KeyController.new, KeyController.new,
); );
} }

View file

@ -1,22 +1,20 @@
import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/shared_prefs.dart"; import "package:nexus/controllers/shared_prefs.dart";
class MemberListOpenedController extends Notifier<bool> { class MemberListOpenedController extends AsyncNotifier<bool> {
static const String key = "memberListOpened"; static const String key = "memberListOpened";
@override @override
bool build() => Future<bool> build() async =>
ref.watch(SharedPrefsController.provider).requireValue.getBool(key) ?? await ref.watch(SharedPrefsController.provider).getBool(key) ?? true;
true;
Future<void> set(bool value) async { Future<void> set(bool value) async {
final prefs = ref.watch(SharedPrefsController.provider).requireValue; state = .data(value);
state = value; await ref.watch(SharedPrefsController.provider).setBool(key, value);
prefs.setBool(key, value);
} }
static final provider = NotifierProvider<MemberListOpenedController, bool>( static final provider =
AsyncNotifierProvider<MemberListOpenedController, bool>(
MemberListOpenedController.new, MemberListOpenedController.new,
); );
} }

View file

@ -6,10 +6,8 @@ import "package:nexus/models/content/content.dart";
import "package:nexus/models/event.dart"; import "package:nexus/models/event.dart";
import "package:nexus/models/requests/get_room_state.dart"; import "package:nexus/models/requests/get_room_state.dart";
class MembersController extends AsyncNotifier<ISet<Event>> { class MembersController(final String roomId)
final String roomId; extends AsyncNotifier<ISet<Event>> {
MembersController(this.roomId);
@override @override
Future<ISet<Event>> build() async { Future<ISet<Event>> build() async {
final room = ref.watch( final room = ref.watch(

View file

@ -5,10 +5,8 @@ import "package:nexus/models/configs/members_by_status.dart";
import "package:nexus/models/content/membership.dart"; import "package:nexus/models/content/membership.dart";
import "package:nexus/models/event.dart"; import "package:nexus/models/event.dart";
class MembersByStatusController extends AsyncNotifier<ISet<Event>> { class MembersByStatusController(final MembersByStatusConfig config)
final MembersByStatusConfig config; extends AsyncNotifier<ISet<Event>> {
MembersByStatusController(this.config);
@override @override
Future<ISet<Event>> build() => ref.watch( Future<ISet<Event>> build() => ref.watch(
MembersController.provider(config.roomId).selectAsync( MembersController.provider(config.roomId).selectAsync(

View file

@ -8,11 +8,8 @@ import "package:nexus/models/content/content.dart";
import "package:nexus/models/content/power_levels.dart"; import "package:nexus/models/content/power_levels.dart";
import "package:nexus/models/event.dart"; import "package:nexus/models/event.dart";
class MembersGroupedController class MembersGroupedController(final MembersByStatusConfig config)
extends AsyncNotifier<IList<MapEntry<int?, ISet<Event>>>> { extends AsyncNotifier<IList<MapEntry<int?, ISet<Event>>>> {
final MembersByStatusConfig config;
MembersGroupedController(this.config);
@override @override
Future<IList<MapEntry<int?, ISet<Event>>>> build() async { Future<IList<MapEntry<int?, ISet<Event>>>> build() async {
final room = ref.watch( final room = ref.watch(

View file

@ -1,14 +1,15 @@
import "dart:async"; import "dart:async";
import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_riverpod/flutter_riverpod.dart";
class MultiProviderController extends AsyncNotifier<void> { class MultiProviderController(final IList<AsyncNotifierProvider> providers)
MultiProviderController(this.providers); extends AsyncNotifier<void> {
final IList<AsyncNotifierProvider> providers;
@override @override
Future<void> build() => Future<void> build() => .wait(
.wait(providers.map((provider) => ref.watch(provider.future))); providers.map((provider) => ref.watch(provider.future)),
eagerError: true,
);
static final provider = static final provider =
AsyncNotifierProvider.family< AsyncNotifierProvider.family<

View file

@ -0,0 +1,162 @@
import "dart:async";
import "dart:io";
import "package:flutter/services.dart";
import "package:material_ui/material_ui.dart";
import "package:nexus/controllers/portal.dart";
import "package:nexus/main.dart";
import "package:flutter_local_notifications/flutter_local_notifications.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/widgets/pages/notifications.dart";
import "package:xdg_desktop_portal/xdg_desktop_portal.dart";
class NotificationController
extends AsyncNotifier<FlutterLocalNotificationsPlugin> {
@override
Future<FlutterLocalNotificationsPlugin> build() async {
final notifications = FlutterLocalNotificationsPlugin();
if (!Platform.isLinux) {
final darwin = DarwinInitializationSettings();
await notifications.initialize(
settings: .new(
windows: .new(
appName: "Nexus",
appUserModelId: "nexus.federated.nexus",
guid: "dde78daf-130f-4e46-a80a-e31deeab45d7",
),
android: .new("ic_launcher_foreground"),
iOS: darwin,
macOS: darwin,
),
onDidReceiveNotificationResponse: (details) {
if (details.payload case final eventId?) {
if (navigatorKey.currentContext case final context?) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => NotificationsPage(
highlightedEventId: eventId,
defaultToAllNotifications: true,
),
),
);
}
}
},
);
}
if (Platform.isLinux) {
const notificationChannel = MethodChannel("nexus/notifications");
notificationChannel.setMethodCallHandler((call) async {
if (call.method != "notificationClicked") {
return;
}
final eventId = call.arguments as String;
if (navigatorKey.currentContext case final context?) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => NotificationsPage(
highlightedEventId: eventId,
defaultToAllNotifications: true,
),
),
);
}
});
ref.onDispose(() => notificationChannel.setMethodCallHandler(null));
}
return notifications;
}
Future<bool> requestPermissions() async {
if (Platform.isLinux) {
return true;
}
final controller = await future;
if (Platform.isIOS) {
return await controller
.resolvePlatformSpecificImplementation<
IOSFlutterLocalNotificationsPlugin
>()
?.requestPermissions(alert: true, badge: true, sound: true) ??
true;
} else if (Platform.isMacOS) {
return await controller
.resolvePlatformSpecificImplementation<
MacOSFlutterLocalNotificationsPlugin
>()
?.requestPermissions(alert: true, badge: true, sound: true) ??
true;
} else if (Platform.isAndroid) {
return await controller
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin
>()
?.requestNotificationsPermission() ??
true;
}
return true;
}
Future<void> send({
required int id,
required String title,
String? body,
File? icon,
String? payload,
}) async {
debugPrint("Sending notification for $id");
if (Platform.isLinux) {
final portal = await ref.watch(PortalController.provider.future);
await portal.notification.addNotification(
id.toString(),
title: title,
body: body,
icon: icon == null ? null : XdgNotificationIconFile(icon.path),
defaultAction: "app.event",
defaultActionTarget: payload,
);
} else {
final notificationDetails = NotificationDetails(
android: .new(
"messages",
"Messages",
largeIcon: icon == null ? null : FilePathAndroidBitmap(icon.path),
),
// TODO: See if icons can be added to iOS, macOS, and Windows
// notifications (#68)
iOS: .new(),
macOS: .new(),
windows: .new(),
);
final controller = await future;
await controller.show(
id: id,
title: title,
body: body,
notificationDetails: notificationDetails,
payload: payload,
);
}
}
static final provider =
AsyncNotifierProvider<
NotificationController,
FlutterLocalNotificationsPlugin
>(NotificationController.new);
}

View file

@ -0,0 +1,53 @@
import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/client.dart";
import "package:nexus/models/event.dart";
typedef NotificationsRequest = (UnreadType? unreadType, String? roomId);
class NotificationsController([final NotificationsRequest? request])
extends AsyncNotifier<IList<Event>> {
@override
Future<IList<Event>> build() async {
final client = ref.read(ClientController.provider.notifier);
final (unreadType, roomId) = request ?? (null, null);
return await client.getMentions(
.new(
maxTimestamp: .now(),
unreadType: unreadType ?? .highlight,
roomId: roomId,
),
);
}
Future<void> loadOlder() async {
final currentNotifications = await future;
state = .loading();
state = await .guard(() async {
final lastTs = currentNotifications.lastOrNull?.timestamp;
if (lastTs == null) return const .empty();
final client = ref.read(ClientController.provider.notifier);
final (unreadType, roomId) = request ?? (null, null);
final newNotifications = await client.getMentions(
.new(
maxTimestamp: lastTs,
unreadType: unreadType ?? .highlight,
roomId: roomId,
),
);
return currentNotifications.addAll(newNotifications);
});
}
static final provider = AsyncNotifierProvider.family
.autoDispose<
NotificationsController,
IList<Event>,
NotificationsRequest?
>(NotificationsController.new);
}

View file

@ -4,10 +4,8 @@ import "package:nexus/controllers/event.dart";
import "package:nexus/controllers/pinned_ids.dart"; import "package:nexus/controllers/pinned_ids.dart";
import "package:nexus/models/event.dart"; import "package:nexus/models/event.dart";
class PinnedEventsController extends AsyncNotifier<IList<Event>> { class PinnedEventsController(final String roomId)
final String roomId; extends AsyncNotifier<IList<Event>> {
PinnedEventsController(this.roomId);
@override @override
Future<IList<Event>> build() async { Future<IList<Event>> build() async {
final pinIds = ref.watch(PinnedIdsController.provider(roomId)); final pinIds = ref.watch(PinnedIdsController.provider(roomId));
@ -15,9 +13,8 @@ class PinnedEventsController extends AsyncNotifier<IList<Event>> {
return (await Future.wait( return (await Future.wait(
pinIds.map( pinIds.map(
(eventId) => ref.watch( (eventId) => ref.watch(
EventController.provider( EventController.provider(.new(eventId: eventId, roomId: roomId))
.new(eventId: eventId, roomId: roomId), .future,
).future,
), ),
), ),
)).nonNulls.toIList(); )).nonNulls.toIList();

View file

@ -5,10 +5,7 @@ import "package:nexus/controllers/rooms.dart";
import "package:nexus/models/content/content.dart"; import "package:nexus/models/content/content.dart";
import "package:nexus/models/content/pinned_events.dart"; import "package:nexus/models/content/pinned_events.dart";
class PinnedIdsController extends Notifier<IList<String>> { class PinnedIdsController(final String roomId) extends Notifier<IList<String>> {
final String roomId;
PinnedIdsController(this.roomId);
@override @override
IList<String> build() { IList<String> build() {
final room = ref.watch( final room = ref.watch(

View file

@ -0,0 +1,22 @@
import "dart:io";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:xdg_desktop_portal/xdg_desktop_portal.dart";
class PortalController extends AsyncNotifier<XdgDesktopPortalClient> {
@override
Future<XdgDesktopPortalClient> build() async {
final portal = XdgDesktopPortalClient();
if (!await File("/.flatpak-info").exists()) {
await portal.registerApplication("nexus.federated.nexus");
}
ref.onDispose(portal.close);
return portal;
}
static final provider =
AsyncNotifierProvider<PortalController, XdgDesktopPortalClient>(
PortalController.new,
);
}

View file

@ -6,10 +6,8 @@ import "package:nexus/models/configs/power_level.dart";
import "package:nexus/models/content/content.dart"; import "package:nexus/models/content/content.dart";
import "package:nexus/models/content/power_levels.dart"; import "package:nexus/models/content/power_levels.dart";
class PowerLevelController extends Notifier<bool> { class PowerLevelController(final PowerLevelConfig config)
final PowerLevelConfig config; extends Notifier<bool> {
PowerLevelController(this.config);
@override @override
bool build() { bool build() {
if (config case EventPowerLevelConfig(:final eventType)) { if (config case EventPowerLevelConfig(:final eventType)) {

View file

@ -2,13 +2,11 @@ import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/client.dart"; import "package:nexus/controllers/client.dart";
import "package:nexus/models/profile_response.dart"; import "package:nexus/models/profile_response.dart";
class ProfileController extends AsyncNotifier<ProfileResponse> { class ProfileController(final String userId)
final String userId; extends AsyncNotifier<ProfileResponse> {
ProfileController(this.userId);
@override @override
Future<ProfileResponse> build() { Future<ProfileResponse> build() {
final client = ref.watch(ClientController.provider.notifier); final client = ref.read(ClientController.provider.notifier);
return client.getProfile(userId); return client.getProfile(userId);
} }

View file

@ -0,0 +1,31 @@
import "dart:convert";
import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/key.dart";
class PushKeyController(final String instance) extends AsyncNotifier<String?> {
@override
Future<String?> build() async => json.decode(
await ref.watch(KeyController.provider(KeyController.pushKeyKey).future) ??
"{}",
)[instance];
Future<void> set(String? value) async {
final provider = KeyController.provider(KeyController.pushKeyKey);
final notifier = ref.watch(provider.notifier);
final prefs = IMap(json.decode(await ref.watch(provider.future) ?? "{}"));
state = .data(value);
notifier.set(
json.encode(
prefs.add(instance, value).where((_, value) => value != null).unlock,
),
);
}
static final provider =
AsyncNotifierProvider.family<PushKeyController, String?, String>(
PushKeyController.new,
);
}

View file

@ -5,10 +5,8 @@ import "package:nexus/controllers/rooms.dart";
import "package:nexus/models/configs/reactions.dart"; import "package:nexus/models/configs/reactions.dart";
import "package:nexus/models/content/reaction.dart"; import "package:nexus/models/content/reaction.dart";
class ReactionsController extends AsyncNotifier<IMap<String, IList<String>>> { class ReactionsController(final ReactionsConfig config)
final ReactionsConfig config; extends AsyncNotifier<IMap<String, IList<String>>> {
ReactionsController(this.config);
@override @override
Future<IMap<String, IList<String>>> build() async { Future<IMap<String, IList<String>>> build() async {
final eventInfo = ref.watch( final eventInfo = ref.watch(

View file

@ -0,0 +1,44 @@
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/account_data.dart";
import "package:nexus/controllers/client.dart";
import "package:nexus/models/account_data.dart";
class RecentEmojiController extends Notifier<IList<RecentEmoji>> {
@override
IList<RecentEmoji> build() => ref.watch(
AccountDataController.provider.select(
(value) => value.recentEmoji.recentEmoji,
),
);
Future<void> add(String emoji) => ref
.watch(ClientController.provider.notifier)
.setAccountData(
.new(
type: AccountData.recentEmojiKey,
content: RecentEmojiData(
recentEmoji: .new([
.new(
emoji: emoji,
total:
(state
.firstWhereOrNull(
(element) => element.emoji == emoji,
)
?.total ??
0) +
1,
),
...state.whereNot((element) => element.emoji == emoji),
]),
),
),
);
static final provider =
NotifierProvider.autoDispose<RecentEmojiController, IList<RecentEmoji>>(
RecentEmojiController.new,
);
}

Some files were not shown because too many files have changed in this diff Show more