diff --git a/.github/workflows/flatpak.yml b/.github/workflows/flatpak.yml index d280bc6..5e693f0 100644 --- a/.github/workflows/flatpak.yml +++ b/.github/workflows/flatpak.yml @@ -34,4 +34,4 @@ jobs: uses: actions/upload-artifact@v6 with: name: flatpak-${{ matrix.arch }} - path: result/nexus.federated.nexus.flatpak \ No newline at end of file + path: result/nexus.federated.Nexus.flatpak \ No newline at end of file diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index d9c8dd5..c010486 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -19,7 +19,7 @@ jobs: - name: Set up Flutter uses: subosito/flutter-action@v2 with: - flutter-version: 3.44.4 + flutter-version: 3.41.9 - name: Set up Go uses: actions/setup-go@v6 @@ -52,6 +52,12 @@ jobs: cp /mingw64/bin/libwinpthread-1.dll build/windows/x64/runner/Release/ cp /mingw64/bin/libstdc++-6.dll build/windows/x64/runner/Release/ + - name: Upload exe zip + uses: actions/upload-artifact@v6 + with: + name: windows-portable + path: build/windows/x64/runner/Release/ + - name: Install Inno Setup run: choco install innosetup -y diff --git a/.vscode/settings.json b/.vscode/settings.json index 2ff533e..068916b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,7 +10,6 @@ "msgtype", "muks", "prefs", - "unban", - "unredact" + "unban" ] } diff --git a/README.md b/README.md index 4233429..bb61124 100644 --- a/README.md +++ b/README.md @@ -19,10 +19,13 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend. - [x] Linux - [x] Windows - [x] Android - - [ ] MacOS ([#50](https://git.federated.nexus/Nexus/nexus/issues/50)) + - [ ] MacOS - [ ] iOS - [ ] Web (may not be possible) -- [x] Login (via OAuth) +- [x] Login + - [x] Username / password auth + - [ ] OAuth / OIDC + - [x] Improve initial sync experience - [x] Rooms / Spaces - [x] Displaying and choosing - [x] Reading, showing unread @@ -86,12 +89,14 @@ 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 -- [x] Pins - - [x] Displaying - - [x] Pinning/Unpinning +- [ ] Pins + - [ ] Displaying + - [ ] Creating - [ ] Threads - [x] Profile popouts - [x] Working actions @@ -108,18 +113,44 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend. - [ ] 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 -- [x] Settings +- [ ] Settings ([#37](https://git.federated.nexus/Nexus/nexus/issues/37)) + - [ ] Matrix: URIs vs Matrix.to links + - [ ] Light/Dark mode + - [ ] Remote Gomuks instance + - [ ] SSD or CSD + - [ ] Align your message bubbles to left or right + - [ ] Show media by default + - [ ] Dynamic Theming + - [ ] Personas + - [ ] Setting per-message profiles for users (MSC4461) + - [ ] Explain how to send messages using a certain PMP + - [ ] 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 ## 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 EXE](https://nightly.link/Henry-Hiles/nexus/workflows/windows/main/windows-installer.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) - 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/Nexus/nexus` + ## Build it yourself ### Prerequisites diff --git a/android/app/build.gradle b/android/app/build.gradle index 2e7fb67..fd51ea0 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -30,7 +30,7 @@ if (keystorePropertiesFile.exists()) { } android { - namespace = "nexus.federated.nexus" + namespace = "nexus.federated.Nexus" ndkVersion = flutter.ndkVersion compileSdk = 34 @@ -40,11 +40,12 @@ 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() diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index a0a0bcc..666977e 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -22,14 +22,6 @@ android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize"> - - - - - - - - main(List args) => build(args, (input, output) async { if (!input.config.buildCodeAssets) return; @@ -12,24 +10,14 @@ Future main(List args) => build(args, (input, output) async { final targetArch = codeConfig.targetArchitecture; String libFileName; - Map extraEnv = {}; + Map env = {}; switch (targetOS) { case OS.linux: libFileName = "libgomuks.so"; break; case OS.macOS: libFileName = "libgomuks.dylib"; - extraEnv = { - "SDKROOT": await getXCodeSDK(), - "CGO_ENABLED": "1", - "GOARCH": switch (targetArch) { - Architecture.arm64 => "arm64", - Architecture.x64 => "amd64", - _ => throw UnsupportedError( - "Unsupported macOS architecture: $targetArch", - ), - }, - }; + env = {"SDKROOT": await getXCodeSDK()}; break; case OS.windows: libFileName = "libgomuks.dll"; @@ -39,48 +27,23 @@ Future main(List args) => build(args, (input, output) async { final targetNdkApi = codeConfig.android.targetNdkApi; - Future 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.", ); } - // 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 hostTag = _ndkHostTag(); + final (goArch, ccTriple) = _androidArch(targetArch); final cc = "$ndkHome/toolchains/llvm/prebuilt/$hostTag/bin/$ccTriple$targetNdkApi-clang"; - extraEnv = {"GOOS": "android", "CC": cc}; + env = {"CGO_ENABLED": "1", "GOOS": "android", "GOARCH": goArch, "CC": cc}; break; default: throw UnsupportedError("Unsupported OS: $targetOS"); @@ -93,10 +56,10 @@ Future main(List 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 = [ "sqlite_fts5", "goolm", - // goheif/dav1d is not supported on Android, would need to be fixed upstream if (targetOS == OS.android) "noheic", ].join(","); print( @@ -106,17 +69,7 @@ Future main(List args) => build(args, (input, output) async { "go", ["build", "-tags", tags, "-o", libFile.path, "-buildmode=c-shared"], workingDirectory: gomuksBuildDir.resolve("pkg/ffi/").toFilePath(), - 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, - }, + environment: env.isNotEmpty ? env : null, ); if (result.exitCode != 0) { @@ -141,3 +94,43 @@ Future main(List args) => build(args, (input, output) async { ..dependencies.add(gomuksBuildDir); print("Done!"); }); + +Future _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"); + } +} diff --git a/ios/.gitignore b/ios/.gitignore new file mode 100644 index 0000000..7a7f987 --- /dev/null +++ b/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..6f637da --- /dev/null +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + nexus.federated.Nexus + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 15.5 + + diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..ec97fc6 --- /dev/null +++ b/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..c4855bf --- /dev/null +++ b/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/ios/Podfile b/ios/Podfile new file mode 100644 index 0000000..9c6fa1f --- /dev/null +++ b/ios/Podfile @@ -0,0 +1,55 @@ +platform :ios, 16 +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) +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' + 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 ################### diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..44f96da --- /dev/null +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,582 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* 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 */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 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 PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 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 = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 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 = ""; }; + 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 = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3965FF1D950A2B2E051F6219 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 7226BEAE3D25CA120A434CC9 /* Pods */ = { + isa = PBXGroup; + children = ( + DFFCB3F45BF495277AE4D3EE /* Pods-Runner.debug.xcconfig */, + CA00A9F43BB1AB7D908E180A /* Pods-Runner.release.xcconfig */, + 6A2D47C573B065AD356C7183 /* Pods-Runner.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 7226BEAE3D25CA120A434CC9 /* Pods */, + E396C277672D83FCFDB51CCB /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; + E396C277672D83FCFDB51CCB /* Frameworks */ = { + isa = PBXGroup; + children = ( + 5E598BBDD2ED0112E344B590 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + A3B5CB395514F4BF9CDF304E /* [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 */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + A3B5CB395514F4BF9CDF304E /* [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; + }; + 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 */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + 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; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = nexus.federated.Nexus; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + 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; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = nexus.federated.Nexus; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + 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; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = nexus.federated.Nexus; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..e3773d4 --- /dev/null +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..21a3cc1 --- /dev/null +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000..b636303 --- /dev/null +++ b/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@main +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d0d98aa --- /dev/null +++ b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1 @@ +{"images":[{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@3x.png","scale":"3x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@1x.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@1x.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"Icon-App-83.5x83.5@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"Icon-App-1024x1024@1x.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}} \ No newline at end of file diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000..0d531c4 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000..da4acee Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000..a3cfb1d Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000..adbdcd5 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000..fee4302 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000..4d21624 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 0000000..3e7a859 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 0000000..a3cfb1d Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000..518d397 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000..25f2b47 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png new file mode 100644 index 0000000..8f79bb9 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png new file mode 100644 index 0000000..c48dec6 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png new file mode 100644 index 0000000..99d44e8 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png new file mode 100644 index 0000000..6f987f0 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000..25f2b47 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000..fcf969a Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png new file mode 100644 index 0000000..16dea29 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png new file mode 100644 index 0000000..3366fb5 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000..81c9cb8 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 0000000..efda04b Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000..3774574 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000..0bedcf2 --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f2e259c --- /dev/null +++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist new file mode 100644 index 0000000..b89f32a --- /dev/null +++ b/ios/Runner/Info.plist @@ -0,0 +1,69 @@ + + + + + CADisableMinimumFrameDurationOnPhone + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Nexus + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + nexus + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSSupportsOpeningDocumentsInPlace + + LSApplicationQueriesSchemes + + file + https + http + mailto + tel + + UIFileSharingEnabled + + ITSAppUsesNonExemptEncryption + + LSRequiresIPhoneOS + + NSCameraUsageDescription + This app needs camera access to scan asset QR codes + NSMicrophoneUsageDescription + This app doesn't use your microphone. + NSPhotoLibraryUsageDescription + This app doesn't use your photo library. + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/lib/controllers/account_data.dart b/lib/controllers/account_data_controller.dart similarity index 100% rename from lib/controllers/account_data.dart rename to lib/controllers/account_data_controller.dart diff --git a/lib/controllers/auth_url.dart b/lib/controllers/auth_url.dart deleted file mode 100644 index 1fe0695..0000000 --- a/lib/controllers/auth_url.dart +++ /dev/null @@ -1,32 +0,0 @@ -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 { - final Uri homeserver; - AuthUrlController(this.homeserver); - - @override - Future 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< - AuthUrlController, - OAuthAuthCodeResponse, - Uri - >(AuthUrlController.new); -} diff --git a/lib/controllers/author.dart b/lib/controllers/author_controller.dart similarity index 93% rename from lib/controllers/author.dart rename to lib/controllers/author_controller.dart index bbbe068..77202ca 100644 --- a/lib/controllers/author.dart +++ b/lib/controllers/author_controller.dart @@ -1,6 +1,6 @@ import "dart:async"; import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/user.dart"; +import "package:nexus/controllers/user_controller.dart"; import "package:nexus/models/content/membership.dart"; import "package:nexus/models/event.dart"; diff --git a/lib/controllers/client.dart b/lib/controllers/client_controller.dart similarity index 76% rename from lib/controllers/client.dart rename to lib/controllers/client_controller.dart index ef0f372..fb57735 100644 --- a/lib/controllers/client.dart +++ b/lib/controllers/client_controller.dart @@ -5,33 +5,29 @@ 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.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/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/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/helpers/extensions/gomuks_buffer.dart"; import "package:nexus/main.dart"; import "package:nexus/models/event.dart"; -import "package:nexus/models/oauth_auth_code_response.dart"; import "package:nexus/models/paginate.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/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/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_membership.dart"; -import "package:nexus/models/requests/set_state.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/room.dart"; import "package:nexus/models/sync_data.dart"; import "package:nexus/src/third_party/gomuks.g.dart"; @@ -163,10 +159,7 @@ class ClientController extends AsyncNotifier { calloc.free(bufferPointer); final json = response.buf.toJson(); - if (response.command.cast().toDartString() == "error") { - throw json; - } - + if (json is String) throw json; return json; } @@ -176,27 +169,8 @@ class ClientController extends AsyncNotifier { Future sendMessage(SendMessageRequest request) async => Event.fromJson(await _sendCommand("send_message", request.toJson())); - Future 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?) ?? {}), - "event_id": request.relatesTo, - "rel_type": request.relationType, - }, - }, - }), - ); - } - - Future setState(SetStateRequest request) async => - await _sendCommand("set_state", request.toJson()); + Future sendEvent(SendEventRequest request) async => + Event.fromJson(await _sendCommand("send_event", request.toJson())); Future verify(String recoveryKey) async { try { @@ -266,8 +240,6 @@ class ClientController extends AsyncNotifier { Future setMembership(SetMembershipRequest request) => _sendCommand("set_membership", request.toJson()); - Future logout() => _sendCommand("logout"); - Future markRead(Room room) async { final eventRowId = room.timeline[room.timeline.keys.reduce(max)]; final event = eventRowId == null ? null : room.events[eventRowId]; @@ -280,19 +252,14 @@ class ClientController extends AsyncNotifier { }); } - Future registerClient(OAuthRegisterClientRequest request) async => - (await _sendCommand( - "oauth_register_client", - request.toJson(), - ))["client_id"]; - - Future getAuthUrl(OAuthGetAuthUrl request) async => - .fromJson( - await _sendCommand("oauth_get_authorization_url", request.toJson()), - ); - - Future exchangeToken(OAuthExchangeTokenRequest request) async => - await _sendCommand("oauth_exchange_token", request.toJson()); + Future login(LoginRequest login) async { + try { + await _sendCommand("login", login.toJson()); + return null; + } catch (error) { + return error.toString(); + } + } Future discoverHomeserver(Uri homeserver) async { try { diff --git a/lib/controllers/client_id.dart b/lib/controllers/client_id.dart deleted file mode 100644 index ac8f5cb..0000000 --- a/lib/controllers/client_id.dart +++ /dev/null @@ -1,33 +0,0 @@ -import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/client.dart"; - -class ClientIdController extends AsyncNotifier { - final Uri homeserver; - ClientIdController(this.homeserver); - - @override - Future 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.new, - ); -} diff --git a/lib/controllers/client_state.dart b/lib/controllers/client_state_controller.dart similarity index 100% rename from lib/controllers/client_state.dart rename to lib/controllers/client_state_controller.dart diff --git a/lib/controllers/cross_cache.dart b/lib/controllers/cross_cache_controller.dart similarity index 100% rename from lib/controllers/cross_cache.dart rename to lib/controllers/cross_cache_controller.dart diff --git a/lib/controllers/emoji.dart b/lib/controllers/emoji_controller.dart similarity index 100% rename from lib/controllers/emoji.dart rename to lib/controllers/emoji_controller.dart diff --git a/lib/controllers/event.dart b/lib/controllers/event_controller.dart similarity index 82% rename from lib/controllers/event.dart rename to lib/controllers/event_controller.dart index b7b417a..94992ca 100644 --- a/lib/controllers/event.dart +++ b/lib/controllers/event_controller.dart @@ -1,9 +1,9 @@ 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/controllers/client_controller.dart"; +import "package:nexus/controllers/rooms_controller.dart"; import "package:nexus/models/event.dart"; -import "package:nexus/models/requests/get_event.dart"; +import "package:nexus/models/requests/get_event_request.dart"; class EventController extends AsyncNotifier { final GetEventRequest request; diff --git a/lib/controllers/header.dart b/lib/controllers/header_controller.dart similarity index 82% rename from lib/controllers/header.dart rename to lib/controllers/header_controller.dart index 3e173ad..295cf04 100644 --- a/lib/controllers/header.dart +++ b/lib/controllers/header_controller.dart @@ -1,6 +1,6 @@ import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/client.dart"; -import "package:nexus/controllers/client_state.dart"; +import "package:nexus/controllers/client_controller.dart"; +import "package:nexus/controllers/client_state_controller.dart"; class HeaderController extends AsyncNotifier> { @override diff --git a/lib/controllers/init_complete.dart b/lib/controllers/init_complete_controller.dart similarity index 100% rename from lib/controllers/init_complete.dart rename to lib/controllers/init_complete_controller.dart diff --git a/lib/controllers/key.dart b/lib/controllers/key_controller.dart similarity index 91% rename from lib/controllers/key.dart rename to lib/controllers/key_controller.dart index eff3ab2..59d49ca 100644 --- a/lib/controllers/key.dart +++ b/lib/controllers/key_controller.dart @@ -1,5 +1,5 @@ import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/shared_prefs.dart"; +import "package:nexus/controllers/shared_prefs_controller.dart"; class KeyController extends Notifier { final String key; diff --git a/lib/controllers/members_by_status.dart b/lib/controllers/members_by_status_controller.dart similarity index 88% rename from lib/controllers/members_by_status.dart rename to lib/controllers/members_by_status_controller.dart index 613f7b1..2b49903 100644 --- a/lib/controllers/members_by_status.dart +++ b/lib/controllers/members_by_status_controller.dart @@ -1,7 +1,7 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/members.dart"; -import "package:nexus/models/configs/members_by_status.dart"; +import "package:nexus/controllers/members_controller.dart"; +import "package:nexus/models/configs/members_by_status_config.dart"; import "package:nexus/models/content/membership.dart"; import "package:nexus/models/event.dart"; diff --git a/lib/controllers/members.dart b/lib/controllers/members_controller.dart similarity index 87% rename from lib/controllers/members.dart rename to lib/controllers/members_controller.dart index 8566b40..57fc5be 100644 --- a/lib/controllers/members.dart +++ b/lib/controllers/members_controller.dart @@ -1,10 +1,10 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/client.dart"; -import "package:nexus/controllers/rooms.dart"; +import "package:nexus/controllers/client_controller.dart"; +import "package:nexus/controllers/rooms_controller.dart"; import "package:nexus/models/content/content.dart"; import "package:nexus/models/event.dart"; -import "package:nexus/models/requests/get_room_state.dart"; +import "package:nexus/models/requests/get_room_state_request.dart"; class MembersController extends AsyncNotifier> { final String roomId; diff --git a/lib/controllers/members_grouped.dart b/lib/controllers/members_grouped_controller.dart similarity index 88% rename from lib/controllers/members_grouped.dart rename to lib/controllers/members_grouped_controller.dart index 6f41e9d..85fddb0 100644 --- a/lib/controllers/members_grouped.dart +++ b/lib/controllers/members_grouped_controller.dart @@ -1,9 +1,9 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/members_by_status.dart"; -import "package:nexus/controllers/room_creators.dart"; -import "package:nexus/controllers/rooms.dart"; -import "package:nexus/models/configs/members_by_status.dart"; +import "package:nexus/controllers/members_by_status_controller.dart"; +import "package:nexus/controllers/room_creators_controller.dart"; +import "package:nexus/controllers/rooms_controller.dart"; +import "package:nexus/models/configs/members_by_status_config.dart"; import "package:nexus/models/content/content.dart"; import "package:nexus/models/content/power_levels.dart"; import "package:nexus/models/event.dart"; diff --git a/lib/controllers/multi_provider.dart b/lib/controllers/multi_provider_controller.dart similarity index 100% rename from lib/controllers/multi_provider.dart rename to lib/controllers/multi_provider_controller.dart diff --git a/lib/controllers/pinned_events.dart b/lib/controllers/pinned_events.dart deleted file mode 100644 index 914a301..0000000 --- a/lib/controllers/pinned_events.dart +++ /dev/null @@ -1,30 +0,0 @@ -import "package:fast_immutable_collections/fast_immutable_collections.dart"; -import "package:hooks_riverpod/hooks_riverpod.dart"; -import "package:nexus/controllers/event.dart"; -import "package:nexus/controllers/pinned_ids.dart"; -import "package:nexus/models/event.dart"; - -class PinnedEventsController extends AsyncNotifier> { - final String roomId; - PinnedEventsController(this.roomId); - - @override - Future> build() async { - final pinIds = ref.watch(PinnedIdsController.provider(roomId)); - - return (await Future.wait( - pinIds.map( - (eventId) => ref.watch( - EventController.provider( - .new(eventId: eventId, roomId: roomId), - ).future, - ), - ), - )).nonNulls.toIList(); - } - - static final provider = AsyncNotifierProvider.family - .autoDispose, String>( - PinnedEventsController.new, - ); -} diff --git a/lib/controllers/pinned_ids.dart b/lib/controllers/pinned_ids.dart deleted file mode 100644 index 3d1e5da..0000000 --- a/lib/controllers/pinned_ids.dart +++ /dev/null @@ -1,53 +0,0 @@ -import "package:fast_immutable_collections/fast_immutable_collections.dart"; -import "package:hooks_riverpod/hooks_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/pinned_events.dart"; - -class PinnedIdsController extends Notifier> { - final String roomId; - PinnedIdsController(this.roomId); - - @override - IList build() { - final room = ref.watch( - RoomsController.provider.select((rooms) => rooms[roomId]), - ); - - if (room == null) return .new(); - - final pinnedRowId = room.state[EventType.pinnedEvents.type]?[""]; - final pinnedStateEvent = pinnedRowId == null - ? null - : room.events[pinnedRowId]; - - if (pinnedStateEvent?.content case PinnedEventsContent content) { - return content.pinnedEvents; - } - - return .new(); - } - - Future addPin(String eventId) async => - setPinned(.new(pinnedEvents: .new(state.add(eventId)))); - - Future removePin(String eventId) async => - setPinned(.new(pinnedEvents: .new(state.remove(eventId)))); - - Future setPinned(PinnedEventsContent content) => ref - .read(ClientController.provider.notifier) - .setState( - .new( - roomId: roomId, - type: EventType.pinnedEvents.type, - stateKey: "", - content: content, - ), - ); - - static final provider = NotifierProvider.family - .autoDispose, String>( - PinnedIdsController.new, - ); -} diff --git a/lib/controllers/power_level.dart b/lib/controllers/power_level_controller.dart similarity index 90% rename from lib/controllers/power_level.dart rename to lib/controllers/power_level_controller.dart index 2f0c72e..94e6bd2 100644 --- a/lib/controllers/power_level.dart +++ b/lib/controllers/power_level_controller.dart @@ -1,8 +1,8 @@ import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/client_state.dart"; -import "package:nexus/controllers/room_creators.dart"; -import "package:nexus/controllers/rooms.dart"; -import "package:nexus/models/configs/power_level.dart"; +import "package:nexus/controllers/client_state_controller.dart"; +import "package:nexus/controllers/room_creators_controller.dart"; +import "package:nexus/controllers/rooms_controller.dart"; +import "package:nexus/models/configs/power_level_config.dart"; import "package:nexus/models/content/content.dart"; import "package:nexus/models/content/power_levels.dart"; diff --git a/lib/controllers/profile.dart b/lib/controllers/profile_controller.dart similarity index 89% rename from lib/controllers/profile.dart rename to lib/controllers/profile_controller.dart index 32525a3..9aa0e09 100644 --- a/lib/controllers/profile.dart +++ b/lib/controllers/profile_controller.dart @@ -1,5 +1,5 @@ import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/client.dart"; +import "package:nexus/controllers/client_controller.dart"; import "package:nexus/models/profile.dart"; class ProfileController extends AsyncNotifier { diff --git a/lib/controllers/reactions.dart b/lib/controllers/reactions_controller.dart similarity index 90% rename from lib/controllers/reactions.dart rename to lib/controllers/reactions_controller.dart index 615a59d..db9af9b 100644 --- a/lib/controllers/reactions.dart +++ b/lib/controllers/reactions_controller.dart @@ -1,8 +1,8 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/client.dart"; -import "package:nexus/controllers/rooms.dart"; -import "package:nexus/models/configs/reactions.dart"; +import "package:nexus/controllers/client_controller.dart"; +import "package:nexus/controllers/rooms_controller.dart"; +import "package:nexus/models/configs/reactions_config.dart"; import "package:nexus/models/content/reaction.dart"; class ReactionsController extends AsyncNotifier>> { diff --git a/lib/controllers/room_chat.dart b/lib/controllers/room_chat_controller.dart similarity index 93% rename from lib/controllers/room_chat.dart rename to lib/controllers/room_chat_controller.dart index b79010c..b93885e 100644 --- a/lib/controllers/room_chat.dart +++ b/lib/controllers/room_chat_controller.dart @@ -4,14 +4,14 @@ import "package:collection/collection.dart"; import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:fluttertagger/fluttertagger.dart"; -import "package:nexus/controllers/client.dart"; -import "package:nexus/controllers/rooms.dart"; +import "package:nexus/controllers/client_controller.dart"; +import "package:nexus/controllers/rooms_controller.dart"; import "package:nexus/models/content/content.dart"; import "package:nexus/models/content/reaction.dart"; import "package:nexus/models/event.dart"; -import "package:nexus/models/requests/redact_event.dart"; +import "package:nexus/models/requests/redact_event_request.dart"; import "package:nexus/models/relation_type.dart"; -import "package:nexus/models/requests/send_message.dart"; +import "package:nexus/models/requests/send_message_request.dart"; import "package:nexus/models/room.dart"; class RoomChatController extends AsyncNotifier?> { @@ -205,11 +205,15 @@ class RoomChatController extends AsyncNotifier?> { .new( roomId: roomId, type: EventType.reaction.type, - content: ReactionContent(key: reaction), + content: { + "m.relates_to": { + "event_id": event.eventId, + "rel_type": "m.annotation", + "key": reaction, + }, + }, synchronous: true, disableEncryption: true, - relatesTo: event.eventId, - relationType: "m.annotation", ), ); } diff --git a/lib/controllers/room_creators.dart b/lib/controllers/room_creators_controller.dart similarity index 100% rename from lib/controllers/room_creators.dart rename to lib/controllers/room_creators_controller.dart diff --git a/lib/controllers/rooms.dart b/lib/controllers/rooms_controller.dart similarity index 100% rename from lib/controllers/rooms.dart rename to lib/controllers/rooms_controller.dart diff --git a/lib/controllers/settings.dart b/lib/controllers/settings.dart deleted file mode 100644 index 572d2e9..0000000 --- a/lib/controllers/settings.dart +++ /dev/null @@ -1,27 +0,0 @@ -import "dart:convert"; -import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/settings_file.dart"; -import "package:nexus/models/settings.dart"; - -class SettingsController extends AsyncNotifier { - @override - Future build() async { - final file = await ref.watch(SettingsFileController.provider.future); - - try { - return Settings.fromJson(json.decode(await file.readAsString())); - } catch (_) { - return Settings(); - } - } - - Future set(Settings settings) async { - state = AsyncData(settings); - final file = await ref.watch(SettingsFileController.provider.future); - await file.writeAsString(json.encode(settings.toJson())); - } - - static final provider = AsyncNotifierProvider( - SettingsController.new, - ); -} diff --git a/lib/controllers/settings_file.dart b/lib/controllers/settings_file.dart deleted file mode 100644 index 2c050bc..0000000 --- a/lib/controllers/settings_file.dart +++ /dev/null @@ -1,23 +0,0 @@ -import "dart:io"; -import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:path/path.dart"; -import "package:path_provider/path_provider.dart"; -import "package:xdg_directories/xdg_directories.dart"; - -class SettingsFileController extends AsyncNotifier { - @override - Future build() async { - final directory = await switch (Platform.isLinux) { - true => Directory( - join(configHome.absolute.path, "nexus"), - ).create(recursive: true), - false => getApplicationSupportDirectory(), - }; - - return File(join(directory.absolute.path, "config.json")); - } - - static final provider = AsyncNotifierProvider( - SettingsFileController.new, - ); -} diff --git a/lib/controllers/settings_sections.dart b/lib/controllers/settings_sections.dart deleted file mode 100644 index 1704ed0..0000000 --- a/lib/controllers/settings_sections.dart +++ /dev/null @@ -1,135 +0,0 @@ -import "dart:io"; -import "package:fast_immutable_collections/fast_immutable_collections.dart"; -import "package:flutter/material.dart"; -import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:intl/intl.dart"; -import "package:m3e_buttons/m3e_buttons.dart"; -import "package:nexus/controllers/client.dart"; -import "package:nexus/controllers/settings.dart"; -import "package:nexus/models/settings_category.dart"; -import "package:nexus/main.dart"; -import "package:nexus/widgets/settings/dialog_list_tile.dart"; - -class SettingsSectionsController - extends AsyncNotifier>> { - @override - Future>> build() async { - final settings = await ref.watch(SettingsController.provider.future); - - return .new({ - "General": .new([ - .new( - title: "Appearance", - icon: Icons.brush, - settings: .new([ - .new( - title: "Theme", - description: - "Toggle between Light Mode, Dark Mode, and System themes.", - icon: Icons.contrast, - builder: (title, description, icon) => DialogListTile( - icon: Icon(icon), - title: title, - subtitle: Text(description), - initialValue: settings.theme, - options: ThemeMode.values, - getName: (option) => toBeginningOfSentenceCase(option.name), - onChanged: (value) => ref - .watch(SettingsController.provider.notifier) - .set(settings.copyWith(theme: value)) - .onError(showError), - ), - ), - .new( - title: "Use Dynamic Theme", - icon: Icons.palette, - description: - "Toggle on or off Dynamic Theme. Only available on Android, Linux, Windows, or MacOS.", - builder: (title, description, icon) => SwitchListTile( - title: Text(title), - subtitle: Text(description), - secondary: Icon(icon), - value: settings.useDynamicTheming, - onChanged: - (Platform.isAndroid || - Platform.isLinux || - Platform.isMacOS || - Platform.isWindows) - ? (value) => ref - .watch(SettingsController.provider.notifier) - .set(settings.copyWith(useDynamicTheming: value)) - .onError(showError) - : null, - ), - ), - ]), - ), - .new( - title: "Behavior", - icon: Icons.psychology, - settings: .new([ - .new( - title: "Linux Mobile Mode", - description: - "Enables some fixes for Linux mobile, e.g. disabling dragging appbar for moving window.", - icon: Icons.construction, - builder: (title, description, icon) => SwitchListTile( - title: Text(title), - subtitle: Text(description), - secondary: Icon(icon), - value: settings.linuxMobileMode, - onChanged: Platform.isLinux - ? (value) => ref - .watch(SettingsController.provider.notifier) - .set(settings.copyWith(linuxMobileMode: value)) - .onError(showError) - : null, - ), - ), - ]), - ), - ]), - "Account": .new([ - .new(title: "Profile", icon: Icons.person, settings: .new([])), - .new( - title: "Other", - icon: Icons.key, - settings: .new([ - .new( - title: "Log Out", - description: - "Log out of your account, returning you to the login page.", - builder: (title, description, icon) => Builder( - builder: (context) { - final colorScheme = Theme.of(context).colorScheme; - return M3EButton.icon( - onPressed: () async { - await ref - .watch(ClientController.provider.notifier) - .logout(); - if (context.mounted) Navigator.of(context).pop(); - }, - label: Text(title), - icon: Icon(icon), - tooltip: description, - decoration: .styleFrom( - backgroundColor: colorScheme.errorContainer, - foregroundColor: colorScheme.onErrorContainer, - ), - ); - }, - ), - icon: Icons.logout, - ), - ]), - ), - ]), - }); - } - - static final provider = - AsyncNotifierProvider.autoDispose< - SettingsSectionsController, - IMap> - >(SettingsSectionsController.new); -} diff --git a/lib/controllers/shared_prefs.dart b/lib/controllers/shared_prefs_controller.dart similarity index 100% rename from lib/controllers/shared_prefs.dart rename to lib/controllers/shared_prefs_controller.dart diff --git a/lib/controllers/space_edges.dart b/lib/controllers/space_edges_controller.dart similarity index 100% rename from lib/controllers/space_edges.dart rename to lib/controllers/space_edges_controller.dart diff --git a/lib/controllers/spaces.dart b/lib/controllers/spaces_controller.dart similarity index 94% rename from lib/controllers/spaces.dart rename to lib/controllers/spaces_controller.dart index 24113ad..03a6b8a 100644 --- a/lib/controllers/spaces.dart +++ b/lib/controllers/spaces_controller.dart @@ -2,10 +2,10 @@ import "package:collection/collection.dart"; import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/account_data.dart"; -import "package:nexus/controllers/rooms.dart"; -import "package:nexus/controllers/top_level_spaces.dart"; -import "package:nexus/controllers/space_edges.dart"; +import "package:nexus/controllers/account_data_controller.dart"; +import "package:nexus/controllers/rooms_controller.dart"; +import "package:nexus/controllers/top_level_spaces_controller.dart"; +import "package:nexus/controllers/space_edges_controller.dart"; import "package:nexus/models/room.dart"; import "package:nexus/models/space.dart"; import "package:nexus/models/subspace.dart"; diff --git a/lib/controllers/sync_status.dart b/lib/controllers/sync_status_controller.dart similarity index 100% rename from lib/controllers/sync_status.dart rename to lib/controllers/sync_status_controller.dart diff --git a/lib/controllers/top_level_spaces.dart b/lib/controllers/top_level_spaces_controller.dart similarity index 100% rename from lib/controllers/top_level_spaces.dart rename to lib/controllers/top_level_spaces_controller.dart diff --git a/lib/controllers/url_preview.dart b/lib/controllers/url_preview_controller.dart similarity index 92% rename from lib/controllers/url_preview.dart rename to lib/controllers/url_preview_controller.dart index 0f807bb..3bdaae7 100644 --- a/lib/controllers/url_preview.dart +++ b/lib/controllers/url_preview_controller.dart @@ -1,8 +1,8 @@ import "dart:convert"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:http/http.dart"; -import "package:nexus/controllers/client_state.dart"; -import "package:nexus/controllers/header.dart"; +import "package:nexus/controllers/client_state_controller.dart"; +import "package:nexus/controllers/header_controller.dart"; import "package:nexus/helpers/extensions/mxc_to_https.dart"; import "package:nexus/models/open_graph_data.dart"; diff --git a/lib/controllers/user.dart b/lib/controllers/user_controller.dart similarity index 87% rename from lib/controllers/user.dart rename to lib/controllers/user_controller.dart index b366c88..7d20e90 100644 --- a/lib/controllers/user.dart +++ b/lib/controllers/user_controller.dart @@ -1,10 +1,10 @@ import "dart:async"; import "package:collection/collection.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/members.dart"; -import "package:nexus/controllers/profile.dart"; +import "package:nexus/controllers/members_controller.dart"; +import "package:nexus/controllers/profile_controller.dart"; import "package:nexus/helpers/extensions/get_localpart.dart"; -import "package:nexus/models/configs/user.dart"; +import "package:nexus/models/configs/user_config.dart"; import "package:nexus/models/content/membership.dart"; class UserController extends AsyncNotifier { diff --git a/lib/controllers/via.dart b/lib/controllers/via_controller.dart similarity index 96% rename from lib/controllers/via.dart rename to lib/controllers/via_controller.dart index d9227ba..0b5890a 100644 --- a/lib/controllers/via.dart +++ b/lib/controllers/via_controller.dart @@ -1,7 +1,7 @@ import "package:collection/collection.dart"; import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/client_state.dart"; +import "package:nexus/controllers/client_state_controller.dart"; import "package:nexus/models/content/content.dart"; import "package:nexus/models/content/membership.dart"; import "package:nexus/models/content/power_levels.dart"; diff --git a/lib/helpers/extensions/get_headers.dart b/lib/helpers/extensions/get_headers.dart index 0fcec48..e1bb5f3 100644 --- a/lib/helpers/extensions/get_headers.dart +++ b/lib/helpers/extensions/get_headers.dart @@ -1,5 +1,5 @@ import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/header.dart"; +import "package:nexus/controllers/header_controller.dart"; extension GetHeaders on WidgetRef { Map get headers => diff --git a/lib/helpers/extensions/scheme_to_theme.dart b/lib/helpers/extensions/scheme_to_theme.dart index fdda5d8..b7d7972 100644 --- a/lib/helpers/extensions/scheme_to_theme.dart +++ b/lib/helpers/extensions/scheme_to_theme.dart @@ -12,7 +12,7 @@ extension SchemeToTheme on ColorScheme { backgroundColor: surfaceContainerLow, ), textTheme: ThemeData( - fontFamilyFallback: ["sans", "emoji", "fallback-sans", "fallback-emoji"], + fontFamilyFallback: ["sans", "emoji"], brightness: brightness, ).textTheme, inputDecorationTheme: const InputDecorationTheme( diff --git a/lib/helpers/extensions/show_about_dialog.dart b/lib/helpers/extensions/show_about_dialog.dart deleted file mode 100644 index 2f24a3e..0000000 --- a/lib/helpers/extensions/show_about_dialog.dart +++ /dev/null @@ -1,83 +0,0 @@ -import "package:flutter/material.dart"; -import "package:flutter_svg/flutter_svg.dart"; -import "package:hooks_riverpod/hooks_riverpod.dart"; -import "package:m3e_card_list/m3e_card_list.dart"; -import "package:nexus/helpers/launch_helper.dart"; -import "package:package_info_plus/package_info_plus.dart"; - -extension ShowContextMenu on BuildContext { - Future showAboutDialog(WidgetRef ref) async { - final packageInfo = await PackageInfo.fromPlatform(); - - if (mounted) { - showDialog( - context: this, - builder: (context) => AlertDialog( - content: Column( - mainAxisSize: .min, - spacing: 16, - children: [ - Row( - spacing: 12, - children: [ - SvgPicture.asset("assets/icon.svg", width: 64), - Expanded( - child: Column( - crossAxisAlignment: .start, - children: [ - Wrap( - crossAxisAlignment: .center, - spacing: 4, - children: [ - Text( - "Nexus", - style: Theme.of(context).textTheme.headlineMedium, - ), - Text("(${packageInfo.version})"), - ], - ), - - Text( - "A simple and user-friendly Matrix client", - overflow: .ellipsis, - style: Theme.of(context).textTheme.titleMedium, - ), - ], - ), - ), - ], - ), - M3ECardColumn( - onTap: (index) => - ref.watch(LaunchHelper.provider).launchUrl(switch (index) { - 0 => Uri.https("git.federated.nexus", "nexus/nexus"), - _ => Uri.https("liberapay.com", "QuadRadical"), - }), - children: [ - ListTile( - leading: Icon(Icons.commit), - title: Text("Source Code"), - ), - ListTile( - leading: Icon(Icons.favorite, color: Colors.pinkAccent), - title: Text("Donate"), - ), - ], - ), - ], - ), - actions: [ - TextButton( - onPressed: () => showLicensePage(context: context), - child: Text("View licenses"), - ), - TextButton( - onPressed: Navigator.of(context).pop, - child: Text("Close"), - ), - ], - ), - ); - } - } -} diff --git a/lib/helpers/font_licenses.dart b/lib/helpers/font_licenses.dart deleted file mode 100644 index 41567c2..0000000 --- a/lib/helpers/font_licenses.dart +++ /dev/null @@ -1,196 +0,0 @@ -import "package:flutter/foundation.dart"; - -const fontLicenses = [ - LicenseEntryWithLineBreaks( - ["Noto Color Emoji"], - """Copyright 2021 Google Inc. All Rights Reserved. - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE.""", - ), - LicenseEntryWithLineBreaks( - ["Roboto"], - """Copyright 2011 The Roboto Project Authors (https://github.com/googlefonts/roboto-classic) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://openfontlicense.org - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE.""", - ), -]; diff --git a/lib/main.dart b/lib/main.dart index 05731e4..f71776e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,18 +4,16 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter/foundation.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:media_kit/media_kit.dart"; -import "package:nexus/controllers/client.dart"; -import "package:nexus/controllers/client_state.dart"; -import "package:nexus/controllers/header.dart"; -import "package:nexus/controllers/multi_provider.dart"; -import "package:nexus/controllers/settings.dart"; -import "package:nexus/controllers/shared_prefs.dart"; +import "package:nexus/controllers/client_controller.dart"; +import "package:nexus/controllers/client_state_controller.dart"; +import "package:nexus/controllers/header_controller.dart"; +import "package:nexus/controllers/multi_provider_controller.dart"; +import "package:nexus/controllers/shared_prefs_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; import "package:nexus/helpers/extensions/scheme_to_theme.dart"; -import "package:nexus/helpers/font_licenses.dart"; -import "package:nexus/pages/chat.dart"; -import "package:nexus/pages/select_server.dart"; -import "package:nexus/pages/verify.dart"; +import "package:nexus/pages/chat_page.dart"; +import "package:nexus/pages/select_server_page.dart"; +import "package:nexus/pages/verify_page.dart"; import "package:nexus/widgets/error_dialog.dart"; import "package:nexus/widgets/loading.dart"; import "package:window_manager/window_manager.dart"; @@ -72,14 +70,11 @@ void main() async { await windowManager.setMinimumSize(Size.square(500)); } - LicenseRegistry.addLicense(() => Stream.fromIterable(fontLicenses)); - FlutterError.onError = (FlutterErrorDetails details) => showError(details.exception.toString(), details.stack); runApp( ProviderScope( - retry: null, observers: [ // Change false to true if you want debug information on provider reloads // ignore: dead_code @@ -95,43 +90,20 @@ class App extends StatelessWidget { @override Widget build(BuildContext context) => DynamicColorBuilder( - builder: (lightDynamic, darkDynamic) => Consumer( - builder: (context, ref, child) => MaterialApp( - navigatorKey: navigatorKey, - debugShowCheckedModeBanner: false, - // Use indigo to work around bugs in theme generation - theme: - (ref - .watch(SettingsController.provider) - .maybeWhen( - orElse: () => lightDynamic, - data: (settings) => - settings.useDynamicTheming ? lightDynamic : null, - ) ?? - ColorScheme.fromSeed(seedColor: Colors.indigo)) - .theme, - darkTheme: - (ref - .watch(SettingsController.provider) - .maybeWhen( - orElse: () => darkDynamic, - data: (settings) => - settings.useDynamicTheming ? darkDynamic : null, - ) ?? - ColorScheme.fromSeed( - seedColor: Colors.indigo, - brightness: Brightness.dark, - )) - .theme, - themeMode: ref - .watch(SettingsController.provider) - .maybeWhen( - data: (settings) => settings.theme, - orElse: () => ThemeMode.system, - ), - home: child, - ), - child: Scaffold( + builder: (lightDynamic, darkDynamic) => MaterialApp( + navigatorKey: navigatorKey, + debugShowCheckedModeBanner: false, + // Use indigo to work around bugs in theme generation + theme: (lightDynamic ?? ColorScheme.fromSeed(seedColor: Colors.indigo)) + .theme, + darkTheme: + (darkDynamic ?? + ColorScheme.fromSeed( + seedColor: Colors.indigo, + brightness: Brightness.dark, + )) + .theme, + home: Scaffold( body: Consumer( builder: (_, ref, _) => ref .watch( diff --git a/lib/models/configs/members_by_status.dart b/lib/models/configs/members_by_status_config.dart similarity index 83% rename from lib/models/configs/members_by_status.dart rename to lib/models/configs/members_by_status_config.dart index 29fc471..8aef586 100644 --- a/lib/models/configs/members_by_status.dart +++ b/lib/models/configs/members_by_status_config.dart @@ -1,7 +1,7 @@ import "package:freezed_annotation/freezed_annotation.dart"; import "package:nexus/models/membership_status.dart"; -part "members_by_status.freezed.dart"; -part "members_by_status.g.dart"; +part "members_by_status_config.freezed.dart"; +part "members_by_status_config.g.dart"; @freezed abstract class MembersByStatusConfig with _$MembersByStatusConfig { diff --git a/lib/models/configs/power_level.dart b/lib/models/configs/power_level_config.dart similarity index 88% rename from lib/models/configs/power_level.dart rename to lib/models/configs/power_level_config.dart index ed9c5e8..197e171 100644 --- a/lib/models/configs/power_level.dart +++ b/lib/models/configs/power_level_config.dart @@ -1,7 +1,7 @@ import "package:freezed_annotation/freezed_annotation.dart"; import "package:nexus/models/content/content.dart"; -import "package:nexus/models/membership_action.dart"; -part "power_level.freezed.dart"; +import "package:nexus/models/requests/membership_action.dart"; +part "power_level_config.freezed.dart"; @freezed sealed class PowerLevelConfig with _$PowerLevelConfig { diff --git a/lib/models/configs/reactions.dart b/lib/models/configs/reactions_config.dart similarity index 83% rename from lib/models/configs/reactions.dart rename to lib/models/configs/reactions_config.dart index 787b28c..5cae859 100644 --- a/lib/models/configs/reactions.dart +++ b/lib/models/configs/reactions_config.dart @@ -1,6 +1,6 @@ import "package:freezed_annotation/freezed_annotation.dart"; -part "reactions.freezed.dart"; -part "reactions.g.dart"; +part "reactions_config.freezed.dart"; +part "reactions_config.g.dart"; @freezed abstract class ReactionsConfig with _$ReactionsConfig { diff --git a/lib/models/configs/user.dart b/lib/models/configs/user_config.dart similarity index 83% rename from lib/models/configs/user.dart rename to lib/models/configs/user_config.dart index 0331597..4f3f8ff 100644 --- a/lib/models/configs/user.dart +++ b/lib/models/configs/user_config.dart @@ -1,6 +1,6 @@ import "package:freezed_annotation/freezed_annotation.dart"; -part "user.freezed.dart"; -part "user.g.dart"; +part "user_config.freezed.dart"; +part "user_config.g.dart"; @freezed abstract class UserConfig with _$UserConfig { diff --git a/lib/models/content/message.dart b/lib/models/content/message.dart index 6314161..b5e308c 100644 --- a/lib/models/content/message.dart +++ b/lib/models/content/message.dart @@ -10,7 +10,7 @@ part "message.g.dart"; @Freezed(unionKey: "msgtype", fallbackUnion: "default") abstract class MessageContent extends Content with _$MessageContent { MessageContent._(); - factory MessageContent({String? body}) = UnknownMessageContent; + factory MessageContent({required String body}) = UnknownMessageContent; @FreezedUnionValue("m.text") factory MessageContent.text({ diff --git a/lib/models/content/pinned_events.dart b/lib/models/content/pinned_events.dart index 8aea838..d17a0de 100644 --- a/lib/models/content/pinned_events.dart +++ b/lib/models/content/pinned_events.dart @@ -7,9 +7,8 @@ part "pinned_events.g.dart"; @freezed abstract class PinnedEventsContent extends Content with _$PinnedEventsContent { PinnedEventsContent._(); - factory PinnedEventsContent({ - @Default(IList.empty()) @JsonKey(name: "pinned") IList pinnedEvents, - }) = _PinnedEventsContent; + factory PinnedEventsContent({@Default(IList.empty()) IList pinned}) = + _PinnedEventsContent; factory PinnedEventsContent.fromJson(Map json) => _$PinnedEventsContentFromJson(json); diff --git a/lib/models/content/reaction.dart b/lib/models/content/reaction.dart index 0f81bc0..3115ae0 100644 --- a/lib/models/content/reaction.dart +++ b/lib/models/content/reaction.dart @@ -3,7 +3,7 @@ import "package:nexus/models/content/content.dart"; part "reaction.freezed.dart"; part "reaction.g.dart"; -@Freezed(toJson: false) +@freezed abstract class ReactionContent extends Content with _$ReactionContent { ReactionContent._(); static String? keyJsonFromJson(Map json, String key) => @@ -13,11 +13,6 @@ abstract class ReactionContent extends Content with _$ReactionContent { @JsonKey(readValue: ReactionContent.keyJsonFromJson) String? key, }) = _ReactionContent; - @override - Map toJson() => { - "m.relates_to": {"key": key}, - }; - factory ReactionContent.fromJson(Map json) => _$ReactionContentFromJson(json); } diff --git a/lib/models/oauth_auth_code_response.dart b/lib/models/oauth_auth_code_response.dart deleted file mode 100644 index 5bb3f3f..0000000 --- a/lib/models/oauth_auth_code_response.dart +++ /dev/null @@ -1,15 +0,0 @@ -import "package:freezed_annotation/freezed_annotation.dart"; -part "oauth_auth_code_response.freezed.dart"; -part "oauth_auth_code_response.g.dart"; - -@freezed -abstract class OAuthAuthCodeResponse with _$OAuthAuthCodeResponse { - const factory OAuthAuthCodeResponse({ - required String state, - required String codeVerifier, - required Uri url, - }) = _OAuthAuthCodeResponse; - - factory OAuthAuthCodeResponse.fromJson(Map json) => - _$OAuthAuthCodeResponseFromJson(json); -} diff --git a/lib/models/requests/get_event.dart b/lib/models/requests/get_event_request.dart similarity index 77% rename from lib/models/requests/get_event.dart rename to lib/models/requests/get_event_request.dart index 2665a2a..4fcf7b6 100644 --- a/lib/models/requests/get_event.dart +++ b/lib/models/requests/get_event_request.dart @@ -1,9 +1,10 @@ import "package:freezed_annotation/freezed_annotation.dart"; -part "get_event.freezed.dart"; -part "get_event.g.dart"; +part "get_event_request.freezed.dart"; +part "get_event_request.g.dart"; -@freezed +@Freezed() abstract class GetEventRequest with _$GetEventRequest { + const GetEventRequest._(); const factory GetEventRequest({ required String roomId, required String eventId, diff --git a/lib/models/requests/get_related_events.dart b/lib/models/requests/get_related_events_request.dart similarity index 82% rename from lib/models/requests/get_related_events.dart rename to lib/models/requests/get_related_events_request.dart index 52d2716..7e2244f 100644 --- a/lib/models/requests/get_related_events.dart +++ b/lib/models/requests/get_related_events_request.dart @@ -1,6 +1,6 @@ import "package:freezed_annotation/freezed_annotation.dart"; -part "get_related_events.freezed.dart"; -part "get_related_events.g.dart"; +part "get_related_events_request.freezed.dart"; +part "get_related_events_request.g.dart"; @freezed abstract class GetRelatedEventsRequest with _$GetRelatedEventsRequest { diff --git a/lib/models/requests/get_room_state.dart b/lib/models/requests/get_room_state_request.dart similarity index 84% rename from lib/models/requests/get_room_state.dart rename to lib/models/requests/get_room_state_request.dart index d3f52f7..8ee05f0 100644 --- a/lib/models/requests/get_room_state.dart +++ b/lib/models/requests/get_room_state_request.dart @@ -1,6 +1,6 @@ import "package:freezed_annotation/freezed_annotation.dart"; -part "get_room_state.freezed.dart"; -part "get_room_state.g.dart"; +part "get_room_state_request.freezed.dart"; +part "get_room_state_request.g.dart"; @freezed abstract class GetRoomStateRequest with _$GetRoomStateRequest { diff --git a/lib/models/requests/join_room.dart b/lib/models/requests/join_room_request.dart similarity index 85% rename from lib/models/requests/join_room.dart rename to lib/models/requests/join_room_request.dart index 8487298..d6b411e 100644 --- a/lib/models/requests/join_room.dart +++ b/lib/models/requests/join_room_request.dart @@ -1,7 +1,7 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:freezed_annotation/freezed_annotation.dart"; -part "join_room.freezed.dart"; -part "join_room.g.dart"; +part "join_room_request.freezed.dart"; +part "join_room_request.g.dart"; @freezed abstract class JoinRoomRequest with _$JoinRoomRequest { diff --git a/lib/models/requests/login_request.dart b/lib/models/requests/login_request.dart new file mode 100644 index 0000000..b3704fa --- /dev/null +++ b/lib/models/requests/login_request.dart @@ -0,0 +1,15 @@ +import "package:freezed_annotation/freezed_annotation.dart"; +part "login_request.freezed.dart"; +part "login_request.g.dart"; + +@freezed +abstract class LoginRequest with _$LoginRequest { + const factory LoginRequest({ + required String username, + required String password, + required String homeserverUrl, + }) = _LoginRequest; + + factory LoginRequest.fromJson(Map json) => + _$LoginRequestFromJson(json); +} diff --git a/lib/models/membership_action.dart b/lib/models/requests/membership_action.dart similarity index 100% rename from lib/models/membership_action.dart rename to lib/models/requests/membership_action.dart diff --git a/lib/models/requests/oauth/exchange_token.dart b/lib/models/requests/oauth/exchange_token.dart deleted file mode 100644 index 9d55c1f..0000000 --- a/lib/models/requests/oauth/exchange_token.dart +++ /dev/null @@ -1,17 +0,0 @@ -import "package:freezed_annotation/freezed_annotation.dart"; -part "exchange_token.freezed.dart"; -part "exchange_token.g.dart"; - -@freezed -abstract class OAuthExchangeTokenRequest with _$OAuthExchangeTokenRequest { - const factory OAuthExchangeTokenRequest({ - required Uri homeserverUrl, - required String codeVerifier, - required Uri redirectUri, - required String code, - required String clientId, - }) = _OAuthExchangeTokenRequest; - - factory OAuthExchangeTokenRequest.fromJson(Map json) => - _$OAuthExchangeTokenRequestFromJson(json); -} diff --git a/lib/models/requests/oauth/get_auth_url.dart b/lib/models/requests/oauth/get_auth_url.dart deleted file mode 100644 index 5ca5b6f..0000000 --- a/lib/models/requests/oauth/get_auth_url.dart +++ /dev/null @@ -1,43 +0,0 @@ -import "dart:math"; - -import "package:fast_immutable_collections/fast_immutable_collections.dart"; -import "package:freezed_annotation/freezed_annotation.dart"; -part "get_auth_url.freezed.dart"; -part "get_auth_url.g.dart"; - -@freezed -abstract class OAuthGetAuthUrl with _$OAuthGetAuthUrl { - const factory OAuthGetAuthUrl({ - required ResponseMode responseMode, - required Uri homeserverUrl, - required Uri redirectUri, - required IList scopes, - required String clientId, - String? userIdHint, - }) = _OAuthGetAuthUrl; - - factory OAuthGetAuthUrl.fromJson(Map json) => - _$OAuthGetAuthUrlFromJson(json); -} - -abstract class Scope { - static final openid = "openid"; - static final email = "email"; - static final clientApi = "urn:matrix:client:api:*"; - - static final _deviceChars = IList( - ("ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789" - "-._~") - .split(""), - ); - - static String get _deviceId => - _deviceChars.shuffle(Random.secure()).sublist(0, 10).join(); - - static String get device => "urn:matrix:client:device:$_deviceId"; -} - -@JsonEnum(fieldRename: .snake) -enum ResponseMode { query, fragment } diff --git a/lib/models/requests/oauth/register_client.dart b/lib/models/requests/oauth/register_client.dart deleted file mode 100644 index c4ef68f..0000000 --- a/lib/models/requests/oauth/register_client.dart +++ /dev/null @@ -1,47 +0,0 @@ -import "package:fast_immutable_collections/fast_immutable_collections.dart"; -import "package:freezed_annotation/freezed_annotation.dart"; -part "register_client.freezed.dart"; -part "register_client.g.dart"; - -@freezed -abstract class OAuthRegisterClientRequest with _$OAuthRegisterClientRequest { - const factory OAuthRegisterClientRequest({ - required Uri homeserverUrl, - @Default(ApplicationType.web) ApplicationType applicationType, - String? clientName, - required Uri clientUri, - Uri? logoUri, - Uri? policyUri, - Uri? tosUri, - IList? grantTypes, - IList? redirectUris, - IList? responseTypes, - AuthMethod? authMethod, - }) = _OAuthRegisterClientRequest; - - factory OAuthRegisterClientRequest.fromJson(Map json) => - _$OAuthRegisterClientRequestFromJson(json); -} - -enum ApplicationType { native, web } - -@JsonEnum(fieldRename: .snake) -enum ResponseType { code, idToken } - -@JsonEnum(fieldRename: .snake) -enum AuthMethod { - clientSecretPost, - clientSecretBasic, - clientSecretJwt, - privateKeyJwt, - none, -} - -@JsonEnum(fieldRename: .snake) -enum GrantType { - authorizationCode, - refreshToken, - clientCredentials, - @JsonValue("urn:ietf:params:oauth:grant-type:device_code") - deviceCode, -} diff --git a/lib/models/requests/paginate.dart b/lib/models/requests/paginate_request.dart similarity index 84% rename from lib/models/requests/paginate.dart rename to lib/models/requests/paginate_request.dart index ddc62f3..44cf8ec 100644 --- a/lib/models/requests/paginate.dart +++ b/lib/models/requests/paginate_request.dart @@ -1,6 +1,6 @@ import "package:freezed_annotation/freezed_annotation.dart"; -part "paginate.freezed.dart"; -part "paginate.g.dart"; +part "paginate_request.freezed.dart"; +part "paginate_request.g.dart"; @freezed abstract class PaginateRequest with _$PaginateRequest { diff --git a/lib/models/requests/redact_event.dart b/lib/models/requests/redact_event.dart deleted file mode 100644 index 3a01673..0000000 --- a/lib/models/requests/redact_event.dart +++ /dev/null @@ -1,3 +0,0 @@ -import "package:nexus/models/requests/report.dart"; - -typedef RedactEventRequest = ReportRequest; diff --git a/lib/models/requests/redact_event_request.dart b/lib/models/requests/redact_event_request.dart new file mode 100644 index 0000000..fed2255 --- /dev/null +++ b/lib/models/requests/redact_event_request.dart @@ -0,0 +1,3 @@ +import "package:nexus/models/requests/report_request.dart"; + +typedef RedactEventRequest = ReportRequest; diff --git a/lib/models/requests/report.dart b/lib/models/requests/report_request.dart similarity index 84% rename from lib/models/requests/report.dart rename to lib/models/requests/report_request.dart index f87b1f1..749ad60 100644 --- a/lib/models/requests/report.dart +++ b/lib/models/requests/report_request.dart @@ -1,6 +1,6 @@ import "package:freezed_annotation/freezed_annotation.dart"; -part "report.freezed.dart"; -part "report.g.dart"; +part "report_request.freezed.dart"; +part "report_request.g.dart"; @freezed abstract class ReportRequest with _$ReportRequest { diff --git a/lib/models/requests/send_event.dart b/lib/models/requests/send_event_request.dart similarity index 69% rename from lib/models/requests/send_event.dart rename to lib/models/requests/send_event_request.dart index 196c0b5..da5de32 100644 --- a/lib/models/requests/send_event.dart +++ b/lib/models/requests/send_event_request.dart @@ -1,16 +1,13 @@ import "package:freezed_annotation/freezed_annotation.dart"; -import "package:nexus/models/content/content.dart"; -part "send_event.freezed.dart"; -part "send_event.g.dart"; +part "send_event_request.freezed.dart"; +part "send_event_request.g.dart"; @freezed abstract class SendEventRequest with _$SendEventRequest { const factory SendEventRequest({ required String roomId, required String type, - required Content content, - String? relatesTo, - String? relationType, + required Map content, @Default(false) bool synchronous, @Default(false) bool disableEncryption, }) = _SendEventRequest; diff --git a/lib/models/requests/send_message.dart b/lib/models/requests/send_message_request.dart similarity index 94% rename from lib/models/requests/send_message.dart rename to lib/models/requests/send_message_request.dart index 628007f..883c585 100644 --- a/lib/models/requests/send_message.dart +++ b/lib/models/requests/send_message_request.dart @@ -1,8 +1,8 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:freezed_annotation/freezed_annotation.dart"; import "package:nexus/models/relation_type.dart"; -part "send_message.freezed.dart"; -part "send_message.g.dart"; +part "send_message_request.freezed.dart"; +part "send_message_request.g.dart"; @freezed abstract class SendMessageRequest with _$SendMessageRequest { diff --git a/lib/models/requests/set_membership.dart b/lib/models/requests/set_membership_request.dart similarity index 78% rename from lib/models/requests/set_membership.dart rename to lib/models/requests/set_membership_request.dart index 4bbe8a3..dd0e1f2 100644 --- a/lib/models/requests/set_membership.dart +++ b/lib/models/requests/set_membership_request.dart @@ -1,7 +1,7 @@ import "package:freezed_annotation/freezed_annotation.dart"; -import "package:nexus/models/membership_action.dart"; -part "set_membership.freezed.dart"; -part "set_membership.g.dart"; +import "package:nexus/models/requests/membership_action.dart"; +part "set_membership_request.freezed.dart"; +part "set_membership_request.g.dart"; @freezed abstract class SetMembershipRequest with _$SetMembershipRequest { diff --git a/lib/models/requests/set_state.dart b/lib/models/requests/set_state.dart deleted file mode 100644 index c92763d..0000000 --- a/lib/models/requests/set_state.dart +++ /dev/null @@ -1,23 +0,0 @@ -import "package:freezed_annotation/freezed_annotation.dart"; -import "package:nexus/models/content/content.dart"; -import "package:nexus/models/ms_duration.dart"; -part "set_state.freezed.dart"; -part "set_state.g.dart"; - -@freezed -abstract class SetStateRequest with _$SetStateRequest { - const factory SetStateRequest({ - required String roomId, - required String type, - required String stateKey, - required Content content, - - @JsonKey(name: "delay_ms", includeIfNull: false) - @MSDuration() - @Default(null) - Duration? delay, - }) = _SetStateRequest; - - factory SetStateRequest.fromJson(Map json) => - _$SetStateRequestFromJson(json); -} diff --git a/lib/models/setting.dart b/lib/models/setting.dart deleted file mode 100644 index 6a49845..0000000 --- a/lib/models/setting.dart +++ /dev/null @@ -1,16 +0,0 @@ -import "package:flutter/material.dart"; - -class Setting { - final String title; - final String description; - final IconData icon; - final Widget Function(String title, String description, IconData icon) - builder; - - Setting({ - required this.title, - required this.description, - required this.builder, - required this.icon, - }); -} diff --git a/lib/models/settings.dart b/lib/models/settings.dart deleted file mode 100644 index eddc2da..0000000 --- a/lib/models/settings.dart +++ /dev/null @@ -1,16 +0,0 @@ -import "package:flutter/material.dart"; -import "package:freezed_annotation/freezed_annotation.dart"; -part "settings.freezed.dart"; -part "settings.g.dart"; - -@freezed -abstract class Settings with _$Settings { - const factory Settings({ - @Default(ThemeMode.system) ThemeMode theme, - @Default(true) bool useDynamicTheming, - @Default(false) bool linuxMobileMode, - }) = _Settings; - - factory Settings.fromJson(Map json) => - _$SettingsFromJson(json); -} diff --git a/lib/models/settings_category.dart b/lib/models/settings_category.dart deleted file mode 100644 index 88bfdd0..0000000 --- a/lib/models/settings_category.dart +++ /dev/null @@ -1,14 +0,0 @@ -import "package:fast_immutable_collections/fast_immutable_collections.dart"; -import "package:flutter/material.dart"; -import "package:freezed_annotation/freezed_annotation.dart"; -import "package:nexus/models/setting.dart"; -part "settings_category.freezed.dart"; - -@freezed -abstract class SettingsCategory with _$SettingsCategory { - const factory SettingsCategory({ - required String title, - required IconData icon, - required IList settings, - }) = _SettingsCategory; -} diff --git a/lib/pages/chat.dart b/lib/pages/chat.dart deleted file mode 100644 index 316c9af..0000000 --- a/lib/pages/chat.dart +++ /dev/null @@ -1,53 +0,0 @@ -import "package:flutter/material.dart"; -import "package:hooks_riverpod/hooks_riverpod.dart"; -import "package:nexus/controllers/emoji.dart"; -import "package:nexus/controllers/init_complete.dart"; -import "package:nexus/controllers/key.dart"; -import "package:nexus/widgets/appbar.dart"; -import "package:nexus/widgets/sidebar.dart"; -import "package:nexus/widgets/room_chat.dart"; -import "package:nexus/widgets/loading.dart"; - -class ChatPage extends ConsumerWidget { - const ChatPage({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) => LayoutBuilder( - builder: (context, constraints) { - final isDesktop = constraints.maxWidth > 650; - final showMembersByDefault = constraints.maxWidth > 1000; - final initComplete = ref.watch(InitCompleteController.provider); - final roomId = ref.watch(KeyController.provider(KeyController.roomKey)); - - ref.read(EmojiController.provider); - - return SafeArea( - child: Scaffold( - appBar: initComplete ? null : Appbar(), - body: initComplete - ? Row( - children: [ - if (isDesktop) Sidebar(isDesktop: isDesktop), - Expanded( - child: RoomChat( - roomId: roomId, - isDesktop: isDesktop, - showMembersByDefault: showMembersByDefault, - ), - ), - ], - ) - : Center( - child: Column( - mainAxisSize: .min, - children: [Loading(), Text("Syncing...")], - ), - ), - drawer: isDesktop || !initComplete - ? null - : Sidebar(isDesktop: isDesktop), - ), - ); - }, - ); -} diff --git a/lib/pages/chat_page.dart b/lib/pages/chat_page.dart new file mode 100644 index 0000000..2c0ef97 --- /dev/null +++ b/lib/pages/chat_page.dart @@ -0,0 +1,50 @@ +import "package:flutter/material.dart"; +import "package:hooks_riverpod/hooks_riverpod.dart"; +import "package:nexus/controllers/emoji_controller.dart"; +import "package:nexus/controllers/init_complete_controller.dart"; +import "package:nexus/controllers/key_controller.dart"; +import "package:nexus/widgets/appbar.dart"; +import "package:nexus/widgets/sidebar.dart"; +import "package:nexus/widgets/room_chat.dart"; +import "package:nexus/widgets/loading.dart"; + +class ChatPage extends ConsumerWidget { + const ChatPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) => LayoutBuilder( + builder: (context, constraints) { + final isDesktop = constraints.maxWidth > 650; + final showMembersByDefault = constraints.maxWidth > 1000; + final initComplete = ref.watch(InitCompleteController.provider); + final roomId = ref.watch(KeyController.provider(KeyController.roomKey)); + ref.read(EmojiController.provider); + + return Scaffold( + appBar: initComplete ? null : Appbar(), + body: initComplete + ? Row( + children: [ + if (isDesktop) Sidebar(isDesktop: isDesktop), + Expanded( + child: RoomChat( + roomId: roomId, + isDesktop: isDesktop, + showMembersByDefault: showMembersByDefault, + ), + ), + ], + ) + : Center( + child: Column( + mainAxisSize: .min, + children: [Loading(), Text("Syncing...")], + ), + ), + drawer: isDesktop || !initComplete + ? null + : Sidebar(isDesktop: isDesktop), + ); + }, + ); +} diff --git a/lib/pages/login_page.dart b/lib/pages/login_page.dart new file mode 100644 index 0000000..f7a3f1a --- /dev/null +++ b/lib/pages/login_page.dart @@ -0,0 +1,99 @@ +import "package:flutter/material.dart"; +import "package:flutter_hooks/flutter_hooks.dart"; +import "package:hooks_riverpod/hooks_riverpod.dart"; +import "package:nexus/controllers/client_controller.dart"; +import "package:nexus/widgets/appbar.dart"; +import "package:nexus/helpers/required_validator_helper.dart"; + +class LoginPage extends HookConsumerWidget { + final Uri homeserver; + const LoginPage({super.key, required this.homeserver}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final client = ref.watch(ClientController.provider.notifier); + + final isLoading = useState(false); + final username = useTextEditingController(); + final password = useTextEditingController(); + + final inputError = useState(null); + final formKey = useRef(GlobalKey()); + + Future tryLogin() async { + isLoading.value = true; + + try { + if (formKey.value.currentState?.validate() != true) return; + + final error = await client.login( + .new( + username: username.text, + password: password.text, + homeserverUrl: homeserver.origin, + ), + ); + + if (error != null) { + inputError.value = error; + isLoading.value = false; + } else { + if (context.mounted) Navigator.of(context).pop(); + } + } finally { + isLoading.value = false; + } + } + + return Scaffold( + appBar: Appbar( + leading: IconButton( + icon: Icon(Icons.arrow_back), + onPressed: Navigator.of(context).pop, + ), + ), + body: AlertDialog( + title: Text("Login to ${homeserver.host}"), + content: Form( + key: formKey.value, + child: Column( + mainAxisSize: .min, + crossAxisAlignment: .start, + children: [ + TextFormField( + autofocus: true, + textInputAction: .next, + autovalidateMode: .onUserInteraction, + validator: requiredValidator, + decoration: .new(label: Text("Username")), + controller: username, + ), + SizedBox(height: 12), + TextFormField( + textInputAction: .done, + decoration: .new( + label: Text("Password"), + errorText: inputError.value, + errorMaxLines: 5, + ), + autovalidateMode: .onUserInteraction, + validator: requiredValidator, + controller: password, + obscureText: true, + onFieldSubmitted: (_) => tryLogin(), + // Don't defocus on submit + onEditingComplete: () {}, + ), + ], + ), + ), + actions: [ + TextButton( + onPressed: isLoading.value ? null : tryLogin, + child: Text("Sign In"), + ), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/pages/select_server.dart b/lib/pages/select_server.dart deleted file mode 100644 index 118e70a..0000000 --- a/lib/pages/select_server.dart +++ /dev/null @@ -1,206 +0,0 @@ -import "package:app_links/app_links.dart"; -import "package:flutter/material.dart"; -import "package:flutter_hooks/flutter_hooks.dart"; -import "package:flutter_svg/flutter_svg.dart"; -import "package:hooks_riverpod/hooks_riverpod.dart"; -import "package:nexus/controllers/auth_url.dart"; -import "package:nexus/controllers/client.dart"; -import "package:nexus/controllers/client_id.dart"; -import "package:nexus/helpers/launch_helper.dart"; -import "package:nexus/main.dart"; -import "package:nexus/models/homeserver.dart"; -import "package:nexus/widgets/appbar.dart"; -import "package:nexus/widgets/divider_text.dart"; - -class SelectServerPage extends HookConsumerWidget { - const SelectServerPage({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final theme = Theme.of(context); - - final launch = ref.watch(LaunchHelper.provider).launchUrl; - - final isLoading = useState(false); - final homeserverUrl = useTextEditingController(); - - Future setHomeserver(Uri? newHomeserver) async { - isLoading.value = true; - - try { - if (newHomeserver?.hasScheme == false) { - newHomeserver = Uri.https(newHomeserver!.path); - } - - final newUrl = newHomeserver == null - ? null - : await ref - .read(ClientController.provider.notifier) - .discoverHomeserver(newHomeserver); - - if (context.mounted) { - if (newUrl == null) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - "Homeserver verification failed. Is your homeserver down?", - style: .new(color: theme.colorScheme.onErrorContainer), - ), - backgroundColor: theme.colorScheme.errorContainer, - ), - ); - } else { - final codeResponse = await ref.watch( - AuthUrlController.provider(newUrl).future, - ); - - await ref.watch(LaunchHelper.provider).launchUrl(codeResponse.url); - - AppLinks().uriLinkStream.listen((encodedUri) async { - final code = encodedUri.queryParameters["code"]; - - if (code != null) { - await ref - .watch(ClientController.provider.notifier) - .exchangeToken( - .new( - homeserverUrl: newUrl, - codeVerifier: codeResponse.codeVerifier, - redirectUri: .new( - scheme: "nexus.federated.nexus", - path: "/", - ), - code: code, - clientId: await ref.watch( - ClientIdController.provider(newUrl).future, - ), - ), - ) - .onError(showError); - } - }); - // await Navigator.of(context).push( - // MaterialPageRoute(builder: (_) => LoginPage(homeserver: newUrl)), - // ); - } - } - } catch (error, stackTrace) { - showError(error, stackTrace); - } finally { - isLoading.value = false; - } - } - - return Scaffold( - appBar: Appbar(), - body: SafeArea( - child: Center( - child: ConstrainedBox( - constraints: .new(maxWidth: 600), - child: ListView( - padding: .symmetric(vertical: 8, horizontal: 12), - children: [ - Row( - children: [ - SvgPicture.asset("assets/icon.svg", width: 128), - SizedBox(width: 12), - Expanded( - child: Column( - crossAxisAlignment: .start, - children: [ - Text("Nexus", style: theme.textTheme.displayMedium), - Text( - "A Simple Matrix Client", - style: theme.textTheme.headlineMedium, - overflow: .ellipsis, - ), - ], - ), - ), - ], - ), - Padding(padding: .symmetric(vertical: 12), child: Divider()), - DividerText("Enter a homeserver domain:"), - Row( - spacing: 8, - children: [ - Expanded( - child: TextField( - textInputAction: .done, - autofocus: true, - onSubmitted: (text) => setHomeserver(.tryParse(text)), - controller: homeserverUrl, - decoration: .new( - labelText: "Homeserver URL", - hintText: "matrix.org", - ), - ), - ), - IconButton.filled( - tooltip: "Confirm homeserver choice", - onPressed: isLoading.value - ? null - : () => setHomeserver(.tryParse(homeserverUrl.text)), - icon: Icon(Icons.check), - ), - ], - ), - DividerText("Or, choose from some popular homeservers:"), - ...([ - .new( - name: "Matrix.org", - description: - "The Matrix.org Foundation offers the matrix.org homeserver as an easy entry point for anyone wanting to try out Matrix.", - url: .https("matrix.org"), - iconUrl: - "https://raw.githubusercontent.com/element-hq/logos/refs/heads/master/matrix/matrix-favicon${Theme.brightnessOf(context) == Brightness.dark ? "-white" : ""}.png", - ), - .new( - name: "Federated Nexus", - description: - "Federated Nexus is a community resource hosting multiple FOSS (especially federated) services, including Matrix and Forgejo. By the same developers who made Nexus client.", - url: .https("federated.nexus"), - iconUrl: "https://federated.nexus/images/icon.png", - ), - .new( - name: "Unredacted", - description: - "Unredacted is a 501(c)(3) non-profit organization that builds Internet infrastructure and services to help people evade censorship and protect their right to privacy.", - url: .https("unredacted.org", "services/si/matrix"), - iconUrl: "https://unredacted.org/favicon.ico", - ), - ].map( - (homeserver) => Card( - child: ListTile( - enabled: !isLoading.value, - title: Text(homeserver.name), - leading: Image.network( - homeserver.iconUrl, - errorBuilder: (_, _, _) => SizedBox.shrink(), - height: 32, - ), - subtitle: Text(homeserver.description), - onTap: isLoading.value - ? null - : () => setHomeserver(homeserver.url), - trailing: IconButton( - tooltip: "Launch homeserver info page", - onPressed: () => launch(homeserver.url), - icon: Icon(Icons.info_outline), - ), - ), - ), - )), - - TextButton( - onPressed: () => launch(.https("servers.joinmatrix.org")), - child: Text("See more homeservers..."), - ), - ], - ), - ), - ), - ), - ); - } -} diff --git a/lib/pages/select_server_page.dart b/lib/pages/select_server_page.dart new file mode 100644 index 0000000..f0e7dff --- /dev/null +++ b/lib/pages/select_server_page.dart @@ -0,0 +1,169 @@ +import "package:flutter/material.dart"; +import "package:flutter_hooks/flutter_hooks.dart"; +import "package:flutter_svg/flutter_svg.dart"; +import "package:hooks_riverpod/hooks_riverpod.dart"; +import "package:nexus/controllers/client_controller.dart"; +import "package:nexus/helpers/launch_helper.dart"; +import "package:nexus/models/homeserver.dart"; +import "package:nexus/pages/login_page.dart"; +import "package:nexus/widgets/appbar.dart"; +import "package:nexus/widgets/divider_text.dart"; + +class SelectServerPage extends HookConsumerWidget { + const SelectServerPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final theme = Theme.of(context); + + final launch = ref.watch(LaunchHelper.provider).launchUrl; + + final isLoading = useState(false); + final homeserverUrl = useTextEditingController(); + + Future setHomeserver(Uri? newHomeserver) async { + isLoading.value = true; + + try { + if (newHomeserver?.hasScheme == false) { + newHomeserver = Uri.https(newHomeserver!.path); + } + + final newUrl = newHomeserver == null + ? null + : await ref + .watch(ClientController.provider.notifier) + .discoverHomeserver(newHomeserver); + + if (context.mounted) { + if (newUrl == null) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + "Homeserver verification failed. Is your homeserver down?", + style: .new(color: theme.colorScheme.onErrorContainer), + ), + backgroundColor: theme.colorScheme.errorContainer, + ), + ); + } else { + await Navigator.of(context).push( + MaterialPageRoute(builder: (_) => LoginPage(homeserver: newUrl)), + ); + } + } + } finally { + isLoading.value = false; + } + } + + return Scaffold( + appBar: Appbar(), + body: Center( + child: ConstrainedBox( + constraints: .new(maxWidth: 600), + child: ListView( + children: [ + Row( + children: [ + SvgPicture.asset("assets/icon.svg", width: 128), + SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: .start, + children: [ + Text("Nexus", style: theme.textTheme.displayMedium), + Text( + "A Simple Matrix Client", + style: theme.textTheme.headlineMedium, + overflow: .ellipsis, + ), + ], + ), + ), + ], + ), + Padding(padding: .symmetric(vertical: 12), child: Divider()), + DividerText("Enter a homeserver domain:"), + Row( + spacing: 8, + children: [ + Expanded( + child: TextField( + textInputAction: .done, + autofocus: true, + onSubmitted: (text) => setHomeserver(.tryParse(text)), + controller: homeserverUrl, + decoration: .new( + labelText: "Homeserver URL", + hintText: "matrix.org", + ), + ), + ), + IconButton.filled( + tooltip: "Confirm homeserver choice", + onPressed: isLoading.value + ? null + : () => setHomeserver(.tryParse(homeserverUrl.text)), + icon: Icon(Icons.check), + ), + ], + ), + DividerText("Or, choose from some popular homeservers:"), + ...([ + .new( + name: "Matrix.org", + description: + "The Matrix.org Foundation offers the matrix.org homeserver as an easy entry point for anyone wanting to try out Matrix.", + url: .https("matrix.org"), + iconUrl: + "https://raw.githubusercontent.com/element-hq/logos/refs/heads/master/matrix/matrix-favicon${Theme.brightnessOf(context) == Brightness.dark ? "-white" : ""}.png", + ), + .new( + name: "Federated Nexus", + description: + "Federated Nexus is a community resource hosting multiple FOSS (especially federated) services, including Matrix and Forgejo. By the same developers who made Nexus client.", + url: .https("federated.nexus"), + iconUrl: "https://federated.nexus/images/icon.png", + ), + .new( + name: "Unredacted", + description: + "Unredacted is a 501(c)(3) non-profit organization that builds Internet infrastructure and services to help people evade censorship and protect their right to privacy.", + url: .https("unredacted.org", "services/si/matrix"), + iconUrl: "https://unredacted.org/favicon.ico", + ), + ].map( + (homeserver) => Card( + child: ListTile( + enabled: !isLoading.value, + title: Text(homeserver.name), + leading: Image.network( + homeserver.iconUrl, + errorBuilder: (_, _, _) => SizedBox.shrink(), + height: 32, + ), + subtitle: Text(homeserver.description), + onTap: isLoading.value + ? null + : () => setHomeserver(homeserver.url), + trailing: IconButton( + tooltip: "Launch homeserver info page", + onPressed: () => launch(homeserver.url), + icon: Icon(Icons.info_outline), + ), + ), + ), + )), + + TextButton( + onPressed: () => launch(.https("servers.joinmatrix.org")), + child: Text("See more homeservers..."), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart deleted file mode 100644 index 96b9b82..0000000 --- a/lib/pages/settings.dart +++ /dev/null @@ -1,238 +0,0 @@ -import "package:collection/collection.dart"; -import "package:fast_immutable_collections/fast_immutable_collections.dart"; -import "package:flutter/material.dart"; -import "package:flutter_hooks/flutter_hooks.dart"; -import "package:hooks_riverpod/hooks_riverpod.dart"; -import "package:m3e_card_list/m3e_card_list.dart"; -import "package:navigation_rail_m3e/navigation_rail_m3e.dart"; -import "package:nexus/controllers/settings_sections.dart"; -import "package:nexus/helpers/extensions/better_when.dart"; -import "package:nexus/helpers/extensions/show_about_dialog.dart"; -import "package:nexus/pages/settings_category.dart"; -import "package:nexus/widgets/divider_text.dart"; -import "package:nexus/widgets/highlight_wrapper.dart"; -import "package:super_sliver_list/super_sliver_list.dart"; - -class SettingsPage extends ConsumerWidget { - const SettingsPage({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) => LayoutBuilder( - builder: (_, constraints) => HookBuilder( - builder: (context) { - final categoriesArePages = constraints.maxWidth < 550; - - final selected = useState(0); - - final highlightedMatch = useState<(int, int)?>(null); - final listController = useRef(ListController()); - final scrollController = useScrollController(); - - final searchBar = SearchAnchor.bar( - barHintText: "Search...", - isFullScreen: categoriesArePages, - suggestionsBuilder: (suggestionsContext, controller) async { - final categories = await ref.watch( - SettingsSectionsController.provider.future, - ); - final query = controller.text.toLowerCase(); - - final matches = categories.values.flattenedToList - .asMap() - .entries - .expand( - (categoryEntry) => categoryEntry.value.settings - .asMap() - .entries - .where( - (settingEntry) => - settingEntry.value.title.toLowerCase().contains( - query, - ) || - settingEntry.value.description - .toLowerCase() - .contains(query), - ) - .map( - (settingEntry) => ( - (categoryEntry.key, settingEntry.key), - settingEntry.value, - ), - ), - ) - .toIList(); - - return matches.map( - (match) => ListTile( - onTap: () async { - if (context.mounted) Navigator.of(suggestionsContext).pop(); - controller.text = ""; - - if (categoriesArePages) { - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => SettingsCategoryPage( - match.$1.$1, - initialHighlight: match.$1.$2, - ), - ), - ); - } else { - selected.value = match.$1.$1; - listController.value.animateToItem( - index: match.$1.$2, - scrollController: scrollController, - alignment: 0.5, - duration: (_) => .new(milliseconds: 700), - curve: (_) => Curves.easeInOut, - ); - highlightedMatch.value = match.$1; - await Future.delayed(.new(seconds: 1), () { - if (highlightedMatch.value == match.$1) { - highlightedMatch.value = null; - } - }); - } - }, - leading: Icon(match.$2.icon), - title: Text(match.$2.title), - subtitle: Text(match.$2.description), - ), - ); - }, - ); - - final settingsContent = Scaffold( - appBar: AppBar( - title: Text("Settings"), - actionsPadding: .symmetric(horizontal: 12), - actions: [ - IconButton( - onPressed: () => context.showAboutDialog(ref), - icon: Icon(Icons.info_outline), - ), - ], - ), - body: ref - .watch(SettingsSectionsController.provider) - .betterWhen( - data: (sections) => categoriesArePages - ? CustomScrollView( - slivers: [ - SliverToBoxAdapter( - child: Padding( - padding: EdgeInsets.all(12).copyWith(bottom: 8), - child: searchBar, - ), - ), - ...sections - .mapTo( - (categoryGroup, categories) => [ - SliverToBoxAdapter( - child: Padding( - padding: EdgeInsets.symmetric( - horizontal: 16, - ).copyWith(bottom: 4), - child: DividerText(categoryGroup), - ), - ), - SliverM3ECardList( - padding: .symmetric( - horizontal: 4, - vertical: 8, - ), - margin: .symmetric(horizontal: 12), - color: Theme.of( - context, - ).colorScheme.primaryContainer, - itemCount: categories.length, - onTap: (index) => - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => - SettingsCategoryPage(index), - ), - ), - itemBuilder: (context, index) => ListTile( - leading: Icon(categories[index].icon), - title: Text(categories[index].title), - ), - ), - ], - ) - .flattened, - ], - ) - : Row( - children: [ - NavigationRailM3E( - type: .alwaysExpand, - trailing: searchBar, - scrollable: true, - sections: sections - .mapTo( - (categoryGroup, categories) => - NavigationRailM3ESection( - header: DividerText(categoryGroup), - destinations: categories - .map( - (category) => - NavigationRailM3EDestination( - icon: Icon(category.icon), - label: category.title, - ), - ) - .toList(), - ), - ) - .toList(), - selectedIndex: selected.value, - onDestinationSelected: (value) => - selected.value = value, - ), - VerticalDivider(), - Expanded( - child: SuperListView( - listController: listController.value, - controller: scrollController, - padding: .symmetric(vertical: 12), - children: sections - .values - .flattenedToList[selected.value] - .settings - .mapIndexed( - (index, setting) => Padding( - padding: .only(bottom: 4), - child: HighlightWrapper( - setting.builder( - setting.title, - setting.description, - setting.icon, - ), - isHighlighted: - highlightedMatch.value == - (selected.value, index), - ), - ), - ) - .toList(), - ), - ), - ], - ), - ), - ); - - return constraints.maxWidth < 650 - ? settingsContent - : Dialog( - constraints: .loose(Size(900, 600)), - child: ClipRRect( - borderRadius: BorderRadiusGeometry.circular(12), - child: settingsContent, - ), - ); - }, - ), - ); -} diff --git a/lib/pages/settings_category.dart b/lib/pages/settings_category.dart deleted file mode 100644 index b21d048..0000000 --- a/lib/pages/settings_category.dart +++ /dev/null @@ -1,79 +0,0 @@ -import "dart:async"; - -import "package:collection/collection.dart"; -import "package:flutter/material.dart"; -import "package:flutter_hooks/flutter_hooks.dart"; -import "package:hooks_riverpod/hooks_riverpod.dart"; -import "package:nexus/controllers/settings_sections.dart"; -import "package:nexus/helpers/extensions/better_when.dart"; -import "package:nexus/widgets/highlight_wrapper.dart"; -import "package:super_sliver_list/super_sliver_list.dart"; - -class SettingsCategoryPage extends HookConsumerWidget { - final int index; - final int? initialHighlight; - const SettingsCategoryPage(this.index, {this.initialHighlight, super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final highlight = useState(initialHighlight); - final listController = useRef(ListController()); - final scrollController = useScrollController(); - - useEffect(() { - if (initialHighlight == null) return null; - Timer? timer; - - void listener() => WidgetsBinding.instance.addPostFrameCallback((_) { - if (!listController.value.isAttached) return; - - listController.value.animateToItem( - index: initialHighlight!, - scrollController: scrollController, - alignment: 0.5, - duration: (_) => .new(milliseconds: 700), - curve: (_) => Curves.easeInOut, - ); - timer = Timer(.new(seconds: 1), () { - highlight.value = null; - }); - listController.value.removeListener(listener); - }); - - listController.value.addListener(listener); - return timer?.cancel; - }, []); - - return ref - .watch(SettingsSectionsController.provider) - .betterWhen( - data: (sections) => Scaffold( - appBar: AppBar( - title: Text(sections.values.flattenedToList[index].title), - ), - body: SafeArea( - child: SuperListView( - controller: scrollController, - listController: listController.value, - padding: .symmetric(vertical: 12, horizontal: 8), - children: sections.values.flattenedToList[index].settings - .mapIndexed( - (index, setting) => Padding( - padding: .only(bottom: 4), - child: HighlightWrapper( - setting.builder( - setting.title, - setting.description, - setting.icon, - ), - isHighlighted: highlight.value == index, - ), - ), - ) - .toList(), - ), - ), - ), - ); - } -} diff --git a/lib/pages/settings_page.dart b/lib/pages/settings_page.dart new file mode 100644 index 0000000..505904c --- /dev/null +++ b/lib/pages/settings_page.dart @@ -0,0 +1,11 @@ +import "package:flutter/material.dart"; +import "package:flutter_riverpod/flutter_riverpod.dart"; + +class SettingsPage extends ConsumerWidget { + const SettingsPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Placeholder(); + } +} diff --git a/lib/pages/verify.dart b/lib/pages/verify_page.dart similarity index 97% rename from lib/pages/verify.dart rename to lib/pages/verify_page.dart index 1b4f7ad..bf5c9f3 100644 --- a/lib/pages/verify.dart +++ b/lib/pages/verify_page.dart @@ -1,7 +1,7 @@ import "package:flutter/material.dart"; import "package:flutter_hooks/flutter_hooks.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; -import "package:nexus/controllers/client.dart"; +import "package:nexus/controllers/client_controller.dart"; import "package:nexus/widgets/appbar.dart"; import "package:nexus/helpers/required_validator_helper.dart"; diff --git a/lib/widgets/appbar.dart b/lib/widgets/appbar.dart index 9da03aa..811788a 100644 --- a/lib/widgets/appbar.dart +++ b/lib/widgets/appbar.dart @@ -1,11 +1,9 @@ import "dart:io"; import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter/material.dart"; -import "package:hooks_riverpod/hooks_riverpod.dart"; -import "package:nexus/controllers/settings.dart"; import "package:window_manager/window_manager.dart"; -class Appbar extends ConsumerWidget implements PreferredSizeWidget { +class Appbar extends StatelessWidget implements PreferredSizeWidget { final Widget? leading; final Widget? title; final Color? backgroundColor; @@ -27,7 +25,7 @@ class Appbar extends ConsumerWidget implements PreferredSizeWidget { Size get preferredSize => const .fromHeight(kToolbarHeight); @override - Widget build(BuildContext context, WidgetRef ref) { + Widget build(BuildContext context) { Future maximize() async { final isMaximized = await windowManager.isMaximized(); @@ -39,13 +37,7 @@ class Appbar extends ConsumerWidget implements PreferredSizeWidget { } return GestureDetector( - onPanStart: ref - .watch(SettingsController.provider) - .whenOrNull( - data: (settings) => settings.linuxMobileMode - ? null - : (_) => windowManager.startDragging(), - ), + onPanStart: (_) => windowManager.startDragging(), child: AppBar( leading: InkWell(onTap: onTap, child: leading), backgroundColor: backgroundColor, diff --git a/lib/widgets/avatar_or_hash.dart b/lib/widgets/avatar_or_hash.dart index 1cca8c6..20f4eac 100644 --- a/lib/widgets/avatar_or_hash.dart +++ b/lib/widgets/avatar_or_hash.dart @@ -2,8 +2,8 @@ import "package:color_hash/color_hash.dart"; import "package:cross_cache/cross_cache.dart"; import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/client_state.dart"; -import "package:nexus/controllers/cross_cache.dart"; +import "package:nexus/controllers/client_state_controller.dart"; +import "package:nexus/controllers/cross_cache_controller.dart"; import "package:nexus/helpers/extensions/get_headers.dart"; import "package:nexus/helpers/extensions/mxc_to_https.dart"; diff --git a/lib/widgets/composer/composer.dart b/lib/widgets/composer/composer.dart index ef91dbe..d4c645d 100644 --- a/lib/widgets/composer/composer.dart +++ b/lib/widgets/composer/composer.dart @@ -4,7 +4,7 @@ import "package:flutter/services.dart"; import "package:flutter_hooks/flutter_hooks.dart"; import "package:fluttertagger/fluttertagger.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; -import "package:nexus/controllers/power_level.dart"; +import "package:nexus/controllers/power_level_controller.dart"; import "package:nexus/models/content/message.dart"; import "package:nexus/models/event.dart"; import "package:nexus/models/relation_type.dart"; diff --git a/lib/widgets/composer/mention_overlay.dart b/lib/widgets/composer/mention_overlay.dart index 962ea84..ea5dc6a 100644 --- a/lib/widgets/composer/mention_overlay.dart +++ b/lib/widgets/composer/mention_overlay.dart @@ -1,8 +1,8 @@ import "package:flutter/material.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; -import "package:nexus/controllers/members_by_status.dart"; -import "package:nexus/controllers/rooms.dart"; -import "package:nexus/controllers/via.dart"; +import "package:nexus/controllers/members_by_status_controller.dart"; +import "package:nexus/controllers/rooms_controller.dart"; +import "package:nexus/controllers/via_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; import "package:nexus/helpers/extensions/get_localpart.dart"; import "package:nexus/models/content/membership.dart"; diff --git a/lib/widgets/emoji_picker_button.dart b/lib/widgets/emoji_picker_button.dart index 2ac906a..bbe1cdc 100644 --- a/lib/widgets/emoji_picker_button.dart +++ b/lib/widgets/emoji_picker_button.dart @@ -1,7 +1,7 @@ import "package:emoji_text_field/emoji_text_field.dart"; import "package:flutter/material.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; -import "package:nexus/controllers/emoji.dart"; +import "package:nexus/controllers/emoji_controller.dart"; class EmojiPickerButton extends HookConsumerWidget { final TextEditingController? controller; diff --git a/lib/widgets/expandable_image.dart b/lib/widgets/expandable_image.dart index 4990458..fdfabff 100644 --- a/lib/widgets/expandable_image.dart +++ b/lib/widgets/expandable_image.dart @@ -2,7 +2,7 @@ import "package:cross_cache/cross_cache.dart"; import "package:flutter/material.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:m3e_buttons/m3e_buttons.dart"; -import "package:nexus/controllers/cross_cache.dart"; +import "package:nexus/controllers/cross_cache_controller.dart"; import "package:nexus/helpers/extensions/get_headers.dart"; import "package:nexus/widgets/error_dialog.dart"; diff --git a/lib/widgets/highlight_wrapper.dart b/lib/widgets/highlight_wrapper.dart index c7e568e..920db9e 100644 --- a/lib/widgets/highlight_wrapper.dart +++ b/lib/widgets/highlight_wrapper.dart @@ -14,7 +14,7 @@ class HighlightWrapper extends StatelessWidget { ? Theme.of(context).colorScheme.onSurface.withAlpha(50) : Colors.transparent, duration: .new(milliseconds: 250), - child: Material(color: Colors.transparent, child: child), + child: child, ), ); } diff --git a/lib/widgets/html/html.dart b/lib/widgets/html/html.dart index 4113eac..85ec9ae 100644 --- a/lib/widgets/html/html.dart +++ b/lib/widgets/html/html.dart @@ -3,8 +3,8 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart"; -import "package:nexus/controllers/client_state.dart"; -import "package:nexus/controllers/cross_cache.dart"; +import "package:nexus/controllers/client_state_controller.dart"; +import "package:nexus/controllers/cross_cache_controller.dart"; import "package:nexus/helpers/extensions/get_headers.dart"; import "package:nexus/helpers/extensions/link_to_mention.dart"; import "package:nexus/helpers/extensions/mxc_to_https.dart"; diff --git a/lib/widgets/html/mention_chip.dart b/lib/widgets/html/mention_chip.dart index f6105e3..8e0ce11 100644 --- a/lib/widgets/html/mention_chip.dart +++ b/lib/widgets/html/mention_chip.dart @@ -1,6 +1,6 @@ import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/user.dart"; +import "package:nexus/controllers/user_controller.dart"; import "package:nexus/helpers/extensions/link_to_mention.dart"; import "package:nexus/helpers/extensions/show_user_popover.dart"; diff --git a/lib/widgets/join_dialog.dart b/lib/widgets/join_dialog.dart index 50b36f2..d420dea 100644 --- a/lib/widgets/join_dialog.dart +++ b/lib/widgets/join_dialog.dart @@ -2,9 +2,9 @@ import "package:collection/collection.dart"; import "package:flutter/material.dart"; import "package:flutter_hooks/flutter_hooks.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; -import "package:nexus/controllers/client.dart"; -import "package:nexus/controllers/key.dart"; -import "package:nexus/controllers/spaces.dart"; +import "package:nexus/controllers/client_controller.dart"; +import "package:nexus/controllers/key_controller.dart"; +import "package:nexus/controllers/spaces_controller.dart"; import "package:nexus/helpers/extensions/link_to_mention.dart"; class JoinDialog extends HookWidget { diff --git a/lib/widgets/lazy_loading/message_avatar.dart b/lib/widgets/lazy_loading/message_avatar.dart index 93c2554..b65e377 100644 --- a/lib/widgets/lazy_loading/message_avatar.dart +++ b/lib/widgets/lazy_loading/message_avatar.dart @@ -1,6 +1,6 @@ import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/author.dart"; +import "package:nexus/controllers/author_controller.dart"; import "package:nexus/helpers/extensions/get_localpart.dart"; import "package:nexus/helpers/extensions/show_user_popover.dart"; import "package:nexus/models/event.dart"; diff --git a/lib/widgets/lazy_loading/message_displayname.dart b/lib/widgets/lazy_loading/message_displayname.dart index bd5733c..a817122 100644 --- a/lib/widgets/lazy_loading/message_displayname.dart +++ b/lib/widgets/lazy_loading/message_displayname.dart @@ -1,6 +1,6 @@ import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/author.dart"; +import "package:nexus/controllers/author_controller.dart"; import "package:nexus/helpers/extensions/get_localpart.dart"; import "package:nexus/helpers/extensions/show_user_popover.dart"; import "package:nexus/helpers/extensions/string_to_color.dart"; diff --git a/lib/widgets/member_list.dart b/lib/widgets/member_list.dart index 92bb179..6326421 100644 --- a/lib/widgets/member_list.dart +++ b/lib/widgets/member_list.dart @@ -4,8 +4,8 @@ import "package:flutter_hooks/flutter_hooks.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:m3e_buttons/m3e_buttons.dart"; import "package:m3e_card_list/m3e_card_list.dart"; -import "package:nexus/controllers/members_by_status.dart"; -import "package:nexus/controllers/members_grouped.dart"; +import "package:nexus/controllers/members_by_status_controller.dart"; +import "package:nexus/controllers/members_grouped_controller.dart"; import "package:nexus/helpers/extensions/get_localpart.dart"; import "package:nexus/helpers/extensions/string_to_color.dart"; import "package:nexus/models/content/membership.dart"; @@ -33,156 +33,152 @@ class MemberList extends HookConsumerWidget { return Drawer( shape: Border(), - child: Scaffold( - backgroundColor: Colors.transparent, - body: Column( - children: [ - Padding( - padding: .symmetric(vertical: 8), - child: M3EToggleButtonGroup( - selectedIndex: statusIndex.value, - onSelectedIndexChanged: (index) => - statusIndex.value = index ?? statusIndex.value, - actions: options - .mapTo( - (name, value) => M3EToggleButtonGroupAction( - checkedLabel: Text( - "$name${switch (ref.watch(MembersByStatusController.provider(.new(roomId: roomId, status: value)))) { - AsyncData(:final value) || AsyncLoading(:final value?) => " (${value.length})", - _ => "", - }}", + child: Column( + children: [ + if (Scaffold.of(context).hasEndDrawer) + AppBar( + scrolledUnderElevation: 0, + leading: Icon(Icons.people), + title: Text("Members"), + actionsPadding: .only(right: 4), + actions: [ + IconButton( + onPressed: Scaffold.of(context).closeEndDrawer, + icon: Icon(Icons.close), + tooltip: "Close member list", + ), + ], + ), + Padding( + padding: .symmetric(vertical: 8), + child: M3EToggleButtonGroup( + selectedIndex: statusIndex.value, + onSelectedIndexChanged: (index) => + statusIndex.value = index ?? statusIndex.value, + actions: options + .mapTo( + (name, value) => M3EToggleButtonGroupAction( + checkedLabel: Text( + "$name${switch (ref.watch(MembersByStatusController.provider(.new(roomId: roomId, status: value)))) { + AsyncData(:final value) || AsyncLoading(:final value?) => " (${value.length})", + _ => "", + }}", + ), + label: Text(name), + ), + ) + .toList(), + ), + ), + + switch (ref.watch( + MembersGroupedController.provider( + .new(roomId: roomId, status: status), + ), + )) { + AsyncError(:final error, :final stackTrace) => ErrorDialog( + error, + stackTrace, + ), + AsyncData(:final value) || AsyncLoading(:final value?) => + value.isEmpty + ? Center( + child: Padding( + padding: .symmetric(vertical: 18), + child: Text( + "No ${options.keys.toIList()[statusIndex.value]} Members", + style: Theme.of(context).textTheme.headlineSmall, ), - label: Text(name), ), ) - .toList(), - ), - ), - - switch (ref.watch( - MembersGroupedController.provider( - .new(roomId: roomId, status: status), - ), - )) { - AsyncError(:final error, :final stackTrace) => ErrorDialog( - error, - stackTrace, - ), - AsyncData(:final value) || AsyncLoading(:final value?) => - value.isEmpty - ? Center( - child: Padding( - padding: .symmetric(vertical: 18), - child: Text( - "No ${options.keys.toIList()[statusIndex.value]} Members", - style: Theme.of(context).textTheme.headlineSmall, - ), - ), - ) - : Expanded( - child: CustomScrollView( - slivers: [ - for (final MapEntry(key: powerLevel, value: members) - in value) ...[ - SliverToBoxAdapter( - child: Padding( - padding: .symmetric(horizontal: 16), - child: DividerText( - powerLevel == null - ? "Creators" - : "Power Level $powerLevel", - ), + : Expanded( + child: CustomScrollView( + slivers: [ + for (final MapEntry(key: powerLevel, value: members) + in value) ...[ + SliverToBoxAdapter( + child: Padding( + padding: .symmetric(horizontal: 16), + child: DividerText( + powerLevel == null + ? "Creators" + : "Power Level $powerLevel", ), ), - SliverM3ECardList( - padding: .all(4), - color: Theme.of( - context, - ).colorScheme.surfaceContainerHigh, - margin: .symmetric(horizontal: 12, vertical: 4), - itemCount: members.length, - itemBuilder: (context, index) => - switch (members[index].content) { - MembershipContent( - :final avatarUrl, - :final displayName, - ) => - ListTile( - title: Text( - displayName ?? - members[index] - .stateKey! - .localpart, - overflow: .ellipsis, - style: .new( - color: members[index] + ), + SliverM3ECardList( + padding: .all(4), + color: Theme.of( + context, + ).colorScheme.surfaceContainerHigh, + margin: .symmetric(horizontal: 12, vertical: 4), + itemCount: members.length, + itemBuilder: (context, index) => + switch (members[index].content) { + MembershipContent( + :final avatarUrl, + :final displayName, + ) => + ListTile( + title: Text( + displayName ?? + members[index] .stateKey! - .colorHash, - fontWeight: .bold, - ), - ), - subtitle: Text( - members[index].stateKey!, - overflow: .ellipsis, - ), - leading: AvatarOrHash( - avatarUrl, - height: 36, - displayName ?? - members[index] - .stateKey! - .localpart, + .localpart, + overflow: .ellipsis, + style: .new( + color: members[index] + .stateKey! + .colorHash, + fontWeight: .bold, ), ), - _ => throw Exception( - "Member content was not MembershipContent", - ), - }, - onTap: (index) { - final member = members[index]; - if (member.content - case MembershipContent content) { - showModalBottomSheet( - constraints: .loose( - .new( - 500, - (context.size?.height ?? 1000) - 80, + subtitle: Text( + members[index].stateKey!, + overflow: .ellipsis, + ), + leading: AvatarOrHash( + avatarUrl, + height: 36, + displayName ?? + members[index] + .stateKey! + .localpart, ), ), - isScrollControlled: true, - context: context, - builder: (context) => UserBottomSheet( - content, - member.stateKey!, - roomId: roomId, + _ => throw Exception( + "Member content was not MembershipContent", + ), + }, + onTap: (index) { + final member = members[index]; + if (member.content + case MembershipContent content) { + showModalBottomSheet( + constraints: BoxConstraints.loose( + Size( + 500, + (context.size?.height ?? 1000) - 80, ), - ); - } - }, - ), - ], + ), + isScrollControlled: true, + context: context, + builder: (context) => UserBottomSheet( + content, + member.stateKey!, + roomId: roomId, + ), + ); + } + }, + ), ], - ), + ], ), - AsyncLoading _ => Loading(), - }, - ], - ), - appBar: Scaffold.of(context).hasEndDrawer - ? AppBar( - scrolledUnderElevation: 0, - leading: Icon(Icons.people), - title: Text("Members"), - actionsPadding: .only(right: 4), - actions: [ - IconButton( - onPressed: Scaffold.of(context).closeEndDrawer, - icon: Icon(Icons.close), - tooltip: "Close member list", - ), - ], - ) - : null, + ), + AsyncLoading _ => Loading(), + }, + ], ), ); } diff --git a/lib/widgets/message_image.dart b/lib/widgets/message_image.dart index 9a4caaf..654ac27 100644 --- a/lib/widgets/message_image.dart +++ b/lib/widgets/message_image.dart @@ -2,7 +2,7 @@ import "package:cross_cache/cross_cache.dart"; import "package:flutter/material.dart"; import "package:flutter_blurhash/flutter_blurhash.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/cross_cache.dart"; +import "package:nexus/controllers/cross_cache_controller.dart"; import "package:nexus/helpers/extensions/get_headers.dart"; import "package:nexus/models/info/image.dart" as i; import "package:nexus/widgets/expandable_image.dart"; diff --git a/lib/widgets/pinned_events_drawer.dart b/lib/widgets/pinned_events_drawer.dart deleted file mode 100644 index 7cb9fe3..0000000 --- a/lib/widgets/pinned_events_drawer.dart +++ /dev/null @@ -1,93 +0,0 @@ -import "package:fast_immutable_collections/fast_immutable_collections.dart"; -import "package:flutter/material.dart"; -import "package:hooks_riverpod/hooks_riverpod.dart"; -import "package:nexus/controllers/pinned_events.dart"; -import "package:nexus/models/event.dart"; -import "package:nexus/widgets/error_dialog.dart"; -import "package:nexus/widgets/loading.dart"; -import "package:nexus/widgets/renderers/event.dart"; - -class PinnedEventsDrawer extends HookConsumerWidget { - final String roomId; - final IList Function(Event event) getEventOptions; - final Future Function(String eventId) jumpToId; - const PinnedEventsDrawer( - this.roomId, { - required this.getEventOptions, - required this.jumpToId, - super.key, - }); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final pinsProvider = ref.watch(PinnedEventsController.provider(roomId)); - final theme = Theme.of(context); - - return Drawer( - width: 400, - child: Scaffold( - backgroundColor: Colors.transparent, - appBar: AppBar( - scrolledUnderElevation: 0, - leading: Icon(Icons.push_pin), - title: Text("Pinned Events"), - actionsPadding: .only(right: 4), - actions: [ - IconButton( - onPressed: Scaffold.of(context).closeEndDrawer, - icon: Icon(Icons.close), - tooltip: "Close pinned events", - ), - ], - ), - body: switch (pinsProvider) { - AsyncData(:final value) when value.isEmpty => Center( - child: Column( - mainAxisSize: .min, - children: [ - Icon( - Icons.push_pin_outlined, - size: 48, - color: theme.colorScheme.onSurface, - ), - SizedBox(height: 12), - Text("No pinned events", style: theme.textTheme.headlineSmall), - ], - ), - ), - AsyncData(:final value) || - AsyncLoading(:final value?) => ListView.builder( - padding: .all(8), - reverse: true, - itemCount: value.length, - itemBuilder: (context, index) { - final event = value.reversed[index]; - - return InkWell( - borderRadius: .circular(12), - onTap: () { - Navigator.of(context).pop(); - jumpToId(event.eventId); - }, - child: Padding( - padding: .symmetric(vertical: 4), - child: EventRenderer( - event, - maxLines: 2, - isGrouped: false, - getEventOptions: getEventOptions, - ), - ), - ); - }, - ), - AsyncLoading() => Loading(), - AsyncError(:final error, :final stackTrace) => ErrorDialog( - error, - stackTrace, - ), - }, - ), - ); - } -} diff --git a/lib/widgets/reaction_row.dart b/lib/widgets/reaction_row.dart index 2035b59..e41c2eb 100644 --- a/lib/widgets/reaction_row.dart +++ b/lib/widgets/reaction_row.dart @@ -2,10 +2,10 @@ import "package:cross_cache/cross_cache.dart"; import "package:flutter/material.dart"; import "package:flutter_hooks/flutter_hooks.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/client_state.dart"; -import "package:nexus/controllers/cross_cache.dart"; -import "package:nexus/controllers/reactions.dart"; -import "package:nexus/controllers/room_chat.dart"; +import "package:nexus/controllers/client_state_controller.dart"; +import "package:nexus/controllers/cross_cache_controller.dart"; +import "package:nexus/controllers/reactions_controller.dart"; +import "package:nexus/controllers/room_chat_controller.dart"; import "package:nexus/helpers/extensions/get_headers.dart"; import "package:nexus/helpers/extensions/mxc_to_https.dart"; import "package:nexus/models/event.dart"; @@ -21,98 +21,93 @@ class ReactionRow extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final clientState = ref.watch(ClientStateController.provider); - return Padding( - padding: .only(top: 4), - child: switch (ref.watch( - ReactionsController.provider( - .new(roomId: event.roomId, eventRowId: event.rowId), - ), - )) { - AsyncData(value: final IMap>? reactors) || - AsyncLoading(value: final reactors) => Wrap( - spacing: 4, - runSpacing: 4, - children: event.reactions - .where((_, value) => value != 0) - .mapTo( - (reaction, count) => HookBuilder( - builder: (context) { - final enabled = useState(true); + return switch (ref.watch( + ReactionsController.provider( + .new(roomId: event.roomId, eventRowId: event.rowId), + ), + )) { + AsyncData(value: final IMap>? reactors) || + AsyncLoading(value: final reactors) => Wrap( + spacing: 4, + runSpacing: 4, + children: event.reactions + .where((_, value) => value != 0) + .mapTo( + (reaction, count) => HookBuilder( + builder: (context) { + final enabled = useState(true); - final selected = - reactors?[reaction]?.contains(clientState!.userId) ?? - false; - return Tooltip( - message: reactors?[reaction]?.join(", ") ?? "", - child: ChoiceChip( - showCheckmark: false, - selected: selected, - label: Row( - mainAxisSize: .min, - spacing: 8, - children: [ - Flexible( - child: reaction.startsWith("mxc://") - ? Image( - height: 20, - image: CachedNetworkImage( - headers: ref.headers, - Uri.parse(reaction) - .mxcToHttps( - clientState!.homeserverUrl!, - ) - .toString(), - ref.watch( - CrossCacheController.provider, - ), - ), - ) - : Text(reaction, overflow: .ellipsis), - ), - Text(count.toString(), overflow: .ellipsis), - ], - ), - onSelected: enabled.value - ? (value) async { - enabled.value = false; - try { - final controller = ref.watch( - RoomChatController.provider( - event.roomId, - ).notifier, - ); - - if (selected) { - await controller - .removeReaction( - reaction, - event, - clientState!.userId!, - ) - .onError(showError); - } else { - await controller - .sendReaction(reaction, event) - .onError(showError); - } - } finally { - enabled.value = true; - } - } - : null, + final selected = + reactors?[reaction]?.contains(clientState!.userId) ?? + false; + return Tooltip( + message: reactors?[reaction]?.join(", ") ?? "", + child: ChoiceChip( + showCheckmark: false, + selected: selected, + label: Row( + mainAxisSize: .min, + spacing: 8, + children: [ + Flexible( + child: reaction.startsWith("mxc://") + ? Image( + height: 20, + image: CachedNetworkImage( + headers: ref.headers, + Uri.parse(reaction) + .mxcToHttps( + clientState!.homeserverUrl!, + ) + .toString(), + ref.watch(CrossCacheController.provider), + ), + ) + : Text(reaction, overflow: .ellipsis), + ), + Text(count.toString(), overflow: .ellipsis), + ], ), - ); - }, - ), - ) - .toList(), - ), + onSelected: enabled.value + ? (value) async { + enabled.value = false; + try { + final controller = ref.watch( + RoomChatController.provider( + event.roomId, + ).notifier, + ); - AsyncError(:final error, :final stackTrace) => ErrorDialog( - error, - stackTrace, - ), - }, - ); + if (selected) { + await controller + .removeReaction( + reaction, + event, + clientState!.userId!, + ) + .onError(showError); + } else { + await controller + .sendReaction(reaction, event) + .onError(showError); + } + } finally { + enabled.value = true; + } + } + : null, + ), + ); + }, + ), + ) + .toList(), + ), + + AsyncError(:final error, :final stackTrace) => ErrorDialog( + error, + stackTrace, + ), + }; } } diff --git a/lib/widgets/renderers/generic_event.dart b/lib/widgets/renderers/generic_event.dart index 6dfae53..4f4380a 100644 --- a/lib/widgets/renderers/generic_event.dart +++ b/lib/widgets/renderers/generic_event.dart @@ -7,7 +7,7 @@ class GenericEventRenderer extends StatelessWidget { @override Widget build(BuildContext context) => Padding( - padding: .symmetric(vertical: 4), + padding: .only(bottom: 8), child: Row( spacing: 8, children: [ diff --git a/lib/widgets/renderers/message.dart b/lib/widgets/renderers/message.dart index b731cd0..3470246 100644 --- a/lib/widgets/renderers/message.dart +++ b/lib/widgets/renderers/message.dart @@ -2,8 +2,8 @@ import "package:collection/collection.dart"; import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:linkify/linkify.dart"; -import "package:nexus/controllers/client_state.dart"; -import "package:nexus/controllers/event.dart"; +import "package:nexus/controllers/client_state_controller.dart"; +import "package:nexus/controllers/event_controller.dart"; import "package:nexus/helpers/extensions/mxc_to_https.dart"; import "package:nexus/models/content/encrypted.dart"; import "package:nexus/models/content/message.dart"; @@ -271,27 +271,19 @@ class MessageRenderer extends ConsumerWidget { case final UrlElement link?) UrlPreview(link.url), + SizedBox(height: 4), ReactionRow(event), ], ], ), - MessageContent(:final body) => - body == null - ? Text( - "This message is redacted", - style: errorStyle, - ) - : Row( - spacing: 8, - mainAxisSize: .min, - children: [ - Text( - "Unknown message type:", - style: errorStyle, - ), - Text(body), - ], - ), + MessageContent(:final body) => Row( + spacing: 8, + mainAxisSize: .min, + children: [ + Text("Unknown message type:", style: errorStyle), + Text(body), + ], + ), _ => throw Exception("This is impossible"), }, ], diff --git a/lib/widgets/room_appbar.dart b/lib/widgets/room_appbar.dart index 4598881..e1d5708 100644 --- a/lib/widgets/room_appbar.dart +++ b/lib/widgets/room_appbar.dart @@ -1,26 +1,24 @@ import "package:flutter/material.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; -import "package:nexus/controllers/rooms.dart"; +import "package:nexus/controllers/rooms_controller.dart"; import "package:nexus/helpers/extensions/mxc_to_https.dart"; import "package:nexus/widgets/appbar.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; import "package:nexus/widgets/expandable_image.dart"; -import "package:nexus/controllers/client_state.dart"; +import "package:nexus/controllers/client_state_controller.dart"; import "package:nexus/widgets/linkified_text.dart"; import "package:nexus/widgets/room_menu.dart"; class RoomAppbar extends ConsumerWidget implements PreferredSizeWidget { final bool isDesktop; final void Function(BuildContext context)? onOpenMemberList; - final void Function()? onOpenPinnedMessagesList; - final void Function() onOpenDrawer; + final void Function(BuildContext context) onOpenDrawer; final String? roomId; const RoomAppbar({ required this.roomId, required this.isDesktop, required this.onOpenDrawer, this.onOpenMemberList, - this.onOpenPinnedMessagesList, super.key, }); @@ -103,7 +101,7 @@ class RoomAppbar extends ConsumerWidget implements PreferredSizeWidget { height: 24, fallback: Icon(Icons.numbers), ) - : DrawerButton(onPressed: onOpenDrawer), + : DrawerButton(onPressed: () => onOpenDrawer(context)), scrolledUnderElevation: 0, title: room == null ? null @@ -130,7 +128,7 @@ class RoomAppbar extends ConsumerWidget implements PreferredSizeWidget { ? .new() : .new([ IconButton( - onPressed: onOpenPinnedMessagesList?.call, + onPressed: null, icon: Icon(Icons.push_pin), tooltip: "Open pinned messages", ), diff --git a/lib/widgets/room_chat.dart b/lib/widgets/room_chat.dart index 0fdd7c7..e1036cc 100644 --- a/lib/widgets/room_chat.dart +++ b/lib/widgets/room_chat.dart @@ -4,20 +4,19 @@ import "package:flutter/services.dart"; import "package:flutter_hooks/flutter_hooks.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:measure_size/measure_size.dart"; -import "package:nexus/controllers/account_data.dart"; -import "package:nexus/controllers/client.dart"; -import "package:nexus/controllers/client_state.dart"; -import "package:nexus/controllers/pinned_ids.dart"; -import "package:nexus/controllers/power_level.dart"; -import "package:nexus/controllers/rooms.dart"; -import "package:nexus/controllers/room_chat.dart"; -import "package:nexus/controllers/via.dart"; +import "package:nexus/controllers/account_data_controller.dart"; +import "package:nexus/controllers/client_controller.dart"; +import "package:nexus/controllers/client_state_controller.dart"; +import "package:nexus/controllers/power_level_controller.dart"; +import "package:nexus/controllers/rooms_controller.dart"; +import "package:nexus/controllers/room_chat_controller.dart"; +import "package:nexus/controllers/via_controller.dart"; +import "package:nexus/models/configs/power_level_config.dart"; import "package:nexus/models/content/message.dart"; import "package:nexus/models/event.dart"; import "package:nexus/models/relation_type.dart"; import "package:nexus/widgets/composer/composer.dart"; import "package:nexus/widgets/emoji_picker_button.dart"; -import "package:nexus/widgets/pinned_events_drawer.dart"; import "package:nexus/widgets/renderers/event.dart"; import "package:nexus/widgets/member_list.dart"; import "package:nexus/widgets/room_appbar.dart"; @@ -62,7 +61,8 @@ class RoomChat extends HookConsumerWidget { appBar: RoomAppbar( roomId: this.roomId, isDesktop: isDesktop, - onOpenDrawer: () => Scaffold.of(context).openDrawer(), + onOpenDrawer: (_) => Scaffold.of(context).openDrawer(), + onOpenMemberList: null, ), body: nothing, ); @@ -81,27 +81,6 @@ class RoomChat extends HookConsumerWidget { final topEventBeforeLoad = useState(null); - Future jumpToId(String eventId) async { - final index = controllerData.value?.indexWhere( - (element) => element.eventId == eventId, - ); - if (index == null) return; - - listController.value.animateToItem( - index: index, - scrollController: scrollController, - alignment: 0.5, - duration: (_) => .new(milliseconds: 700), - curve: (_) => Curves.easeInOut, - ); - highlightedEvent.value = eventId; - await Future.delayed(.new(seconds: 1), () { - if (highlightedEvent.value == eventId) { - highlightedEvent.value = null; - } - }); - } - Future loadOlder() async { if (controllerData case AsyncData(:final value?)) { topEventBeforeLoad.value = value.firstOrNull?.eventId; @@ -198,7 +177,6 @@ class RoomChat extends HookConsumerWidget { IList getEventOptions(Event event) { final danger = theme.colorScheme.error; final isSentByMe = event.sender == userId; - return [ if (ref.watch( PowerLevelController.provider( @@ -251,7 +229,7 @@ class RoomChat extends HookConsumerWidget { ), if (ref.watch( PowerLevelController.provider( - .new(eventType: .message, roomId: roomId), + PowerLevelConfig(eventType: .message, roomId: roomId), ), )) PopupMenuItem( @@ -271,35 +249,6 @@ class RoomChat extends HookConsumerWidget { }, child: ListTile(leading: Icon(Icons.edit), title: Text("Edit")), ), - if (ref.watch( - PowerLevelController.provider( - .state(eventType: .pinnedEvents, roomId: roomId), - ), - )) - switch (ref - .watch(PinnedIdsController.provider(roomId)) - .contains(event.eventId)) { - bool isPinned => PopupMenuItem( - onTap: () async { - try { - final notifier = ref.read( - PinnedIdsController.provider(roomId).notifier, - ); - if (isPinned) { - await notifier.removePin(event.eventId); - } else { - await notifier.addPin(event.eventId); - } - } catch (error, stackTrace) { - showError(error, stackTrace); - } - }, - child: ListTile( - leading: Icon(Icons.push_pin), - title: Text(isPinned == true ? "Unpin Event" : "Pin Event"), - ), - ), - }, PopupMenuItem( onTap: () async { final room = ref.watch( @@ -435,130 +384,131 @@ class RoomChat extends HookConsumerWidget { } return Scaffold( - endDrawer: PinnedEventsDrawer( - roomId, - getEventOptions: getEventOptions, - jumpToId: jumpToId, + appBar: RoomAppbar( + roomId: roomId, + isDesktop: isDesktop, + onOpenDrawer: (_) => Scaffold.of(context).openDrawer(), + onOpenMemberList: (thisContext) { + memberListOpened.value = !memberListOpened.value; + Scaffold.of(thisContext).openEndDrawer(); + }, ), - body: Builder( - builder: (middleContext) => Scaffold( - endDrawer: showMembersByDefault ? null : MemberList(roomId), - appBar: RoomAppbar( - roomId: roomId, - isDesktop: isDesktop, - onOpenDrawer: Scaffold.of(context).openDrawer, - onOpenMemberList: (thisContext) { - memberListOpened.value = !memberListOpened.value; - Scaffold.of(thisContext).openEndDrawer(); - }, - onOpenPinnedMessagesList: () { - Scaffold.of(middleContext).openEndDrawer(); - }, - ), - body: Row( - children: [ - Expanded( - child: Stack( - children: [ - Positioned.fill( - child: Padding( - padding: .symmetric(horizontal: 4), - child: switch (controllerData) { - AsyncData(:final value?) || - AsyncLoading(:final value?) => CustomScrollView( - controller: scrollController, - slivers: [ - SliverToBoxAdapter( - child: Padding( - padding: .symmetric(vertical: 36), - child: Center( - child: ElevatedButton( - onPressed: controllerData is AsyncData - ? loadOlder - : null, - child: Text("Load More"), - ), - ), + body: Row( + children: [ + Expanded( + child: Stack( + children: [ + Positioned.fill( + child: Padding( + padding: .symmetric(horizontal: 4), + child: switch (controllerData) { + AsyncData(:final value?) || + AsyncLoading(:final value?) => CustomScrollView( + controller: scrollController, + slivers: [ + SliverToBoxAdapter( + child: Padding( + padding: .symmetric(vertical: 36), + child: Center( + child: ElevatedButton( + onPressed: controllerData is AsyncData + ? loadOlder + : null, + child: Text("Load More"), ), ), - - SuperSliverList.builder( - listController: listController.value, - itemCount: value.length, - itemBuilder: (_, index) { - final event = value[index]; - final previousEvent = value.getOrNull( - index - 1, - ); - return HighlightWrapper( - EventRenderer( - event, - onTapReply: () => - jumpToId(event.replyTo!), - getEventOptions: getEventOptions, - isGrouped: - previousEvent?.content - is MessageContent && - previousEvent?.redactedBy == null && - previousEvent?.relationType != - "m.replace" && - "${event.sender}${event.pmp?.id}" == - "${previousEvent?.sender}${previousEvent?.pmp?.id}", - ), - isHighlighted: - highlightedEvent.value == event.eventId, - ); - }, - ), - - SliverPadding( - padding: .only(bottom: composerSize.value), - ), - ], + ), ), - AsyncData() => nothing, - AsyncLoading() => Loading(), - AsyncError(:final error, :final stackTrace) => - ErrorDialog(error, stackTrace), - }, - ), - ), - Positioned( - bottom: 0, - left: 0, - right: 0, - child: MeasureSize( - onChange: (size) => composerSize.value = size.height, - child: Composer( - roomId, - node: composerNode, - onSend: - (text, {required shouldMention, required tags}) => - notifier - .send( - text, - tags: tags, - relationType: relationType.value, - shouldMention: shouldMention, - relation: relatedEvent.value, - ) - .onError(showError), - relationType: relationType.value, - relatedEvent: relatedEvent.value, - onDismiss: () => relatedEvent.value = null, - ), - ), - ), - ], - ), - ), - if (memberListOpened.value == true && showMembersByDefault) - MemberList(roomId), - ], + SuperSliverList.builder( + listController: listController.value, + itemCount: value.length, + itemBuilder: (_, index) { + final event = value[index]; + final previousEvent = value.getOrNull(index - 1); + return HighlightWrapper( + EventRenderer( + event, + onTapReply: () async { + final replyId = event.replyTo; + listController.value.animateToItem( + index: value.indexWhere( + (element) => element.eventId == replyId, + ), + scrollController: scrollController, + alignment: 0.5, + duration: (_) => .new(milliseconds: 700), + curve: (_) => Curves.easeInOut, + ); + highlightedEvent.value = replyId; + await Future.delayed(.new(seconds: 1), () { + if (highlightedEvent.value == replyId) { + highlightedEvent.value = null; + } + }); + }, + getEventOptions: getEventOptions, + isGrouped: + previousEvent?.content + is MessageContent && + previousEvent?.redactedBy == null && + previousEvent?.relationType != + "m.replace" && + "${event.sender}${event.pmp?.id}" == + "${previousEvent?.sender}${previousEvent?.pmp?.id}", + ), + isHighlighted: + highlightedEvent.value == event.eventId, + ); + }, + ), + + SliverPadding( + padding: .only(bottom: composerSize.value), + ), + ], + ), + AsyncData() => nothing, + AsyncLoading() => Loading(), + AsyncError(:final error, :final stackTrace) => + ErrorDialog(error, stackTrace), + }, + ), + ), + Positioned( + bottom: 0, + left: 0, + right: 0, + child: MeasureSize( + onChange: (size) => composerSize.value = size.height, + child: Composer( + roomId, + node: composerNode, + onSend: (text, {required shouldMention, required tags}) => + notifier + .send( + text, + tags: tags, + relationType: relationType.value, + shouldMention: shouldMention, + relation: relatedEvent.value, + ) + .onError(showError), + relationType: relationType.value, + relatedEvent: relatedEvent.value, + onDismiss: () => relatedEvent.value = null, + ), + ), + ), + ], + ), ), - ), + + if (memberListOpened.value == true && showMembersByDefault) + MemberList(roomId), + ], ), + endDrawer: showMembersByDefault ? null : MemberList(roomId), ); } } diff --git a/lib/widgets/room_menu.dart b/lib/widgets/room_menu.dart index 95936df..f4313fa 100644 --- a/lib/widgets/room_menu.dart +++ b/lib/widgets/room_menu.dart @@ -2,8 +2,8 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/client.dart"; -import "package:nexus/controllers/via.dart"; +import "package:nexus/controllers/client_controller.dart"; +import "package:nexus/controllers/via_controller.dart"; import "package:nexus/models/room.dart"; class RoomMenu extends ConsumerWidget { diff --git a/lib/widgets/settings/dialog_list_tile.dart b/lib/widgets/settings/dialog_list_tile.dart deleted file mode 100644 index f0a236c..0000000 --- a/lib/widgets/settings/dialog_list_tile.dart +++ /dev/null @@ -1,67 +0,0 @@ -import "package:flutter/material.dart"; -import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/widgets/settings/radio_dialog.dart"; - -class DialogListTile extends ConsumerWidget { - final T? initialValue; - final String title; - final Widget? subtitle; - final List options; - final bool required; - final Icon icon; - final void Function(T value)? onChanged; - final String Function(T option) getName; - const DialogListTile({ - super.key, - required this.icon, - required this.title, - required this.initialValue, - required this.options, - required this.onChanged, - required this.getName, - this.subtitle, - this.required = true, - }); - - @override - Widget build(BuildContext context, WidgetRef ref) => FormField( - validator: (value) => - value == null && required == true ? "This field is required." : null, - initialValue: initialValue, - builder: (field) => InputDecorator( - decoration: InputDecoration( - errorText: field.errorText, - contentPadding: EdgeInsets.zero, - enabledBorder: InputBorder.none, - ), - child: ListTile( - enabled: onChanged != null, - onTap: () => showDialog( - context: context, - builder: (context) => RadioDialog( - title: title, - getName: getName, - onChanged: onChanged == null - ? null - : (value) { - field.didChange(value); - onChanged!.call(value); - }, - value: field.value, - options: options, - ), - ), - title: Text(title), - subtitle: subtitle, - leading: icon, - trailing: Chip( - label: Text( - field.value == null ? "None" : getName(field.value as T), - overflow: TextOverflow.ellipsis, - style: onChanged == null ? .new(color: Colors.grey) : null, - ), - ), - ), - ), - ); -} diff --git a/lib/widgets/settings/radio_dialog.dart b/lib/widgets/settings/radio_dialog.dart deleted file mode 100644 index 7695b9d..0000000 --- a/lib/widgets/settings/radio_dialog.dart +++ /dev/null @@ -1,55 +0,0 @@ -import "package:flutter/material.dart"; -import "package:flutter_hooks/flutter_hooks.dart"; - -class RadioDialog extends HookWidget { - final T? value; - final String title; - final List options; - final void Function(T value)? onChanged; - final String Function(T option) getName; - const RadioDialog({ - super.key, - required this.title, - required this.value, - required this.options, - required this.onChanged, - required this.getName, - }); - - @override - Widget build(BuildContext context) { - final mutValue = useState(null); - return AlertDialog( - title: Text(title), - content: RadioGroup( - groupValue: mutValue.value ?? value, - onChanged: (value) => mutValue.value = value ?? mutValue.value, - - child: Column( - mainAxisSize: MainAxisSize.min, - children: options - .map( - (option) => RadioListTile( - enabled: onChanged != null, - value: option, - title: Text(getName(option)), - dense: true, - ), - ) - .toList(), - ), - ), - actions: [ - TextButton(onPressed: Navigator.of(context).pop, child: Text("Cancel")), - if (onChanged != null) - TextButton( - onPressed: () { - if (mutValue.value != null) onChanged!(mutValue.value as T); - Navigator.of(context).pop(); - }, - child: Text("OK"), - ), - ], - ); - } -} diff --git a/lib/widgets/sidebar.dart b/lib/widgets/sidebar.dart index 1bd6cf5..56f8d57 100644 --- a/lib/widgets/sidebar.dart +++ b/lib/widgets/sidebar.dart @@ -3,10 +3,9 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter/material.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:navigation_rail_m3e/navigation_rail_m3e.dart"; -import "package:nexus/controllers/key.dart"; -import "package:nexus/controllers/spaces.dart"; +import "package:nexus/controllers/key_controller.dart"; +import "package:nexus/controllers/spaces_controller.dart"; import "package:nexus/models/room.dart"; -import "package:nexus/pages/settings.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; import "package:nexus/widgets/divider_widget.dart"; import "package:nexus/widgets/join_dialog.dart"; @@ -182,10 +181,10 @@ class Sidebar extends HookConsumerWidget { ), IconButton( tooltip: "Open settings", - onPressed: () => showDialog( - context: context, - builder: (_) => SettingsPage(), - ), + onPressed: null, + // () => Navigator.of( + // context, + // ).push(MaterialPageRoute(builder: (_) => SettingsPage())), icon: Icon(Icons.settings), ), ], diff --git a/lib/widgets/url_preview.dart b/lib/widgets/url_preview.dart index 19fe06b..669c756 100644 --- a/lib/widgets/url_preview.dart +++ b/lib/widgets/url_preview.dart @@ -1,8 +1,8 @@ import "package:cross_cache/cross_cache.dart"; import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/cross_cache.dart"; -import "package:nexus/controllers/url_preview.dart"; +import "package:nexus/controllers/cross_cache_controller.dart"; +import "package:nexus/controllers/url_preview_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; import "package:nexus/helpers/extensions/get_headers.dart"; import "package:nexus/helpers/launch_helper.dart"; diff --git a/lib/widgets/user_bottom_sheet.dart b/lib/widgets/user_bottom_sheet.dart index 55e2502..d422e01 100644 --- a/lib/widgets/user_bottom_sheet.dart +++ b/lib/widgets/user_bottom_sheet.dart @@ -4,15 +4,15 @@ import "package:flutter_hooks/flutter_hooks.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:intl/intl.dart"; import "package:m3e_buttons/m3e_buttons.dart"; -import "package:nexus/controllers/client.dart"; -import "package:nexus/controllers/client_state.dart"; -import "package:nexus/controllers/power_level.dart"; -import "package:nexus/controllers/profile.dart"; +import "package:nexus/controllers/client_controller.dart"; +import "package:nexus/controllers/client_state_controller.dart"; +import "package:nexus/controllers/power_level_controller.dart"; +import "package:nexus/controllers/profile_controller.dart"; import "package:nexus/helpers/extensions/better_when.dart"; import "package:nexus/helpers/extensions/get_localpart.dart"; import "package:nexus/helpers/extensions/mxc_to_https.dart"; import "package:nexus/models/content/membership.dart"; -import "package:nexus/models/membership_action.dart"; +import "package:nexus/models/requests/membership_action.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; import "package:nexus/main.dart"; import "package:nexus/widgets/expandable_image.dart"; diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index 2e0c766..fee47c5 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -7,7 +7,7 @@ project(runner LANGUAGES CXX) set(BINARY_NAME "nexus") # The unique GTK application identifier for this application. See: # https://wiki.gnome.org/HowDoI/ChooseApplicationID -set(APPLICATION_ID "nexus.federated.nexus") +set(APPLICATION_ID "nexus.federated.Nexus") # Explicitly opt in to modern CMake behaviors to avoid warnings with recent # versions of CMake. diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 603ea6a..755a54e 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -8,7 +8,6 @@ #include #include -#include #include #include #include @@ -22,9 +21,6 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); file_selector_plugin_register_with_registrar(file_selector_linux_registrar); - g_autoptr(FlPluginRegistrar) gtk_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "GtkPlugin"); - gtk_plugin_register_with_registrar(gtk_registrar); g_autoptr(FlPluginRegistrar) media_kit_libs_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitLibsLinuxPlugin"); media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 7d8f046..b9ca03c 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -5,7 +5,6 @@ list(APPEND FLUTTER_PLUGIN_LIST dynamic_color file_selector_linux - gtk media_kit_libs_linux media_kit_video screen_retriever_linux diff --git a/linux/nexus.federated.nexus.desktop b/linux/nexus.federated.Nexus.desktop similarity index 58% rename from linux/nexus.federated.nexus.desktop rename to linux/nexus.federated.Nexus.desktop index d61734e..d3fa575 100644 --- a/linux/nexus.federated.nexus.desktop +++ b/linux/nexus.federated.Nexus.desktop @@ -2,9 +2,8 @@ Name=Nexus GenericName=Matrix Client Comment=A simple and user-friendly Matrix client -Exec=nexus %u +Exec=nexus Icon=nexus Terminal=false Type=Application -Categories=Chat;Network;InstantMessaging; -MimeType=x-scheme-handler/nexus.federated.nexus; \ No newline at end of file +Categories=Chat;Network;InstantMessaging; \ No newline at end of file diff --git a/linux/nix/pkg/default.nix b/linux/nix/pkg/default.nix index 4e00959..c879f24 100644 --- a/linux/nix/pkg/default.nix +++ b/linux/nix/pkg/default.nix @@ -29,14 +29,15 @@ flutter.buildFlutterApplication { autoPubspecLock = src + "/pubspec.lock"; gitHashes = { + window_size = "sha256-XelNtp7tpZ91QCEcvewVphNUtgQX7xrp5QP0oFo6DgM="; emoji_text_field = "sha256-3TOys09EP2GRo6pUBGPXaqBlE39O2Cmwt42Hs1cTDKo="; - linkify = "sha256-TpMD6+0zyY6i9l+6d8ErnVufmepCv362rCtnbOht/z4="; + linkify = "sha256-mxV/XHLxF9cn7sUPr2SUNjVmDr5lbxkuGCbNdyiZi2c="; navigation_rail_m3e = "sha256-+2awDTQnK58gGRY1nuHckG/jjxarsYSRu9ovR4i4TEc="; }; postInstall = '' install -D assets/icon.svg $out/share/icons/hicolor/scalable/apps/nexus.svg - install -Dm755 linux/nexus.federated.nexus.desktop -t $out/share/applications + install -Dm755 linux/nexus.federated.Nexus.desktop -t $out/share/applications wrapProgram $out/bin/nexus \ --suffix LD_LIBRARY_PATH : $out/app/nexus/lib ''; diff --git a/linux/nix/pkg/gomuks.nix b/linux/nix/pkg/gomuks.nix index ad0412e..fbe402b 100644 --- a/linux/nix/pkg/gomuks.nix +++ b/linux/nix/pkg/gomuks.nix @@ -11,7 +11,7 @@ buildGoModule (finalAttrs: { src = "${src}/gomuks"; - vendorHash = "sha256-mNZEHOGByVqK1kSLC/Cf1VEvkDxRen+TmoV5CXvGrZ4="; + vendorHash = "sha256-/Wvx5WjnlPpQILpNqo9075F3nox0Dm8PfqGgck4CifQ="; buildPhase = '' runHook preBuild diff --git a/linux/runner/my_application.cc b/linux/runner/my_application.cc index 62e5450..abf5dc5 100644 --- a/linux/runner/my_application.cc +++ b/linux/runner/my_application.cc @@ -23,13 +23,6 @@ static void first_frame_cb(MyApplication* self, FlView *view) // Implements GApplication::activate. static void my_application_activate(GApplication* application) { MyApplication* self = MY_APPLICATION(application); - - GList* windows = gtk_application_get_windows(GTK_APPLICATION(application)); - if (windows) { - gtk_window_present(GTK_WINDOW(windows->data)); - return; - } - GtkWindow* window = GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); @@ -100,7 +93,7 @@ static gboolean my_application_local_command_line(GApplication* application, gch g_application_activate(application); *exit_status = 0; - return FALSE; + return TRUE; } // Implements GApplication::startup. @@ -147,6 +140,6 @@ MyApplication* my_application_new() { return MY_APPLICATION(g_object_new(my_application_get_type(), "application-id", APPLICATION_ID, - "flags", G_APPLICATION_HANDLES_COMMAND_LINE | G_APPLICATION_HANDLES_OPEN, + "flags", G_APPLICATION_NON_UNIQUE, nullptr)); } diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 7b5a69f..c1b7cee 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,7 +5,6 @@ import FlutterMacOS import Foundation -import app_links import dynamic_color import file_picker import file_selector_macos @@ -19,7 +18,6 @@ import wakelock_plus import window_manager func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin")) DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin")) FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin")) FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) diff --git a/macos/Runner/Info.plist b/macos/Runner/Info.plist index 36999ec..4789daa 100644 --- a/macos/Runner/Info.plist +++ b/macos/Runner/Info.plist @@ -28,18 +28,5 @@ MainMenu NSPrincipalClass NSApplication - CFBundleURLTypes - - - CFBundleURLName - - auth_callback - CFBundleURLSchemes - - - nexus.federated.nexus - - - diff --git a/pubspec.lock b/pubspec.lock index ca82296..4e30e17 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,74 +5,42 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: a49d6cf99e8d8e7a8e93668d09ced0bbdb954d0b4fccc2f5f9241c6b87fad95c + sha256: "5b7468c326d2f8a4f630056404ca0d291ade42918f4a3c6233618e724f39da8e" url: "https://pub.dev" source: hosted - version: "99.0.0" + version: "92.0.0" analysis_server_plugin: dependency: transitive description: name: analysis_server_plugin - sha256: "3960b28ee740004df39f85d5ebfc91785f7a90e51fd7c9a185e86a36b2f581b4" + sha256: "44adba4d74a2541173bad4c11531d2a4d22810c29c5ddb458a38e9f4d0e5eac7" url: "https://pub.dev" source: hosted - version: "0.3.14" + version: "0.3.4" analyzer: dependency: transitive description: name: analyzer - sha256: "663efa951fb8a45e06f491223a604c93820598f20e6a99c25617a1576065e8b7" + sha256: "70e4b1ef8003c64793a9e268a551a82869a8a96f39deb73dea28084b0e8bf75e" url: "https://pub.dev" source: hosted - version: "12.1.0" + version: "9.0.0" analyzer_buffer: dependency: transitive description: name: analyzer_buffer - sha256: "445b77e2054fa3e8c8a8ef1b5e9e6b23bb8028fffd34b5e60eaef315b7750674" + sha256: ff4bd291778c7417fe53fe24ee0d0a1f1ffe281a2d4ea887e7094f16e36eace7 url: "https://pub.dev" source: hosted - version: "0.3.3" + version: "0.3.0" analyzer_plugin: dependency: transitive description: name: analyzer_plugin - sha256: "0057a98d64d7bb872b0c87dff6e73d2c2d80c77156e7a03f127a26f8aa240649" + sha256: "6645a029da947ffd823d98118f385d4bd26b54eb069c006b22e0b94e451814b5" url: "https://pub.dev" source: hosted - version: "0.14.8" - app_links: - dependency: "direct main" - description: - name: app_links - sha256: f8db46d2ea9ff6f3a37191a7fd5b7813da1253ace8c32c1b9eadace41d1188ea - url: "https://pub.dev" - source: hosted - version: "7.2.1" - app_links_linux: - dependency: transitive - description: - name: app_links_linux - sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81 - url: "https://pub.dev" - source: hosted - version: "1.0.3" - app_links_platform_interface: - dependency: transitive - description: - name: app_links_platform_interface - sha256: "7546f09a6e93f4a2df2fe2bd40a5c6c64310ac461b036d82b43033be7a59f809" - url: "https://pub.dev" - source: hosted - version: "2.0.4" - app_links_web: - dependency: transitive - description: - name: app_links_web - sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555 - url: "https://pub.dev" - source: hosted - version: "1.0.4" + version: "0.13.11" archive: dependency: transitive description: @@ -109,34 +77,34 @@ packages: dependency: transitive description: name: build - sha256: "45d14a0fb23e018d8287c32fc98d726ce466b231928ed9b9200f29bd3ccd39ae" + sha256: a156715e7cd728130c592f30552575908aae5b100005fbc1f0fb16b3c03a3d10 url: "https://pub.dev" source: hosted - version: "4.0.7" + version: "4.0.6" build_config: dependency: transitive description: name: build_config - sha256: f2c223156a26eea323e6244b85141d76413a80aeee9fe0b380773789fabaf8ae + sha256: "4070d2a59f8eec34c97c86ceb44403834899075f66e8a9d59706f8e7834f6f71" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.0" build_daemon: dependency: transitive description: name: build_daemon - sha256: fd754058c342243718d5171a95f352cfc9fcf0cba8cfa26df67cb13a5836db78 + sha256: bf05f6e12cfea92d3c09308d7bcdab1906cd8a179b023269eed00c071004b957 url: "https://pub.dev" source: hosted - version: "4.1.2" + version: "4.1.1" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "5367e521935b102bdf1e735d2aab461e36b2edca6517662d088dd04cc39f8d16" + sha256: "1523ce62448ebac2c15a8ba5fbad8acac169788658a7dd2a1c2d9c2a9318b9a6" url: "https://pub.dev" source: hosted - version: "2.15.1" + version: "2.15.0" built_collection: dependency: transitive description: @@ -213,10 +181,10 @@ packages: dependency: "direct main" description: name: code_assets - sha256: bf394f466ba9205f1812a0433b392d6af280f155f56651eda7c18cc32ed493b8 + sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.0.0" collection: dependency: "direct main" description: @@ -245,10 +213,10 @@ packages: dependency: transitive description: name: coverage - sha256: "956a3de0725ca232ad353565a8290d3357592bf4250f6f298a185e2d949c5d3d" + sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d" url: "https://pub.dev" source: hosted - version: "1.15.1" + version: "1.15.0" cross_cache: dependency: "direct main" description: @@ -261,10 +229,10 @@ packages: dependency: transitive description: name: cross_file - sha256: "92c9c43c383bfa1c32079d3bc492d55d6d4318044b7b47edaff8971cbb555c51" + sha256: "28bb3ae56f117b5aec029d702a90f57d285cd975c3c5c281eaca38dbc47c5937" url: "https://pub.dev" source: hosted - version: "0.3.5+4" + version: "0.3.5+2" crypto: dependency: transitive description: @@ -285,34 +253,34 @@ packages: dependency: transitive description: name: dart_style - sha256: a4c1ccfee44c7e75ed80484071a5c142a385345e658fd8bd7c4b5c97e7198f98 + sha256: a9c30492da18ff84efe2422ba2d319a89942d93e58eb0b73d32abe822ef54b7b url: "https://pub.dev" source: hosted - version: "3.1.8" + version: "3.1.3" dbus: dependency: transitive description: name: dbus - sha256: "0ce9b0a839e6dee59a37a623d2fc26a35bbbe6404213e419b0d6411023d62645" + sha256: d0c98dcd4f5169878b6cf8f6e0a52403a9dff371a3e2f019697accbf6f44a270 url: "https://pub.dev" source: hosted - version: "0.7.14" + version: "0.7.12" dio: dependency: transitive description: name: dio - sha256: ea2bad3c89a27635ce2d85cce4d6b199da49a5a48ec77b03e45b65a3b90922b0 + sha256: aff32c08f92787a557dd5c0145ac91536481831a01b4648136373cddb0e64f8c url: "https://pub.dev" source: hosted - version: "5.10.0" + version: "5.9.2" dio_web_adapter: dependency: transitive description: name: dio_web_adapter - sha256: dd58dc3861eb36edb13b217efc006a1c21e5bbc341de8c229b85634fa5e362e4 + sha256: "2f9e64323a7c3c7ef69567d5c800424a11f8337b8b228bad02524c9fb3c1f340" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.1.2" dynamic_color: dependency: "direct main" description: @@ -342,10 +310,10 @@ packages: dependency: transitive description: name: equatable - sha256: "3bce007a596ff8b3119c45d68aaef631272537c03d30e5d4534dd24bf4c5eaa2" + sha256: "3e0141505477fd8ad55d6eb4e7776d3fe8430be8e497ccb1521370c3f21a3e2b" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.0.8" fab_m3e: dependency: transitive description: @@ -378,14 +346,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.0" - ffi_leak_tracker: - dependency: transitive - description: - name: ffi_leak_tracker - sha256: "4093d4ef9ca06ffe2786e73bfb25e22aa92112b9bb4ec941f11e3e6b61489a97" - url: "https://pub.dev" - source: hosted - version: "0.1.2" ffigen: dependency: "direct main" description: @@ -406,10 +366,10 @@ packages: dependency: "direct main" description: name: file_picker - sha256: fdc6a37f715d19f35b131decf1ce39242eeed5ddae18c0818c3eccb731ab76be + sha256: f13a03000d942e476bc1ff0a736d2e9de711d2f89a95cd4c1d88f861c3348387 url: "https://pub.dev" source: hosted - version: "12.0.0-beta.7" + version: "11.0.2" file_selector_linux: dependency: transitive description: @@ -504,18 +464,18 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: "3854fe5e3bff0b113c658f260b90c95dea17c92db0f2addeac2e343dd9969785" + sha256: "38d1c268de9097ff59cf0e844ac38759fc78f76836d37edad06fa21e182055a0" url: "https://pub.dev" source: hosted - version: "2.0.35" + version: "2.0.34" flutter_riverpod: dependency: "direct main" description: name: flutter_riverpod - sha256: "9255e1e3ad6e38906a1b4f8287678f95f378744c5b46b1985588543f3f19046e" + sha256: "4e166be88e1dbbaa34a280bdb744aeae73b7ef25fdf8db7a3bb776760a3648e2" url: "https://pub.dev" source: hosted - version: "3.3.2" + version: "3.3.1" flutter_svg: dependency: "direct main" description: @@ -554,10 +514,10 @@ packages: dependency: "direct dev" description: name: freezed - sha256: "8599ba37236328ff6f97269ecce875d251a367eb2929c9bbf9b811b2ce56c74e" + sha256: f23ea33b3863f119b58ed1b586e881a46bd28715ddcc4dbc33104524e3434131 url: "https://pub.dev" source: hosted - version: "3.2.6-dev.1" + version: "3.2.5" freezed_annotation: dependency: "direct main" description: @@ -598,30 +558,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.2" - gtk: - dependency: transitive - description: - name: gtk - sha256: "4ff85b2a16724029dd9e5bbb5a94b6918f9973f74ba571c949d2002801879cf5" - url: "https://pub.dev" - source: hosted - version: "2.2.0" hooks: dependency: "direct main" description: name: hooks - sha256: "9a62a50b50b769a737bc0a8ff381f333529df3ab746b2f6b02e83760231455ba" + sha256: "025f060e86d2d4c3c47b56e33caf7f93bf9283340f26d23424ebcfccf34f621e" url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "1.0.3" hooks_riverpod: dependency: "direct main" description: name: hooks_riverpod - sha256: dcaf59f0f41489ff08ce61438ada564d67f40cfa37cc7c8589da78fb600c2edc + sha256: "08527ec06aaef75e4b78694e045ef0cd8346594eaf9cc18b0f866398b07b93b1" url: "https://pub.dev" source: hosted - version: "3.3.2" + version: "3.3.1" html: dependency: transitive description: @@ -666,10 +618,10 @@ packages: dependency: transitive description: name: idb_shim - sha256: "3448298f244bc76a14aca71461eeab6add2b8a877930521c42c2e8b71b644590" + sha256: d46b09e116508e817f5ea2d8e1f6f55fb98bf7966175152809fd29791bfba3b8 url: "https://pub.dev" source: hosted - version: "2.9.6+2" + version: "2.9.1" image: dependency: transitive description: @@ -682,18 +634,18 @@ packages: dependency: "direct main" description: name: image_picker - sha256: d8402284df184bc05f4a2210c6c23983b0720f4cd87cbd05c5390a78af602667 + sha256: "91c025426c2881c551100bce834e201c835a170151545f58d17da5180ca7d9ac" url: "https://pub.dev" source: hosted - version: "1.2.3" + version: "1.2.2" image_picker_android: dependency: transitive description: name: image_picker_android - sha256: "6f3a1995eafb000333174fae92202622033b0ee7fd917a6cd3730295264df84a" + sha256: d5b3e1774af29c9ab00103afb0d4614070f924d2e0057ac867ec98800114793f url: "https://pub.dev" source: hosted - version: "0.8.13+19" + version: "0.8.13+17" image_picker_for_web: dependency: transitive description: @@ -762,18 +714,18 @@ packages: dependency: "direct main" description: name: json_annotation - sha256: "2a743920d81b7910627f68ee2c9ac1fc0bfee32b9fc3403587d7c6791ca12f80" + sha256: cb09e7dac6210041fad964ed7fbee004f14258b4eca4040f72d1234062ace4c8 url: "https://pub.dev" source: hosted - version: "4.12.0" + version: "4.11.0" json_serializable: dependency: "direct dev" description: name: json_serializable - sha256: ffcd10cde35a93b2abbbcc26bd9971f4ca93763e8abe78d855e3c4177797e501 + sha256: "44729f5c45748e6748f6b9a57ab8f7e4336edc8ae41fc295070e3814e616a6c0" url: "https://pub.dev" source: hosted - version: "6.14.0" + version: "6.13.0" leak_tracker: dependency: transitive description: @@ -803,7 +755,7 @@ packages: description: path: "." ref: "fix/consecutive-periods-loose-url" - resolved-ref: f2f42b06f84dbbd03fb7c3741f7fbacbe5459a4b + resolved-ref: e990021f30b8535b462d41a39f37019045ae55f4 url: "https://github.com/appelladev/linkify" source: git version: "5.0.0" @@ -827,10 +779,10 @@ packages: dependency: "direct main" description: name: m3e_buttons - sha256: af1b19bd5ec7327d20b5a57c6c91e1ed30ee15a4dbade781c0cdbc72e6b04b5e + sha256: "50cdf9ba30fb3ab529afafb0e837484549f8599f1f109ac07da50951febaace1" url: "https://pub.dev" source: hosted - version: "0.0.4" + version: "0.0.3" m3e_card_list: dependency: "direct main" description: @@ -939,10 +891,10 @@ packages: dependency: transitive description: name: meta - sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.17.0" mime: dependency: transitive description: @@ -959,6 +911,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + native_toolchain_c: + dependency: transitive + description: + name: native_toolchain_c + sha256: "6ba77bb18063eebe9de401f5e6437e95e1438af0a87a3a39084fbd37c90df572" + url: "https://pub.dev" + source: hosted + version: "0.17.6" navigation_rail_m3e: dependency: "direct main" description: @@ -980,10 +940,10 @@ packages: dependency: transitive description: name: objective_c - sha256: "6cb691c686fa2838c6deb34980d426145c2a5d537491cb83d463c33cdbc726ed" + sha256: "100a1c87616ab6ed41ec263b083c0ef3261ee6cd1dc3b0f35f8ddfa4f996fe52" url: "https://pub.dev" source: hosted - version: "9.4.1" + version: "9.3.0" package_config: dependency: transitive description: @@ -993,21 +953,21 @@ packages: source: hosted version: "2.2.0" package_info_plus: - dependency: "direct main" + dependency: transitive description: name: package_info_plus - sha256: "127e1751e37ffb2ff4658beeaca77bad0c27bf5f932bd3a501c2296926d4b481" + sha256: "468c26b4254ab01979fa5e4a98cb343ea3631b9acee6f21028997419a80e1a20" url: "https://pub.dev" source: hosted - version: "10.2.1" + version: "9.0.1" package_info_plus_platform_interface: dependency: transitive description: name: package_info_plus_platform_interface - sha256: db762cb2f4f25ee60fb6359773861b0f199e00b90d237bd85a76a1e806b46ef4 + sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086" url: "https://pub.dev" source: hosted - version: "4.1.0" + version: "3.2.1" path: dependency: "direct main" description: @@ -1028,12 +988,12 @@ packages: dependency: "direct main" description: name: path_provider - sha256: a7f4874f987173da295a61c181b8ee71dab59b332a486b391babf26a1b884825 + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.5" path_provider_android: - dependency: "direct overridden" + dependency: transitive description: name: path_provider_android sha256: "149441ca6e4f38193b2e004c0ca6376a3d11f51fa5a77552d8bd4d2b0c0912ba" @@ -1052,18 +1012,18 @@ packages: dependency: transitive description: name: path_provider_linux - sha256: "58c2005f147315b11e9b4a7bc889cd5203e250cba8e3f012dae259b4972b5c16" + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.2.1" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - sha256: "484838772624c3a4b94f1e44a3e19897fee738f2d5c4ce448443b0417f7c9dda" + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.2" path_provider_windows: dependency: transitive description: @@ -1148,26 +1108,26 @@ packages: dependency: transitive description: name: riverpod - sha256: "17100416c51db7810c71a7bb2c34d1f881faa0074fd452afb0c4db6f8f126c76" + sha256: "8c22216be8ad3ef2b44af3a329693558c98eca7b8bd4ef495c92db0bba279f83" url: "https://pub.dev" source: hosted - version: "3.3.2" + version: "3.2.1" riverpod_analyzer_utils: dependency: transitive description: name: riverpod_analyzer_utils - sha256: "3e275138862ccc22ed61444a1f9a840f753094c367f28f4123f50289cd204d68" + sha256: e55bc08c084a424e1bbdc303fe8ea75daafe4269b68fd0e0f6f1678413715b66 url: "https://pub.dev" source: hosted - version: "1.0.0-dev.10" + version: "1.0.0-dev.9" riverpod_lint: dependency: "direct dev" description: name: riverpod_lint - sha256: "166f29492228dc471b6fe294092560ccd5545ed15c9fc155622455b58b2451a9" + sha256: "64e8debf5b719a37d48b9785dd595d34133fdcd84b8fd07157a621c54ab2156f" url: "https://pub.dev" source: hosted - version: "3.1.4" + version: "3.1.3" rxdart: dependency: transitive description: @@ -1180,58 +1140,58 @@ packages: dependency: transitive description: name: safe_local_storage - sha256: "494b982d5edb71030650ea463d939670e91b232b588323dc75229d2c5f23e7b7" + sha256: "287ea1f667c0b93cdc127dccc707158e2d81ee59fba0459c31a0c7da4d09c755" url: "https://pub.dev" source: hosted - version: "2.0.6" + version: "2.0.3" screen_retriever: dependency: transitive description: name: screen_retriever - sha256: ace919117a7520c13a50a6259e60c4a0d4cbe98809468792a91b5c5adada2aa6 + sha256: "570dbc8e4f70bac451e0efc9c9bb19fa2d6799a11e6ef04f946d7886d2e23d0c" url: "https://pub.dev" source: hosted - version: "0.2.2" + version: "0.2.0" screen_retriever_linux: dependency: transitive description: name: screen_retriever_linux - sha256: "7b52006a5ceae1f3d5af7f77188c3290d6e7d8ded16d99809bea84967c65c257" + sha256: f7f8120c92ef0784e58491ab664d01efda79a922b025ff286e29aa123ea3dd18 url: "https://pub.dev" source: hosted - version: "0.2.2" + version: "0.2.0" screen_retriever_macos: dependency: transitive description: name: screen_retriever_macos - sha256: a1489b99cce597c45a54b9aae1cd94c8d4705353b7e0bb2457a6e4de44e0ad8a + sha256: "71f956e65c97315dd661d71f828708bd97b6d358e776f1a30d5aa7d22d78a149" url: "https://pub.dev" source: hosted - version: "0.2.2" + version: "0.2.0" screen_retriever_platform_interface: dependency: transitive description: name: screen_retriever_platform_interface - sha256: "94a5535277510a63184ca178ce12a1449bc0b38618879aa1c18bf57369c5064a" + sha256: ee197f4581ff0d5608587819af40490748e1e39e648d7680ecf95c05197240c0 url: "https://pub.dev" source: hosted - version: "0.2.2" + version: "0.2.0" screen_retriever_windows: dependency: transitive description: name: screen_retriever_windows - sha256: dafc6922b0bfbf1d48cf3ccbf519b4fff47bdcb820da1728ea6db675fecc9324 + sha256: "449ee257f03ca98a57288ee526a301a430a344a161f9202b4fcc38576716fe13" url: "https://pub.dev" source: hosted - version: "0.2.2" + version: "0.2.0" sembast: dependency: transitive description: name: sembast - sha256: a58b26925e23071cf0f4754d8449aabe829e9a9930a872fb18e6ae4c2c88e025 + sha256: "93654267ad36e72ef130ffc05970287f42955b40f07d0efd264e64f7215fa1de" url: "https://pub.dev" source: hosted - version: "3.8.9+1" + version: "3.8.7" shared_preferences: dependency: "direct main" description: @@ -1244,10 +1204,10 @@ packages: dependency: transitive description: name: shared_preferences_android - sha256: "0634e64bd719f89c012f392938e173521f535d3ecaf66558fa94a056d22b5cc7" + sha256: e8d4762b1e2e8578fc4d0fd548cebf24afd24f49719c08974df92834565e2c53 url: "https://pub.dev" source: hosted - version: "2.4.27" + version: "2.4.23" shared_preferences_foundation: dependency: transitive description: @@ -1417,10 +1377,10 @@ packages: dependency: transitive description: name: synchronized - sha256: "93b153dcb6a26dcddee6ca087dd634b53e38c10b5aa163e8e49501a776456153" + sha256: "63896c27e81b28f8cb4e69ead0d3e8f03f1d1e5fc531a3e579cabed6a2c7c9e5" url: "https://pub.dev" source: hosted - version: "3.4.1" + version: "3.4.0+1" term_glyph: dependency: transitive description: @@ -1433,26 +1393,26 @@ packages: dependency: transitive description: name: test - sha256: "8d9ceddbab833f180fbefed08afa76d7c03513dfdba87ffcec2718b02bbcbf20" + sha256: "280d6d890011ca966ad08df7e8a4ddfab0fb3aa49f96ed6de56e3521347a9ae7" url: "https://pub.dev" source: hosted - version: "1.31.0" + version: "1.30.0" test_api: dependency: transitive description: name: test_api - sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" + sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" url: "https://pub.dev" source: hosted - version: "0.7.11" + version: "0.7.10" test_core: dependency: transitive description: name: test_core - sha256: "1991d4cfe85d5043241acac92962c3977c8d2f2add1ee73130c7b286417d1d34" + sha256: "0381bd1585d1a924763c308100f2138205252fb90c9d4eeaf28489ee65ccde51" url: "https://pub.dev" source: hosted - version: "0.6.17" + version: "0.6.16" timeago: dependency: "direct main" description: @@ -1513,10 +1473,10 @@ packages: dependency: transitive description: name: url_launcher_android - sha256: b413d49b73867ac08dd2f9890efd3cc11f2a0e577618d50843440a1fb3776c32 + sha256: "3bb000251e55d4a209aa0e2e563309dc9bb2befea2295fd0cec1f51760aac572" url: "https://pub.dev" source: hosted - version: "6.3.32" + version: "6.3.29" url_launcher_ios: dependency: transitive description: @@ -1569,18 +1529,18 @@ packages: dependency: transitive description: name: uuid - sha256: "9b129329f58692f6e6578329498a8fe9fbe98f090beb764ffbb8ee2eadd01dcd" + sha256: "1fef9e8e11e2991bb773070d4656b7bd5d850967a2456cfc83cf47925ba79489" url: "https://pub.dev" source: hosted - version: "4.6.0" + version: "4.5.3" vector_graphics: dependency: transitive description: name: vector_graphics - sha256: "2306c03da2ba81724afeb589c351ebbc0aa7d86005925be8f8735856dbe5e42d" + sha256: "4d35a36400983c3457c289d4d553b5308f506ea84f7e51c7a564651b5525209a" url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.1" vector_graphics_codec: dependency: transitive description: @@ -1593,10 +1553,10 @@ packages: dependency: transitive description: name: vector_graphics_compiler - sha256: "142a9146f447d15b10bdc00e21d5f4d83e5b32bb5f8f8f5a04c75311344923a3" + sha256: "98e7e94de127b46a86ef46197fff84ff99f3d3b80a708390d717ad731efef598" url: "https://pub.dev" source: hosted - version: "1.2.6" + version: "1.2.2" vector_math: dependency: transitive description: @@ -1617,10 +1577,10 @@ packages: dependency: transitive description: name: wakelock_plus - sha256: "824c5bba0f800e86d32e57d3d1843c531f090005cc89d9a837933e6601093d53" + sha256: ddf3db70eaa10c37558ff817519b85d527dbd21034fd5d8e1c2e85f31588f1c1 url: "https://pub.dev" source: hosted - version: "1.6.1" + version: "1.5.2" wakelock_plus_platform_interface: dependency: transitive description: @@ -1673,20 +1633,20 @@ packages: dependency: transitive description: name: win32 - sha256: ba6f4bba816c8d7e3c1580e170f3786d216951cc6b94babc3b814c08d2cb2738 + sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e url: "https://pub.dev" source: hosted - version: "6.3.0" + version: "5.15.0" window_manager: dependency: "direct main" description: name: window_manager - sha256: "05c231fd7b23d2380f14c5cc10b7b93d60d4fa4a2fb4e0f032de27e44b5560e9" + sha256: "7eb6d6c4164ec08e1bf978d6e733f3cebe792e2a23fb07cbca25c2872bfdbdcd" url: "https://pub.dev" source: hosted - version: "0.5.2" + version: "0.5.1" xdg_directories: - dependency: "direct main" + dependency: transitive description: name: xdg_directories sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" @@ -1718,5 +1678,5 @@ packages: source: hosted version: "2.2.4" sdks: - dart: ">=3.12.2 <4.0.0" - flutter: ">=3.44.0" + dart: ">=3.11.5 <4.0.0" + flutter: ">=3.38.4" diff --git a/pubspec.yaml b/pubspec.yaml index 0002bdd..da8b1ec 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,22 +4,14 @@ version: 0.1.0 publish_to: none flutter: - fonts: - - family: fallback-emoji - fonts: - - asset: assets/fonts/NotoColorEmoji.ttf - - family: fallback-sans - fonts: - - asset: assets/fonts/Roboto.ttf assets: - assets/ uses-material-design: true environment: - sdk: "^3.12.2" + sdk: "^3.11.5" dependency_overrides: - path_provider_android: 2.2.23 # Pinned to avoid JNI linkify: git: url: https://github.com/appelladev/linkify @@ -30,31 +22,31 @@ dependencies: sdk: flutter flutter_localizations: sdk: flutter - flutter_riverpod: 3.3.2 - hooks_riverpod: 3.3.2 + flutter_riverpod: 3.3.1 + hooks_riverpod: 3.3.1 intl: 0.20.2 fast_immutable_collections: 11.2.0 - path_provider: 2.1.6 + path_provider: 2.1.5 url_launcher: 6.3.2 freezed_annotation: 3.1.0 - image_picker: 1.2.3 - file_picker: 12.0.0-beta.7 + image_picker: 1.2.2 + file_picker: 11.0.2 path: 1.9.1 dynamic_color: 1.8.1 collection: 1.19.1 - window_manager: 0.5.2 + window_manager: 0.5.1 color_hash: 1.0.1 flutter_widget_from_html_core: 0.17.2 flutter_svg: 2.3.0 - json_annotation: 4.12.0 + json_annotation: 4.11.0 shared_preferences: 2.5.5 fluttertagger: 2.3.2 dynamic_polls: 0.0.7 flutter_hooks: 0.21.3+1 cross_cache: 1.1.0 ffi: 2.2.0 - hooks: 2.0.2 - code_assets: 1.2.1 + hooks: 1.0.3 + code_assets: 1.0.0 ffigen: 20.1.1 timeago: 3.7.1 http: 1.6.0 @@ -68,24 +60,21 @@ dependencies: media_kit: 1.2.6 media_kit_video: 2.0.1 media_kit_libs_video: 1.0.7 - measure_size: 5.0.2 - m3e_buttons: 0.0.4 + measure_size: ^5.0.2 + m3e_buttons: ^0.0.3 navigation_rail_m3e: git: url: https://github.com/Henry-Hiles/material_3_expressive path: packages/navigation_rail_m3e - m3e_card_list: 0.1.0 - xdg_directories: 1.1.0 - package_info_plus: 10.2.1 - app_links: 7.2.1 + m3e_card_list: ^0.1.0 dev_dependencies: - build_runner: ^2.15.1 + build_runner: 2.15.0 flutter_lints: 6.0.0 - freezed: 3.2.6-dev.1 - riverpod_lint: 3.1.4 + freezed: 3.2.5 + riverpod_lint: 3.1.3 flutter_launcher_icons: 0.14.4 - json_serializable: 6.14.0 + json_serializable: 6.13.0 flutter_launcher_icons: ios: true diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 0694802..7938787 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,7 +6,6 @@ #include "generated_plugin_registrant.h" -#include #include #include #include @@ -16,8 +15,6 @@ #include void RegisterPlugins(flutter::PluginRegistry* registry) { - AppLinksPluginCApiRegisterWithRegistrar( - registry->GetRegistrarForPlugin("AppLinksPluginCApi")); DynamicColorPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("DynamicColorPluginCApi")); FileSelectorWindowsRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 238ef93..64ef5b5 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,7 +3,6 @@ # list(APPEND FLUTTER_PLUGIN_LIST - app_links dynamic_color file_selector_windows media_kit_libs_windows_video diff --git a/windows/installer.iss b/windows/installer.iss index 22f4dcf..c5004c3 100644 --- a/windows/installer.iss +++ b/windows/installer.iss @@ -14,8 +14,4 @@ Source: "..\build\windows\x64\runner\Release\*"; DestDir: "{app}"; Flags: recurs [Icons] Name: "{group}\Nexus"; Filename: "{app}\nexus.exe" -Name: "{commondesktop}\Nexus"; Filename: "{app}\nexus.exe" - -[Registry] -Root: HKCU; Subkey: "Software\Classes\nexus.federated.nexus"; ValueType: string; ValueName: "URL Protocol"; ValueData: "" -Root: HKCU; Subkey: "Software\Classes\nexus.federated.nexus\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\nexus.exe"" ""%1""" \ No newline at end of file +Name: "{commondesktop}\Nexus"; Filename: "{app}\nexus.exe" \ No newline at end of file diff --git a/windows/runner/Runner.rc b/windows/runner/Runner.rc index 24405eb..3583d23 100644 --- a/windows/runner/Runner.rc +++ b/windows/runner/Runner.rc @@ -89,11 +89,11 @@ BEGIN BEGIN BLOCK "040904e4" BEGIN - VALUE "CompanyName", "nexus.federated.nexus" "\0" + VALUE "CompanyName", "nexus.federated.Nexus" "\0" VALUE "FileDescription", "nexus" "\0" VALUE "FileVersion", VERSION_AS_STRING "\0" VALUE "InternalName", "nexus" "\0" - VALUE "LegalCopyright", "Copyright (C) 2025 nexus.federated.nexus. All rights reserved." "\0" + VALUE "LegalCopyright", "Copyright (C) 2025 nexus.federated.Nexus. All rights reserved." "\0" VALUE "OriginalFilename", "nexus.exe" "\0" VALUE "ProductName", "nexus" "\0" VALUE "ProductVersion", VERSION_AS_STRING "\0" diff --git a/windows/runner/main.cpp b/windows/runner/main.cpp index 590e663..453f9cf 100644 --- a/windows/runner/main.cpp +++ b/windows/runner/main.cpp @@ -1,17 +1,12 @@ #include #include #include -#include "app_links/app_links_plugin_c_api.h" + #include "flutter_window.h" #include "utils.h" int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, _In_ wchar_t *command_line, _In_ int show_command) { - // Forward the link to an existing instance, if any, then exit. - // You may ignore the result if you need to create another window. - if (SendAppLinkToInstance()) { - return EXIT_SUCCESS; - } // Attach to console when present (e.g., 'flutter run') or create a // new console when running with a debugger. if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {