Watch
1
0
Fork
You've already forked nexus
0
forked from Nexus/nexus

Compare commits

...
Sign in to create a new pull request.
Author SHA1 Message Date
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
83e8084213
catch parsing errors for invalid replies 2026-08-08 11:31:07 -04:00
ae5b6e2b40
bump gomuks, fix profile issues caused by this 2026-08-05 16:28:47 -04:00
1621e917c0
slight formatting change for video player 2026-08-05 16:03:49 -04:00
d1c2b2dd0a
show reactions inline in non-message events that are in bubbles, e.g. stickers
Fixes #59.
2026-08-05 16:03:41 -04:00
82a0dd90b0
update gomuks hash 2026-08-04 21:08:51 -04:00
f97c5548a3
add invite blocking support plus msc4494 support
To accomplish this I also reworked how account data is stored and added a method to fetch capabilities, and another method to set account data
2026-08-04 21:04:55 -04:00
115bec5f05
slightly tweak readme
[skip ci]
2026-07-31 11:55:48 -04:00
fc4aa5a10d
persist member list opened status 2026-07-31 10:20:06 -04:00
f3c31db244
update build requirements 2026-07-29 21:57:03 -04:00
419d75ca83
wrap messages with unknown message types 2026-07-29 19:46:39 -04:00
9831446c66
add a little padding to send errors 2026-07-29 16:18:07 -04:00
d7f49568a7
move attachment sending check to be earlier
fixes sending with keyboard before attachment uploaded
2026-07-29 12:11:57 -04:00
f0f426a647
improve matcher for encrypted media 2026-07-29 12:06:19 -04:00
80fa9673bb
fix pending attachments disappearing when new events are sent 2026-07-29 11:58:12 -04:00
8c25228bd9
comment out macos CI auto running [skip ci] 2026-07-28 20:39:03 -04:00
9bf664624a
clone submodules on macos 2026-07-28 17:43:51 -04:00
e8b90591d5
only include wayland in devshell on macos 2026-07-28 17:16:21 -04:00
b2813a0eb5
use devshell for macos 2026-07-28 17:04:06 -04:00
9298eb815d
use libclang.dylib on macos 2026-07-28 15:57:39 -04:00
06075f75c5
make gomuks nix package build to a .dylib on macos 2026-07-28 15:49:27 -04:00
60968f5f3e
make macos builder build app from nix 2026-07-28 15:29:41 -04:00
afd6168535
fix macos CI name
[skip ci]
2026-07-28 15:15:20 -04:00
2a1cc33ee3
add macos ci
hopefully this works first try :3
2026-07-28 15:13:14 -04:00
5f13d3fbf5
don't show errors when url preview fails 2026-07-27 22:02:20 -04:00
eca9d3b411
use gomuks for URL previews 2026-07-27 16:01:40 -04:00
16fdf0c420
download audio and video using gomuks 2026-07-27 15:46:08 -04:00
3536f74851
mark encrypted media as done 2026-07-26 23:11:01 -04:00
fa451621de
use gomuks for downloading media 2026-07-26 23:01:36 -04:00
90230651e6
default auth method to none
Fixes auth on MAS
2026-07-26 18:26:20 -04:00
92b88e46ac
fix attachments disappearing when editing messages 2026-07-26 17:21:01 -04:00
1799e02844
update gomuks vendorHash 2026-07-26 15:13:39 -04:00
cf26ec7130
bump gomuks 2026-07-26 14:40:20 -04:00
716541b457
fixup oauth animation 2026-07-24 10:13:20 -04:00
965c7ea2dc
add media for twim 2026-07-24 09:59:50 -04:00
85d02f2ac9
bump gomuks 2026-07-23 15:29:23 -04:00
1c446c4cdf
bump gomuks
fixes no loading issue when restarting verification after closing app
2026-07-23 13:58:33 -04:00
51430d2830
Verify state on login
This fixes "already logged in errors" if you started logging in before cancelling, and then trying again.
2026-07-22 21:53:42 -04:00
4e208764f5
remove outdated comment 2026-07-22 16:45:47 -04:00
eecc307f04
disable provider auto retry 2026-07-22 16:35:18 -04:00
6221eb4a7a
bump gomuks
Fixes overly aggressive caching on login, causing issues when changing login server
2026-07-22 16:19:06 -04:00
370549072e
make logout properly pop all settings on mobile 2026-07-22 15:57:21 -04:00
80b938c0be
fix wrong settings categories opening when multiple sections exist 2026-07-22 15:37:50 -04:00
87e1ef0d9b
clean up if statements into one 2026-07-22 12:00:35 -04:00
f8c705531c
ignore setState during build
It seems to happen randomly right now.
2026-07-22 11:59:10 -04:00
2a91959333
fix encrypted uploads 2026-07-21 21:34:49 -04:00
34ddd6d32b
change font weight for tooltips 2026-07-21 20:57:58 -04:00
a8726feef6
allow sending messages without a body if they have an attachment 2026-07-21 20:27:21 -04:00
bd1bad76d7
add camera picking support
Not tested.
2026-07-21 20:21:16 -04:00
c4f63d18b5
add support for sending media
Still needs camera support.
2026-07-21 20:19:01 -04:00
a8285548e8
custom style for tooltips 2026-07-21 18:25:38 -04:00
cdad9b760e
add settings to login page, but gate login settings 2026-07-21 18:00:52 -04:00
06c5411dfc
fix bug flutter bump introduced in mention overlay 2026-07-21 16:28:05 -04:00
f6b5bd4057
bump gomuks 2026-07-21 16:27:53 -04:00
0da3b4da22
don't require non-null metadata 2026-07-21 16:25:15 -04:00
a557cffdbf
remove old comment in gradle 2026-07-20 20:41:09 -04:00
9d7156d176
remove un-needed context 2026-07-20 20:40:24 -04:00
b792247575
slight wording tweak 2026-07-20 20:39:12 -04:00
35fbe1094f
update login status in readme 2026-07-20 20:38:26 -04:00
05d1c3b04a
link to macos issue 2026-07-20 20:38:11 -04:00
22e58e3d05
fix search logic when multiple categories exist 2026-07-20 20:00:44 -04:00
09bc9bdcbd
fix highlightwrapper on new flutter version 2026-07-20 19:55:19 -04:00
7d106ad3a2 OAuth Support (#53)
Rips out the old auth method, adds OAuth support. Fixes #51.

Tested Platforms:
- [x] Linux
    - [x] Flatpak
    - [x] Native (Nix)
- [x] Android
- [ ] Windows
- [ ] MacOS

Reviewed-on: Nexus/nexus#53
2026-07-20 19:46:18 -04:00
5df975f607
wrap settings category in safearea 2026-07-19 00:30:41 -04:00
8f3f9fbef1
wrap select server page in safe area 2026-07-19 00:20:04 -04:00
a9ac891ec4
wrap chat page in a safearea 2026-07-19 00:12:15 -04:00
7992f0e815
treewide(refactor): rename files to follow the same style 2026-07-18 12:12:54 -04:00
15d441e4c4
mark search as fullscreen when categories are pages 2026-07-17 22:20:54 -04:00
7492d3a3d4
add padding to select server page 2026-07-17 22:18:42 -04:00
518b0dd11e
mark settings as done in readme 2026-07-17 20:37:59 -04:00
0ef9b25320 Add settings page (#46)
Fixes #37, fixes #47.

Reviewed-on: Nexus/nexus#46
2026-07-17 20:36:41 -04:00
06824784cb
change event padding 2026-07-14 20:22:39 -04:00
08d67553be
fix message bubble alignment 2026-07-14 20:17:44 -04:00
8b2d354a60
use .of(context) instead of .pop(context) 2026-07-14 20:10:40 -04:00
8773faf7c4
reverse pins list 2026-07-14 19:58:39 -04:00
818e582ddd Support for pinned messages (#49)
The first draft to support pinned messages. Fixes #39.

Co-authored-by: Henry-Hiles <henry@henryhiles.com>
Reviewed-on: Nexus/nexus#49
2026-07-14 19:29:47 -04:00
71a4274ff2
refactor build.dart 2026-07-13 18:38:34 -04:00
7b734a89ac
add client perm for MacOS 2026-07-07 20:05:18 -04:00
ceb749d272
trim SDK path 2026-07-07 19:58:31 -04:00
45240f6cb0
fix generate.dart on macos 2026-07-07 19:26:41 -04:00
4c90fbd7d1
get xcode sdk 2026-07-07 18:51:27 -04:00
8e76da4ac2
remove macos titlebar
Credit to @mirameow:matrix.org for this!
2026-07-07 18:26:37 -04:00
195ebc776a
add macos folder 2026-07-07 15:49:07 -04:00
a1e217ac78
more restrictive ai policy 2026-07-03 14:04:07 -04:00
148bc8f95c
improve creation of editSource 2026-06-23 11:32:37 -04:00
b037dc06c8
add pulseaudio socket to flatpak 2026-06-23 11:11:05 -04:00
3c7c74f745
hide URL previews if generating them throws an error 2026-06-23 10:50:20 -04:00
b727d0342d
bump gomuks 2026-06-21 17:52:05 -04:00
db5735e9e2
Fixup aspect ratio calculation for non-null widths 2026-06-20 23:48:15 -04:00
7c01611859
Default to 1:1 when no width/height are given for an image 2026-06-20 23:39:05 -04:00
632c02a517
update windows tags 2026-06-18 14:52:18 -04:00
355c564123
update gomuks vendorHash 2026-06-18 14:48:37 -04:00
1af957d4a1
bump gomuks 2026-06-18 14:44:56 -04:00
1986e2a627
fixes to creator power level logic 2026-06-17 23:24:25 -04:00
ab40600746 Take into account creators when checking power levels (#44)
Fixes #40.

Reviewed-on: Nexus/nexus#44
Reviewed-by: Henry Hiles <henry@henryhiles.com>
2026-06-17 23:16:51 -04:00
2d0f41000e
improve expandable image viewer 2026-06-13 20:49:45 -04:00
3349ca7253
wrap expanded image stack in a safearea to help with notch issues 2026-06-13 18:44:02 -04:00
18657eb980
improved expandable image widget 2026-06-13 18:31:14 -04:00
b4b157c39f
fix check
Don't check off clickable room mentions (because its not true)
2026-06-12 15:50:16 -04:00
a76b6e6ed3
remove screenshots, now that twim is out 2026-06-12 11:02:00 -04:00
5bbd472999
use gingers profile for an example 2026-06-12 09:56:19 -04:00
6c0e149c25
add profile screenshot 2026-06-12 09:55:28 -04:00
b26c144eea
add new screenshots of sidebars 2026-06-12 09:51:42 -04:00
ddc8db8326
some keyboard navigation fixes
This still isnt perfect, it sometimes skips over nodes, needs investigation.
2026-06-11 13:41:06 -04:00
bc307cbcda
up size of spaces in sidebar 2026-06-11 12:50:47 -04:00
e40de37b9d
move message button above kick/ban 2026-06-10 10:41:25 -04:00
7757825e27
add placeholder while profile loading 2026-06-10 10:40:21 -04:00
795e9b6e9b
add l10n todo comment 2026-06-09 21:15:15 -04:00
abba60c28b
onTapUp -> onTap when possible 2026-06-09 20:40:31 -04:00
8cea4b6cf3
fix user sheet on small screens 2026-06-09 20:37:21 -04:00
d46646d781
redesign user popover 2026-06-09 20:34:56 -04:00
e15d947fac
rename FlashWrapper to HighlightWrapper 2026-06-08 11:03:34 -04:00
562ce5d4ff
Add link to settings issue 2026-06-07 17:12:54 -04:00
f60189bfb8
fix mark all as read behavior with subspaces 2026-06-07 16:57:57 -04:00
309d0df581
fix unread logic to take into account subspaces 2026-06-06 21:19:01 -04:00
3310d9b907
nicer error handling for message parses 2026-06-06 19:13:45 -04:00
8c047827de
Fix text overflows for long subspace names 2026-06-06 19:02:58 -04:00
d513e466fd
Render subspace avatars, if available 2026-06-06 18:40:32 -04:00
84a729aea8
new screenshots, update readme 2026-06-06 17:38:54 -04:00
23e0aa3c4e
change image fit 2026-06-06 17:30:24 -04:00
f0a26b58d1
fix incorrect selected hint 2026-06-06 17:09:54 -04:00
78b5abea7d
fix out of range error 2026-06-06 17:03:06 -04:00
e1d7a30a06
Support one level of subspaces 2026-06-06 15:11:40 -04:00
0c61623a94
fix fallback for null displayName in mention chip 2026-06-06 13:15:41 -04:00
0a9b71230b
set sidebar width to infinity 2026-06-06 13:04:44 -04:00
15c057707f
slightly lower size of sidebar 2026-06-06 13:04:12 -04:00
9cc18e16b8
only show appbar on member list if its a drawer 2026-06-06 13:02:51 -04:00
240984a832
Don't connect M3EToggleButtonGroup
I think it looks nicer this way.
2026-06-06 12:25:09 -04:00
633e83b915
slight cleanups 2026-06-06 12:21:42 -04:00
c5122fc34f
add some more padding to member list 2026-06-06 10:55:12 -04:00
457de3c77c
more performant member list 2026-06-06 10:51:56 -04:00
c9aa0173d8
fix navigation rail hash 2026-06-06 10:18:40 -04:00
22aa26fd05
fix keyboard navigation of forms
Including submit with keyboard, and not defocusing on submit of a form
2026-06-06 10:15:29 -04:00
c10c74a1f3
change max line size for navigation rail 2026-06-05 23:32:12 -04:00
46136c09ad
fix padding color of space sidebar 2026-06-05 21:47:13 -04:00
5f9622e1c9
try to fix android build 2026-06-05 21:39:51 -04:00
6e02cce84f
WIP subspace support 2026-06-05 21:23:06 -04:00
7e7e6877e2
add hash for navigation_rail_m3e to fix nix build 2026-06-05 20:42:52 -04:00
895ab3c96f
redesign sidebar to be m3e 2026-06-05 20:38:03 -04:00
b25840756d
add back shift+enter for newline 2026-06-05 19:40:12 -04:00
735a3357d7
fix logic for deciding when to fetch members 2026-06-05 18:38:27 -04:00
02b7892fb0
better handle when room is null 2026-06-05 18:33:16 -04:00
3afb4befa5
add null check in room chat controller 2026-06-05 18:10:41 -04:00
a11663eece
use M3EToggleButtonGroup 2026-06-05 18:10:41 -04:00
7f12efd338
add some more padding 2026-06-05 18:10:41 -04:00
fcdada6f3e
add support for v12 creators 2026-06-05 18:10:41 -04:00
0c950247b0
improve UI of member list, sort by power level 2026-06-05 18:10:41 -04:00
27dca24889
better login flow
Co-authored-by: Henry-Hiles <henry@henryhiles.com>
2026-06-05 17:28:53 -04:00
621bb74cc9
turn up max zoom of expandable image viewer 2026-06-05 12:52:30 -04:00
33c3a568f9
remove extra indent from nix build 2026-06-04 21:14:53 -04:00
561f6ecc84
use correct fallback for icons 2026-06-04 20:49:14 -04:00
a7ccf0ff00
fix not showing first membership events 2026-06-04 19:26:18 -04:00
0e984fd95b
change some icons for events 2026-06-04 19:25:28 -04:00
c898b2671a
add link to #35 from readme 2026-06-03 10:48:07 -04:00
e63e5a8c08
fix auto scroll down sometimes not triggering 2026-06-03 10:41:34 -04:00
a87c9dc678
warm up emojis controller 2026-06-03 10:24:05 -04:00
5fcc31b427
add a slight offset to the maxScrollExtent jump to not accidentally mark room as read 2026-06-03 10:18:20 -04:00
99270f4bd1
don't reverse CustomScrollView
Reviewed-on: Nexus/nexus#34
2026-06-02 21:09:35 -04:00
d2ec5f035b
treewide: use dot shorthands where possible
Now this feature is stable, we should use it. I might have missed some usecases, but these can be added in future commits.
2026-06-02 12:50:56 -04:00
6281c1d13a
make UrlPreviewController not autoDispose 2026-05-31 22:13:46 -04:00
60315de04a
update screenshots 2026-05-31 22:02:22 -04:00
eb87cbc17b
more elegantly handle empty displaynames 2026-05-31 21:46:29 -04:00
130dbac879
Link to Effective Dart: Style guide in DEVELOPMENT.md
Co-authored-by: istalri <oldangrydanishgirl.unit949@passinbox.com>
2026-05-31 13:46:19 -04:00
2dd3fed62f
add info block for stddef issues 2026-05-31 13:02:41 -04:00
f60a499875
better loading state for message displayname widget 2026-05-30 11:35:12 -04:00
a72ef5ea2d
fix incorrect message grouping heuristics 2026-05-30 11:30:07 -04:00
1b81e31c13
add early return on build hook 2026-05-30 11:19:27 -04:00
db12fd2e9a
remove extra curly bracket in logger 2026-05-30 11:11:19 -04:00
60454ed249
remove deprecated argument to build_runner 2026-05-29 13:14:06 -04:00
0a333e92d1
add an AI policy 2026-05-29 13:10:01 -04:00
31c3173bb5
add member list to readme 2026-05-28 19:16:29 -04:00
c6904e9766
Fix some alignment issues 2026-05-26 21:56:44 -04:00
e5b7512e79
nicer action buttons on user popover 2026-05-26 21:50:40 -04:00
5daa861a31
round menus 2026-05-26 21:26:40 -04:00
82860deff1
make padding symmetrical for load more button 2026-05-26 21:04:17 -04:00
2ec442b35b
fix conditional rendering of popover actions 2026-05-26 20:56:32 -04:00
786c8cb3e2
remove un-needed double check of condition 2026-05-26 20:18:28 -04:00
a150ef2ecf
Fix some textOnly handling 2026-05-26 20:04:09 -04:00
bb037c8162
set max lines for pmp text to 1 2026-05-26 19:58:01 -04:00
eac59c58f5
center align pmp text 2026-05-26 19:54:56 -04:00
4e5c709fb9
slight refactor of callback in message avatar widget 2026-05-26 19:53:05 -04:00
451875b137
add sticker support 2026-05-26 19:43:25 -04:00
e1c81b504a
render TopicContent 2026-05-26 19:30:57 -04:00
f27db22151
render ServerACLContent 2026-05-26 19:29:47 -04:00
3d6ebedd94
render PinnedEventsContent 2026-05-26 19:25:41 -04:00
70eba46c76
add and render HistoryVisibilityContent 2026-05-26 19:22:53 -04:00
ec64a81fed
add create event, fix generic event icon 2026-05-26 19:13:19 -04:00
2ba620350d
refactor message renderer into its own widget 2026-05-26 19:11:09 -04:00
a0c2eefc1e
Add room alias support 2026-05-26 18:44:27 -04:00
9939e59429
Don't underline room topic
This makes finding out that the room dialog exists slightly difficult, but it looks nicer.

We could look into another way to make it obvious that room icon, title, and description are clickable.
2026-05-26 15:35:08 -04:00
e69f04f6e7
Add room details dialog
Fixes #20
2026-05-26 15:31:37 -04:00
4848840538
increase padding for no permission message 2026-05-26 13:09:32 -04:00
64b3127fd1
show last displayname if displayname loading 2026-05-26 12:14:35 -04:00
b836c3b06e
show messages whilst sending 2026-05-26 12:07:43 -04:00
d9f62a9de9
show last avatar if avatar loading 2026-05-26 12:07:35 -04:00
32dd08fd91
expand no permissions message 2026-05-26 10:42:23 -04:00
6de046bf7b
Fix sizing of event previews 2026-05-26 10:14:26 -04:00
ebbb4dc662
make pmp via text smaller 2026-05-26 09:52:35 -04:00
1d6a121ec4
calculate image aspect ratio for more accurate blurhash 2026-05-26 09:47:23 -04:00
ed0292468a
show hashed colors while avatar loading 2026-05-26 09:38:29 -04:00
8b310c955d
Mark windows support as added 2026-05-25 12:01:12 -04:00
14b140e23d
make SDK bound less strict 2026-05-24 10:53:56 -04:00
b47e61e005
rename chat box to composer 2026-05-23 20:21:44 -04:00
05bc9034d1
measure size of chat box to dynamically adjust padding
Fixes #21
2026-05-23 20:14:33 -04:00
d7ea233b18
copy mingw dlls windows 2026-05-23 18:31:36 -04:00
70ffbedba4
fix typo in DEVELOPMENT.md 2026-05-23 12:29:56 -04:00
a1386790a9
update gomuks vendorHash 2026-05-23 12:21:04 -04:00
68d2f654cc
bump gomuks 2026-05-23 12:17:40 -04:00
5368bacf1d
Add a code style guide to DEVELOPMENT.md 2026-05-23 12:13:41 -04:00
564fe3c964 Update flutter version in windows CI 2026-05-21 17:05:39 -04:00
16cf126df4 Remove flutter chat (#26)
Had to squash merge manually as Forgejo was erroring
2026-05-21 17:02:08 -04:00
bd1d5ea745
fixup bugs related to PMP
Closes #25, as "Explain how to send messages using a certain PMP" is now added to the progress list.
2026-05-09 21:42:51 -04:00
1ca802c78b
fix gomuks vendor hash 2026-05-09 21:01:08 -04:00
7b9eda2d36
update submodule 2026-05-09 20:57:47 -04:00
63eb001c09
bump gomuks 2026-05-09 18:25:15 -04:00
00c3503c1f
Add development instructions 2026-05-09 18:24:35 -04:00
469a625c40
add mark as read for rooms/DMs, fixes #24 2026-05-09 11:10:52 -04:00
66ef2de027
fix android build by overriding path_provider_android back to an earlier version
I don't know why this is needed
2026-05-08 22:17:11 -04:00
972c143de2
fix windows build
Bumps flutter version for windows.yml, fixing build
2026-05-08 21:41:33 -04:00
2f39985afb
bump deps 2026-05-08 21:34:22 -04:00
0ab466d011
fix Android icon
Adds a monochrome icon, along with reverting the foreground to its previous state, except 512x512. Should hopefully be the end of Android icon chicanery.
2026-05-06 19:13:23 -04:00
7857bcdc2e
fix icon sizing on login page 2026-05-06 18:17:22 -04:00
9faff092ed
higher res PNGs 2026-05-06 18:16:07 -04:00
0949fa6523
improve android app icon 2026-05-06 18:07:25 -04:00
e310f0f60e
fix some power level checks, fixes #19 2026-05-04 12:53:04 -04:00
7d8b267986
update progress list [skip ci] 2026-05-04 00:06:31 -04:00
c945c26413
Fix outdated repo links 2026-05-03 20:29:59 -04:00
4dc16a5529
Revert "WIP removal of new_events_controller"
This reverts commit 5a99616e9c.
2026-05-03 12:10:37 -04:00
ebd4b8a765
higher res pngs 2026-05-03 12:02:21 -04:00
26b95fac69
fix android icons 2026-05-02 16:42:55 -04:00
152331b262
Don't show icon in readme as forgejo messes up formatting 2026-05-02 16:36:10 -04:00
5f56c52fe0
use center tag 2026-05-02 10:12:32 -04:00
a8ae10c9ce
new icons 2026-05-02 10:02:40 -04:00
ad36dcb2f3 Update README.md 2026-04-28 23:12:07 -04:00
Zach Russell
8b4cd75076 Update windows build chain docs 2026-04-28 23:12:07 -04:00
5a99616e9c
WIP removal of new_events_controller [skip ci] 2026-04-28 20:44:12 -04:00
def69d85e7
add option for aligning bubbles to progress list 2026-04-24 20:03:14 -04:00
f4b2dcb824
hopefully fix debugLocked error 2026-04-14 18:09:31 -04:00
50e1a8e4c7
remove un-needed async 2026-04-14 10:58:25 -04:00
82dab26fd4
fix emoji dep hash 2026-04-13 23:18:01 -04:00
313dc377ec
fetch emoji list from gemoji for a more complete emoji list 2026-04-13 22:44:22 -04:00
347 changed files with 12075 additions and 5713 deletions

View file

@ -34,4 +34,4 @@ jobs:
uses: actions/upload-artifact@v6
with:
name: flatpak-${{ matrix.arch }}
path: result/nexus.federated.Nexus.flatpak
path: result/nexus.federated.nexus.flatpak

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.44.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

47
.github/workflows/macos.yml vendored Normal file
View file

@ -0,0 +1,47 @@
name: "Build MacOS 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.44.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 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
with:
name: nexus.dmg
path: ${{ steps.create-dmg.outputs.dmg_path }}

View file

@ -19,17 +19,24 @@ jobs:
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: 3.41.5
flutter-version: 3.44.4
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: gomuks/go.mod
- name: Setup MSYS2
uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
install: >-
mingw-w64-x86_64-gcc
- name: Go build
run: |
cd gomuks/pkg/ffi
go build -tags goolm -o ../../../libgomuks.dll -buildmode=c-shared
go build -tags goolm,sqlite_fts5 -o ../../../libgomuks.dll -buildmode=c-shared
- name: Build with Flutter
run: |
@ -38,11 +45,12 @@ jobs:
flutter pub run build_runner build
flutter build windows --release
- name: Upload exe zip
uses: actions/upload-artifact@v6
with:
name: windows-portable
path: build/windows/x64/runner/Release/
- name: Copy MinGW runtime DLLs
shell: msys2 {0}
run: |
cp /mingw64/bin/libgcc_s_seh-1.dll build/windows/x64/runner/Release/
cp /mingw64/bin/libwinpthread-1.dll build/windows/x64/runner/Release/
cp /mingw64/bin/libstdc++-6.dll build/windows/x64/runner/Release/
- name: Install Inno Setup
run: choco install innosetup -y

View file

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

View file

@ -5,8 +5,12 @@
"fluttertagger",
"Gomuks",
"Homeserver",
"Linkified",
"localpart",
"msgtype",
"muks",
"prefs"
"prefs",
"unban",
"unredact"
]
}

70
DEVELOPMENT.md Normal file
View file

@ -0,0 +1,70 @@
# Development Documentation
## Build instructions
Build instructions can be found in [README.md](./README.md#build-it-yourself).
## Updating Gomuks
You can run the following command to update the Gomuks submodule:
```sh
git submodule update --remote
```
## Code Style
See [Effective Dart: Style](https://dart.dev/effective-dart/style) for general rules. There are some extra rules detailed below:
### Controllers and Helpers ([Riverpod](https://pub.dev/packages/riverpod))
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
ref.watch(MyController.provider.notifier).helperMethod()
```
We use an object oriented style for controllers, where `provider` is a static member on the controller class. E.g.
```dart
class MyController extends AsyncNotifier<DataThisControllerExposes> {
final SomeInputType input;
MyController(this.input);
@override
Future<DataThisControllerExposes> build() async {
return input.foo;
}
static final provider =
AsyncNotifierProvider.family<MyController, DataThisControllerExposes, SomeInputType>(
AuthorController.new,
);
}
```
Providers which are not controllers, e.g. they expose no data, only methods, should instead live in `lib/helpers/`. For an example, see `lib/helpers/launch_helper.dart`. Other, non-provider helpers, like extensions or helper methods can also go in `lib/helpers/`.
### Don't use StatefulWidgets ([Flutter Hooks](https://pub.dev/packages/flutter_hooks))
This project uses Flutter Hooks to help with boilerplate that StatefulWidgets create. Instead of using a StatefulWidget, we just use hooks like `useState` or `useEffect` in the build method of a `HookWidget`, which is a drop in replacement for `StatelessWidget`. If you need both a `WidgetRef` to watch providers, and access to hooks, use `HookConsumerWidget`.
### Models ([Freezed](https://pub.dev/packages/freezed))
We use Freezed for our models to avoid boilerplate and enforce an immutable style of state and data modeling throughout the code. See their documentation for more info, or see our existing models in `lib/models/`.
### Immutable Data Collections ([Fast Immutable Collections](https://pub.dev/packages/fast_immutable_collections))
When possible, use immutable collections instead of the mutable equivalent. For example, use `IMap` over `Map`, `IList` over `List`, `ISet` over `Set`. This matches the immutable style of Riverpod and Freezed.
### Don't create globals
When possible, we prefer not to create global variables or methods. You can usually replace a global variable with a Riverpod controller, and a global method with an extension method.
## LLM/AI Assisted Contributions
LLM generated code is NOT allowed. All contributions should be written by humans. Using LLMs for interacting with others, e.g. for Comments, PRs, etc, is also not allowed.
## Code of Conduct
All contributions must follow the [Federated Nexus Code of Conduct](https://federated.nexus/code/).

120
README.md
View file

@ -15,21 +15,14 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend.
## Progress
- [ ] New logo
- [ ] Make context menus appear as bottom sheets on mobile
- [x] Move from the Dart SDK to the Gomuks Backend with Dart bindings: https://git.federated.nexus/Henry-Hiles/nexus/pulls/2
- [ ] Allow using remote Gomuks over websocket
- [ ] Platform Support
- [x] Platform Support
- [x] Linux
- [ ] Windows (WIP)
- [ ] MacOS
- [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
- [ ] iOS
- [x] MacOS - Unsigned .dmg only
- [x] iOS - Unsigned .ipa only
- [ ] Web (may not be possible)
- [x] Login
- [x] Username / password auth
- [ ] OAuth / OIDC
- [x] Improve initial sync experience
- [x] Login (via OAuth)
- [x] Rooms / Spaces
- [x] Displaying and choosing
- [x] Reading, showing unread
@ -43,7 +36,7 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend.
- [x] `matrix:` Uri
- [x] Matrix.to link
- [ ] From space
- [ ] Exploring
- [ ] From directory
- [x] Leaving
- [x] Subspaces
- [x] Messages
@ -54,8 +47,8 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend.
- [x] HTML/Markdown
- [x] Replies
- [x] Choose ping on/off
- [ ] Per message profiles
- [ ] Attachments
- [x] Per message profiles
- [x] Attachments
- [ ] Commands with [MSC4391](https://github.com/matrix-org/matrix-spec-proposals/pull/4391)
- [x] Mentions
- [x] Users
@ -76,7 +69,7 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend.
- [x] Edits
- [x] Attachments
- [x] Unencrypted
- [ ] Encrypted
- [x] Encrypted
- [x] Blurhashing
- [ ] Downloading attachments
- [x] Opening attachments in their own view
@ -85,7 +78,7 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend.
- [x] Users
- [x] Clickable
- [x] Rooms
- [x] Clickable
- [ ] Clickable
- [x] Matrix URIs
- [x] Matrix.to links
- [x] Events
@ -93,14 +86,12 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend.
- [ ] Clickable
- [x] Custom emojis/stickers
- [x] History loading
- [x] Backwards
- [ ] Forwards
- [x] Editing
- [x] Deleting
- [x] Reactions
- [ ] Pins
- [ ] Displaying
- [ ] Creating
- [x] Pins
- [x] Displaying
- [x] Pinning/Unpinning
- [ ] Threads
- [x] Profile popouts
- [x] Working actions
@ -111,61 +102,70 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend.
- [ ] Reporting
- [x] Events
- [ ] Rooms
- [ ] Notifications using UnifiedPush
- [x] Member list
- [x] Sort by power level
- [ ] Colors based off of power level
- [ ] Notifications using UnifiedPush ([#35](https://git.federated.nexus/Nexus/nexus/issues/35))
- [ ] Group calls using [MSC4195](https://github.com/matrix-org/matrix-spec-proposals/pull/4195)
- [ ] Invites
- [ ] Settings
- [ ] Matrix: URIs vs Matrix.to links
- [ ] Light/Dark mode
- [ ] SSD or CSD
- [ ] Show media by default
- [ ] Dynamic Theming
- [ ] Devices
- [ ] Viewing devices
- [ ] Verifying devices
- [ ] URL preview: Server / Sending Client (Beeper spec) / None
- [ ] Account changes
- [ ] Display name
- [ ] Profile picture
- [ ] Timezone
- [ ] Pronouns
- [ ] Password
- [ ] About
- [x] Log Out
- [x] Settings
## Try it out
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)
- Windows
- [Portable Build](https://nightly.link/Henry-Hiles/nexus/workflows/windows/main/windows-portable.zip)
- [Installer](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)
- [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)
- Flatpak
- [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)
Or, try the Nix package: `nix run git+https://git.federated.nexus/Henry-Hiles/nexus`
## Build it yourself
### Prerequisites
#### Linux
- With Nix: Either use direnv and `direnv allow`, or `nix flake develop`
- Without Nix: Install Flutter, Go, Git, Libclang, and Glibc. Do not use any Snap packages, they cause various compilation issues.
- On NixOS: Either use direnv and `direnv allow`, or `nix flake develop`
- On other distros: Install Flutter, Go, Git, Libclang, Libass, MPV, and Glibc. Do not use any Snap packages, they cause various compilation issues.
#### Windows / MacOS
#### Windows
I don't really know. You will need Flutter, Git, Go, and Visual Studio tools, and otherwise I guess just keep installing stuff until there aren't any errors. I will look into this sometimeTM.
You will need:
- Flutter
- Git
- Libass
- MPV
- Go
- Visual Studio 2022 (Desktop development with C++)
- [MSYS2/MinGW-w64 GCC](https://www.msys2.org/) (for CGO)
- [LLVM/Clang + libclang](https://clang.llvm.org/get_started.html) (for `ffigen`)
On Windows, make sure these are available in your shell `PATH`:
- `C:\msys64\ucrt64\bin` (or your MinGW bin path containing `x86_64-w64-mingw32-gcc.exe`)
- `C:\Program Files\LLVM\bin` (contains `clang.exe` and `libclang.dll`)
For `dart scripts/generate.dart`, you may also need:
```powershell
$env:CPATH = "C:\msys64\ucrt64\include"
```
#### MacOS
Similar prerequisites apply (Flutter, Git, Go, C toolchain, LLVM/libclang), but exact setup has not been fully documented yet.
### Clone repo
First, clone and open the repo:
```sh
git clone --recurse-submodules https://git.federated.nexus/Henry-Hiles/nexus
git clone --recurse-submodules https://git.federated.nexus/Nexus/nexus
cd nexus
```
@ -183,10 +183,17 @@ Generate Gomuks bindings:
dart scripts/generate.dart
```
> [!NOTE]
> If you are having issues with `stddef.h` not being found, try setting CPATH manually:
>
> ```sh
> export CPATH="$(clang -v 2>&1 | grep "Selected GCC installation" | rev | cut -d' ' -f1 | rev)/include"
> ```
Build generated files, and watch for new changes:
```sh
flutter pub run build_runner watch --delete-conflicting-outputs
flutter pub run build_runner watch
```
Run the app:
@ -195,6 +202,13 @@ Run the app:
flutter run
```
Development instructions can be found in [DEVELOPMENT.md](./DEVELOPMENT.md).
## Community
Join the [Nexus Client Matrix Room](https://matrix.to/#/#nexus:federated.nexus) for questions or help with developing or using Nexus Client.
Join the [Nexus Client Matrix room](https://matrix.to/#/#nexus:federated.nexus) for questions or help with developing or using Nexus Client.
# Credits
Thank you Hylke Bons (https://planetpeanut.studio) for making the amazing icon for Nexus!
Thank you Tulir Asokan for making [Gomuks](https://github.com/gomuks/gomuks), and helping us integrate it into Nexus!

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

@ -30,7 +30,7 @@ if (keystorePropertiesFile.exists()) {
}
android {
namespace = "nexus.federated.Nexus"
namespace = "nexus.federated.nexus"
ndkVersion = flutter.ndkVersion
compileSdk = 34
@ -40,12 +40,11 @@ android {
}
kotlinOptions {
// do we want to update.. eventually?
jvmTarget = "17"
}
defaultConfig {
applicationId = "nexus.federated.Nexus"
applicationId = "nexus.federated.nexus"
minSdk = 29
targetSdkVersion flutter.targetSdkVersion
versionCode = flutterVersionCode.toInteger()

View file

@ -22,6 +22,14 @@
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<meta-data android:name="flutter_deeplinking_enabled" android:value="false" />
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="nexus.federated.nexus" />
</intent-filter>
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"

View file

@ -1,4 +1,4 @@
package nexus.federated.Nexus
package nexus.federated.nexus
import io.flutter.embedding.android.FlutterActivity

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

View file

@ -6,4 +6,9 @@
android:drawable="@drawable/ic_launcher_foreground"
android:inset="16%" />
</foreground>
<monochrome>
<inset
android:drawable="@drawable/ic_launcher_monochrome"
android:inset="16%" />
</monochrome>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

Before After
Before After

View file

@ -1,3 +1,7 @@
org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true
# This builtInKotlin flag was added automatically by Flutter migrator
android.builtInKotlin=false
# This newDsl flag was added automatically by Flutter migrator
android.newDsl=false

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Before After
Before After

View file

@ -1,21 +1,22 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="100mm"
height="100mm"
viewBox="0 0 100 100"
width="512"
height="512"
viewBox="0 0 512 512"
fill="none"
version="1.1"
id="svg1"
xml:space="preserve"
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
id="svg11"
sodipodi:docname="background.svg"
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
inkscape:export-filename="background.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview1"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview11"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
@ -23,40 +24,234 @@
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
inkscape:zoom="1.0847363"
inkscape:cx="57.156749"
inkscape:cy="214.33781"
inkscape:window-width="1904"
inkscape:window-height="971"
inkscape:zoom="0.69191503"
inkscape:cx="-71.540576"
inkscape:cy="281.10388"
inkscape:window-width="2544"
inkscape:window-height="1363"
inkscape:window-x="35"
inkscape:window-y="32"
inkscape:window-maximized="0"
inkscape:current-layer="layer1" /><defs
id="defs1"><linearGradient
id="linearGradient10"
inkscape:collect="always"><stop
style="stop-color:#c7a312;stop-opacity:1;"
offset="0"
id="stop10" /><stop
style="stop-color:#26a0b3;stop-opacity:1;"
inkscape:current-layer="svg11" />
<defs
id="defs11">
<radialGradient
id="paint0_radial_4033_8"
cx="0"
cy="0"
r="1"
gradientTransform="matrix(3.5,24.5214,-15.8099,2.26053,80,52.9904)"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#72AAEE"
id="stop10" />
<stop
offset="1"
id="stop11" /></linearGradient><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient10"
id="linearGradient11"
x1="20.031296"
y1="32.697563"
x2="90.709213"
y2="66.3423"
gradientUnits="userSpaceOnUse" /></defs><g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"><rect
style="fill:url(#linearGradient11);fill-opacity:1;stroke:none;stroke-width:7.99999;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
id="rect10"
width="100"
height="100"
stop-color="#3584E4"
id="stop11" />
</radialGradient>
<mask
id="mask0_4033_8"
maskUnits="userSpaceOnUse"
x="56"
y="46"
width="21"
height="36">
<path
d="m 76.4223,63.1501 c 0.3482,0.5123 0.3457,1.1859 -0.0063,1.6956 L 65.0175,81.3524 C 64.7375,81.7579 64.2761,82 63.7832,82 h -5.9804 c -1.1981,0 -1.9127,-1.3352 -1.2481,-2.332 l 9.8906,-14.8359 c 0.3359,-0.5039 0.3359,-1.1603 0,-1.6641 L 57.0729,49.1094 C 56.1869,47.7803 57.1396,46 58.737,46 h 5.2334 c 0.4967,0 0.9613,0.2459 1.2405,0.6568 z"
fill="#2779dd"
id="path9" />
</mask>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath11">
<rect
width="128"
height="128"
fill="#ffffff"
id="rect12"
x="0"
y="0"
ry="28.294127" /></g></svg>
transform="rotate(-30)" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath12">
<rect
width="128"
height="128"
fill="#ffffff"
id="rect13"
x="0"
y="0"
transform="rotate(-30)" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath13">
<rect
width="128"
height="128"
fill="#ffffff"
id="rect14"
x="0"
y="0"
transform="rotate(-30)" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath14">
<rect
width="128"
height="128"
fill="#ffffff"
id="rect15"
x="0"
y="0"
transform="rotate(-30)" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath15">
<rect
width="128"
height="128"
fill="#ffffff"
id="rect16"
x="0"
y="0"
transform="rotate(-30)" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath16">
<rect
width="128"
height="128"
fill="#ffffff"
id="rect17"
x="0"
y="0"
transform="rotate(-30)" />
</clipPath>
<radialGradient
id="paint0_radial_4033_8-3"
cx="0"
cy="0"
r="1"
gradientTransform="matrix(3.5,24.5214,-15.8099,2.26053,174.26633,65.9904)"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#72AAEE"
id="stop10-6" />
<stop
offset="1"
stop-color="#3584E4"
id="stop11-7" />
</radialGradient>
<radialGradient
id="paint0_radial_4033_8-35"
cx="0"
cy="0"
r="1"
gradientTransform="matrix(3.5,24.5214,-15.8099,2.26053,80,52.9904)"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#72AAEE"
id="stop10-62" />
<stop
offset="1"
stop-color="#3584E4"
id="stop11-9" />
</radialGradient>
<mask
id="mask0_4033_8-1"
maskUnits="userSpaceOnUse"
x="56"
y="46"
width="21"
height="36">
<path
d="m 76.4223,63.1501 c 0.3482,0.5123 0.3457,1.1859 -0.0063,1.6956 L 65.0175,81.3524 C 64.7375,81.7579 64.2761,82 63.7832,82 h -5.9804 c -1.1981,0 -1.9127,-1.3352 -1.2481,-2.332 l 9.8906,-14.8359 c 0.3359,-0.5039 0.3359,-1.1603 0,-1.6641 L 57.0729,49.1094 C 56.1869,47.7803 57.1396,46 58.737,46 h 5.2334 c 0.4967,0 0.9613,0.2459 1.2405,0.6568 z"
fill="#2779dd"
id="path9-2" />
</mask>
<radialGradient
id="paint0_radial_4033_8-9"
cx="0"
cy="0"
r="1"
gradientTransform="matrix(14,98.0856,-63.2396,9.04212,320,211.9616)"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#72AAEE"
id="stop10-3" />
<stop
offset="1"
stop-color="#3584E4"
id="stop11-6" />
</radialGradient>
</defs>
<rect
width="512"
height="512"
fill="#ffffff"
id="rect1"
x="0"
y="0"
style="stroke-width:4" />
<rect
x="-1.5384758"
y="-122.66472"
width="35.5569"
height="291.86301"
transform="matrix(3.4641016,2,-2,3.4641016,0,0)"
fill="#9141ac"
id="rect2"
clip-path="url(#clipPath16)" />
<rect
x="34.018467"
y="-122.66468"
width="26.6677"
height="291.86301"
transform="matrix(3.4641016,2,-2,3.4641016,0,0)"
fill="#62a0ea"
id="rect3"
clip-path="url(#clipPath15)" />
<rect
x="60.68605"
y="-122.66468"
width="26.6677"
height="291.86301"
transform="matrix(3.4641016,2,-2,3.4641016,0,0)"
fill="#57e389"
id="rect4"
clip-path="url(#clipPath14)" />
<rect
x="87.353859"
y="-122.66468"
width="26.6677"
height="291.86301"
transform="matrix(3.4641016,2,-2,3.4641016,0,0)"
fill="#f5c211"
id="rect5"
clip-path="url(#clipPath13)" />
<rect
x="114.02161"
y="-122.66477"
width="26.6677"
height="291.86301"
transform="matrix(3.4641016,2,-2,3.4641016,0,0)"
fill="#ff7800"
id="rect6"
clip-path="url(#clipPath12)" />
<rect
x="140.68942"
y="-122.66477"
width="35.5569"
height="291.86301"
transform="matrix(3.4641016,2,-2,3.4641016,0,0)"
fill="#ed333b"
id="rect7"
clip-path="url(#clipPath11)" />
</svg>

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Before After
Before After

Binary file not shown.

BIN
assets/fonts/Roboto.ttf Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Before After
Before After

View file

@ -1,20 +1,19 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="100mm"
height="100mm"
viewBox="0 0 100 100"
width="512"
height="512"
viewBox="0 0 512 512"
fill="none"
version="1.1"
id="svg1"
xml:space="preserve"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
sodipodi:docname="nexus.svg"
id="svg11"
sodipodi:docname="foreground.svg"
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview1"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview11"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
@ -22,105 +21,137 @@
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
inkscape:zoom="1.0847363"
inkscape:cx="58.07863"
inkscape:cy="214.3378"
inkscape:window-width="1896"
inkscape:window-height="987"
inkscape:zoom="0.87695313"
inkscape:cx="152.23163"
inkscape:cy="347.22494"
inkscape:window-width="2544"
inkscape:window-height="1363"
inkscape:window-x="35"
inkscape:window-y="32"
inkscape:window-maximized="0"
inkscape:current-layer="layer1" /><defs
id="defs1" /><g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"><path
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
d="M 19.377906,68.106953 80.937684,32.43771"
id="path10" /><path
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
d="m 19.044488,32.469148 61.61782,35.569625"
id="path9" /><path
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
d="M 50,85.574911 V 14.425087"
id="path8" /><circle
style="fill:none;stroke:#ffffff;stroke-width:7.11498;stroke-linecap:round;stroke-linejoin:round"
id="path1"
cx="50"
cy="50"
r="35.574913" /><circle
style="fill:#09bd05;stroke:#ffffff;stroke-width:5;stroke-linecap:round;stroke-linejoin:round;fill-opacity:1;stroke-dasharray:none"
id="path2"
cx="50"
cy="84.604881"
r="8.2508707" /><circle
style="fill:#fe1e24;stroke:#ffffff;stroke-width:5;stroke-linecap:round;stroke-linejoin:round;fill-opacity:1;stroke-dasharray:none"
id="circle2"
cx="50"
cy="15.395123"
r="8.2508707" /><circle
style="fill:#fe941d;stroke:#ffffff;stroke-width:5;stroke-linecap:round;stroke-linejoin:round;fill-opacity:1;stroke-dasharray:none"
id="circle3"
cx="-68.30127"
cy="52.906147"
r="8.2508707"
transform="rotate(-120)" /><circle
style="fill:#001996;stroke:#ffffff;stroke-width:5;stroke-linecap:round;stroke-linejoin:round;fill-opacity:1;stroke-dasharray:none"
id="circle4"
cx="-68.30127"
cy="-16.30361"
r="8.2508707"
transform="rotate(-120)" /><circle
style="fill:#ffff04;stroke:#ffffff;stroke-width:5;stroke-linecap:round;stroke-linejoin:round;fill-opacity:1;stroke-dasharray:none"
id="circle5"
cx="-18.301271"
cy="102.90615"
r="8.2508707"
transform="rotate(-60)" /><circle
style="fill:#770287;stroke:#ffffff;stroke-width:5;stroke-linecap:round;stroke-linejoin:round;fill-opacity:1;stroke-dasharray:none"
id="circle6"
cx="-18.301271"
cy="33.696392"
r="8.2508707"
transform="rotate(-60)" /><circle
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:6.75;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
inkscape:current-layer="svg11" />
<path
d="m 256,92 c 90.5748,0 164,73.4252 164,164 0,90.5748 -73.4252,164 -164,164 -34.5828,0 -66.6592,-10.712 -93.1092,-28.9884 l -39.2072,8.7656 c -6.8668,1.5348 -12.9952,-4.594 -11.4608,-11.4608 l 8.7616,-39.2108 C 102.7104,322.6564 92,290.5808 92,256 92,165.4252 165.4252,92 256,92 Z"
fill="#ffffff"
id="path7"
cx="50"
cy="50"
r="9.7918472" /><g
inkscape:label="Layer 1"
id="layer1-3"
transform="matrix(0.08246781,0,0,0.08246781,38.828362,38.828362)"
style="stroke:#ffffff"><text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-size:96.3615px;line-height:0;font-family:'Noto Serif';-inkscape-font-specification:'Noto Serif, Heavy';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:end;text-anchor:end;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0;stroke-dasharray:none;stroke-opacity:1"
x="-305.64749"
y="194.14493"
id="text2819"><tspan
sodipodi:role="line"
id="tspan2817"
style="stroke:#ffffff;stroke-width:0"
x="-305.64749"
y="194.14493" /></text><circle
style="fill:#354b5f;fill-opacity:1;stroke:#ffffff;stroke-width:0;stroke-dasharray:none;stroke-opacity:1"
id="path342"
cx="135.46666"
cy="135.46666"
r="135.46666" /><text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-size:96.3615px;line-height:0;font-family:'Noto Serif';-inkscape-font-specification:'Noto Serif, Heavy';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:end;text-anchor:end;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0;stroke-dasharray:none;stroke-opacity:1"
x="-305.64749"
y="194.14493"
id="text2819-3"><tspan
sodipodi:role="line"
id="tspan2817-5"
style="stroke:#ffffff;stroke-width:0"
x="-305.64749"
y="194.14493" /></text><g
aria-label=""
id="text2827-6"
style="font-size:132.452px;line-height:0;font-family:PowerlineSymbols;-inkscape-font-specification:'PowerlineSymbols, Normal';text-align:end;text-anchor:end;fill:#4e94e4;fill-opacity:1;stroke:#ffffff;stroke-width:0"><path
d="M 95.096912,209.8167 143.88912,135.46666 95.096912,61.116629 h 32.818568 l 47.92093,74.350031 -47.92093,74.35004 z"
id="path2883-2"
style="fill:#4e94e4;fill-opacity:1;stroke:#ffffff;stroke-width:0" /></g></g></g></svg>
style="stroke-width:4" />
<path
d="m 304.9188,251.4672 c 1.8572,2.732 1.844,6.3248 -0.0332,9.0432 L 260.6664,324.546 C 259.1728,326.7088 256.712,328 254.0836,328 H 234.948 c -6.3896,0 -10.2004,-7.1212 -6.6564,-12.4376 l 36.75,-55.1248 c 1.7916,-2.6872 1.7916,-6.188 0,-8.8752 l -36.75,-55.1248 C 224.7476,191.1212 228.5584,184 234.948,184 h 19.8748 c 2.6492,0 5.1268,1.3116 6.616,3.5028 z"
fill="url(#paint0_radial_4033_8)"
id="path8"
style="fill:url(#paint0_radial_4033_8);stroke-width:4" />
<g
mask="url(#mask0_4033_8)"
id="g9"
transform="scale(4)">
<rect
x="52"
y="46"
width="17"
height="4"
fill="#2779dd"
id="rect9" />
</g>
<defs
id="defs11">
<radialGradient
id="paint0_radial_4033_8"
cx="0"
cy="0"
r="1"
gradientTransform="matrix(14,98.0856,-63.2396,9.04212,320,211.9616)"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#72AAEE"
id="stop10" />
<stop
offset="1"
stop-color="#3584E4"
id="stop11" />
</radialGradient>
<mask
id="mask0_4033_8"
maskUnits="userSpaceOnUse"
x="56"
y="46"
width="21"
height="36">
<path
d="m 76.4223,63.1501 c 0.3482,0.5123 0.3457,1.1859 -0.0063,1.6956 L 65.0175,81.3524 C 64.7375,81.7579 64.2761,82 63.7832,82 h -5.9804 c -1.1981,0 -1.9127,-1.3352 -1.2481,-2.332 l 9.8906,-14.8359 c 0.3359,-0.5039 0.3359,-1.1603 0,-1.6641 L 57.0729,49.1094 C 56.1869,47.7803 57.1396,46 58.737,46 h 5.2334 c 0.4967,0 0.9613,0.2459 1.2405,0.6568 z"
fill="#2779dd"
id="path9" />
</mask>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath11">
<rect
width="128"
height="128"
fill="#ffffff"
id="rect12"
x="0"
y="0"
transform="rotate(-30)" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath12">
<rect
width="128"
height="128"
fill="#ffffff"
id="rect13"
x="0"
y="0"
transform="rotate(-30)" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath13">
<rect
width="128"
height="128"
fill="#ffffff"
id="rect14"
x="0"
y="0"
transform="rotate(-30)" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath14">
<rect
width="128"
height="128"
fill="#ffffff"
id="rect15"
x="0"
y="0"
transform="rotate(-30)" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath15">
<rect
width="128"
height="128"
fill="#ffffff"
id="rect16"
x="0"
y="0"
transform="rotate(-30)" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath16">
<rect
width="128"
height="128"
fill="#ffffff"
id="rect17"
x="0"
y="0"
transform="rotate(-30)" />
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 6 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Before After
Before After

View file

@ -1,21 +1,22 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="100mm"
height="100mm"
viewBox="0 0 100 100"
width="512"
height="512"
viewBox="0 0 512 512"
fill="none"
version="1.1"
id="svg1"
xml:space="preserve"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
id="svg35"
sodipodi:docname="icon.svg"
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
inkscape:export-filename="icon.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview1"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview35"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
@ -23,128 +24,311 @@
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
inkscape:zoom="1.0847363"
inkscape:cx="57.61769"
inkscape:cy="214.33781"
inkscape:window-width="1896"
inkscape:window-height="963"
inkscape:zoom="1.321682"
inkscape:cx="69.608271"
inkscape:cy="120.67956"
inkscape:window-width="2544"
inkscape:window-height="1363"
inkscape:window-x="35"
inkscape:window-y="32"
inkscape:window-maximized="0"
inkscape:current-layer="layer1" /><defs
id="defs1"><linearGradient
id="linearGradient10"
inkscape:collect="always"><stop
style="stop-color:#c7a312;stop-opacity:1;"
offset="0"
id="stop10" /><stop
style="stop-color:#26a0b3;stop-opacity:1;"
offset="1"
id="stop11" /></linearGradient><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient10"
id="linearGradient11"
x1="20.031296"
y1="32.697563"
x2="90.709213"
y2="66.3423"
gradientUnits="userSpaceOnUse" /></defs><g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"><rect
style="fill:url(#linearGradient11);fill-opacity:1;stroke:none;stroke-width:7.99999;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
id="rect10"
width="100"
height="100"
x="0"
y="0"
ry="28.294127" /><path
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
d="M 19.377906,68.106953 80.937684,32.43771"
id="path10" /><path
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
d="m 19.044488,32.469148 61.61782,35.569625"
id="path9" /><path
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
d="M 50,85.574911 V 14.425087"
id="path8" /><circle
style="fill:none;stroke:#ffffff;stroke-width:7.11498;stroke-linecap:round;stroke-linejoin:round"
id="path1"
cx="50"
cy="50"
r="35.574913" /><circle
style="fill:#09bd05;stroke:#ffffff;stroke-width:5;stroke-linecap:round;stroke-linejoin:round;fill-opacity:1;stroke-dasharray:none"
id="path2"
cx="50"
cy="84.604881"
r="8.2508707" /><circle
style="fill:#fe1e24;stroke:#ffffff;stroke-width:5;stroke-linecap:round;stroke-linejoin:round;fill-opacity:1;stroke-dasharray:none"
id="circle2"
cx="50"
cy="15.395123"
r="8.2508707" /><circle
style="fill:#fe941d;stroke:#ffffff;stroke-width:5;stroke-linecap:round;stroke-linejoin:round;fill-opacity:1;stroke-dasharray:none"
id="circle3"
cx="-68.30127"
cy="52.906147"
r="8.2508707"
transform="rotate(-120)" /><circle
style="fill:#001996;stroke:#ffffff;stroke-width:5;stroke-linecap:round;stroke-linejoin:round;fill-opacity:1;stroke-dasharray:none"
id="circle4"
cx="-68.30127"
cy="-16.30361"
r="8.2508707"
transform="rotate(-120)" /><circle
style="fill:#ffff04;stroke:#ffffff;stroke-width:5;stroke-linecap:round;stroke-linejoin:round;fill-opacity:1;stroke-dasharray:none"
id="circle5"
cx="-18.301271"
cy="102.90615"
r="8.2508707"
transform="rotate(-60)" /><circle
style="fill:#770287;stroke:#ffffff;stroke-width:5;stroke-linecap:round;stroke-linejoin:round;fill-opacity:1;stroke-dasharray:none"
id="circle6"
cx="-18.301271"
cy="33.696392"
r="8.2508707"
transform="rotate(-60)" /><circle
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:6.75;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
inkscape:current-layer="svg35" />
<mask
id="mask0_4023_558"
maskUnits="userSpaceOnUse"
x="12"
y="100"
width="88"
height="16">
<path
d="m 100,104 c 0,6.627 -5.3726,12 -12,12 H 24 c -6.6274,0 -12,-5.373 -12,-12 v -4 c 0,6.627 5.3726,12 12,12 h 64 c 6.6274,0 12,-5.373 12,-12 z"
fill="#d9d9d9"
id="path6" />
</mask>
<mask
id="mask1_4023_558"
maskUnits="userSpaceOnUse"
x="77"
y="27"
width="21"
height="36">
<path
d="m 97.4223,44.1501 c 0.3482,0.5122 0.3457,1.1859 -0.0063,1.6956 L 86.0175,62.3523 C 85.7375,62.7579 85.2761,63 84.7832,63 h -5.9804 c -1.1981,0 -1.9127,-1.3352 -1.2481,-2.3321 l 9.8906,-14.8358 c 0.3359,-0.5039 0.3359,-1.1603 0,-1.6642 L 78.0729,30.1094 C 77.1869,28.7803 78.1396,27 79.737,27 h 5.2334 c 0.4967,0 0.9613,0.2459 1.2405,0.6567 z"
fill="#2779dd"
id="path14" />
</mask>
<g
id="g36"
transform="scale(4)">
<g
clip-path="url(#clip0_4023_558)"
id="g6">
<rect
x="62.0205"
y="-52.425598"
width="24"
height="197"
transform="rotate(30,62.0205,-52.4256)"
fill="#9141ac"
id="rect1" />
<rect
x="82.805099"
y="-40.425598"
width="18"
height="197"
transform="rotate(30,82.8051,-40.4256)"
fill="#62a0ea"
id="rect2" />
<rect
x="98.3936"
y="-31.4256"
width="18"
height="197"
transform="rotate(30,98.3936,-31.4256)"
fill="#57e389"
id="rect3" />
<rect
x="113.982"
y="-22.4256"
width="18"
height="197"
transform="rotate(30,113.982,-22.4256)"
fill="#f5c211"
id="rect4" />
<rect
x="129.57001"
y="-13.4256"
width="18"
height="197"
transform="rotate(30,129.57,-13.4256)"
fill="#ff7800"
id="rect5" />
<rect
x="145.159"
y="-4.4256301"
width="24"
height="197"
transform="rotate(30,145.159,-4.42563)"
fill="#ed333b"
id="rect6" />
</g>
<g
mask="url(#mask0_4023_558)"
id="g10">
<path
d="m 12,100 h 24 v 16 H 12 Z"
fill="url(#paint0_linear_4023_558)"
id="path7"
cx="50"
cy="50"
r="9.7918472" /><g
inkscape:label="Layer 1"
id="layer1-3"
transform="matrix(0.08246781,0,0,0.08246781,38.828362,38.828362)"
style="stroke:#ffffff"><text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-size:96.3615px;line-height:0;font-family:'Noto Serif';-inkscape-font-specification:'Noto Serif, Heavy';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:end;text-anchor:end;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0;stroke-dasharray:none;stroke-opacity:1"
x="-305.64749"
y="194.14493"
id="text2819"><tspan
sodipodi:role="line"
id="tspan2817"
style="stroke:#ffffff;stroke-width:0"
x="-305.64749"
y="194.14493" /></text><circle
style="fill:#354b5f;fill-opacity:1;stroke:#ffffff;stroke-width:0;stroke-dasharray:none;stroke-opacity:1"
id="path342"
cx="135.46666"
cy="135.46666"
r="135.46666" /><text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-size:96.3615px;line-height:0;font-family:'Noto Serif';-inkscape-font-specification:'Noto Serif, Heavy';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:end;text-anchor:end;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0;stroke-dasharray:none;stroke-opacity:1"
x="-305.64749"
y="194.14493"
id="text2819-3"><tspan
sodipodi:role="line"
id="tspan2817-5"
style="stroke:#ffffff;stroke-width:0"
x="-305.64749"
y="194.14493" /></text><g
aria-label=""
id="text2827-6"
style="font-size:132.452px;line-height:0;font-family:PowerlineSymbols;-inkscape-font-specification:'PowerlineSymbols, Normal';text-align:end;text-anchor:end;fill:#4e94e4;fill-opacity:1;stroke:#ffffff;stroke-width:0"><path
d="M 95.096912,209.8167 143.88912,135.46666 95.096912,61.116629 h 32.818568 l 47.92093,74.350031 -47.92093,74.35004 z"
id="path2883-2"
style="fill:#4e94e4;fill-opacity:1;stroke:#ffffff;stroke-width:0" /></g></g></g></svg>
style="fill:url(#paint0_linear_4023_558)" />
<path
d="m 12,100 h 5 v 16 h -5 z"
fill="url(#paint1_linear_4023_558)"
id="path8"
style="fill:url(#paint1_linear_4023_558)" />
<rect
x="36"
y="100"
width="21"
height="16"
fill="#ca9005"
id="rect8" />
<rect
x="57"
y="100"
width="21"
height="16"
fill="#c64600"
id="rect9" />
<rect
x="78"
y="100"
width="22"
height="16"
fill="url(#paint2_linear_4023_558)"
id="rect10"
style="fill:url(#paint2_linear_4023_558)" />
</g>
<rect
opacity="0.2"
x="24.5"
y="110.5"
width="63"
height="1"
stroke="url(#paint3_linear_4023_558)"
id="rect11"
style="stroke:url(#paint3_linear_4023_558)" />
<path
d="m 85,4 c 22.644,0 41,18.3563 41,41 v 4 c 0,22.6437 -18.356,41 -41,41 -8.3322,0 -16.0825,-2.4875 -22.5526,-6.7576 -0.4653,-0.3071 -1.0341,-0.4203 -1.5783,-0.2987 l -8.8369,1.9749 C 52.0134,84.9228 52,84.9395 52,84.9588 52,84.9816 51.9816,85 51.9588,85 h -0.4617 c -0.0787,0.0036 -0.1564,0.004 -0.2334,0 H 51 c -1.1046,0 -2,-0.8954 -2,-2 v -0.2617 c -0.0043,-0.0811 -0.0042,-0.1632 0,-0.2461 V 78.9749 C 49,78.7126 49.2126,78.5 49.4749,78.5 c 0.2224,0 0.4151,-0.1543 0.4635,-0.3714 l 1.117,-4.9987 C 51.177,72.5857 51.0638,72.017 50.7567,71.5517 46.487,65.0818 44,57.3317 44,49 V 45 C 44,22.3563 62.3563,4 85,4 Z"
fill="url(#paint4_linear_4023_558)"
id="path11"
style="fill:url(#paint4_linear_4023_558)" />
<path
d="m 85,4 c 22.644,0 41,18.3563 41,41 0,22.6437 -18.356,41 -41,41 -8.6457,0 -16.6648,-2.6781 -23.2773,-7.2471 l -9.8018,2.1914 c -1.7167,0.3836 -3.2488,-1.1485 -2.8652,-2.8652 l 2.1904,-9.8027 C 46.6776,61.6641 44,53.6452 44,45 44,22.3563 62.3563,4 85,4 Z"
fill="#ffffff"
id="path12" />
<path
d="m 97.2297,43.8668 c 0.4642,0.683 0.4609,1.5812 -0.0083,2.2608 L 86.1666,62.1365 C 85.7932,62.6772 85.178,63 84.5209,63 H 79.737 c -1.5974,0 -2.5502,-1.7803 -1.6641,-3.1094 l 9.1875,-13.7812 c 0.4478,-0.6718 0.4478,-1.547 0,-2.2188 L 78.0729,30.1094 C 77.1868,28.7803 78.1396,27 79.737,27 h 4.9687 c 0.6623,0 1.2817,0.3279 1.654,0.8757 z"
fill="url(#paint5_radial_4023_558)"
id="path13"
style="fill:url(#paint5_radial_4023_558)" />
<g
mask="url(#mask1_4023_558)"
id="g14">
<rect
x="73"
y="27"
width="17"
height="4"
fill="#2779dd"
id="rect14" />
</g>
</g>
<defs
id="defs35">
<linearGradient
id="paint0_linear_4023_558"
x1="34"
y1="108"
x2="12"
y2="108"
gradientUnits="userSpaceOnUse">
<stop
offset="0.401381"
stop-color="#26A269"
id="stop14" />
<stop
offset="0.801049"
stop-color="#8AEB52"
id="stop15" />
<stop
offset="1"
stop-color="#26A269"
id="stop16" />
</linearGradient>
<linearGradient
id="paint1_linear_4023_558"
x1="12"
y1="108"
x2="17"
y2="108"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#1A5FB4"
id="stop17" />
<stop
offset="1"
stop-color="#35E0F6"
id="stop18" />
</linearGradient>
<linearGradient
id="paint2_linear_4023_558"
x1="100"
y1="108"
x2="78"
y2="108"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#A51D2D"
id="stop19" />
<stop
offset="0.195858"
stop-color="#E5673C"
id="stop20" />
<stop
offset="0.5983"
stop-color="#A51D2D"
id="stop21" />
</linearGradient>
<linearGradient
id="paint3_linear_4023_558"
x1="88"
y1="111.329"
x2="24"
y2="111.329"
gradientUnits="userSpaceOnUse">
<stop
offset="0.102371"
stop-color="white"
stop-opacity="0"
id="stop22" />
<stop
offset="0.253808"
stop-color="white"
id="stop23" />
<stop
offset="0.747697"
stop-color="white"
id="stop24" />
<stop
offset="0.895556"
stop-color="white"
stop-opacity="0"
id="stop25" />
</linearGradient>
<linearGradient
id="paint4_linear_4023_558"
x1="44"
y1="48.036098"
x2="126"
y2="48.036098"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#DBEBF4"
id="stop26" />
<stop
offset="0.147387"
stop-color="#B1D4E7"
id="stop27" />
<stop
offset="0.186621"
stop-color="#8DC0DC"
id="stop28" />
<stop
offset="0.203755"
stop-color="#49AEE7"
id="stop29" />
<stop
offset="0.276122"
stop-color="#7AB5D7"
id="stop30" />
<stop
offset="0.399628"
stop-color="#B3D6E7"
id="stop31" />
<stop
offset="0.507537"
stop-color="#B3D6E7"
id="stop32" />
<stop
offset="1"
stop-color="#DBEBF4"
id="stop33" />
</linearGradient>
<radialGradient
id="paint5_radial_4023_558"
cx="0"
cy="0"
r="1"
gradientTransform="matrix(3.5,24.5214,-15.8099,2.26053,101,33.9904)"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#72AAEE"
id="stop34" />
<stop
offset="1"
stop-color="#3584E4"
id="stop35" />
</radialGradient>
<clipPath
id="clip0_4023_558">
<rect
x="12"
y="36"
width="88"
height="80"
rx="12"
fill="#ffffff"
id="rect35" />
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Before After
Before After

BIN
assets/mobile.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

156
assets/mobile.svg Normal file
View file

@ -0,0 +1,156 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="512"
height="512"
viewBox="0 0 512 512"
fill="none"
version="1.1"
id="svg11"
sodipodi:docname="mobile.svg"
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview11"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:zoom="30.03125"
inkscape:cx="14.51821"
inkscape:cy="11.038502"
inkscape:window-width="2544"
inkscape:window-height="1363"
inkscape:window-x="35"
inkscape:window-y="32"
inkscape:window-maximized="0"
inkscape:current-layer="svg11" />
<g
id="g11"
transform="scale(4)">
<g
clip-path="url(#clip0_4033_8)"
id="g10">
<rect
width="128"
height="128"
fill="#ffffff"
id="rect1"
x="0"
y="0" />
<rect
x="60"
y="-107"
width="35.5569"
height="291.86301"
transform="rotate(30,60,-107)"
fill="#9141ac"
id="rect2" />
<rect
x="90.793198"
y="-89.221497"
width="26.6677"
height="291.86301"
transform="rotate(30,90.7932,-89.2215)"
fill="#62a0ea"
id="rect3" />
<rect
x="113.888"
y="-75.887703"
width="26.6677"
height="291.86301"
transform="rotate(30,113.888,-75.8877)"
fill="#57e389"
id="rect4" />
<rect
x="136.983"
y="-62.553799"
width="26.6677"
height="291.86301"
transform="rotate(30,136.983,-62.5538)"
fill="#f5c211"
id="rect5" />
<rect
x="160.078"
y="-49.220001"
width="26.6677"
height="291.86301"
transform="rotate(30,160.078,-49.22)"
fill="#ff7800"
id="rect6" />
<rect
x="183.173"
y="-35.886101"
width="35.5569"
height="291.86301"
transform="rotate(30,183.173,-35.8861)"
fill="#ed333b"
id="rect7" />
<path
d="m 64,23 c 22.6437,0 41,18.3563 41,41 0,22.6437 -18.3563,41 -41,41 -8.6457,0 -16.6648,-2.678 -23.2773,-7.2471 l -9.8018,2.1914 c -1.7167,0.3837 -3.2488,-1.1485 -2.8652,-2.8652 l 2.1904,-9.8027 C 25.6776,80.6641 23,72.6452 23,64 23,41.3563 41.3563,23 64,23 Z"
fill="#ffffff"
id="path7" />
<path
d="m 76.2297,62.8668 c 0.4643,0.683 0.461,1.5812 -0.0083,2.2608 L 65.1666,81.1365 C 64.7932,81.6772 64.178,82 63.5209,82 H 58.737 c -1.5974,0 -2.5501,-1.7803 -1.6641,-3.1094 l 9.1875,-13.7812 c 0.4479,-0.6718 0.4479,-1.547 0,-2.2188 L 57.0729,49.1094 C 56.1869,47.7803 57.1396,46 58.737,46 h 4.9687 c 0.6623,0 1.2817,0.3279 1.654,0.8757 z"
fill="url(#paint0_radial_4033_8)"
id="path8"
style="fill:url(#paint0_radial_4033_8)" />
<mask
id="mask0_4033_8"
maskUnits="userSpaceOnUse"
x="56"
y="46"
width="21"
height="36">
<path
d="m 76.4223,63.1501 c 0.3482,0.5123 0.3457,1.1859 -0.0063,1.6956 L 65.0175,81.3524 C 64.7375,81.7579 64.2761,82 63.7832,82 h -5.9804 c -1.1981,0 -1.9127,-1.3352 -1.2481,-2.332 l 9.8906,-14.8359 c 0.3359,-0.5039 0.3359,-1.1603 0,-1.6641 L 57.0729,49.1094 C 56.1869,47.7803 57.1396,46 58.737,46 h 5.2334 c 0.4967,0 0.9613,0.2459 1.2405,0.6568 z"
fill="#2779dd"
id="path9" />
</mask>
<g
mask="url(#mask0_4033_8)"
id="g9">
<rect
x="52"
y="46"
width="17"
height="4"
fill="#2779dd"
id="rect9" />
</g>
</g>
</g>
<defs
id="defs11">
<radialGradient
id="paint0_radial_4033_8"
cx="0"
cy="0"
r="1"
gradientTransform="matrix(3.5,24.5214,-15.8099,2.26053,80,52.9904)"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#72AAEE"
id="stop10" />
<stop
offset="1"
stop-color="#3584E4"
id="stop11" />
</radialGradient>
<clipPath
id="clip0_4033_8">
<rect
width="128"
height="128"
fill="#ffffff"
id="rect11"
x="0"
y="0" />
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

BIN
assets/monochrome.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

178
assets/monochrome.svg Normal file
View file

@ -0,0 +1,178 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="512"
height="512"
viewBox="0 0 512 512"
fill="none"
version="1.1"
id="svg11"
sodipodi:docname="monochrome.svg"
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
inkscape:export-filename="foreground.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview11"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:zoom="1.240199"
inkscape:cx="156.02335"
inkscape:cy="321.3194"
inkscape:window-width="2544"
inkscape:window-height="1363"
inkscape:window-x="35"
inkscape:window-y="32"
inkscape:window-maximized="0"
inkscape:current-layer="svg11" />
<path
d="m 256,92 c 90.5748,0 164,73.4252 164,164 0,90.5748 -73.4252,164 -164,164 -34.5828,0 -66.6592,-10.712 -93.1092,-28.9884 l -39.2072,8.7656 c -6.8668,1.5348 -12.9952,-4.594 -11.4608,-11.4608 l 8.7616,-39.2108 C 102.7104,322.6564 92,290.5808 92,256 92,165.4252 165.4252,92 256,92 Z"
fill="#ffffff"
id="path7"
style="stroke-width:4"
clip-path="url(#clipPath1)"
inkscape:path-effect="#path-effect1" />
<defs
id="defs11">
<inkscape:path-effect
effect="powerclip"
message=""
id="path-effect1"
is_visible="true"
lpeversion="1"
inverse="true"
flatten="false"
hide_clip="false" />
<radialGradient
id="paint0_radial_4033_8"
cx="0"
cy="0"
r="1"
gradientTransform="matrix(14,98.0856,-63.2396,9.04212,320,211.9616)"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#72AAEE"
id="stop10" />
<stop
offset="1"
stop-color="#3584E4"
id="stop11" />
</radialGradient>
<mask
id="mask0_4033_8"
maskUnits="userSpaceOnUse"
x="56"
y="46"
width="21"
height="36">
<path
d="m 76.4223,63.1501 c 0.3482,0.5123 0.3457,1.1859 -0.0063,1.6956 L 65.0175,81.3524 C 64.7375,81.7579 64.2761,82 63.7832,82 h -5.9804 c -1.1981,0 -1.9127,-1.3352 -1.2481,-2.332 l 9.8906,-14.8359 c 0.3359,-0.5039 0.3359,-1.1603 0,-1.6641 L 57.0729,49.1094 C 56.1869,47.7803 57.1396,46 58.737,46 h 5.2334 c 0.4967,0 0.9613,0.2459 1.2405,0.6568 z"
fill="#2779dd"
id="path9" />
</mask>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath11">
<rect
width="128"
height="128"
fill="#ffffff"
id="rect12"
x="0"
y="0"
transform="rotate(-30)" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath12">
<rect
width="128"
height="128"
fill="#ffffff"
id="rect13"
x="0"
y="0"
transform="rotate(-30)" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath13">
<rect
width="128"
height="128"
fill="#ffffff"
id="rect14"
x="0"
y="0"
transform="rotate(-30)" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath14">
<rect
width="128"
height="128"
fill="#ffffff"
id="rect15"
x="0"
y="0"
transform="rotate(-30)" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath15">
<rect
width="128"
height="128"
fill="#ffffff"
id="rect16"
x="0"
y="0"
transform="rotate(-30)" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath16">
<rect
width="128"
height="128"
fill="#ffffff"
id="rect17"
x="0"
y="0"
transform="rotate(-30)" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1">
<path
d="m 304.9188,251.4672 c 1.8572,2.732 1.844,6.3248 -0.0332,9.0432 L 260.6664,324.546 C 259.1728,326.7088 256.712,328 254.0836,328 H 234.948 c -6.3896,0 -10.2004,-7.1212 -6.6564,-12.4376 l 36.75,-55.1248 c 1.7916,-2.6872 1.7916,-6.188 0,-8.8752 l -36.75,-55.1248 C 224.7476,191.1212 228.5584,184 234.948,184 h 19.8748 c 2.6492,0 5.1268,1.3116 6.616,3.5028 z"
fill="url(#paint0_radial_4033_8)"
id="path1"
style="display:none;fill:url(#radialGradient1);stroke-width:4" />
<path
id="lpe_path-effect1"
style="fill:url(#radialGradient1);stroke-width:4"
class="powerclip"
d="M 87,87 H 425 V 425 H 87 Z m 217.9188,164.4672 -43.48,-63.9644 C 259.9496,185.3116 257.472,184 254.8228,184 H 234.948 c -6.3896,0 -10.2004,7.1212 -6.6564,12.4376 l 36.75,55.1248 c 1.7916,2.6872 1.7916,6.188 0,8.8752 l -36.75,55.1248 C 224.7476,320.8788 228.5584,328 234.948,328 h 19.1356 c 2.6284,0 5.0892,-1.2912 6.5828,-3.454 l 44.2192,-64.0356 c 1.8772,-2.7184 1.8904,-6.3112 0.0332,-9.0432 z" />
</clipPath>
<radialGradient
inkscape:collect="always"
xlink:href="#paint0_radial_4033_8"
id="radialGradient1"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(14,98.0856,-63.2396,9.04212,320,211.9616)"
cx="0"
cy="0"
r="1" />
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 423 KiB

After

Width:  |  Height:  |  Size: 616 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 425 KiB

After

Width:  |  Height:  |  Size: 616 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View file

@ -1,126 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="100mm"
height="100mm"
viewBox="0 0 100 100"
version="1.1"
id="svg1"
xml:space="preserve"
inkscape:version="1.4.3 (0d15f75042, 2025-12-25)"
sodipodi:docname="smallerForeground.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
inkscape:zoom="1.0847363"
inkscape:cx="58.078632"
inkscape:cy="213.41592"
inkscape:window-width="2544"
inkscape:window-height="1363"
inkscape:window-x="35"
inkscape:window-y="32"
inkscape:window-maximized="0"
inkscape:current-layer="layer1" /><defs
id="defs1" /><g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"><path
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:5.41161;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
d="M 29.285638,62.248476 70.927843,38.119963"
id="path10" /><path
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:5.41161;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
d="M 29.060097,38.141229 70.741564,62.202356"
id="path9" /><path
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:5.41161;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
d="M 50,74.064703 V 25.935297"
id="path8" /><circle
style="fill:none;stroke:#ffffff;stroke-width:4.81294;stroke-linecap:round;stroke-linejoin:round"
id="path1"
cx="50"
cy="50"
r="24.064703" /><circle
style="fill:#09bd05;fill-opacity:1;stroke:#ffffff;stroke-width:3.38226;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
id="path2"
cx="50"
cy="73.408524"
r="5.5813141" /><circle
style="fill:#fe1e24;fill-opacity:1;stroke:#ffffff;stroke-width:3.38226;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
id="circle2"
cx="50"
cy="26.59148"
r="5.5813141" /><circle
style="fill:#fe941d;fill-opacity:1;stroke:#ffffff;stroke-width:3.38226;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
id="circle3"
cx="-68.30127"
cy="41.709789"
r="5.5813141"
transform="rotate(-120)" /><circle
style="fill:#001996;fill-opacity:1;stroke:#ffffff;stroke-width:3.38226;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
id="circle4"
cx="-68.30127"
cy="-5.1072531"
r="5.5813141"
transform="rotate(-120)" /><circle
style="fill:#ffff04;fill-opacity:1;stroke:#ffffff;stroke-width:3.38226;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
id="circle5"
cx="-18.301271"
cy="91.709793"
r="5.5813141"
transform="rotate(-60)" /><circle
style="fill:#770287;fill-opacity:1;stroke:#ffffff;stroke-width:3.38226;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
id="circle6"
cx="-18.301271"
cy="44.89275"
r="5.5813141"
transform="rotate(-60)" /><circle
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:4.56605;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
id="path7"
cx="50"
cy="50"
r="6.6237097" /><g
inkscape:label="Layer 1"
id="layer1-3"
transform="matrix(0.05578547,0,0,0.05578547,42.442929,42.442929)"
style="stroke:#ffffff"><text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-size:96.3615px;line-height:0;font-family:'Noto Serif';-inkscape-font-specification:'Noto Serif, Heavy';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:end;text-anchor:end;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0;stroke-dasharray:none;stroke-opacity:1"
x="-305.64749"
y="194.14493"
id="text2819"><tspan
sodipodi:role="line"
id="tspan2817"
style="stroke:#ffffff;stroke-width:0"
x="-305.64749"
y="194.14493" /></text><circle
style="fill:#354b5f;fill-opacity:1;stroke:#ffffff;stroke-width:0;stroke-dasharray:none;stroke-opacity:1"
id="path342"
cx="135.46666"
cy="135.46666"
r="135.46666" /><text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;font-size:96.3615px;line-height:0;font-family:'Noto Serif';-inkscape-font-specification:'Noto Serif, Heavy';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;text-align:end;text-anchor:end;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0;stroke-dasharray:none;stroke-opacity:1"
x="-305.64749"
y="194.14493"
id="text2819-3"><tspan
sodipodi:role="line"
id="tspan2817-5"
style="stroke:#ffffff;stroke-width:0"
x="-305.64749"
y="194.14493" /></text><g
aria-label=""
id="text2827-6"
style="font-size:132.452px;line-height:0;font-family:PowerlineSymbols;-inkscape-font-specification:'PowerlineSymbols, Normal';text-align:end;text-anchor:end;fill:#4e94e4;fill-opacity:1;stroke:#ffffff;stroke-width:0"><path
d="M 95.096912,209.8167 143.88912,135.46666 95.096912,61.116629 h 32.818568 l 47.92093,74.350031 -47.92093,74.35004 z"
id="path2883-2"
style="fill:#4e94e4;fill-opacity:1;stroke:#ffffff;stroke-width:0" /></g></g></g></svg>

Before

Width:  |  Height:  |  Size: 6 KiB

BIN
assets/twim/oauth.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
assets/twim/settings.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

24
flake.lock generated
View file

@ -5,11 +5,11 @@
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1767609335,
"narHash": "sha256-feveD98mQpptwrAEggBQKJTYbvwwglSbOv53uCfH9PY=",
"lastModified": 1785627969,
"narHash": "sha256-4dtXQk/NMePegK/nWp5NSeuZKLATItOq61lpEvmXqGw=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "250481aafeb741edfe23d29195671c19b36b6dca",
"rev": "427bf4bd9435fdf21321c8cc628c24efc14c0f7a",
"type": "github"
},
"original": {
@ -42,11 +42,11 @@
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1774604963,
"narHash": "sha256-MtAW1FIdirSlUAAO7s1u9auv5y3I6t3uJ+GeEbqiqxI=",
"lastModified": 1774860670,
"narHash": "sha256-YjJkQrvxrErXtfDi3obUn6rNmkA+CIAZ3f5NgL5xuYE=",
"owner": "neobrain",
"repo": "nix2flatpak",
"rev": "3e04657fbcb49956ac301410b071a7f0b2ad5988",
"rev": "61d68e21e3fbc2d57590051f48736bea271f4aba",
"type": "github"
},
"original": {
@ -73,11 +73,11 @@
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1765674936,
"narHash": "sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo=",
"lastModified": 1785031560,
"narHash": "sha256-OmshNvn2vupOFpYinLUu+1Dnpu4n7Q5N3ggGVNHpkUI=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "2075416fcb47225d9b68ac469a5c4801a9c4dd85",
"rev": "0e79af5e3d4dcfcd676ab5ba3f95d2e3352e078c",
"type": "github"
},
"original": {
@ -88,11 +88,11 @@
},
"nixpkgs_2": {
"locked": {
"lastModified": 1767640445,
"narHash": "sha256-UWYqmD7JFBEDBHWYcqE6s6c77pWdcU/i+bwD6XxMb8A=",
"lastModified": 1785967620,
"narHash": "sha256-IItrdb7Puk05RqOBWZYFC5X6Wl1sJmCfh5MWVHw5iMM=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "9f0c42f8bc7151b8e7e5840fb3bd454ad850d8c5",
"rev": "b7c2ada94fe99c15b0dbcf4d11fd7850b957a436",
"type": "github"
},
"original": {

View file

@ -52,12 +52,13 @@
flatpak = inputs.nix2flatpak.lib.${system}.mkFlatpak {
appName = "Nexus";
developer = "QuadRadical";
appId = "nexus.federated.Nexus";
appId = "nexus.federated.nexus";
package = default;
runtime = "org.gnome.Platform/49";
permissions = {
share = [ "network" ];
sockets = [
"pulseaudio"
"fallback-x11"
"wayland"
];

2
gomuks

@ -1 +1 @@
Subproject commit daa0ba028e7d89ba9fc7580fc8099348e6145cb3
Subproject commit 9444dd293664c300c6e710bb6cddcd9c6cab365d

View file

@ -1,20 +1,59 @@
import "dart:io";
import "package:collection/collection.dart";
import "package:hooks/hooks.dart";
import "package:code_assets/code_assets.dart";
import "package:nexus/helpers/extensions/get_xcode_sdk.dart";
import "package:path/path.dart";
Future<void> main(List<String> args) => build(args, (input, output) async {
if (!input.config.buildCodeAssets) return;
final codeConfig = input.config.code;
final targetOS = codeConfig.targetOS;
final targetArch = codeConfig.targetArchitecture;
String libFileName;
Map<String, String> env = {};
Map<String, String> extraEnv = {};
IOSSdk? iosSdk;
String? iosSdkPath;
String? iosTargetTriple;
switch (targetOS) {
case OS.linux:
libFileName = "libgomuks.so";
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:
libFileName = "libgomuks.dylib";
extraEnv = {
"SDKROOT": await getXCodeTool(),
"MACOSX_DEPLOYMENT_TARGET": codeConfig.macOS.targetVersion.toString(),
};
break;
case OS.windows:
libFileName = "libgomuks.dll";
@ -24,23 +63,48 @@ Future<void> main(List<String> args) => build(args, (input, output) async {
final targetNdkApi = codeConfig.android.targetNdkApi;
Future<String?> findNdkFromSdk() async {
final androidHome =
Platform.environment["ANDROID_HOME"] ??
Platform.environment["ANDROID_SDK_ROOT"];
if (androidHome == null) return null;
final ndkDir = Directory(join(androidHome, "ndk"));
if (!await ndkDir.exists()) return null;
final versions = await ndkDir.list().toList();
return versions.sortedBy((file) => file.path).lastOrNull?.path;
}
final ndkHome =
Platform.environment["ANDROID_NDK_HOME"] ??
Platform.environment["ANDROID_NDK_ROOT"] ??
Platform.environment["NDK_HOME"] ??
await _findNdkFromSdk();
await findNdkFromSdk();
if (ndkHome == null) {
throw Exception(
"Could not find Android NDK. Set ANDROID_NDK_HOME or install via sdkmanager.",
);
}
final hostTag = _ndkHostTag();
final (goArch, ccTriple) = _androidArch(targetArch);
// TODO: Someone please give me a way to detect host architecture so I can change this
final hostTag =
Platform.environment["ANDROID_HOST_TAG"] ??
"${Platform.operatingSystem}-x86_64";
final ccTriple = switch (targetArch) {
Architecture.arm64 => "aarch64-linux-android",
Architecture.arm => "armv7a-linux-androideabi",
Architecture.x64 => "x86_64-linux-android",
Architecture.ia32 => "i686-linux-android",
_ => throw UnsupportedError(
"Unsupported Android architecture: $targetArch",
),
};
final cc =
"$ndkHome/toolchains/llvm/prebuilt/$hostTag/bin/$ccTriple$targetNdkApi-clang";
env = {"CGO_ENABLED": "1", "GOOS": "android", "GOARCH": goArch, "CC": cc};
extraEnv = {"GOOS": "android", "CC": cc};
break;
default:
throw UnsupportedError("Unsupported OS: $targetOS");
@ -53,16 +117,41 @@ Future<void> main(List<String> args) => build(args, (input, output) async {
final buildDir = input.packageRoot.resolve("build/");
libFile = buildDir.resolve("${targetArch.name}/$libFileName");
// goheif/dav1d supported on Android would need to fix upstream
final tags = targetOS == OS.android ? "goolm,noheic" : "goolm";
final tags = [
"sqlite_fts5",
"goolm",
// goheif/dav1d is not supported on Android or iOS, would need to be fixed upstream
if (targetOS == OS.android || targetOS == OS.iOS) "noheic",
].join(",");
final archiveFile = targetOS == OS.iOS
? buildDir.resolve("${targetArch.name}/libgomuks.a")
: libFile;
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(
"go",
["build", "-tags", tags, "-o", libFile.path, "-buildmode=c-shared"],
[
"build",
"-tags",
tags,
"-o",
archiveFile.path,
"-buildmode=${targetOS == OS.iOS ? "c-archive" : "c-shared"}",
],
workingDirectory: gomuksBuildDir.resolve("pkg/ffi/").toFilePath(),
environment: env.isNotEmpty ? env : null,
environment: {
"CGO_ENABLED": "1",
"GOARCH": switch (targetArch) {
Architecture.arm64 => "arm64",
Architecture.arm => "arm",
Architecture.x64 => "amd64",
Architecture.ia32 => "386",
_ => throw UnsupportedError("Unsupported architecture: $targetArch"),
},
...extraEnv,
},
);
if (result.exitCode != 0) {
@ -70,6 +159,35 @@ Future<void> main(List<String> args) => build(args, (input, output) async {
"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";
@ -87,43 +205,3 @@ Future<void> main(List<String> args) => build(args, (input, output) async {
..dependencies.add(gomuksBuildDir);
print("Done!");
});
Future<String?> _findNdkFromSdk() async {
// pretty sure this wont be needed with nix, i'll get this removed
final androidHome =
Platform.environment["ANDROID_HOME"] ??
Platform.environment["ANDROID_SDK_ROOT"];
if (androidHome == null) return null;
final ndkDir = Directory("$androidHome/ndk");
if (!await ndkDir.exists()) return null;
final versions = await ndkDir.list().toList();
if (versions.isEmpty) return null;
versions.sort((a, b) => a.path.compareTo(b.path));
return versions.last.path;
}
String _ndkHostTag() {
if (Platform.isMacOS) {
return "darwin-x86_64";
} else if (Platform.isLinux) {
return "linux-x86_64";
} else if (Platform.isWindows) {
return "windows-x86_64";
}
throw UnsupportedError("Unsupported host platform for Android NDK");
}
(String goArch, String ccTriple) _androidArch(Architecture arch) {
switch (arch) {
case Architecture.arm64:
return ("arm64", "aarch64-linux-android");
case Architecture.arm:
return ("arm", "armv7a-linux-androideabi");
case Architecture.x64:
return ("amd64", "x86_64-linux-android");
case Architecture.ia32:
return ("386", "i686-linux-android");
default:
throw UnsupportedError("Unsupported Android architecture: $arch");
}
}

View file

@ -7,7 +7,7 @@
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>nexus.federated.Nexus</string>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
@ -20,7 +20,5 @@
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>15.5</string>
</dict>
</plist>

View file

@ -1,4 +1,7 @@
platform :ios, 16
# 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', {
@ -26,30 +29,15 @@ flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
use_modular_headers!
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|
target.build_configurations.each do |config|
if config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] == '8.0'
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0'
flutter_additional_ios_build_settings(target)
end
end
flutter_additional_ios_build_settings target
end
################ Awesome Notifications pod modification 1 ###################
awesome_pod_file = File.expand_path(File.join('plugins', 'awesome_notifications', 'ios', 'Scripts', 'AwesomePodFile'), '.symlinks')
require awesome_pod_file
update_awesome_pod_build_settings(installer)
################ Awesome Notifications pod modification 1 ###################
end
################ Awesome Notifications pod modification 2 ###################
awesome_pod_file = File.expand_path(File.join('plugins', 'awesome_notifications', 'ios', 'Scripts', 'AwesomePodFile'), '.symlinks')
require awesome_pod_file
update_awesome_main_target_settings('Runner', File.dirname(File.realpath(__FILE__)), flutter_root)
################ Awesome Notifications pod modification 2 ###################

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

@ -8,14 +8,28 @@
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3965FF1D950A2B2E051F6219 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E598BBDD2ED0112E344B590 /* Pods_Runner.framework */; };
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;
@ -32,12 +46,19 @@
/* 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>"; };
5E598BBDD2ED0112E344B590 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
6A2D47C573B065AD356C7183 /* 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>"; };
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; };
@ -45,35 +66,67 @@
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>"; };
CA00A9F43BB1AB7D908E180A /* 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>"; };
DFFCB3F45BF495277AE4D3EE /* 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>"; };
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 = (
3965FF1D950A2B2E051F6219 /* Pods_Runner.framework in Frameworks */,
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */,
88D55A5F1C7625122EBEE29D /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
7226BEAE3D25CA120A434CC9 /* Pods */ = {
1CFFAE23B502BCCEB2B191A3 /* Pods */ = {
isa = PBXGroup;
children = (
DFFCB3F45BF495277AE4D3EE /* Pods-Runner.debug.xcconfig */,
CA00A9F43BB1AB7D908E180A /* Pods-Runner.release.xcconfig */,
6A2D47C573B065AD356C7183 /* Pods-Runner.profile.xcconfig */,
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 */,
@ -88,8 +141,9 @@
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
7226BEAE3D25CA120A434CC9 /* Pods */,
E396C277672D83FCFDB51CCB /* Frameworks */,
331C8082294A63A400263BE5 /* RunnerTests */,
1CFFAE23B502BCCEB2B191A3 /* Pods */,
594EB1F65336CF9C7958F6FA /* Frameworks */,
);
sourceTree = "<group>";
};
@ -97,6 +151,7 @@
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
331C8081294A63A400263BE5 /* RunnerTests.xctest */,
);
name = Products;
sourceTree = "<group>";
@ -111,41 +166,55 @@
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "<group>";
};
E396C277672D83FCFDB51CCB /* Frameworks */ = {
isa = PBXGroup;
children = (
5E598BBDD2ED0112E344B590 /* Pods_Runner.framework */,
);
name = Frameworks;
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 = (
A3B5CB395514F4BF9CDF304E /* [CP] Check Pods Manifest.lock */,
0830C6758DA3ECA81FB12386 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
C3C8545BDAC52D360D298A29 /* [CP] Embed Pods Frameworks */,
F46F66CC32F585BBF2FB675E /* [CP] Copy Pods Resources */,
1D165FDFB9D0D939B041966D /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
packageProductDependencies = (
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */,
);
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
@ -160,6 +229,10 @@
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
331C8080294A63A400263BE5 = {
CreatedOnToolsVersion = 14.0;
TestTargetID = 97C146ED1CF9000F007C117D;
};
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
@ -175,16 +248,27 @@
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;
@ -199,6 +283,45 @@
/* 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;
@ -230,7 +353,7 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
A3B5CB395514F4BF9CDF304E /* [CP] Check Pods Manifest.lock */ = {
EE82EC529EB6C0F1FA480659 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -245,61 +368,44 @@
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
"$(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;
};
C3C8545BDAC52D360D298A29 /* [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;
};
F46F66CC32F585BBF2FB675E /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\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;
@ -324,6 +430,7 @@
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++";
@ -353,6 +460,7 @@
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;
@ -374,20 +482,17 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = NO;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = AppIcon;
BUILD_LIBRARY_FOR_DISTRIBUTION = NO;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = P3ZD9TNSKZ;
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_BUNDLE_IDENTIFIER = nexus.federated.nexus;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@ -395,10 +500,61 @@
};
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++";
@ -428,6 +584,7 @@
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;
@ -454,6 +611,7 @@
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++";
@ -483,6 +641,7 @@
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;
@ -506,20 +665,17 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = NO;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = AppIcon;
BUILD_LIBRARY_FOR_DISTRIBUTION = NO;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = P3ZD9TNSKZ;
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_BUNDLE_IDENTIFIER = nexus.federated.nexus;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@ -532,20 +688,17 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = NO;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = AppIcon;
BUILD_LIBRARY_FOR_DISTRIBUTION = NO;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = P3ZD9TNSKZ;
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_BUNDLE_IDENTIFIER = nexus.federated.nexus;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@ -556,6 +709,16 @@
/* 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 = (
@ -577,6 +740,20 @@
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

@ -5,6 +5,24 @@
<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"

View file

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 846 B

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 4 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 5 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Before After
Before After

View file

@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<false/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
@ -22,30 +22,40 @@
<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>LSSupportsOpeningDocumentsInPlace</key>
<true/>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>file</string>
<string>https</string>
<string>http</string>
<string>mailto</string>
<string>tel</string>
</array>
<key>UIFileSharingEnabled</key>
<true/>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to scan asset QR codes</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app doesn't use your microphone.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app doesn't use your photo library.</string>
<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>

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

@ -0,0 +1,22 @@
import "dart:convert";
import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/models/account_data.dart";
class AccountDataController extends Notifier<AccountData> {
@override
AccountData build() => .new();
void update(IMap<String, IMap<String, dynamic>> newAccountData) =>
state = .fromJson({
...json.decode(json.encode(state.toJson())),
...newAccountData
.map((key, value) => MapEntry(key, value["content"]))
.unlock,
});
static final provider = NotifierProvider<AccountDataController, AccountData>(
AccountDataController.new,
);
}

View file

@ -1,16 +0,0 @@
import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/models/account_data.dart";
class AccountDataController extends Notifier<IMap<String, AccountData>> {
@override
IMap<String, AccountData> build() => const IMap.empty();
void update(IMap<String, AccountData> newData) =>
state = IMap({...state.unlock, ...newData.unlock});
static final provider =
NotifierProvider<AccountDataController, IMap<String, AccountData>>(
AccountDataController.new,
);
}

View file

@ -0,0 +1,38 @@
import "package:file_selector/file_selector.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/client.dart";
import "package:nexus/controllers/rooms.dart";
import "package:nexus/models/content/content.dart";
import "package:nexus/models/content/message.dart";
import "package:path/path.dart";
class AttachmentController extends Notifier<(String, MessageContent?)?> {
final String roomId;
AttachmentController(this.roomId);
@override
Null build() => null;
Future<void> add(XFile file) async {
final filename = basename(file.path);
state = (filename, null);
final isEncrypted = ref.read(
RoomsController.provider.select(
(value) =>
value[roomId]?.state[EventType.encryption.type]?.isNotEmpty == true,
),
);
final content = await ref
.watch(ClientController.provider.notifier)
.uploadMedia(.new(path: file.path, encrypt: isEncrypted));
state = (filename, content);
}
static final provider = NotifierProvider.family
.autoDispose<AttachmentController, (String, MessageContent?)?, String>(
AttachmentController.new,
);
}

View file

@ -0,0 +1,30 @@
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/client.dart";
import "package:nexus/controllers/client_id.dart";
import "package:nexus/models/oauth_auth_code_response.dart";
import "package:nexus/models/requests/oauth/get_auth_url.dart";
class AuthUrlController extends AsyncNotifier<OAuthAuthCodeResponse> {
final Uri homeserver;
AuthUrlController(this.homeserver);
@override
Future<OAuthAuthCodeResponse> build() async => ref
.watch(ClientController.provider.notifier)
.getAuthUrl(
.new(
homeserverUrl: homeserver,
redirectUri: .new(scheme: "nexus.federated.nexus", path: "/"),
responseMode: .query,
scopes: .new([Scope.clientApi, Scope.device]),
clientId: await ref.watch(
ClientIdController.provider(homeserver).future,
),
),
);
static final provider = AsyncNotifierProvider.family
.autoDispose<AuthUrlController, OAuthAuthCodeResponse, Uri>(
AuthUrlController.new,
);
}

View file

@ -0,0 +1,30 @@
import "dart:async";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/user.dart";
import "package:nexus/models/content/membership.dart";
import "package:nexus/models/event.dart";
class AuthorController extends AsyncNotifier<MembershipContent> {
final Event event;
AuthorController(this.event);
@override
Future<MembershipContent> build() async {
final member = await ref.watch(
UserController.provider(
.new(roomId: event.roomId, userId: event.sender),
).future,
);
return .new(
status: member.status,
avatarUrl: event.pmp?.avatarUrl ?? member.avatarUrl,
displayName: event.pmp?.displayName ?? member.displayName,
);
}
static final provider =
AsyncNotifierProvider.family<AuthorController, MembershipContent, Event>(
AuthorController.new,
);
}

View file

@ -1,47 +0,0 @@
import "dart:async";
import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:flutter_chat_core/flutter_chat_core.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/client_state_controller.dart";
import "package:nexus/controllers/user_controller.dart";
import "package:nexus/helpers/extensions/get_localpart.dart";
import "package:nexus/models/membership.dart";
import "package:nexus/models/membership_status.dart";
class AuthorController extends AsyncNotifier<Membership> {
final Message message;
AuthorController(this.message);
@override
Future<Membership> build() async {
final member = await ref.watch(
UserController.provider(message.authorId).future,
);
final pmp = message.metadata?["pmp"] == null
? null
: Membership.fromContent(
IMap(message.metadata?["pmp"]),
message.authorId,
ref.watch(
ClientStateController.provider.select(
(value) => value?.homeserverUrl,
),
) ??
"",
);
return Membership(
status: member?.status ?? MembershipStatus.leave,
avatarUrl: pmp?.avatarUrl ?? member?.avatarUrl,
displayName:
pmp?.displayName ?? member?.displayName ?? message.authorId.localpart,
userId: message.authorId,
);
}
static final provider =
AsyncNotifierProvider.family<AuthorController, Membership, Message>(
AuthorController.new,
);
}

View file

@ -1,39 +1,46 @@
import "dart:developer";
import "dart:ffi";
import "dart:io";
import "dart:isolate";
import "package:collection/collection.dart";
import "dart:math";
import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:ffi/ffi.dart";
import "package:flutter/foundation.dart";
import "package:nexus/controllers/account_data_controller.dart";
import "package:nexus/controllers/client_state_controller.dart";
import "package:nexus/controllers/init_complete_controller.dart";
import "package:nexus/controllers/new_events_controller.dart";
import "package:nexus/controllers/rooms_controller.dart";
import "package:nexus/controllers/space_edges_controller.dart";
import "package:nexus/controllers/sync_status_controller.dart";
import "package:nexus/controllers/top_level_spaces_controller.dart";
import "package:nexus/controllers/account_data.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:nexus/models/client_state.dart";
import "package:nexus/models/content/message.dart";
import "package:nexus/models/event.dart";
import "package:nexus/models/oauth_auth_code_response.dart";
import "package:nexus/models/open_graph_data.dart";
import "package:nexus/models/paginate.dart";
import "package:nexus/models/requests/get_event_request.dart";
import "package:nexus/models/requests/get_related_events_request.dart";
import "package:nexus/models/requests/get_room_state_request.dart";
import "package:nexus/models/requests/join_room_request.dart";
import "package:nexus/models/requests/login_request.dart";
import "package:nexus/models/profile.dart";
import "package:nexus/models/requests/paginate_request.dart";
import "package:nexus/models/requests/redact_event_request.dart";
import "package:nexus/models/requests/report_request.dart";
import "package:nexus/models/requests/send_event_request.dart";
import "package:nexus/models/requests/send_message_request.dart";
import "package:nexus/models/requests/set_membership_request.dart";
import "package:nexus/models/requests/download_media.dart";
import "package:nexus/models/requests/get_event.dart";
import "package:nexus/models/requests/get_related_events.dart";
import "package:nexus/models/requests/get_room_state.dart";
import "package:nexus/models/requests/join_room.dart";
import "package:nexus/models/profile_response.dart";
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/register_client.dart";
import "package:nexus/models/requests/paginate.dart";
import "package:nexus/models/requests/redact_event.dart";
import "package:nexus/models/requests/report.dart";
import "package:nexus/models/requests/send_event.dart";
import "package:nexus/models/requests/send_message.dart";
import "package:nexus/models/requests/set_account_data.dart";
import "package:nexus/models/requests/set_membership.dart";
import "package:nexus/models/requests/set_state.dart";
import "package:nexus/models/requests/upload_media.dart";
import "package:nexus/models/room.dart";
import "package:nexus/models/room_summary.dart";
import "package:nexus/models/spec_versions_response.dart";
import "package:nexus/models/sync_data.dart";
import "package:nexus/models/sync_status.dart";
import "package:nexus/src/third_party/gomuks.g.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:path_provider/path_provider.dart";
@ -42,7 +49,7 @@ class ClientController extends AsyncNotifier<int> {
@override
Future<int> build() async {
final Pointer<Char> root;
if (Platform.isAndroid) {
if (Platform.isAndroid || Platform.isIOS) {
final dir = await getApplicationSupportDirectory();
root = "${dir.path}/gomuks".toNativeUtf8().cast();
} else {
@ -68,26 +75,27 @@ class ClientController extends AsyncNotifier<int> {
case "client_state":
ref
.watch(ClientStateController.provider.notifier)
.set(ClientState.fromJson(decodedMuksEvent));
.set(.fromJson(decodedMuksEvent));
break;
case "sync_status":
ref
.watch(SyncStatusController.provider.notifier)
.set(SyncStatus.fromJson(decodedMuksEvent));
.set(.fromJson(decodedMuksEvent));
break;
case "init_complete":
ref.watch(InitCompleteController.provider.notifier).complete();
break;
case "send_complete":
final event = Event.fromJson(decodedMuksEvent["event"]);
if (event.type == "m.room.message") {
ref
.watch(
NewEventsController.provider(event.roomId).notifier,
)
.add(IList([event]));
}
.watch(RoomsController.provider.notifier)
.update(
.new({
event.roomId: .new(events: .new({event.rowId: event})),
}),
.new(),
);
break;
case "sync_complete":
final syncData = SyncData.fromJson(decodedMuksEvent);
@ -127,9 +135,12 @@ class ClientController extends AsyncNotifier<int> {
}
debugPrint("Finished handling $muksEventType...");
} catch (error, stackTrace) {
debugger();
showError(error, stackTrace);
if (kDebugMode) {
debugPrintStack(stackTrace: stackTrace, label: error.toString());
rethrow;
} else {
showError(error, stackTrace);
}
}
});
@ -159,7 +170,10 @@ class ClientController extends AsyncNotifier<int> {
calloc.free(bufferPointer);
final json = response.buf.toJson();
if (json is String) throw json;
if (response.command.cast<Utf8>().toDartString() == "error") {
throw json;
}
return json;
}
@ -169,8 +183,27 @@ class ClientController extends AsyncNotifier<int> {
Future<Event> sendMessage(SendMessageRequest request) async =>
Event.fromJson(await _sendCommand("send_message", request.toJson()));
Future<Event> sendEvent(SendEventRequest request) async =>
Event.fromJson(await _sendCommand("send_event", request.toJson()));
Future<Event> sendEvent(SendEventRequest request) async {
final json = request.toJson();
final content = request.content.toJson();
return Event.fromJson(
await _sendCommand("send_event", {
...json,
"content": {
...content,
"m.relates_to": {
...((content["m.relates_to"] as Map<String, dynamic>?) ?? {}),
"event_id": request.relatesTo,
"rel_type": request.relationType,
},
},
}),
);
}
Future<String?> setState(SetStateRequest request) async =>
await _sendCommand("set_state", request.toJson());
Future<String?> verify(String recoveryKey) async {
try {
@ -181,15 +214,11 @@ class ClientController extends AsyncNotifier<int> {
}
}
Future<String> joinRoom(JoinRoomRequest request) async {
final response = await _sendCommand("join_room", request.toJson());
return response["room_id"];
}
Future<String> joinRoom(JoinRoomRequest request) async =>
(await _sendCommand("join_room", request.toJson()))["room_id"];
Future<String?> getAccessToken() async {
final response = await _sendCommand("get_account_info", {});
return response?["access_token"];
}
Future<RoomSummary> getRoomSummary(JoinRoomRequest request) async =>
.fromJson(await _sendCommand("get_room_summary", request.toJson()));
Future<void> leaveRoom(Room room) async {
if (room.metadata == null) return;
@ -206,9 +235,11 @@ class ClientController extends AsyncNotifier<int> {
(await _sendCommand("get_room_state", request.toJson())) as List?;
final response = await getState(request);
return (response ?? await getState(request.copyWith(refetch: true)) ?? [])
.map((event) => Event.fromJson(event))
.toIList();
return .new(
(response ?? await getState(request.copyWith(refetch: true)) ?? []).map(
(event) => .fromJson(event),
),
);
}
Future<IList<Event>?> getRelatedEvents(
@ -216,24 +247,22 @@ class ClientController extends AsyncNotifier<int> {
) async {
final response =
(await _sendCommand("get_related_events", request.toJson())) as List?;
return response?.map((event) => Event.fromJson(event)).toIList();
return .new(response?.map((event) => .fromJson(event)));
}
Future<Event?> getEvent(GetEventRequest request) async {
final event = request.room.events.firstWhereOrNull(
(event) => event.eventId == request.eventId,
);
if (event != null) return event;
final json = await _sendCommand("get_event", request.toJson());
return json == null ? null : Event.fromJson(json);
return json == null ? null : .fromJson(json);
}
Future<Paginate> paginate(PaginateRequest request) async =>
Paginate.fromJson(await _sendCommand("paginate", request.toJson()));
Future<OpenGraphData> getUrlPreview(Uri url) async =>
.fromJson(await _sendCommand("get_url_preview", {"url": url.toString()}));
Future<Profile> getProfile(String userId) async =>
Profile.fromJson(await _sendCommand("get_profile", {"user_id": userId}));
Future<Paginate> paginate(PaginateRequest request) async =>
.fromJson(await _sendCommand("paginate", request.toJson()));
Future<ProfileResponse> getProfile(String userId) async =>
.fromJson(await _sendCommand("get_profile", {"user_id": userId}));
Future<void> reportEvent(ReportRequest request) =>
_sendCommand("report_event", request.toJson());
@ -241,10 +270,20 @@ class ClientController extends AsyncNotifier<int> {
Future<void> setMembership(SetMembershipRequest request) =>
_sendCommand("set_membership", request.toJson());
Future<void> setAccountData(SetAccountDataRequest request) =>
_sendCommand("set_account_data", request.toJson());
Future<MessageContent> uploadMedia(UploadMediaRequest request) async =>
.fromJson(await _sendCommand("upload_media", request.toJson()));
Future<File> downloadMedia(DownloadMediaRequest request) async =>
.new((await _sendCommand("download_media", request.toJson()))["path"]);
Future<void> logout() => _sendCommand("logout");
Future<void> markRead(Room room) async {
final event = room.events.firstWhereOrNull(
(event) => event.rowId == room.timeline.last.eventRowId,
);
final eventRowId = room.timeline[room.timeline.keys.reduce(max)];
final event = eventRowId == null ? null : room.events[eventRowId];
if (event == null || room.metadata == null) return;
await _sendCommand("mark_read", {
@ -254,21 +293,29 @@ class ClientController extends AsyncNotifier<int> {
});
}
Future<String?> login(LoginRequest login) async {
try {
await _sendCommand("login", login.toJson());
return null;
} catch (error) {
return error.toString();
}
}
Future<String> registerClient(OAuthRegisterClientRequest request) async =>
(await _sendCommand(
"oauth_register_client",
request.toJson(),
))["client_id"];
Future<String?> discoverHomeserver(Uri homeserver) async {
Future<OAuthAuthCodeResponse> getAuthUrl(OAuthGetAuthUrl request) async =>
.fromJson(
await _sendCommand("oauth_get_authorization_url", request.toJson()),
);
Future<void> exchangeToken(OAuthExchangeTokenRequest request) async =>
await _sendCommand("oauth_exchange_token", request.toJson());
Future<SpecVersionsResponse> getSpecVersions() async =>
.fromJson(await _sendCommand("get_versions"));
Future<Uri?> discoverHomeserver(Uri homeserver) async {
try {
final response = await _sendCommand("discover_homeserver", {
"user_id": "@fake-user:${homeserver.host}",
"user_id": "@fake-user:${homeserver.authority}",
});
return response["m.homeserver"]?["base_url"];
return Uri.parse(response["m.homeserver"]?["base_url"]);
} catch (error) {
return null;
}

View file

@ -0,0 +1,33 @@
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/client.dart";
class ClientIdController extends AsyncNotifier<String> {
final Uri homeserver;
ClientIdController(this.homeserver);
@override
Future<String> build() => ref
.watch(ClientController.provider.notifier)
.registerClient(
.new(
clientName: "Nexus",
applicationType: .native,
grantTypes: .new([.authorizationCode, .refreshToken]),
responseTypes: .new([.code]),
logoUri: Uri.https(
"nexus.federated.nexus",
"raw/branch/main/assets/mobile.svg",
),
homeserverUrl: homeserver,
clientUri: Uri.https("nexus.federated.nexus"),
redirectUris: .new([
.new(scheme: "nexus.federated.nexus", path: "/"),
]),
),
);
static final provider =
AsyncNotifierProvider.family<ClientIdController, String, Uri>(
ClientIdController.new,
);
}

View file

@ -5,9 +5,7 @@ class ClientStateController extends Notifier<ClientState?> {
@override
Null build() => null;
void set(ClientState newState) {
state = newState;
}
void set(ClientState newState) => state = newState;
static final provider = NotifierProvider<ClientStateController, ClientState?>(
ClientStateController.new,

View file

@ -1,14 +0,0 @@
import "package:cross_cache/cross_cache.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
class CrossCacheController extends Notifier<CrossCache> {
static const String spaceKey = "space";
static const String roomKey = "room";
@override
CrossCache build() => CrossCache();
static final provider = NotifierProvider<CrossCacheController, CrossCache>(
CrossCacheController.new,
);
}

View file

@ -0,0 +1,84 @@
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

@ -0,0 +1,32 @@
import "package:collection/collection.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/client.dart";
import "package:nexus/controllers/rooms.dart";
import "package:nexus/models/event.dart";
import "package:nexus/models/requests/get_event.dart";
class EventController extends AsyncNotifier<Event?> {
final GetEventRequest request;
EventController(this.request);
@override
Future<Event?> build() async {
final room = ref.watch(
RoomsController.provider.select((value) => value[request.roomId]),
);
final event = room?.events.values.firstWhereOrNull(
(event) => event.eventId == request.eventId,
);
return event ??
await ref
.watch(ClientController.provider.notifier)
.getEvent(request)
.onError((_, _) => null);
}
static final provider = AsyncNotifierProvider.family
.autoDispose<EventController, Event?, GetEventRequest>(
EventController.new,
);
}

View file

@ -1,20 +0,0 @@
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/client_controller.dart";
import "package:nexus/models/event.dart";
import "package:nexus/models/requests/get_event_request.dart";
class EventController extends AsyncNotifier<Event?> {
final GetEventRequest request;
EventController(this.request);
@override
Future<Event?> build() async {
final client = ref.watch(ClientController.provider.notifier);
return await client.getEvent(request).onError((_, _) => null);
}
static final provider = AsyncNotifierProvider.family
.autoDispose<EventController, Event?, GetEventRequest>(
EventController.new,
);
}

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