diff --git a/.bumpversion.cfg b/.bumpversion.cfg index a5b4502..2b4b328 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.4.7+0 +current_version = 0.4.0+0 commit = False tag = False parse = (?P\d+)\.(?P\d+)\.(?P\d+)\+(?P\d+) diff --git a/.gitignore b/.gitignore index 79e7a9c..46dd51f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,11 +5,9 @@ *.swp .DS_Store .atom/ -.build/ .buildlog/ .history .svn/ -.swiftpm/ migrate_working_dir/ # IntelliJ related diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a29048e..4476fbd 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,122 +3,75 @@ stages: - build + - build_flatpak +# - test -#.macos_saas_runners: -# tags: -# - saas-macos-medium-m1 -# image: macos-12-xcode-14 -# before_script: -# - echo "started by ${GITLAB_USER_NAME}" +.macos_saas_runners: + tags: + - saas-macos-medium-m1 + image: macos-12-xcode-14 + before_script: + - echo "started by ${GITLAB_USER_NAME}" -#build_macos: -# extends: -# - .macos_saas_runners -# stage: build -# script: -# - echo "place holder for build" -# - sudo softwareupdate --install-rosetta --agree-to-license -# - git clone https://gitlab.com/veilid/veilid.git ../veilid +build_macos: + extends: + - .macos_saas_runners + stage: build + script: + - echo "place holder for build" + - sudo softwareupdate --install-rosetta --agree-to-license + - git clone https://gitlab.com/veilid/veilid.git ../veilid #- curl –proto ‘=https’ –tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y #- source "$HOME/.cargo/env" #- brew install capnp cmake wabt llvm protobuf openjdk@17 jq cocoapods #- cargo install wasm-bindgen-cli wasm-pack cargo-edit -# - wget https://storage.googleapis.com/flutter_infra_release/releases/stable/macos/flutter_macos_arm64_3.13.5-stable.zip -# - unzip flutter_macos_arm64_3.13.5-stable.zip && export PATH="$PATH:`pwd`/flutter/bin" -# - flutter upgrade -# - yes | flutter doctor --android-licenses -# - flutter config --enable-macos-desktop --enable-ios -# - flutter config --no-analytics -# - dart --disable-analytics -# - flutter doctor -v + - wget https://storage.googleapis.com/flutter_infra_release/releases/stable/macos/flutter_macos_arm64_3.13.5-stable.zip + - unzip flutter_macos_arm64_3.13.5-stable.zip && export PATH="$PATH:`pwd`/flutter/bin" + - flutter upgrade + - yes | flutter doctor --android-licenses + - flutter config --enable-macos-desktop --enable-ios + - flutter config --no-analytics + - dart --disable-analytics + - flutter doctor -v #- flutter build ipa #- flutter build appbundle -# only: -# - schedules + when: manual build_linux_amd64_bundle: - stage: build tags: - saas-linux-medium-amd64 - image: ghcr.io/cirruslabs/flutter:3.29.2 + image: ghcr.io/cirruslabs/flutter:3.19.4 + stage: build script: - apt-get update - - apt-get install -y --no-install-recommends cmake ninja-build clang build-essential pkg-config libgtk-3-dev liblzma-dev lcov rustup - - rustup toolchain install 1.81 --profile minimal --no-self-update + - apt-get install -y --no-install-recommends cmake ninja-build clang build-essential pkg-config libgtk-3-dev liblzma-dev lcov rustc cargo - flutter config --enable-linux-desktop - git clone https://gitlab.com/veilid/veilid.git ../veilid - flutter build linux artifacts: paths: - build/linux/x64/release/bundle/ - rules: - - if: '$CI_COMMIT_TAG =~ /v\d.+/' + when: manual build_linux_amd64_flatpak: tags: - saas-linux-small-amd64 - image: ubuntu:24.04 - stage: build + image: ubuntu:23.04 + stage: build_flatpak dependencies: [build_linux_amd64_bundle] - needs: - - job: build_linux_amd64_bundle - artifacts: true script: - apt-get update - apt-get install -y --no-install-recommends flatpak flatpak-builder gnupg2 elfutils ca-certificates - flatpak remote-add --no-gpg-verify --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo - - flatpak install -y --noninteractive org.gnome.Sdk/x86_64/46 org.gnome.Platform/x86_64/46 app/org.flathub.flatpak-external-data-checker/x86_64/stable org.freedesktop.appstream-glib + - flatpak install -y --noninteractive org.gnome.Sdk/x86_64/45 org.gnome.Platform/x86_64/45 app/org.flathub.flatpak-external-data-checker/x86_64/stable org.freedesktop.appstream-glib - pushd flatpak/ - flatpak-builder --force-clean build-dir com.veilid.veilidchat.yml --repo=repo - flatpak build-bundle repo com.veilid.veilidchat.flatpak com.veilid.veilidchat - popd artifacts: - paths: + paths: - flatpak/com.veilid.veilidchat.flatpak - rules: - - if: '$CI_COMMIT_TAG =~ /v\d.+/' - -build_linux_arm64_bundle: - stage: build - tags: - - saas-linux-small-arm64 - image: ghcr.io/cirruslabs/flutter:3.29.2 - script: - - apt-get update - - apt-get install -y --no-install-recommends cmake ninja-build clang build-essential pkg-config libgtk-3-dev liblzma-dev lcov rustup - - rustup toolchain install 1.81 --profile minimal --no-self-update - - flutter config --enable-linux-desktop - - git clone https://gitlab.com/veilid/veilid.git ../veilid - - flutter build linux - artifacts: - paths: - - build/linux/arm64/release/bundle/ - rules: - - if: '$CI_COMMIT_TAG =~ /v\d.+/' - -build_linux_arm64_flatpak: - tags: - - saas-linux-small-arm64 - image: ubuntu:24.04 - stage: build - dependencies: [build_linux_arm64_bundle] - needs: - - job: build_linux_arm64_bundle - artifacts: true - script: - - apt-get update - - apt-get install -y --no-install-recommends flatpak flatpak-builder gnupg2 elfutils ca-certificates - - flatpak remote-add --no-gpg-verify --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo - - flatpak install -y --noninteractive org.gnome.Sdk/aarch64/46 org.gnome.Platform/aarch64/46 app/org.flathub.flatpak-external-data-checker/aarch64/stable org.freedesktop.appstream-glib - - pushd flatpak/ - - flatpak-builder --force-clean build-dir com.veilid.veilidchat.arm64.yml --repo=repo - - flatpak build-bundle repo com.veilid.veilidchat.flatpak com.veilid.veilidchat - - popd - artifacts: - paths: - - flatpak/com.veilid.veilidchat.flatpak - rules: - - if: '$CI_COMMIT_TAG =~ /v\d.+/' + when: manual #test: # extends: diff --git a/.gitlab/issue_templates/mytemplate.md b/.gitlab/issue_templates/mytemplate.md deleted file mode 100644 index 759ecc2..0000000 --- a/.gitlab/issue_templates/mytemplate.md +++ /dev/null @@ -1,21 +0,0 @@ -First, please search through the existing issues on GitLab ***and*** read our [known issues](https://veilid.com/chat/knownissues) page before opening a new issue. - -Please provide the following information to the best of your ability: - -## Platform in use (Apple or Android) - - -## Network type (Wifi or Cell) -### If you know it, what type of NAT? - - -## Paste in relevant logs -1. Long press the signal meter in VeilidChat to open the console logs -2. Switch the logs to debug -3. Make the issue happen again -4. Go back into the logs and hit the copy all button -5. Paste the logs somewhere you can make edits -- remove all IPs (v4 and v6) -6. Paste or attach that redacted log here - - -## Description of the issue \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a7b1930..0a1a6e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,61 +1,3 @@ -## UNRELEASED ## - -- Fix reconciliation `advance()` -- Add `pool stats` command -- Fixed issue with Android 'back' button exiting the app (#331) -- Deprecated accounts no longer crash application at startup -- Simplify SingleContactMessagesCubit and MessageReconciliation -- Update flutter_chat_ui to 2.0.0 - -## v0.4.7 ## -- *Community Contributions* - - Fix getting stuck on splash screen when veilid is already started @bmv437 / @bgrift - - Fix routing to home after initial account creation @bmv437 / @bgrift - - edit_account_form visual improvements @bmv437 / @bgrift - - Flatpak CI Update @sajattack - - Build Arm64 flatpaks @sajattack -- Dependency updates - -## v0.4.6 ## -- Updated veilid-core to v0.4.4 - - See Veilid changelog for specifics -- UI improvements: Theme fixes, wallpaper option added -- Responsiveness improved -- Contacts workflow more consistent -- Safe-area fixes -- Make layout more mobile-friendly -- Improved contact invitation menus -- Deadlock fixes in veilid_support -- _pollWatch was degenerate and only watched first subkey - -## v0.4.5 ## -- Updated veilid-core to v0.4.1 - - See Veilid changelog for specifics -- DHT speed and reliability improvements - -## v0.4.4 ## -- Update beta dialog with expectations page -- Temporarily disable relay selection aggressiveness - -## v0.4.3 ## -- Flutter upgrade to 3.24.0 -- Contacts UI cleanup -- Incorporate symmetric NAT fix from veilid-core - -## v0.4.2 ## -- Dialogs cleanup -- Incremental chat state work -- Exception handling work -- Cancellable waiting page -- Fix DHTRecordCubit open() retry bug -- Log fix for iOS -- Add ability to delete orphaned chats -- Fix deadlock - -## v0.4.1 ## -- Fix creating new accounts -- Switch to non-bounce scroll physics because a lot of views want 'stick to bottom' scroll behavior - ## v0.4.0 ## - Long conversation support - Account and consistency update diff --git a/android/app/.gitignore b/android/app/.gitignore deleted file mode 100644 index 0e60033..0000000 --- a/android/app/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.cxx diff --git a/android/app/build.gradle b/android/app/build.gradle index 9d84346..f5e4e3d 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,9 +1,3 @@ -plugins { - id "com.android.application" - id "kotlin-android" - id "dev.flutter.flutter-gradle-plugin" -} - def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -12,6 +6,11 @@ if (localPropertiesFile.exists()) { } } +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' @@ -22,6 +21,10 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + def buildConfig = 'debug' def keystoreProperties = new Properties() @@ -32,16 +35,16 @@ if (keystorePropertiesFile.exists()) { } android { - ndkVersion "27.0.12077973" + ndkVersion "26.3.11579264" compileSdkVersion flutter.compileSdkVersion - + compileOptions { - sourceCompatibility JavaVersion.VERSION_17 - targetCompatibility JavaVersion.VERSION_17 + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { - jvmTarget = '17' + jvmTarget = '1.8' } sourceSets { @@ -67,7 +70,7 @@ android { storePassword keystoreProperties['storePassword'] } } - + buildTypes { release { shrinkResources false @@ -79,7 +82,7 @@ android { } } } - + namespace 'com.veilid.veilidchat' } @@ -87,4 +90,6 @@ flutter { source '../..' } -dependencies {} +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" +} diff --git a/android/build.gradle b/android/build.gradle index bc157bd..dbb249e 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,3 +1,16 @@ +buildscript { + ext.kotlin_version = '1.9.10' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.4.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + allprojects { repositories { google() diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index afa1e8e..e1ca574 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip diff --git a/android/settings.gradle b/android/settings.gradle index b1ae36a..44e62bc 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,25 +1,11 @@ -pluginManagement { - def flutterSdkPath = { - def properties = new Properties() - file("local.properties").withInputStream { properties.load(it) } - def flutterSdkPath = properties.getProperty("flutter.sdk") - assert flutterSdkPath != null, "flutter.sdk not set in local.properties" - return flutterSdkPath - }() +include ':app' - includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() - repositories { - google() - mavenCentral() - gradlePluginPortal() - } -} +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } -plugins { - id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "8.8.0" apply false - id "org.jetbrains.kotlin.android" version "1.9.25" apply false -} - -include ":app" \ No newline at end of file +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/assets/i18n/en.json b/assets/i18n/en.json index 53bf461..b2e6907 100644 --- a/assets/i18n/en.json +++ b/assets/i18n/en.json @@ -4,7 +4,6 @@ }, "menu": { "accounts_menu_tooltip": "Accounts Menu", - "settings_tooltip": "Settings", "contacts_tooltip": "Contacts List", "new_chat_tooltip": "Start New Chat", "add_account_tooltip": "Add Account", @@ -13,7 +12,7 @@ }, "splash": { "beta_title": "VeilidChat is BETA SOFTWARE", - "beta_text": "DO NOT USE THIS FOR ANYTHING IMPORTANT\n\nUntil 1.0 is released:\n\n• You should have no expectations of actual privacy, or guarantees of security.\n• You will likely lose accounts, contacts, and messages and need to recreate them.\n\nTo know what to expect, review our known issues located here:\n\n" + "beta_text": "DO NOT USE THIS FOR ANYTHING IMPORTANT\n\nUntil 1.0 is released:\n\n• You should have no expectations of actual privacy, or guarantees of security.\n• You will likely lose accounts, contacts, and messages and need to recreate them.\n\nPlease read our BETA PARTICIPATION GUIDE located here:\n\n" }, "account": { "form_name": "Name", @@ -50,6 +49,7 @@ "edit_account_page": { "titlebar": "Edit Account", "header": "Account Profile", + "update": "Update", "instructions": "This information will be shared with the people you invite to connect with you on VeilidChat.", "error": "Account modification error", "name": "Name", @@ -63,9 +63,9 @@ "destroy_account_description": "Destroy account, removing it completely from all devices everywhere", "destroy_account_confirm_message": "This action is PERMANENT, and your VeilidChat account will no longer be recoverable with the recovery key. Restoring from backups will not recover your account!", "destroy_account_confirm_message_details": "You will lose access to:\n • Your entire message history\n • Your contacts\n • This will not remove your messages you have sent from other people's devices\n", - "failed_to_remove_title": "Failed to remove account", - "try_again_network": "Try again when you have a more stable network connection", - "failed_to_destroy_title": "Failed to destroy account", + "confirm_are_you_sure": "Are you sure you want to do this?", + "failed_to_remove": "Failed to remove account.\n\nTry again when you have a more stable network connection.", + "failed_to_destroy": "Failed to destroy account.\n\nTry again when you have a more stable network connection.", "account_removed": "Account removed successfully", "account_destroyed": "Account destroyed successfully" }, @@ -82,26 +82,16 @@ "view": "View", "share": "Share" }, - "confirmation": { - "confirm": "Confirm", - "discard_changes": "Discard changes?", - "are_you_sure_discard": "Are you sure you want to discard your changes?", - "are_you_sure": "Are you sure you want to do this?" - }, "button": { "ok": "Ok", "cancel": "Cancel", - "edit": "Edit", "delete": "Delete", "accept": "Accept", "reject": "Reject", "finish": "Finish", - "close": "Close", - "yes": "Yes", - "no": "No", - "update": "Update", - "waiting_for_network": "Waiting For Network", - "chat": "Chat" + "yes_proceed": "Yes, proceed", + "no_cancel": "No, cancel", + "waiting_for_network": "Waiting For Network" }, "toast": { "error": "Error", @@ -128,13 +118,12 @@ "contacts": "Contacts", "edit_contact": "Edit Contact", "invitations": "Invitations", - "no_contact_selected": "Select a contact to view or edit", - "new_chat": "Open Chat", - "close_contact": "Close Contact" + "no_contact_selected": "No contact selected", + "new_chat": "New Chat" }, "contact_list": { "contacts": "Contacts", - "invite_people": "No contacts\n\nPress 'Create Invitation' to invite a contact to VeilidChat", + "invite_people": "Invite people to VeilidChat", "search": "Search contacts", "invitation": "Invitation", "loading_contacts": "Loading contacts..." @@ -143,11 +132,13 @@ "form_name": "Name", "form_pronouns": "Pronouns", "form_about": "About", - "form_status": "Status", + "form_status": "Current Status", "form_nickname": "Nickname", "form_notes": "Notes", "form_fingerprint": "Fingerprint", - "form_show_availability": "Show availability" + "form_show_availability": "Show availability", + "save": "Save", + "save_disabled": "Save" }, "availability": { "unspecified": "Unspecified", @@ -158,8 +149,9 @@ "away": "Away" }, "add_contact_sheet": { - "add_contact": "Add Contact", + "new_contact": "New Contact", "create_invite": "Create\nInvitation", + "receive_invite": "Receive\nInvitation", "scan_invite": "Scan\nInvitation", "paste_invite": "Paste\nInvitation" }, @@ -174,21 +166,18 @@ "create_invitation_dialog": { "title": "Create Contact Invitation", "me": "me", - "recipient_name": "Contact Name", - "recipient_hint": "Enter the recipient's name", - "recipient_helper": "Name of the person you are inviting to chat", - "message_hint": "Enter message for contact (optional)", - "message_label": "Message", - "message_helper": "Message to send with invitation", - "fingerprint": "Fingerprint", + "fingerprint": "Fingerprint:", "connect_with_me": "Connect with {name} on VeilidChat!", + "enter_message_hint": "Enter message for contact (optional)", + "message_to_contact": "Message to send with invitation (not encrypted)", "generate": "Generate Invitation", + "message": "Message", "unlocked": "Unlocked", "pin": "PIN", "password": "Password", "protect_this_invitation": "Protect this invitation:", "note": "Note:", - "note_text": "Do not post contact invitations publicly.\n\nContact invitations can be used by anyone. Make sure you send the invitation to your contact over a secure medium, and preferably use a password or pin to ensure that they are the only ones who can unlock the invitation and accept it.", + "note_text": "Contact invitations can be used by anyone. Make sure you send the invitation to your contact over a secure medium, and preferably use a password or pin to ensure that they are the only ones who can unlock the invitation and accept it.", "pin_description": "Choose a PIN to protect the contact invite.\n\nThis level of security is appropriate only for casual connections in public environments for 'shoulder surfing' protection.", "password_description": "Choose a strong password to protect the contact invite.\n\nThis level of security is appropriate when you must be sure the contact invitation is only accepted by its intended recipient. Share this password over a different medium than the invite itself.", "pin_does_not_match": "PIN does not match", @@ -198,7 +187,6 @@ "invitation_copied": "Invitation Copied" }, "invitation_dialog": { - "to": "To", "message_from_contact": "Message from contact", "validating": "Validating...", "failed_to_accept": "Failed to accept contact invitation", @@ -213,9 +201,7 @@ }, "waiting_invitation": { "accepted": "Contact invitation accepted from {name}", - "rejected": "Contact invitation was rejected", - "invalid": "Contact invitation was not valid", - "init_failed": "Contact initialization failed" + "rejected": "Contact invitation was rejected" }, "paste_invitation_dialog": { "title": "Paste Contact Invite", @@ -246,7 +232,6 @@ "password_does_not_match": "Password does not match" }, "chat_list": { - "deleted_contact": "Deleted Contact", "search": "Search chats", "start_a_conversation": "Start A Conversation", "chats": "Chats", @@ -287,7 +272,6 @@ "delivery": "Delivery", "enable_badge": "Enable icon 'badge' bubble", "enable_notifications": "Enable notifications", - "enable_wallpaper": "Enable wallpaper", "message_notification_content": "Message notification content", "invitation_accepted": "On invitation accept/reject", "message_received": "On message received", @@ -315,9 +299,5 @@ "info": "Info", "debug": "Debug", "trace": "Trace" - }, - "date_formatter": { - "just_now": "Just now", - "yesterday": "Yesterday" } } \ No newline at end of file diff --git a/assets/images/grid.svg b/assets/images/grid.svg deleted file mode 100644 index f30d577..0000000 --- a/assets/images/grid.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/images/handshake.png b/assets/images/handshake.png new file mode 100644 index 0000000..d40fad1 Binary files /dev/null and b/assets/images/handshake.png differ diff --git a/assets/images/toilet.png b/assets/images/toilet.png new file mode 100644 index 0000000..6c0efa2 Binary files /dev/null and b/assets/images/toilet.png differ diff --git a/assets/images/toilet.svg b/assets/images/toilet.svg deleted file mode 100644 index 0984274..0000000 --- a/assets/images/toilet.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/assets/images/wallpaper/arctic.svg b/assets/images/wallpaper/arctic.svg deleted file mode 100644 index ebcaee4..0000000 --- a/assets/images/wallpaper/arctic.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/assets/images/wallpaper/babydoll.svg b/assets/images/wallpaper/babydoll.svg deleted file mode 100644 index 55f28a3..0000000 --- a/assets/images/wallpaper/babydoll.svg +++ /dev/null @@ -1,663 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/images/wallpaper/eggplant.svg b/assets/images/wallpaper/eggplant.svg deleted file mode 100644 index 48e6ad3..0000000 --- a/assets/images/wallpaper/eggplant.svg +++ /dev/null @@ -1,495 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/images/wallpaper/elite.svg b/assets/images/wallpaper/elite.svg deleted file mode 100644 index 606d21f..0000000 --- a/assets/images/wallpaper/elite.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/assets/images/wallpaper/forest.svg b/assets/images/wallpaper/forest.svg deleted file mode 100644 index fb61069..0000000 --- a/assets/images/wallpaper/forest.svg +++ /dev/null @@ -1,6888 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/images/wallpaper/garden.svg b/assets/images/wallpaper/garden.svg deleted file mode 100644 index f4e6372..0000000 --- a/assets/images/wallpaper/garden.svg +++ /dev/null @@ -1,8182 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/images/wallpaper/gold.svg b/assets/images/wallpaper/gold.svg deleted file mode 100644 index 16e5ab5..0000000 --- a/assets/images/wallpaper/gold.svg +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/images/wallpaper/grim.svg b/assets/images/wallpaper/grim.svg deleted file mode 100644 index 7c3968f..0000000 --- a/assets/images/wallpaper/grim.svg +++ /dev/null @@ -1,928 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/images/wallpaper/lapis.svg b/assets/images/wallpaper/lapis.svg deleted file mode 100644 index c78fa58..0000000 --- a/assets/images/wallpaper/lapis.svg +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/images/wallpaper/lime.svg b/assets/images/wallpaper/lime.svg deleted file mode 100644 index d65222e..0000000 --- a/assets/images/wallpaper/lime.svg +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/images/wallpaper/scarlet.svg b/assets/images/wallpaper/scarlet.svg deleted file mode 100644 index 7047ca7..0000000 --- a/assets/images/wallpaper/scarlet.svg +++ /dev/null @@ -1,349 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/images/wallpaper/vapor.svg b/assets/images/wallpaper/vapor.svg deleted file mode 100644 index 34bfe59..0000000 --- a/assets/images/wallpaper/vapor.svg +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/update_generated_files.bat b/build.bat similarity index 100% rename from update_generated_files.bat rename to build.bat diff --git a/update_generated_files.sh b/build.sh similarity index 100% rename from update_generated_files.sh rename to build.sh diff --git a/build.yaml b/build.yaml index 77b9050..950fe95 100644 --- a/build.yaml +++ b/build.yaml @@ -1,9 +1,6 @@ targets: $default: builders: - freezed: - options: - generic_argument_factories: true json_serializable: options: explicit_to_json: true diff --git a/dev-setup/flutter_config.sh b/dev-setup/flutter_config.sh index a1b8c8d..1168d74 100755 --- a/dev-setup/flutter_config.sh +++ b/dev-setup/flutter_config.sh @@ -14,13 +14,13 @@ sed -i '' 's/MACOSX_DEPLOYMENT_TARGET = [^;]*/MACOSX_DEPLOYMENT_TARGET = 10.14.6 sed -i '' "s/platform :osx, '[^']*'/platform :osx, '10.14.6'/g" $APPDIR/macos/Podfile # Android: Set NDK version -if [[ "$TMPDIR" != "" ]]; then +if [[ "$TMPDIR" != "" ]]; then ANDTMP=$TMPDIR/andtmp_$(date +%s) -else +else ANDTMP=/tmp/andtmp_$(date +%s) fi cat < $ANDTMP - ndkVersion '27.0.12077973' + ndkVersion '26.3.11579264' EOF sed -i '' -e "/android {/r $ANDTMP" $APPDIR/android/app/build.gradle rm -- $ANDTMP @@ -29,7 +29,7 @@ rm -- $ANDTMP sed -i '' 's/minSdkVersion .*/minSdkVersion Math.max(flutter.minSdkVersion, 24)/g' $APPDIR/android/app/build.gradle # Android: Set gradle plugin version -sed -i '' "s/classpath \'com.android.tools.build:gradle:[^\']*\'/classpath 'com.android.tools.build:gradle:8.8.0'/g" $APPDIR/android/build.gradle +sed -i '' "s/classpath \'com.android.tools.build:gradle:[^\']*\'/classpath 'com.android.tools.build:gradle:7.2.0'/g" $APPDIR/android/build.gradle # Android: Set gradle version -sed -i '' 's/distributionUrl=.*/distributionUrl=https:\/\/services.gradle.org\/distributions\/gradle-8.10.2-all.zip/g' $APPDIR/android/gradle/wrapper/gradle-wrapper.properties +sed -i '' 's/distributionUrl=.*/distributionUrl=https:\/\/services.gradle.org\/distributions\/gradle-7.6.3-all.zip/g' $APPDIR/android/gradle/wrapper/gradle-wrapper.properties diff --git a/devtools_options.yaml b/devtools_options.yaml index 7093540..5c27c3e 100644 --- a/devtools_options.yaml +++ b/devtools_options.yaml @@ -1,3 +1,2 @@ extensions: - - provider: true - - shared_preferences: true \ No newline at end of file + - provider: true \ No newline at end of file diff --git a/flatpak/com.veilid.veilidchat.arm64.yml b/flatpak/com.veilid.veilidchat.arm64.yml deleted file mode 100644 index f1c3d59..0000000 --- a/flatpak/com.veilid.veilidchat.arm64.yml +++ /dev/null @@ -1,35 +0,0 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/flatpak/flatpak-builder/main/data/flatpak-manifest.schema.json - ---- -app-id: com.veilid.veilidchat -runtime: org.gnome.Platform -runtime-version: "46" -sdk: org.gnome.Sdk -command: veilidchat -separate-locales: false -finish-args: - - --share=ipc - - --socket=fallback-x11 - - --socket=wayland - - --device=dri - - --socket=pulseaudio - - --share=network - - --talk-name=org.freedesktop.secrets -modules: - - name: VeilidChat - buildsystem: simple - only-arches: - - aarch64 - build-commands: - - "./build-flatpak.sh" - sources: - - type: dir - path: ../build/linux/arm64/release/ - - type: file - path: build-flatpak.sh - - type: file - path: com.veilid.veilidchat.png - - type: file - path: com.veilid.veilidchat.desktop - - type: file - path: com.veilid.veilidchat.metainfo.xml diff --git a/flatpak/com.veilid.veilidchat.yml b/flatpak/com.veilid.veilidchat.yml index 3db5a02..af45e4b 100644 --- a/flatpak/com.veilid.veilidchat.yml +++ b/flatpak/com.veilid.veilidchat.yml @@ -3,7 +3,7 @@ --- app-id: com.veilid.veilidchat runtime: org.gnome.Platform -runtime-version: "46" +runtime-version: "45" sdk: org.gnome.Sdk command: veilidchat separate-locales: false diff --git a/flutter_01.png b/flutter_01.png deleted file mode 100644 index e69de29..0000000 diff --git a/flutter_02.png b/flutter_02.png deleted file mode 100644 index e69de29..0000000 diff --git a/flutter_03.png b/flutter_03.png deleted file mode 100644 index e69de29..0000000 diff --git a/ios/Podfile b/ios/Podfile index 572283f..2cbcaa2 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, '15.6' +# platform :ios, '12.4' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 2528d2d..536f7a4 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -4,56 +4,62 @@ PODS: - file_saver (0.0.1): - Flutter - Flutter (1.0.0) - - flutter_native_splash (2.4.3): + - flutter_native_splash (0.0.1): - Flutter - - GoogleDataTransport (10.1.0): - - nanopb (~> 3.30910.0) - - PromisesObjC (~> 2.4) - - GoogleMLKit/BarcodeScanning (7.0.0): + - GoogleDataTransport (9.4.1): + - GoogleUtilities/Environment (~> 7.7) + - nanopb (< 2.30911.0, >= 2.30908.0) + - PromisesObjC (< 3.0, >= 1.2) + - GoogleMLKit/BarcodeScanning (6.0.0): - GoogleMLKit/MLKitCore - - MLKitBarcodeScanning (~> 6.0.0) - - GoogleMLKit/MLKitCore (7.0.0): - - MLKitCommon (~> 12.0.0) + - MLKitBarcodeScanning (~> 5.0.0) + - GoogleMLKit/MLKitCore (6.0.0): + - MLKitCommon (~> 11.0.0) - GoogleToolboxForMac/Defines (4.2.1) - GoogleToolboxForMac/Logger (4.2.1): - GoogleToolboxForMac/Defines (= 4.2.1) - "GoogleToolboxForMac/NSData+zlib (4.2.1)": - GoogleToolboxForMac/Defines (= 4.2.1) - - GoogleUtilities/Environment (8.0.2): + - GoogleUtilities/Environment (7.13.3): - GoogleUtilities/Privacy - - GoogleUtilities/Logger (8.0.2): + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUtilities/Logger (7.13.3): - GoogleUtilities/Environment - GoogleUtilities/Privacy - - GoogleUtilities/Privacy (8.0.2) - - GoogleUtilities/UserDefaults (8.0.2): + - GoogleUtilities/Privacy (7.13.3) + - GoogleUtilities/UserDefaults (7.13.3): - GoogleUtilities/Logger - GoogleUtilities/Privacy - - GTMSessionFetcher/Core (3.5.0) - - MLImage (1.0.0-beta6) - - MLKitBarcodeScanning (6.0.0): - - MLKitCommon (~> 12.0) - - MLKitVision (~> 8.0) - - MLKitCommon (12.0.0): - - GoogleDataTransport (~> 10.0) + - GoogleUtilitiesComponents (1.1.0): + - GoogleUtilities/Logger + - GTMSessionFetcher/Core (3.4.1) + - MLImage (1.0.0-beta5) + - MLKitBarcodeScanning (5.0.0): + - MLKitCommon (~> 11.0) + - MLKitVision (~> 7.0) + - MLKitCommon (11.0.0): + - GoogleDataTransport (< 10.0, >= 9.4.1) - GoogleToolboxForMac/Logger (< 5.0, >= 4.2.1) - "GoogleToolboxForMac/NSData+zlib (< 5.0, >= 4.2.1)" - - GoogleUtilities/Logger (~> 8.0) - - GoogleUtilities/UserDefaults (~> 8.0) + - GoogleUtilities/UserDefaults (< 8.0, >= 7.13.0) + - GoogleUtilitiesComponents (~> 1.0) - GTMSessionFetcher/Core (< 4.0, >= 3.3.2) - - MLKitVision (8.0.0): + - MLKitVision (7.0.0): - GoogleToolboxForMac/Logger (< 5.0, >= 4.2.1) - "GoogleToolboxForMac/NSData+zlib (< 5.0, >= 4.2.1)" - GTMSessionFetcher/Core (< 4.0, >= 3.3.2) - - MLImage (= 1.0.0-beta6) - - MLKitCommon (~> 12.0) - - mobile_scanner (6.0.2): + - MLImage (= 1.0.0-beta5) + - MLKitCommon (~> 11.0) + - mobile_scanner (5.1.1): + - Flutter + - GoogleMLKit/BarcodeScanning (~> 6.0.0) + - nanopb (2.30910.0): + - nanopb/decode (= 2.30910.0) + - nanopb/encode (= 2.30910.0) + - nanopb/decode (2.30910.0) + - nanopb/encode (2.30910.0) + - native_device_orientation (0.0.1): - Flutter - - GoogleMLKit/BarcodeScanning (~> 7.0.0) - - nanopb (3.30910.0): - - nanopb/decode (= 3.30910.0) - - nanopb/encode (= 3.30910.0) - - nanopb/decode (3.30910.0) - - nanopb/encode (3.30910.0) - package_info_plus (0.4.5): - Flutter - pasteboard (0.0.1): @@ -69,7 +75,9 @@ PODS: - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS - - sqflite_darwin (0.0.4): + - smart_auth (0.0.1): + - Flutter + - sqflite (0.0.3): - Flutter - FlutterMacOS - system_info_plus (0.0.1): @@ -85,13 +93,15 @@ DEPENDENCIES: - Flutter (from `Flutter`) - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`) - mobile_scanner (from `.symlinks/plugins/mobile_scanner/ios`) + - native_device_orientation (from `.symlinks/plugins/native_device_orientation/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - pasteboard (from `.symlinks/plugins/pasteboard/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - printing (from `.symlinks/plugins/printing/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) + - smart_auth (from `.symlinks/plugins/smart_auth/ios`) + - sqflite (from `.symlinks/plugins/sqflite/darwin`) - system_info_plus (from `.symlinks/plugins/system_info_plus/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - veilid (from `.symlinks/plugins/veilid/ios`) @@ -102,6 +112,7 @@ SPEC REPOS: - GoogleMLKit - GoogleToolboxForMac - GoogleUtilities + - GoogleUtilitiesComponents - GTMSessionFetcher - MLImage - MLKitBarcodeScanning @@ -121,6 +132,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/flutter_native_splash/ios" mobile_scanner: :path: ".symlinks/plugins/mobile_scanner/ios" + native_device_orientation: + :path: ".symlinks/plugins/native_device_orientation/ios" package_info_plus: :path: ".symlinks/plugins/package_info_plus/ios" pasteboard: @@ -133,8 +146,10 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/share_plus/ios" shared_preferences_foundation: :path: ".symlinks/plugins/shared_preferences_foundation/darwin" - sqflite_darwin: - :path: ".symlinks/plugins/sqflite_darwin/darwin" + smart_auth: + :path: ".symlinks/plugins/smart_auth/ios" + sqflite: + :path: ".symlinks/plugins/sqflite/darwin" system_info_plus: :path: ".symlinks/plugins/system_info_plus/ios" url_launcher_ios: @@ -143,33 +158,36 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/veilid/ios" SPEC CHECKSUMS: - camera_avfoundation: 04b44aeb14070126c6529e5ab82cc7c9fca107cf - file_saver: 6cdbcddd690cb02b0c1a0c225b37cd805c2bf8b6 + camera_avfoundation: dd002b0330f4981e1bbcb46ae9b62829237459a4 + file_saver: 503e386464dbe118f630e17b4c2e1190fa0cf808 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf - GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 - GoogleMLKit: eff9e23ec1d90ea4157a1ee2e32a4f610c5b3318 + flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778 + GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a + GoogleMLKit: 97ac7af399057e99182ee8edfa8249e3226a4065 GoogleToolboxForMac: d1a2cbf009c453f4d6ded37c105e2f67a32206d8 - GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d - GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 - MLImage: 0ad1c5f50edd027672d8b26b0fee78a8b4a0fc56 - MLKitBarcodeScanning: 0a3064da0a7f49ac24ceb3cb46a5bc67496facd2 - MLKitCommon: 07c2c33ae5640e5380beaaa6e4b9c249a205542d - MLKitVision: 45e79d68845a2de77e2dd4d7f07947f0ed157b0e - mobile_scanner: af8f71879eaba2bbcb4d86c6a462c3c0e7f23036 - nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 - package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499 - pasteboard: 49088aeb6119d51f976a421db60d8e1ab079b63c - path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 - printing: 54ff03f28fe9ba3aa93358afb80a8595a071dd07 + GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 + GoogleUtilitiesComponents: 679b2c881db3b615a2777504623df6122dd20afe + GTMSessionFetcher: 8000756fc1c19d2e5697b90311f7832d2e33f6cd + MLImage: 1824212150da33ef225fbd3dc49f184cf611046c + MLKitBarcodeScanning: 10ca0845a6d15f2f6e911f682a1998b68b973e8b + MLKitCommon: afec63980417d29ffbb4790529a1b0a2291699e1 + MLKitVision: e858c5f125ecc288e4a31127928301eaba9ae0c1 + mobile_scanner: 8564358885a9253c43f822435b70f9345c87224f + nanopb: 438bc412db1928dac798aa6fd75726007be04262 + native_device_orientation: 348b10c346a60ebbc62fb235a4fdb5d1b61a8f55 + package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c + pasteboard: 982969ebaa7c78af3e6cc7761e8f5e77565d9ce0 + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + printing: 233e1b73bd1f4a05615548e9b5a324c98588640b PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 - share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a - shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 - sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 - system_info_plus: 555ce7047fbbf29154726db942ae785c29211740 - url_launcher_ios: 694010445543906933d732453a59da0a173ae33d - veilid: 3ce560a4f2b568a77a9fd5e23090f2fa97581019 + share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad + shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 + smart_auth: 4bedbc118723912d0e45a07e8ab34039c19e04f2 + sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec + system_info_plus: 5393c8da281d899950d751713575fbf91c7709aa + url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe + veilid: f5c2e662f91907b30cf95762619526ac3e4512fd -PODFILE CHECKSUM: c8bf5b16c34712d5790b0b8d2472cc66ac0a8487 +PODFILE CHECKSUM: 5d504085cd7c7a4d71ee600d7af087cb60ab75b2 -COCOAPODS: 1.16.2 +COCOAPODS: 1.15.2 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index e612191..417e34c 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -156,7 +156,6 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - BuildIndependentTargetsInParallel = YES; LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { @@ -343,7 +342,6 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -354,7 +352,6 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -380,11 +377,9 @@ CLANG_ENABLE_MODULES = YES; DEVELOPMENT_TEAM = XP5LBLT7M7; ENABLE_BITCODE = NO; - ENABLE_USER_SCRIPT_SANDBOXING = NO; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = VeilidChat; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -420,7 +415,6 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -431,7 +425,6 @@ DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -477,7 +470,6 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -488,7 +480,6 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -516,11 +507,9 @@ CLANG_ENABLE_MODULES = YES; DEVELOPMENT_TEAM = XP5LBLT7M7; ENABLE_BITCODE = NO; - ENABLE_USER_SCRIPT_SANDBOXING = NO; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = VeilidChat; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -542,11 +531,9 @@ CLANG_ENABLE_MODULES = YES; DEVELOPMENT_TEAM = XP5LBLT7M7; ENABLE_BITCODE = NO; - ENABLE_USER_SCRIPT_SANDBOXING = NO; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = VeilidChat; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking"; - IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 3e31b44..8b3e7d0 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -48,7 +48,6 @@ ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" debugServiceExtension = "internal" - enableGPUValidationMode = "1" allowLocationSimulation = "YES"> diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index b636303..70693e4 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -1,7 +1,7 @@ import UIKit import Flutter -@main +@UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, diff --git a/lib/account_manager/cubits/account_info_cubit.dart b/lib/account_manager/cubits/account_info_cubit.dart index a5eab11..d9d93fc 100644 --- a/lib/account_manager/cubits/account_info_cubit.dart +++ b/lib/account_manager/cubits/account_info_cubit.dart @@ -23,6 +23,7 @@ class AccountInfoCubit extends Cubit { if (acctInfo != null) { emit(acctInfo); } + break; } }); } diff --git a/lib/account_manager/cubits/account_record_cubit.dart b/lib/account_manager/cubits/account_record_cubit.dart index 16ab2e0..2a3d9e2 100644 --- a/lib/account_manager/cubits/account_record_cubit.dart +++ b/lib/account_manager/cubits/account_record_cubit.dart @@ -1,13 +1,14 @@ import 'dart:async'; import 'package:async_tools/async_tools.dart'; +import 'package:protobuf/protobuf.dart'; import 'package:veilid_support/veilid_support.dart'; import '../../proto/proto.dart' as proto; import '../account_manager.dart'; typedef AccountRecordState = proto.Account; -typedef _SspUpdateState = ( +typedef _sspUpdateState = ( AccountSpec accountSpec, Future Function() onSuccess ); @@ -46,32 +47,54 @@ class AccountRecordCubit extends DefaultDHTRecordCubit { // Public Interface void updateAccount( - AccountSpec accountSpec, Future Function() onChanged) { - _sspUpdate.updateState((accountSpec, onChanged), (state) async { + AccountSpec accountSpec, Future Function() onSuccess) { + _sspUpdate.updateState((accountSpec, onSuccess), (state) async { await _updateAccountAsync(state.$1, state.$2); }); } Future _updateAccountAsync( - AccountSpec accountSpec, Future Function() onChanged) async { - var changed = true; - await record?.eventualUpdateProtobuf(proto.Account.fromBuffer, (old) async { + AccountSpec accountSpec, Future Function() onSuccess) async { + var changed = false; + await record.eventualUpdateProtobuf(proto.Account.fromBuffer, (old) async { + changed = false; if (old == null) { return null; } - final oldAccountSpec = AccountSpec.fromProto(old); - changed = oldAccountSpec != accountSpec; - if (!changed) { - return null; - } + final newAccount = old.deepCopy() + ..profile.name = accountSpec.name + ..profile.pronouns = accountSpec.pronouns + ..profile.about = accountSpec.about + ..profile.availability = accountSpec.availability + ..profile.status = accountSpec.status + //..profile.avatar = + ..profile.timestamp = Veilid.instance.now().toInt64() + ..invisible = accountSpec.invisible + ..autodetectAway = accountSpec.autoAway + ..autoAwayTimeoutMin = accountSpec.autoAwayTimeout + ..freeMessage = accountSpec.freeMessage + ..awayMessage = accountSpec.awayMessage + ..busyMessage = accountSpec.busyMessage; - return accountSpec.updateProto(old); + if (newAccount.profile != old.profile || + newAccount.invisible != old.invisible || + newAccount.autodetectAway != old.autodetectAway || + newAccount.autoAwayTimeoutMin != old.autoAwayTimeoutMin || + newAccount.freeMessage != old.freeMessage || + newAccount.busyMessage != old.busyMessage || + newAccount.awayMessage != old.awayMessage) { + changed = true; + } + if (changed) { + return newAccount; + } + return null; }); if (changed) { - await onChanged(); + await onSuccess(); } } - final _sspUpdate = SingleStateProcessor<_SspUpdateState>(); + final _sspUpdate = SingleStateProcessor<_sspUpdateState>(); } diff --git a/lib/account_manager/cubits/active_local_account_cubit.dart b/lib/account_manager/cubits/active_local_account_cubit.dart index 8856848..58a9cb8 100644 --- a/lib/account_manager/cubits/active_local_account_cubit.dart +++ b/lib/account_manager/cubits/active_local_account_cubit.dart @@ -14,6 +14,7 @@ class ActiveLocalAccountCubit extends Cubit { switch (change) { case AccountRepositoryChange.activeLocalAccount: emit(_accountRepository.getActiveLocalAccount()); + break; // Ignore these case AccountRepositoryChange.localAccounts: case AccountRepositoryChange.userLogins: diff --git a/lib/account_manager/cubits/local_accounts_cubit.dart b/lib/account_manager/cubits/local_accounts_cubit.dart index 3781297..704d8c5 100644 --- a/lib/account_manager/cubits/local_accounts_cubit.dart +++ b/lib/account_manager/cubits/local_accounts_cubit.dart @@ -20,6 +20,7 @@ class LocalAccountsCubit extends Cubit switch (change) { case AccountRepositoryChange.localAccounts: emit(_accountRepository.getLocalAccounts()); + break; // Ignore these case AccountRepositoryChange.userLogins: case AccountRepositoryChange.activeLocalAccount: diff --git a/lib/account_manager/cubits/per_account_collection_bloc_map_cubit.dart b/lib/account_manager/cubits/per_account_collection_bloc_map_cubit.dart index e63b53e..f5334c1 100644 --- a/lib/account_manager/cubits/per_account_collection_bloc_map_cubit.dart +++ b/lib/account_manager/cubits/per_account_collection_bloc_map_cubit.dart @@ -22,24 +22,24 @@ class PerAccountCollectionBlocMapCubit extends BlocMapCubit - add( + Future _addPerAccountCollectionCubit( + {required TypedKey superIdentityRecordKey}) async => + add(() => MapEntry( superIdentityRecordKey, - () => PerAccountCollectionCubit( + PerAccountCollectionCubit( locator: _locator, accountInfoCubit: AccountInfoCubit( accountRepository: _accountRepository, - superIdentityRecordKey: superIdentityRecordKey))); + superIdentityRecordKey: superIdentityRecordKey)))); /// StateFollower ///////////////////////// @override - void removeFromState(TypedKey key) => remove(key); + Future removeFromState(TypedKey key) => remove(key); @override - void updateState( - TypedKey key, LocalAccount? oldValue, LocalAccount newValue) { + Future updateState( + TypedKey key, LocalAccount? oldValue, LocalAccount newValue) async { // Don't replace unless this is a totally different account // The sub-cubit's subscription will update our state later if (oldValue != null) { @@ -53,7 +53,7 @@ class PerAccountCollectionBlocMapCubit extends BlocMapCubit { PerAccountCollectionCubit({ required Locator locator, @@ -35,7 +32,6 @@ class PerAccountCollectionCubit extends Cubit { await _processor.close(); await accountInfoCubit.close(); await _accountRecordSubscription?.cancel(); - await serialFutureClose((this, _kAccountRecordSubscriptionListenKey)); await accountRecordCubit?.close(); await activeSingleContactChatBlocMapCubitUpdater.close(); @@ -82,13 +78,13 @@ class PerAccountCollectionCubit extends Cubit { await _accountRecordSubscription?.cancel(); _accountRecordSubscription = null; + // Update state to 'loading' + nextState = _updateAccountRecordState(nextState, null); + emit(nextState); + // Close AccountRecordCubit await accountRecordCubit?.close(); accountRecordCubit = null; - - // Update state to 'loading' - nextState = await _updateAccountRecordState(nextState, null); - emit(nextState); } else { ///////////////// Logged in /////////////////// @@ -99,22 +95,20 @@ class PerAccountCollectionCubit extends Cubit { // Update state to value nextState = - await _updateAccountRecordState(nextState, accountRecordCubit!.state); + _updateAccountRecordState(nextState, accountRecordCubit!.state); emit(nextState); // Subscribe AccountRecordCubit _accountRecordSubscription ??= accountRecordCubit!.stream.listen((avAccountRecordState) { - serialFuture((this, _kAccountRecordSubscriptionListenKey), () async { - emit(await _updateAccountRecordState(state, avAccountRecordState)); - }); + emit(_updateAccountRecordState(state, avAccountRecordState)); }); } } - Future _updateAccountRecordState( + PerAccountCollectionState _updateAccountRecordState( PerAccountCollectionState prevState, - AsyncValue? avAccountRecordState) async { + AsyncValue? avAccountRecordState) { // Get next state final nextState = prevState.copyWith(avAccountRecordState: avAccountRecordState); @@ -127,8 +121,8 @@ class PerAccountCollectionCubit extends Cubit { .avAccountRecordState?.asData?.value.contactInvitationRecords .toVeilid(); - final contactInvitationListCubit = await contactInvitationListCubitUpdater - .update(accountInfo.userLogin == null || + final contactInvitationListCubit = contactInvitationListCubitUpdater.update( + accountInfo.userLogin == null || contactInvitationListRecordPointer == null ? null : (accountInfo, contactInvitationListRecordPointer)); @@ -137,35 +131,34 @@ class PerAccountCollectionCubit extends Cubit { final contactListRecordPointer = nextState.avAccountRecordState?.asData?.value.contactList.toVeilid(); - final contactListCubit = await contactListCubitUpdater.update( + final contactListCubit = contactListCubitUpdater.update( accountInfo.userLogin == null || contactListRecordPointer == null ? null : (accountInfo, contactListRecordPointer)); // WaitingInvitationsBlocMapCubit - final waitingInvitationsBlocMapCubit = - await waitingInvitationsBlocMapCubitUpdater.update( - accountInfo.userLogin == null || - contactInvitationListCubit == null || - contactListCubit == null - ? null - : ( - accountInfo, - accountRecordCubit!, - contactInvitationListCubit, - contactListCubit, - _locator(), - )); + final waitingInvitationsBlocMapCubit = waitingInvitationsBlocMapCubitUpdater + .update(accountInfo.userLogin == null || + contactInvitationListCubit == null || + contactListCubit == null + ? null + : ( + accountInfo, + accountRecordCubit!, + contactInvitationListCubit, + contactListCubit, + _locator(), + )); // ActiveChatCubit - final activeChatCubit = await activeChatCubitUpdater + final activeChatCubit = activeChatCubitUpdater .update((accountInfo.userLogin == null) ? null : true); // ChatListCubit final chatListRecordPointer = nextState.avAccountRecordState?.asData?.value.chatList.toVeilid(); - final chatListCubit = await chatListCubitUpdater.update( + final chatListCubit = chatListCubitUpdater.update( accountInfo.userLogin == null || chatListRecordPointer == null || activeChatCubit == null @@ -174,7 +167,7 @@ class PerAccountCollectionCubit extends Cubit { // ActiveConversationsBlocMapCubit final activeConversationsBlocMapCubit = - await activeConversationsBlocMapCubitUpdater.update( + activeConversationsBlocMapCubitUpdater.update( accountRecordCubit == null || chatListCubit == null || contactListCubit == null @@ -188,7 +181,7 @@ class PerAccountCollectionCubit extends Cubit { // ActiveSingleContactChatBlocMapCubit final activeSingleContactChatBlocMapCubit = - await activeSingleContactChatBlocMapCubitUpdater.update( + activeSingleContactChatBlocMapCubitUpdater.update( accountInfo.userLogin == null || activeConversationsBlocMapCubit == null ? null diff --git a/lib/account_manager/cubits/user_logins_cubit.dart b/lib/account_manager/cubits/user_logins_cubit.dart index 5623a34..734ced3 100644 --- a/lib/account_manager/cubits/user_logins_cubit.dart +++ b/lib/account_manager/cubits/user_logins_cubit.dart @@ -17,6 +17,7 @@ class UserLoginsCubit extends Cubit { switch (change) { case AccountRepositoryChange.userLogins: emit(_accountRepository.getUserLogins()); + break; // Ignore these case AccountRepositoryChange.localAccounts: case AccountRepositoryChange.activeLocalAccount: diff --git a/lib/account_manager/models/account_info.dart b/lib/account_manager/models/account_info.dart index 8f57add..12ed5e1 100644 --- a/lib/account_manager/models/account_info.dart +++ b/lib/account_manager/models/account_info.dart @@ -13,7 +13,7 @@ enum AccountInfoStatus { } @immutable -class AccountInfo extends Equatable implements ToDebugMap { +class AccountInfo extends Equatable { const AccountInfo({ required this.status, required this.localAccount, @@ -30,13 +30,6 @@ class AccountInfo extends Equatable implements ToDebugMap { localAccount, userLogin, ]; - - @override - Map toDebugMap() => { - 'status': status, - 'localAccount': localAccount, - 'userLogin': userLogin, - }; } extension AccountInfoExt on AccountInfo { diff --git a/lib/account_manager/models/account_spec.dart b/lib/account_manager/models/account_spec.dart index 918e192..539b8d0 100644 --- a/lib/account_manager/models/account_spec.dart +++ b/lib/account_manager/models/account_spec.dart @@ -1,16 +1,12 @@ -import 'package:equatable/equatable.dart'; -import 'package:meta/meta.dart'; -import 'package:protobuf/protobuf.dart'; -import 'package:veilid_support/veilid_support.dart'; +import 'package:flutter/widgets.dart'; import '../../proto/proto.dart' as proto; /// Profile and Account configurable fields /// Some are publicly visible via the proto.Profile /// Some are privately held as proto.Account configurations -@immutable -class AccountSpec extends Equatable { - const AccountSpec( +class AccountSpec { + AccountSpec( {required this.name, required this.pronouns, required this.about, @@ -23,99 +19,37 @@ class AccountSpec extends Equatable { required this.autoAway, required this.autoAwayTimeout}); - const AccountSpec.empty() - : name = '', - pronouns = '', - about = '', - availability = proto.Availability.AVAILABILITY_FREE, - invisible = false, - freeMessage = '', - awayMessage = '', - busyMessage = '', - avatar = null, - autoAway = false, - autoAwayTimeout = 15; - - AccountSpec.fromProto(proto.Account p) - : name = p.profile.name, - pronouns = p.profile.pronouns, - about = p.profile.about, - availability = p.profile.availability, - invisible = p.invisible, - freeMessage = p.freeMessage, - awayMessage = p.awayMessage, - busyMessage = p.busyMessage, - avatar = p.profile.hasAvatar() ? p.profile.avatar : null, - autoAway = p.autodetectAway, - autoAwayTimeout = p.autoAwayTimeoutMin; - String get status { late final String status; switch (availability) { case proto.Availability.AVAILABILITY_AWAY: status = awayMessage; + break; case proto.Availability.AVAILABILITY_BUSY: status = busyMessage; + break; case proto.Availability.AVAILABILITY_FREE: status = freeMessage; + break; case proto.Availability.AVAILABILITY_UNSPECIFIED: case proto.Availability.AVAILABILITY_OFFLINE: status = ''; + break; } return status; } - Future updateProto(proto.Account old) async { - final newProto = old.deepCopy() - ..profile.name = name - ..profile.pronouns = pronouns - ..profile.about = about - ..profile.availability = availability - ..profile.status = status - ..profile.timestamp = Veilid.instance.now().toInt64() - ..invisible = invisible - ..autodetectAway = autoAway - ..autoAwayTimeoutMin = autoAwayTimeout - ..freeMessage = freeMessage - ..awayMessage = awayMessage - ..busyMessage = busyMessage; - - final newAvatar = avatar; - if (newAvatar != null) { - newProto.profile.avatar = newAvatar; - } else { - newProto.profile.clearAvatar(); - } - - return newProto; - } - //////////////////////////////////////////////////////////////////////////// - final String name; - final String pronouns; - final String about; - final proto.Availability availability; - final bool invisible; - final String freeMessage; - final String awayMessage; - final String busyMessage; - final proto.DataReference? avatar; - final bool autoAway; - final int autoAwayTimeout; - - @override - List get props => [ - name, - pronouns, - about, - availability, - invisible, - freeMessage, - awayMessage, - busyMessage, - avatar, - autoAway, - autoAwayTimeout - ]; + String name; + String pronouns; + String about; + proto.Availability availability; + bool invisible; + String freeMessage; + String awayMessage; + String busyMessage; + ImageProvider? avatar; + bool autoAway; + int autoAwayTimeout; } diff --git a/lib/account_manager/models/local_account/local_account.dart b/lib/account_manager/models/local_account/local_account.dart index 81cfb8c..76070ae 100644 --- a/lib/account_manager/models/local_account/local_account.dart +++ b/lib/account_manager/models/local_account/local_account.dart @@ -15,9 +15,8 @@ part 'local_account.freezed.dart'; // and the identitySecretKey optionally encrypted by an unlock code // This is the root of the account information tree for VeilidChat // -@Freezed(toJson: true) -abstract class LocalAccount with _$LocalAccount { - @JsonSerializable() +@freezed +class LocalAccount with _$LocalAccount { const factory LocalAccount({ // The super identity key record for the account, // containing the publicKey in the currentIdentity @@ -41,13 +40,6 @@ abstract class LocalAccount with _$LocalAccount { required String name, }) = _LocalAccount; - factory LocalAccount.fromJson(dynamic json) { - try { - return _$LocalAccountFromJson(json as Map); - // Need to catch any errors here too - // ignore: avoid_catches_without_on_clauses - } catch (e, st) { - throw Exception('invalid local account: $e\n$st'); - } - } + factory LocalAccount.fromJson(dynamic json) => + _$LocalAccountFromJson(json as Map); } diff --git a/lib/account_manager/models/local_account/local_account.freezed.dart b/lib/account_manager/models/local_account/local_account.freezed.dart index 8d7aed1..92e376f 100644 --- a/lib/account_manager/models/local_account/local_account.freezed.dart +++ b/lib/account_manager/models/local_account/local_account.freezed.dart @@ -1,4 +1,3 @@ -// dart format width=80 // coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND // ignore_for_file: type=lint @@ -10,78 +9,45 @@ part of 'local_account.dart'; // FreezedGenerator // ************************************************************************** -// dart format off T _$identity(T value) => value; +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +LocalAccount _$LocalAccountFromJson(Map json) { + return _LocalAccount.fromJson(json); +} + /// @nodoc mixin _$LocalAccount { // The super identity key record for the account, // containing the publicKey in the currentIdentity - SuperIdentity - get superIdentity; // The encrypted currentIdentity secret that goes with + SuperIdentity get superIdentity => + throw _privateConstructorUsedError; // The encrypted currentIdentity secret that goes with // the identityPublicKey with appended salt @Uint8ListJsonConverter() - Uint8List - get identitySecretBytes; // The kind of encryption input used on the account - EncryptionKeyType - get encryptionKeyType; // If account is not hidden, password can be retrieved via - bool - get biometricsEnabled; // Keep account hidden unless account password is entered + Uint8List get identitySecretBytes => + throw _privateConstructorUsedError; // The kind of encryption input used on the account + EncryptionKeyType get encryptionKeyType => + throw _privateConstructorUsedError; // If account is not hidden, password can be retrieved via + bool get biometricsEnabled => + throw _privateConstructorUsedError; // Keep account hidden unless account password is entered // (tries all hidden accounts with auth method (no biometrics)) - bool get hiddenAccount; // Display name for account until it is unlocked - String get name; + bool get hiddenAccount => + throw _privateConstructorUsedError; // Display name for account until it is unlocked + String get name => throw _privateConstructorUsedError; - /// Create a copy of LocalAccount - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) $LocalAccountCopyWith get copyWith => - _$LocalAccountCopyWithImpl( - this as LocalAccount, _$identity); - - /// Serializes this LocalAccount to a JSON map. - Map toJson(); - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is LocalAccount && - (identical(other.superIdentity, superIdentity) || - other.superIdentity == superIdentity) && - const DeepCollectionEquality() - .equals(other.identitySecretBytes, identitySecretBytes) && - (identical(other.encryptionKeyType, encryptionKeyType) || - other.encryptionKeyType == encryptionKeyType) && - (identical(other.biometricsEnabled, biometricsEnabled) || - other.biometricsEnabled == biometricsEnabled) && - (identical(other.hiddenAccount, hiddenAccount) || - other.hiddenAccount == hiddenAccount) && - (identical(other.name, name) || other.name == name)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash( - runtimeType, - superIdentity, - const DeepCollectionEquality().hash(identitySecretBytes), - encryptionKeyType, - biometricsEnabled, - hiddenAccount, - name); - - @override - String toString() { - return 'LocalAccount(superIdentity: $superIdentity, identitySecretBytes: $identitySecretBytes, encryptionKeyType: $encryptionKeyType, biometricsEnabled: $biometricsEnabled, hiddenAccount: $hiddenAccount, name: $name)'; - } + throw _privateConstructorUsedError; } /// @nodoc -abstract mixin class $LocalAccountCopyWith<$Res> { +abstract class $LocalAccountCopyWith<$Res> { factory $LocalAccountCopyWith( - LocalAccount value, $Res Function(LocalAccount) _then) = - _$LocalAccountCopyWithImpl; + LocalAccount value, $Res Function(LocalAccount) then) = + _$LocalAccountCopyWithImpl<$Res, LocalAccount>; @useResult $Res call( {SuperIdentity superIdentity, @@ -95,14 +61,15 @@ abstract mixin class $LocalAccountCopyWith<$Res> { } /// @nodoc -class _$LocalAccountCopyWithImpl<$Res> implements $LocalAccountCopyWith<$Res> { - _$LocalAccountCopyWithImpl(this._self, this._then); +class _$LocalAccountCopyWithImpl<$Res, $Val extends LocalAccount> + implements $LocalAccountCopyWith<$Res> { + _$LocalAccountCopyWithImpl(this._value, this._then); - final LocalAccount _self; - final $Res Function(LocalAccount) _then; + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; - /// Create a copy of LocalAccount - /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -113,50 +80,114 @@ class _$LocalAccountCopyWithImpl<$Res> implements $LocalAccountCopyWith<$Res> { Object? hiddenAccount = null, Object? name = null, }) { - return _then(_self.copyWith( + return _then(_value.copyWith( superIdentity: null == superIdentity - ? _self.superIdentity + ? _value.superIdentity : superIdentity // ignore: cast_nullable_to_non_nullable as SuperIdentity, identitySecretBytes: null == identitySecretBytes - ? _self.identitySecretBytes + ? _value.identitySecretBytes : identitySecretBytes // ignore: cast_nullable_to_non_nullable as Uint8List, encryptionKeyType: null == encryptionKeyType - ? _self.encryptionKeyType + ? _value.encryptionKeyType : encryptionKeyType // ignore: cast_nullable_to_non_nullable as EncryptionKeyType, biometricsEnabled: null == biometricsEnabled - ? _self.biometricsEnabled + ? _value.biometricsEnabled : biometricsEnabled // ignore: cast_nullable_to_non_nullable as bool, hiddenAccount: null == hiddenAccount - ? _self.hiddenAccount + ? _value.hiddenAccount : hiddenAccount // ignore: cast_nullable_to_non_nullable as bool, name: null == name - ? _self.name + ? _value.name : name // ignore: cast_nullable_to_non_nullable as String, - )); + ) as $Val); } - /// Create a copy of LocalAccount - /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $SuperIdentityCopyWith<$Res> get superIdentity { - return $SuperIdentityCopyWith<$Res>(_self.superIdentity, (value) { - return _then(_self.copyWith(superIdentity: value)); + return $SuperIdentityCopyWith<$Res>(_value.superIdentity, (value) { + return _then(_value.copyWith(superIdentity: value) as $Val); }); } } /// @nodoc +abstract class _$$LocalAccountImplCopyWith<$Res> + implements $LocalAccountCopyWith<$Res> { + factory _$$LocalAccountImplCopyWith( + _$LocalAccountImpl value, $Res Function(_$LocalAccountImpl) then) = + __$$LocalAccountImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {SuperIdentity superIdentity, + @Uint8ListJsonConverter() Uint8List identitySecretBytes, + EncryptionKeyType encryptionKeyType, + bool biometricsEnabled, + bool hiddenAccount, + String name}); + @override + $SuperIdentityCopyWith<$Res> get superIdentity; +} + +/// @nodoc +class __$$LocalAccountImplCopyWithImpl<$Res> + extends _$LocalAccountCopyWithImpl<$Res, _$LocalAccountImpl> + implements _$$LocalAccountImplCopyWith<$Res> { + __$$LocalAccountImplCopyWithImpl( + _$LocalAccountImpl _value, $Res Function(_$LocalAccountImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? superIdentity = null, + Object? identitySecretBytes = null, + Object? encryptionKeyType = null, + Object? biometricsEnabled = null, + Object? hiddenAccount = null, + Object? name = null, + }) { + return _then(_$LocalAccountImpl( + superIdentity: null == superIdentity + ? _value.superIdentity + : superIdentity // ignore: cast_nullable_to_non_nullable + as SuperIdentity, + identitySecretBytes: null == identitySecretBytes + ? _value.identitySecretBytes + : identitySecretBytes // ignore: cast_nullable_to_non_nullable + as Uint8List, + encryptionKeyType: null == encryptionKeyType + ? _value.encryptionKeyType + : encryptionKeyType // ignore: cast_nullable_to_non_nullable + as EncryptionKeyType, + biometricsEnabled: null == biometricsEnabled + ? _value.biometricsEnabled + : biometricsEnabled // ignore: cast_nullable_to_non_nullable + as bool, + hiddenAccount: null == hiddenAccount + ? _value.hiddenAccount + : hiddenAccount // ignore: cast_nullable_to_non_nullable + as bool, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc @JsonSerializable() -class _LocalAccount implements LocalAccount { - const _LocalAccount( +class _$LocalAccountImpl implements _LocalAccount { + const _$LocalAccountImpl( {required this.superIdentity, @Uint8ListJsonConverter() required this.identitySecretBytes, required this.encryptionKeyType, @@ -164,6 +195,9 @@ class _LocalAccount implements LocalAccount { required this.hiddenAccount, required this.name}); + factory _$LocalAccountImpl.fromJson(Map json) => + _$$LocalAccountImplFromJson(json); + // The super identity key record for the account, // containing the publicKey in the currentIdentity @override @@ -187,26 +221,16 @@ class _LocalAccount implements LocalAccount { @override final String name; - /// Create a copy of LocalAccount - /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') - _$LocalAccountCopyWith<_LocalAccount> get copyWith => - __$LocalAccountCopyWithImpl<_LocalAccount>(this, _$identity); - - @override - Map toJson() { - return _$LocalAccountToJson( - this, - ); + String toString() { + return 'LocalAccount(superIdentity: $superIdentity, identitySecretBytes: $identitySecretBytes, encryptionKeyType: $encryptionKeyType, biometricsEnabled: $biometricsEnabled, hiddenAccount: $hiddenAccount, name: $name)'; } @override bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _LocalAccount && + other is _$LocalAccountImpl && (identical(other.superIdentity, superIdentity) || other.superIdentity == superIdentity) && const DeepCollectionEquality() @@ -220,7 +244,7 @@ class _LocalAccount implements LocalAccount { (identical(other.name, name) || other.name == name)); } - @JsonKey(includeFromJson: false, includeToJson: false) + @JsonKey(ignore: true) @override int get hashCode => Object.hash( runtimeType, @@ -231,89 +255,50 @@ class _LocalAccount implements LocalAccount { hiddenAccount, name); - @override - String toString() { - return 'LocalAccount(superIdentity: $superIdentity, identitySecretBytes: $identitySecretBytes, encryptionKeyType: $encryptionKeyType, biometricsEnabled: $biometricsEnabled, hiddenAccount: $hiddenAccount, name: $name)'; - } -} - -/// @nodoc -abstract mixin class _$LocalAccountCopyWith<$Res> - implements $LocalAccountCopyWith<$Res> { - factory _$LocalAccountCopyWith( - _LocalAccount value, $Res Function(_LocalAccount) _then) = - __$LocalAccountCopyWithImpl; - @override - @useResult - $Res call( - {SuperIdentity superIdentity, - @Uint8ListJsonConverter() Uint8List identitySecretBytes, - EncryptionKeyType encryptionKeyType, - bool biometricsEnabled, - bool hiddenAccount, - String name}); - - @override - $SuperIdentityCopyWith<$Res> get superIdentity; -} - -/// @nodoc -class __$LocalAccountCopyWithImpl<$Res> - implements _$LocalAccountCopyWith<$Res> { - __$LocalAccountCopyWithImpl(this._self, this._then); - - final _LocalAccount _self; - final $Res Function(_LocalAccount) _then; - - /// Create a copy of LocalAccount - /// with the given fields replaced by the non-null parameter values. + @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - $Res call({ - Object? superIdentity = null, - Object? identitySecretBytes = null, - Object? encryptionKeyType = null, - Object? biometricsEnabled = null, - Object? hiddenAccount = null, - Object? name = null, - }) { - return _then(_LocalAccount( - superIdentity: null == superIdentity - ? _self.superIdentity - : superIdentity // ignore: cast_nullable_to_non_nullable - as SuperIdentity, - identitySecretBytes: null == identitySecretBytes - ? _self.identitySecretBytes - : identitySecretBytes // ignore: cast_nullable_to_non_nullable - as Uint8List, - encryptionKeyType: null == encryptionKeyType - ? _self.encryptionKeyType - : encryptionKeyType // ignore: cast_nullable_to_non_nullable - as EncryptionKeyType, - biometricsEnabled: null == biometricsEnabled - ? _self.biometricsEnabled - : biometricsEnabled // ignore: cast_nullable_to_non_nullable - as bool, - hiddenAccount: null == hiddenAccount - ? _self.hiddenAccount - : hiddenAccount // ignore: cast_nullable_to_non_nullable - as bool, - name: null == name - ? _self.name - : name // ignore: cast_nullable_to_non_nullable - as String, - )); - } + _$$LocalAccountImplCopyWith<_$LocalAccountImpl> get copyWith => + __$$LocalAccountImplCopyWithImpl<_$LocalAccountImpl>(this, _$identity); - /// Create a copy of LocalAccount - /// with the given fields replaced by the non-null parameter values. @override - @pragma('vm:prefer-inline') - $SuperIdentityCopyWith<$Res> get superIdentity { - return $SuperIdentityCopyWith<$Res>(_self.superIdentity, (value) { - return _then(_self.copyWith(superIdentity: value)); - }); + Map toJson() { + return _$$LocalAccountImplToJson( + this, + ); } } -// dart format on +abstract class _LocalAccount implements LocalAccount { + const factory _LocalAccount( + {required final SuperIdentity superIdentity, + @Uint8ListJsonConverter() required final Uint8List identitySecretBytes, + required final EncryptionKeyType encryptionKeyType, + required final bool biometricsEnabled, + required final bool hiddenAccount, + required final String name}) = _$LocalAccountImpl; + + factory _LocalAccount.fromJson(Map json) = + _$LocalAccountImpl.fromJson; + + @override // The super identity key record for the account, +// containing the publicKey in the currentIdentity + SuperIdentity get superIdentity; + @override // The encrypted currentIdentity secret that goes with +// the identityPublicKey with appended salt + @Uint8ListJsonConverter() + Uint8List get identitySecretBytes; + @override // The kind of encryption input used on the account + EncryptionKeyType get encryptionKeyType; + @override // If account is not hidden, password can be retrieved via + bool get biometricsEnabled; + @override // Keep account hidden unless account password is entered +// (tries all hidden accounts with auth method (no biometrics)) + bool get hiddenAccount; + @override // Display name for account until it is unlocked + String get name; + @override + @JsonKey(ignore: true) + _$$LocalAccountImplCopyWith<_$LocalAccountImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/account_manager/models/local_account/local_account.g.dart b/lib/account_manager/models/local_account/local_account.g.dart index 40d55e5..b60c226 100644 --- a/lib/account_manager/models/local_account/local_account.g.dart +++ b/lib/account_manager/models/local_account/local_account.g.dart @@ -6,8 +6,8 @@ part of 'local_account.dart'; // JsonSerializableGenerator // ************************************************************************** -_LocalAccount _$LocalAccountFromJson(Map json) => - _LocalAccount( +_$LocalAccountImpl _$$LocalAccountImplFromJson(Map json) => + _$LocalAccountImpl( superIdentity: SuperIdentity.fromJson(json['super_identity']), identitySecretBytes: const Uint8ListJsonConverter() .fromJson(json['identity_secret_bytes']), @@ -18,7 +18,7 @@ _LocalAccount _$LocalAccountFromJson(Map json) => name: json['name'] as String, ); -Map _$LocalAccountToJson(_LocalAccount instance) => +Map _$$LocalAccountImplToJson(_$LocalAccountImpl instance) => { 'super_identity': instance.superIdentity.toJson(), 'identity_secret_bytes': diff --git a/lib/account_manager/models/per_account_collection_state/per_account_collection_state.dart b/lib/account_manager/models/per_account_collection_state/per_account_collection_state.dart index 24a394c..7fc8f0d 100644 --- a/lib/account_manager/models/per_account_collection_state/per_account_collection_state.dart +++ b/lib/account_manager/models/per_account_collection_state/per_account_collection_state.dart @@ -2,7 +2,6 @@ import 'package:async_tools/async_tools.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:veilid_support/veilid_support.dart'; import '../../../chat/chat.dart'; import '../../../chat_list/chat_list.dart'; @@ -15,9 +14,7 @@ import '../../account_manager.dart'; part 'per_account_collection_state.freezed.dart'; @freezed -sealed class PerAccountCollectionState - with _$PerAccountCollectionState - implements ToDebugMap { +class PerAccountCollectionState with _$PerAccountCollectionState { const factory PerAccountCollectionState({ required AccountInfo accountInfo, required AsyncValue? avAccountRecordState, @@ -32,27 +29,9 @@ sealed class PerAccountCollectionState required ActiveSingleContactChatBlocMapCubit? activeSingleContactChatBlocMapCubit, }) = _PerAccountCollectionState; - const PerAccountCollectionState._(); - - @override - Map toDebugMap() => { - 'accountInfo': accountInfo, - 'avAccountRecordState': avAccountRecordState, - 'accountInfoCubit': accountInfoCubit, - 'accountRecordCubit': accountRecordCubit, - 'contactInvitationListCubit': contactInvitationListCubit, - 'contactListCubit': contactListCubit, - 'waitingInvitationsBlocMapCubit': waitingInvitationsBlocMapCubit, - 'activeChatCubit': activeChatCubit, - 'chatListCubit': chatListCubit, - 'activeConversationsBlocMapCubit': activeConversationsBlocMapCubit, - 'activeSingleContactChatBlocMapCubit': - activeSingleContactChatBlocMapCubit, - }; } extension PerAccountCollectionStateExt on PerAccountCollectionState { - // Returns if the account is ready and logged in bool get isReady => avAccountRecordState != null && avAccountRecordState!.isData && @@ -66,11 +45,7 @@ extension PerAccountCollectionStateExt on PerAccountCollectionState { activeConversationsBlocMapCubit != null && activeSingleContactChatBlocMapCubit != null; - /// If we have a selected account and it is ready and not locked, - /// this will provide the unlocked account's cubits to the context - Widget provideReady({required Widget child}) { - if (isReady) { - return MultiBlocProvider(providers: [ + Widget provide({required Widget child}) => MultiBlocProvider(providers: [ BlocProvider.value(value: accountInfoCubit!), BlocProvider.value(value: accountRecordCubit!), BlocProvider.value(value: contactInvitationListCubit!), @@ -81,9 +56,4 @@ extension PerAccountCollectionStateExt on PerAccountCollectionState { BlocProvider.value(value: activeConversationsBlocMapCubit!), BlocProvider.value(value: activeSingleContactChatBlocMapCubit!), ], child: child); - } else { - // Otherwise we just provide the child - return child; - } - } } diff --git a/lib/account_manager/models/per_account_collection_state/per_account_collection_state.freezed.dart b/lib/account_manager/models/per_account_collection_state/per_account_collection_state.freezed.dart index ac1254b..8dcc549 100644 --- a/lib/account_manager/models/per_account_collection_state/per_account_collection_state.freezed.dart +++ b/lib/account_manager/models/per_account_collection_state/per_account_collection_state.freezed.dart @@ -1,4 +1,3 @@ -// dart format width=80 // coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND // ignore_for_file: type=lint @@ -10,98 +9,46 @@ part of 'per_account_collection_state.dart'; // FreezedGenerator // ************************************************************************** -// dart format off T _$identity(T value) => value; +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + /// @nodoc mixin _$PerAccountCollectionState { - AccountInfo get accountInfo; - AsyncValue? get avAccountRecordState; - AccountInfoCubit? get accountInfoCubit; - AccountRecordCubit? get accountRecordCubit; - ContactInvitationListCubit? get contactInvitationListCubit; - ContactListCubit? get contactListCubit; - WaitingInvitationsBlocMapCubit? get waitingInvitationsBlocMapCubit; - ActiveChatCubit? get activeChatCubit; - ChatListCubit? get chatListCubit; - ActiveConversationsBlocMapCubit? get activeConversationsBlocMapCubit; - ActiveSingleContactChatBlocMapCubit? get activeSingleContactChatBlocMapCubit; + AccountInfo get accountInfo => throw _privateConstructorUsedError; + AsyncValue? get avAccountRecordState => + throw _privateConstructorUsedError; + AccountInfoCubit? get accountInfoCubit => throw _privateConstructorUsedError; + AccountRecordCubit? get accountRecordCubit => + throw _privateConstructorUsedError; + ContactInvitationListCubit? get contactInvitationListCubit => + throw _privateConstructorUsedError; + ContactListCubit? get contactListCubit => throw _privateConstructorUsedError; + WaitingInvitationsBlocMapCubit? get waitingInvitationsBlocMapCubit => + throw _privateConstructorUsedError; + ActiveChatCubit? get activeChatCubit => throw _privateConstructorUsedError; + ChatListCubit? get chatListCubit => throw _privateConstructorUsedError; + ActiveConversationsBlocMapCubit? get activeConversationsBlocMapCubit => + throw _privateConstructorUsedError; + ActiveSingleContactChatBlocMapCubit? + get activeSingleContactChatBlocMapCubit => + throw _privateConstructorUsedError; - /// Create a copy of PerAccountCollectionState - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') + @JsonKey(ignore: true) $PerAccountCollectionStateCopyWith get copyWith => - _$PerAccountCollectionStateCopyWithImpl( - this as PerAccountCollectionState, _$identity); - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is PerAccountCollectionState && - (identical(other.accountInfo, accountInfo) || - other.accountInfo == accountInfo) && - (identical(other.avAccountRecordState, avAccountRecordState) || - other.avAccountRecordState == avAccountRecordState) && - (identical(other.accountInfoCubit, accountInfoCubit) || - other.accountInfoCubit == accountInfoCubit) && - (identical(other.accountRecordCubit, accountRecordCubit) || - other.accountRecordCubit == accountRecordCubit) && - (identical(other.contactInvitationListCubit, - contactInvitationListCubit) || - other.contactInvitationListCubit == - contactInvitationListCubit) && - (identical(other.contactListCubit, contactListCubit) || - other.contactListCubit == contactListCubit) && - (identical(other.waitingInvitationsBlocMapCubit, - waitingInvitationsBlocMapCubit) || - other.waitingInvitationsBlocMapCubit == - waitingInvitationsBlocMapCubit) && - (identical(other.activeChatCubit, activeChatCubit) || - other.activeChatCubit == activeChatCubit) && - (identical(other.chatListCubit, chatListCubit) || - other.chatListCubit == chatListCubit) && - (identical(other.activeConversationsBlocMapCubit, - activeConversationsBlocMapCubit) || - other.activeConversationsBlocMapCubit == - activeConversationsBlocMapCubit) && - (identical(other.activeSingleContactChatBlocMapCubit, - activeSingleContactChatBlocMapCubit) || - other.activeSingleContactChatBlocMapCubit == - activeSingleContactChatBlocMapCubit)); - } - - @override - int get hashCode => Object.hash( - runtimeType, - accountInfo, - avAccountRecordState, - accountInfoCubit, - accountRecordCubit, - contactInvitationListCubit, - contactListCubit, - waitingInvitationsBlocMapCubit, - activeChatCubit, - chatListCubit, - activeConversationsBlocMapCubit, - activeSingleContactChatBlocMapCubit); - - @override - String toString() { - return 'PerAccountCollectionState(accountInfo: $accountInfo, avAccountRecordState: $avAccountRecordState, accountInfoCubit: $accountInfoCubit, accountRecordCubit: $accountRecordCubit, contactInvitationListCubit: $contactInvitationListCubit, contactListCubit: $contactListCubit, waitingInvitationsBlocMapCubit: $waitingInvitationsBlocMapCubit, activeChatCubit: $activeChatCubit, chatListCubit: $chatListCubit, activeConversationsBlocMapCubit: $activeConversationsBlocMapCubit, activeSingleContactChatBlocMapCubit: $activeSingleContactChatBlocMapCubit)'; - } + throw _privateConstructorUsedError; } /// @nodoc -abstract mixin class $PerAccountCollectionStateCopyWith<$Res> { +abstract class $PerAccountCollectionStateCopyWith<$Res> { factory $PerAccountCollectionStateCopyWith(PerAccountCollectionState value, - $Res Function(PerAccountCollectionState) _then) = - _$PerAccountCollectionStateCopyWithImpl; + $Res Function(PerAccountCollectionState) then) = + _$PerAccountCollectionStateCopyWithImpl<$Res, PerAccountCollectionState>; @useResult $Res call( {AccountInfo accountInfo, - AsyncValue? avAccountRecordState, + AsyncValue? avAccountRecordState, AccountInfoCubit? accountInfoCubit, AccountRecordCubit? accountRecordCubit, ContactInvitationListCubit? contactInvitationListCubit, @@ -117,15 +64,16 @@ abstract mixin class $PerAccountCollectionStateCopyWith<$Res> { } /// @nodoc -class _$PerAccountCollectionStateCopyWithImpl<$Res> +class _$PerAccountCollectionStateCopyWithImpl<$Res, + $Val extends PerAccountCollectionState> implements $PerAccountCollectionStateCopyWith<$Res> { - _$PerAccountCollectionStateCopyWithImpl(this._self, this._then); + _$PerAccountCollectionStateCopyWithImpl(this._value, this._then); - final PerAccountCollectionState _self; - final $Res Function(PerAccountCollectionState) _then; + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; - /// Create a copy of PerAccountCollectionState - /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -141,76 +89,177 @@ class _$PerAccountCollectionStateCopyWithImpl<$Res> Object? activeConversationsBlocMapCubit = freezed, Object? activeSingleContactChatBlocMapCubit = freezed, }) { - return _then(_self.copyWith( + return _then(_value.copyWith( accountInfo: null == accountInfo - ? _self.accountInfo + ? _value.accountInfo : accountInfo // ignore: cast_nullable_to_non_nullable as AccountInfo, avAccountRecordState: freezed == avAccountRecordState - ? _self.avAccountRecordState + ? _value.avAccountRecordState : avAccountRecordState // ignore: cast_nullable_to_non_nullable - as AsyncValue?, + as AsyncValue?, accountInfoCubit: freezed == accountInfoCubit - ? _self.accountInfoCubit + ? _value.accountInfoCubit : accountInfoCubit // ignore: cast_nullable_to_non_nullable as AccountInfoCubit?, accountRecordCubit: freezed == accountRecordCubit - ? _self.accountRecordCubit + ? _value.accountRecordCubit : accountRecordCubit // ignore: cast_nullable_to_non_nullable as AccountRecordCubit?, contactInvitationListCubit: freezed == contactInvitationListCubit - ? _self.contactInvitationListCubit + ? _value.contactInvitationListCubit : contactInvitationListCubit // ignore: cast_nullable_to_non_nullable as ContactInvitationListCubit?, contactListCubit: freezed == contactListCubit - ? _self.contactListCubit + ? _value.contactListCubit : contactListCubit // ignore: cast_nullable_to_non_nullable as ContactListCubit?, waitingInvitationsBlocMapCubit: freezed == waitingInvitationsBlocMapCubit - ? _self.waitingInvitationsBlocMapCubit + ? _value.waitingInvitationsBlocMapCubit : waitingInvitationsBlocMapCubit // ignore: cast_nullable_to_non_nullable as WaitingInvitationsBlocMapCubit?, activeChatCubit: freezed == activeChatCubit - ? _self.activeChatCubit + ? _value.activeChatCubit : activeChatCubit // ignore: cast_nullable_to_non_nullable as ActiveChatCubit?, chatListCubit: freezed == chatListCubit - ? _self.chatListCubit + ? _value.chatListCubit : chatListCubit // ignore: cast_nullable_to_non_nullable as ChatListCubit?, activeConversationsBlocMapCubit: freezed == activeConversationsBlocMapCubit - ? _self.activeConversationsBlocMapCubit + ? _value.activeConversationsBlocMapCubit : activeConversationsBlocMapCubit // ignore: cast_nullable_to_non_nullable as ActiveConversationsBlocMapCubit?, activeSingleContactChatBlocMapCubit: freezed == activeSingleContactChatBlocMapCubit - ? _self.activeSingleContactChatBlocMapCubit + ? _value.activeSingleContactChatBlocMapCubit : activeSingleContactChatBlocMapCubit // ignore: cast_nullable_to_non_nullable as ActiveSingleContactChatBlocMapCubit?, - )); + ) as $Val); } - /// Create a copy of PerAccountCollectionState - /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $AsyncValueCopyWith? get avAccountRecordState { - if (_self.avAccountRecordState == null) { + if (_value.avAccountRecordState == null) { return null; } - return $AsyncValueCopyWith(_self.avAccountRecordState!, + return $AsyncValueCopyWith(_value.avAccountRecordState!, (value) { - return _then(_self.copyWith(avAccountRecordState: value)); + return _then(_value.copyWith(avAccountRecordState: value) as $Val); }); } } /// @nodoc +abstract class _$$PerAccountCollectionStateImplCopyWith<$Res> + implements $PerAccountCollectionStateCopyWith<$Res> { + factory _$$PerAccountCollectionStateImplCopyWith( + _$PerAccountCollectionStateImpl value, + $Res Function(_$PerAccountCollectionStateImpl) then) = + __$$PerAccountCollectionStateImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {AccountInfo accountInfo, + AsyncValue? avAccountRecordState, + AccountInfoCubit? accountInfoCubit, + AccountRecordCubit? accountRecordCubit, + ContactInvitationListCubit? contactInvitationListCubit, + ContactListCubit? contactListCubit, + WaitingInvitationsBlocMapCubit? waitingInvitationsBlocMapCubit, + ActiveChatCubit? activeChatCubit, + ChatListCubit? chatListCubit, + ActiveConversationsBlocMapCubit? activeConversationsBlocMapCubit, + ActiveSingleContactChatBlocMapCubit? + activeSingleContactChatBlocMapCubit}); -class _PerAccountCollectionState extends PerAccountCollectionState { - const _PerAccountCollectionState( + @override + $AsyncValueCopyWith? get avAccountRecordState; +} + +/// @nodoc +class __$$PerAccountCollectionStateImplCopyWithImpl<$Res> + extends _$PerAccountCollectionStateCopyWithImpl<$Res, + _$PerAccountCollectionStateImpl> + implements _$$PerAccountCollectionStateImplCopyWith<$Res> { + __$$PerAccountCollectionStateImplCopyWithImpl( + _$PerAccountCollectionStateImpl _value, + $Res Function(_$PerAccountCollectionStateImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? accountInfo = null, + Object? avAccountRecordState = freezed, + Object? accountInfoCubit = freezed, + Object? accountRecordCubit = freezed, + Object? contactInvitationListCubit = freezed, + Object? contactListCubit = freezed, + Object? waitingInvitationsBlocMapCubit = freezed, + Object? activeChatCubit = freezed, + Object? chatListCubit = freezed, + Object? activeConversationsBlocMapCubit = freezed, + Object? activeSingleContactChatBlocMapCubit = freezed, + }) { + return _then(_$PerAccountCollectionStateImpl( + accountInfo: null == accountInfo + ? _value.accountInfo + : accountInfo // ignore: cast_nullable_to_non_nullable + as AccountInfo, + avAccountRecordState: freezed == avAccountRecordState + ? _value.avAccountRecordState + : avAccountRecordState // ignore: cast_nullable_to_non_nullable + as AsyncValue?, + accountInfoCubit: freezed == accountInfoCubit + ? _value.accountInfoCubit + : accountInfoCubit // ignore: cast_nullable_to_non_nullable + as AccountInfoCubit?, + accountRecordCubit: freezed == accountRecordCubit + ? _value.accountRecordCubit + : accountRecordCubit // ignore: cast_nullable_to_non_nullable + as AccountRecordCubit?, + contactInvitationListCubit: freezed == contactInvitationListCubit + ? _value.contactInvitationListCubit + : contactInvitationListCubit // ignore: cast_nullable_to_non_nullable + as ContactInvitationListCubit?, + contactListCubit: freezed == contactListCubit + ? _value.contactListCubit + : contactListCubit // ignore: cast_nullable_to_non_nullable + as ContactListCubit?, + waitingInvitationsBlocMapCubit: freezed == waitingInvitationsBlocMapCubit + ? _value.waitingInvitationsBlocMapCubit + : waitingInvitationsBlocMapCubit // ignore: cast_nullable_to_non_nullable + as WaitingInvitationsBlocMapCubit?, + activeChatCubit: freezed == activeChatCubit + ? _value.activeChatCubit + : activeChatCubit // ignore: cast_nullable_to_non_nullable + as ActiveChatCubit?, + chatListCubit: freezed == chatListCubit + ? _value.chatListCubit + : chatListCubit // ignore: cast_nullable_to_non_nullable + as ChatListCubit?, + activeConversationsBlocMapCubit: freezed == + activeConversationsBlocMapCubit + ? _value.activeConversationsBlocMapCubit + : activeConversationsBlocMapCubit // ignore: cast_nullable_to_non_nullable + as ActiveConversationsBlocMapCubit?, + activeSingleContactChatBlocMapCubit: freezed == + activeSingleContactChatBlocMapCubit + ? _value.activeSingleContactChatBlocMapCubit + : activeSingleContactChatBlocMapCubit // ignore: cast_nullable_to_non_nullable + as ActiveSingleContactChatBlocMapCubit?, + )); + } +} + +/// @nodoc + +class _$PerAccountCollectionStateImpl implements _PerAccountCollectionState { + const _$PerAccountCollectionStateImpl( {required this.accountInfo, required this.avAccountRecordState, required this.accountInfoCubit, @@ -221,13 +270,12 @@ class _PerAccountCollectionState extends PerAccountCollectionState { required this.activeChatCubit, required this.chatListCubit, required this.activeConversationsBlocMapCubit, - required this.activeSingleContactChatBlocMapCubit}) - : super._(); + required this.activeSingleContactChatBlocMapCubit}); @override final AccountInfo accountInfo; @override - final AsyncValue? avAccountRecordState; + final AsyncValue? avAccountRecordState; @override final AccountInfoCubit? accountInfoCubit; @override @@ -248,21 +296,16 @@ class _PerAccountCollectionState extends PerAccountCollectionState { final ActiveSingleContactChatBlocMapCubit? activeSingleContactChatBlocMapCubit; - /// Create a copy of PerAccountCollectionState - /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') - _$PerAccountCollectionStateCopyWith<_PerAccountCollectionState> - get copyWith => - __$PerAccountCollectionStateCopyWithImpl<_PerAccountCollectionState>( - this, _$identity); + String toString() { + return 'PerAccountCollectionState(accountInfo: $accountInfo, avAccountRecordState: $avAccountRecordState, accountInfoCubit: $accountInfoCubit, accountRecordCubit: $accountRecordCubit, contactInvitationListCubit: $contactInvitationListCubit, contactListCubit: $contactListCubit, waitingInvitationsBlocMapCubit: $waitingInvitationsBlocMapCubit, activeChatCubit: $activeChatCubit, chatListCubit: $chatListCubit, activeConversationsBlocMapCubit: $activeConversationsBlocMapCubit, activeSingleContactChatBlocMapCubit: $activeSingleContactChatBlocMapCubit)'; + } @override bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _PerAccountCollectionState && + other is _$PerAccountCollectionStateImpl && (identical(other.accountInfo, accountInfo) || other.accountInfo == accountInfo) && (identical(other.avAccountRecordState, avAccountRecordState) || @@ -310,127 +353,56 @@ class _PerAccountCollectionState extends PerAccountCollectionState { activeConversationsBlocMapCubit, activeSingleContactChatBlocMapCubit); - @override - String toString() { - return 'PerAccountCollectionState(accountInfo: $accountInfo, avAccountRecordState: $avAccountRecordState, accountInfoCubit: $accountInfoCubit, accountRecordCubit: $accountRecordCubit, contactInvitationListCubit: $contactInvitationListCubit, contactListCubit: $contactListCubit, waitingInvitationsBlocMapCubit: $waitingInvitationsBlocMapCubit, activeChatCubit: $activeChatCubit, chatListCubit: $chatListCubit, activeConversationsBlocMapCubit: $activeConversationsBlocMapCubit, activeSingleContactChatBlocMapCubit: $activeSingleContactChatBlocMapCubit)'; - } -} - -/// @nodoc -abstract mixin class _$PerAccountCollectionStateCopyWith<$Res> - implements $PerAccountCollectionStateCopyWith<$Res> { - factory _$PerAccountCollectionStateCopyWith(_PerAccountCollectionState value, - $Res Function(_PerAccountCollectionState) _then) = - __$PerAccountCollectionStateCopyWithImpl; - @override - @useResult - $Res call( - {AccountInfo accountInfo, - AsyncValue? avAccountRecordState, - AccountInfoCubit? accountInfoCubit, - AccountRecordCubit? accountRecordCubit, - ContactInvitationListCubit? contactInvitationListCubit, - ContactListCubit? contactListCubit, - WaitingInvitationsBlocMapCubit? waitingInvitationsBlocMapCubit, - ActiveChatCubit? activeChatCubit, - ChatListCubit? chatListCubit, - ActiveConversationsBlocMapCubit? activeConversationsBlocMapCubit, - ActiveSingleContactChatBlocMapCubit? - activeSingleContactChatBlocMapCubit}); - - @override - $AsyncValueCopyWith? get avAccountRecordState; -} - -/// @nodoc -class __$PerAccountCollectionStateCopyWithImpl<$Res> - implements _$PerAccountCollectionStateCopyWith<$Res> { - __$PerAccountCollectionStateCopyWithImpl(this._self, this._then); - - final _PerAccountCollectionState _self; - final $Res Function(_PerAccountCollectionState) _then; - - /// Create a copy of PerAccountCollectionState - /// with the given fields replaced by the non-null parameter values. + @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - $Res call({ - Object? accountInfo = null, - Object? avAccountRecordState = freezed, - Object? accountInfoCubit = freezed, - Object? accountRecordCubit = freezed, - Object? contactInvitationListCubit = freezed, - Object? contactListCubit = freezed, - Object? waitingInvitationsBlocMapCubit = freezed, - Object? activeChatCubit = freezed, - Object? chatListCubit = freezed, - Object? activeConversationsBlocMapCubit = freezed, - Object? activeSingleContactChatBlocMapCubit = freezed, - }) { - return _then(_PerAccountCollectionState( - accountInfo: null == accountInfo - ? _self.accountInfo - : accountInfo // ignore: cast_nullable_to_non_nullable - as AccountInfo, - avAccountRecordState: freezed == avAccountRecordState - ? _self.avAccountRecordState - : avAccountRecordState // ignore: cast_nullable_to_non_nullable - as AsyncValue?, - accountInfoCubit: freezed == accountInfoCubit - ? _self.accountInfoCubit - : accountInfoCubit // ignore: cast_nullable_to_non_nullable - as AccountInfoCubit?, - accountRecordCubit: freezed == accountRecordCubit - ? _self.accountRecordCubit - : accountRecordCubit // ignore: cast_nullable_to_non_nullable - as AccountRecordCubit?, - contactInvitationListCubit: freezed == contactInvitationListCubit - ? _self.contactInvitationListCubit - : contactInvitationListCubit // ignore: cast_nullable_to_non_nullable - as ContactInvitationListCubit?, - contactListCubit: freezed == contactListCubit - ? _self.contactListCubit - : contactListCubit // ignore: cast_nullable_to_non_nullable - as ContactListCubit?, - waitingInvitationsBlocMapCubit: freezed == waitingInvitationsBlocMapCubit - ? _self.waitingInvitationsBlocMapCubit - : waitingInvitationsBlocMapCubit // ignore: cast_nullable_to_non_nullable - as WaitingInvitationsBlocMapCubit?, - activeChatCubit: freezed == activeChatCubit - ? _self.activeChatCubit - : activeChatCubit // ignore: cast_nullable_to_non_nullable - as ActiveChatCubit?, - chatListCubit: freezed == chatListCubit - ? _self.chatListCubit - : chatListCubit // ignore: cast_nullable_to_non_nullable - as ChatListCubit?, - activeConversationsBlocMapCubit: freezed == - activeConversationsBlocMapCubit - ? _self.activeConversationsBlocMapCubit - : activeConversationsBlocMapCubit // ignore: cast_nullable_to_non_nullable - as ActiveConversationsBlocMapCubit?, - activeSingleContactChatBlocMapCubit: freezed == - activeSingleContactChatBlocMapCubit - ? _self.activeSingleContactChatBlocMapCubit - : activeSingleContactChatBlocMapCubit // ignore: cast_nullable_to_non_nullable - as ActiveSingleContactChatBlocMapCubit?, - )); - } - - /// Create a copy of PerAccountCollectionState - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $AsyncValueCopyWith? get avAccountRecordState { - if (_self.avAccountRecordState == null) { - return null; - } - - return $AsyncValueCopyWith(_self.avAccountRecordState!, - (value) { - return _then(_self.copyWith(avAccountRecordState: value)); - }); - } + _$$PerAccountCollectionStateImplCopyWith<_$PerAccountCollectionStateImpl> + get copyWith => __$$PerAccountCollectionStateImplCopyWithImpl< + _$PerAccountCollectionStateImpl>(this, _$identity); } -// dart format on +abstract class _PerAccountCollectionState implements PerAccountCollectionState { + const factory _PerAccountCollectionState( + {required final AccountInfo accountInfo, + required final AsyncValue? avAccountRecordState, + required final AccountInfoCubit? accountInfoCubit, + required final AccountRecordCubit? accountRecordCubit, + required final ContactInvitationListCubit? contactInvitationListCubit, + required final ContactListCubit? contactListCubit, + required final WaitingInvitationsBlocMapCubit? + waitingInvitationsBlocMapCubit, + required final ActiveChatCubit? activeChatCubit, + required final ChatListCubit? chatListCubit, + required final ActiveConversationsBlocMapCubit? + activeConversationsBlocMapCubit, + required final ActiveSingleContactChatBlocMapCubit? + activeSingleContactChatBlocMapCubit}) = + _$PerAccountCollectionStateImpl; + + @override + AccountInfo get accountInfo; + @override + AsyncValue? get avAccountRecordState; + @override + AccountInfoCubit? get accountInfoCubit; + @override + AccountRecordCubit? get accountRecordCubit; + @override + ContactInvitationListCubit? get contactInvitationListCubit; + @override + ContactListCubit? get contactListCubit; + @override + WaitingInvitationsBlocMapCubit? get waitingInvitationsBlocMapCubit; + @override + ActiveChatCubit? get activeChatCubit; + @override + ChatListCubit? get chatListCubit; + @override + ActiveConversationsBlocMapCubit? get activeConversationsBlocMapCubit; + @override + ActiveSingleContactChatBlocMapCubit? get activeSingleContactChatBlocMapCubit; + @override + @JsonKey(ignore: true) + _$$PerAccountCollectionStateImplCopyWith<_$PerAccountCollectionStateImpl> + get copyWith => throw _privateConstructorUsedError; +} diff --git a/lib/account_manager/models/user_login/user_login.dart b/lib/account_manager/models/user_login/user_login.dart index 4e2f680..7c024cf 100644 --- a/lib/account_manager/models/user_login/user_login.dart +++ b/lib/account_manager/models/user_login/user_login.dart @@ -8,9 +8,8 @@ part 'user_login.g.dart'; // Represents a currently logged in account // User logins are stored in the user_logins tablestore table // indexed by the accountSuperIdentityRecordKey -@Freezed(toJson: true) -sealed class UserLogin with _$UserLogin { - @JsonSerializable() +@freezed +class UserLogin with _$UserLogin { const factory UserLogin({ // SuperIdentity record key for the user // used to index the local accounts table @@ -24,13 +23,6 @@ sealed class UserLogin with _$UserLogin { required Timestamp lastActive, }) = _UserLogin; - factory UserLogin.fromJson(dynamic json) { - try { - return _$UserLoginFromJson(json as Map); - // Need to catch any errors here too - // ignore: avoid_catches_without_on_clauses - } catch (e, st) { - throw Exception('invalid user login: $e\n$st'); - } - } + factory UserLogin.fromJson(dynamic json) => + _$UserLoginFromJson(json as Map); } diff --git a/lib/account_manager/models/user_login/user_login.freezed.dart b/lib/account_manager/models/user_login/user_login.freezed.dart index 914afb8..c93ee7b 100644 --- a/lib/account_manager/models/user_login/user_login.freezed.dart +++ b/lib/account_manager/models/user_login/user_login.freezed.dart @@ -1,4 +1,3 @@ -// dart format width=80 // coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND // ignore_for_file: type=lint @@ -10,65 +9,41 @@ part of 'user_login.dart'; // FreezedGenerator // ************************************************************************** -// dart format off T _$identity(T value) => value; +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +UserLogin _$UserLoginFromJson(Map json) { + return _UserLogin.fromJson(json); +} + /// @nodoc mixin _$UserLogin { // SuperIdentity record key for the user // used to index the local accounts table - TypedKey - get superIdentityRecordKey; // The identity secret as unlocked from the local accounts table - TypedSecret - get identitySecret; // The account record key, owner key and secret pulled from the identity - AccountRecordInfo - get accountRecordInfo; // The time this login was most recently used - Timestamp get lastActive; + Typed get superIdentityRecordKey => + throw _privateConstructorUsedError; // The identity secret as unlocked from the local accounts table + Typed get identitySecret => + throw _privateConstructorUsedError; // The account record key, owner key and secret pulled from the identity + AccountRecordInfo get accountRecordInfo => + throw _privateConstructorUsedError; // The time this login was most recently used + Timestamp get lastActive => throw _privateConstructorUsedError; - /// Create a copy of UserLogin - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) $UserLoginCopyWith get copyWith => - _$UserLoginCopyWithImpl(this as UserLogin, _$identity); - - /// Serializes this UserLogin to a JSON map. - Map toJson(); - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is UserLogin && - (identical(other.superIdentityRecordKey, superIdentityRecordKey) || - other.superIdentityRecordKey == superIdentityRecordKey) && - (identical(other.identitySecret, identitySecret) || - other.identitySecret == identitySecret) && - (identical(other.accountRecordInfo, accountRecordInfo) || - other.accountRecordInfo == accountRecordInfo) && - (identical(other.lastActive, lastActive) || - other.lastActive == lastActive)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash(runtimeType, superIdentityRecordKey, - identitySecret, accountRecordInfo, lastActive); - - @override - String toString() { - return 'UserLogin(superIdentityRecordKey: $superIdentityRecordKey, identitySecret: $identitySecret, accountRecordInfo: $accountRecordInfo, lastActive: $lastActive)'; - } + throw _privateConstructorUsedError; } /// @nodoc -abstract mixin class $UserLoginCopyWith<$Res> { - factory $UserLoginCopyWith(UserLogin value, $Res Function(UserLogin) _then) = - _$UserLoginCopyWithImpl; +abstract class $UserLoginCopyWith<$Res> { + factory $UserLoginCopyWith(UserLogin value, $Res Function(UserLogin) then) = + _$UserLoginCopyWithImpl<$Res, UserLogin>; @useResult $Res call( - {TypedKey superIdentityRecordKey, - TypedSecret identitySecret, + {Typed superIdentityRecordKey, + Typed identitySecret, AccountRecordInfo accountRecordInfo, Timestamp lastActive}); @@ -76,14 +51,15 @@ abstract mixin class $UserLoginCopyWith<$Res> { } /// @nodoc -class _$UserLoginCopyWithImpl<$Res> implements $UserLoginCopyWith<$Res> { - _$UserLoginCopyWithImpl(this._self, this._then); +class _$UserLoginCopyWithImpl<$Res, $Val extends UserLogin> + implements $UserLoginCopyWith<$Res> { + _$UserLoginCopyWithImpl(this._value, this._then); - final UserLogin _self; - final $Res Function(UserLogin) _then; + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; - /// Create a copy of UserLogin - /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -92,54 +68,109 @@ class _$UserLoginCopyWithImpl<$Res> implements $UserLoginCopyWith<$Res> { Object? accountRecordInfo = null, Object? lastActive = null, }) { - return _then(_self.copyWith( + return _then(_value.copyWith( superIdentityRecordKey: null == superIdentityRecordKey - ? _self.superIdentityRecordKey + ? _value.superIdentityRecordKey : superIdentityRecordKey // ignore: cast_nullable_to_non_nullable - as TypedKey, + as Typed, identitySecret: null == identitySecret - ? _self.identitySecret + ? _value.identitySecret : identitySecret // ignore: cast_nullable_to_non_nullable - as TypedSecret, + as Typed, accountRecordInfo: null == accountRecordInfo - ? _self.accountRecordInfo + ? _value.accountRecordInfo : accountRecordInfo // ignore: cast_nullable_to_non_nullable as AccountRecordInfo, lastActive: null == lastActive - ? _self.lastActive + ? _value.lastActive : lastActive // ignore: cast_nullable_to_non_nullable as Timestamp, - )); + ) as $Val); } - /// Create a copy of UserLogin - /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $AccountRecordInfoCopyWith<$Res> get accountRecordInfo { - return $AccountRecordInfoCopyWith<$Res>(_self.accountRecordInfo, (value) { - return _then(_self.copyWith(accountRecordInfo: value)); + return $AccountRecordInfoCopyWith<$Res>(_value.accountRecordInfo, (value) { + return _then(_value.copyWith(accountRecordInfo: value) as $Val); }); } } /// @nodoc +abstract class _$$UserLoginImplCopyWith<$Res> + implements $UserLoginCopyWith<$Res> { + factory _$$UserLoginImplCopyWith( + _$UserLoginImpl value, $Res Function(_$UserLoginImpl) then) = + __$$UserLoginImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {Typed superIdentityRecordKey, + Typed identitySecret, + AccountRecordInfo accountRecordInfo, + Timestamp lastActive}); + @override + $AccountRecordInfoCopyWith<$Res> get accountRecordInfo; +} + +/// @nodoc +class __$$UserLoginImplCopyWithImpl<$Res> + extends _$UserLoginCopyWithImpl<$Res, _$UserLoginImpl> + implements _$$UserLoginImplCopyWith<$Res> { + __$$UserLoginImplCopyWithImpl( + _$UserLoginImpl _value, $Res Function(_$UserLoginImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? superIdentityRecordKey = null, + Object? identitySecret = null, + Object? accountRecordInfo = null, + Object? lastActive = null, + }) { + return _then(_$UserLoginImpl( + superIdentityRecordKey: null == superIdentityRecordKey + ? _value.superIdentityRecordKey + : superIdentityRecordKey // ignore: cast_nullable_to_non_nullable + as Typed, + identitySecret: null == identitySecret + ? _value.identitySecret + : identitySecret // ignore: cast_nullable_to_non_nullable + as Typed, + accountRecordInfo: null == accountRecordInfo + ? _value.accountRecordInfo + : accountRecordInfo // ignore: cast_nullable_to_non_nullable + as AccountRecordInfo, + lastActive: null == lastActive + ? _value.lastActive + : lastActive // ignore: cast_nullable_to_non_nullable + as Timestamp, + )); + } +} + +/// @nodoc @JsonSerializable() -class _UserLogin implements UserLogin { - const _UserLogin( +class _$UserLoginImpl implements _UserLogin { + const _$UserLoginImpl( {required this.superIdentityRecordKey, required this.identitySecret, required this.accountRecordInfo, required this.lastActive}); + factory _$UserLoginImpl.fromJson(Map json) => + _$$UserLoginImplFromJson(json); + // SuperIdentity record key for the user // used to index the local accounts table @override - final TypedKey superIdentityRecordKey; + final Typed superIdentityRecordKey; // The identity secret as unlocked from the local accounts table @override - final TypedSecret identitySecret; + final Typed identitySecret; // The account record key, owner key and secret pulled from the identity @override final AccountRecordInfo accountRecordInfo; @@ -147,26 +178,16 @@ class _UserLogin implements UserLogin { @override final Timestamp lastActive; - /// Create a copy of UserLogin - /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') - _$UserLoginCopyWith<_UserLogin> get copyWith => - __$UserLoginCopyWithImpl<_UserLogin>(this, _$identity); - - @override - Map toJson() { - return _$UserLoginToJson( - this, - ); + String toString() { + return 'UserLogin(superIdentityRecordKey: $superIdentityRecordKey, identitySecret: $identitySecret, accountRecordInfo: $accountRecordInfo, lastActive: $lastActive)'; } @override bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _UserLogin && + other is _$UserLoginImpl && (identical(other.superIdentityRecordKey, superIdentityRecordKey) || other.superIdentityRecordKey == superIdentityRecordKey) && (identical(other.identitySecret, identitySecret) || @@ -177,81 +198,46 @@ class _UserLogin implements UserLogin { other.lastActive == lastActive)); } - @JsonKey(includeFromJson: false, includeToJson: false) + @JsonKey(ignore: true) @override int get hashCode => Object.hash(runtimeType, superIdentityRecordKey, identitySecret, accountRecordInfo, lastActive); - @override - String toString() { - return 'UserLogin(superIdentityRecordKey: $superIdentityRecordKey, identitySecret: $identitySecret, accountRecordInfo: $accountRecordInfo, lastActive: $lastActive)'; - } -} - -/// @nodoc -abstract mixin class _$UserLoginCopyWith<$Res> - implements $UserLoginCopyWith<$Res> { - factory _$UserLoginCopyWith( - _UserLogin value, $Res Function(_UserLogin) _then) = - __$UserLoginCopyWithImpl; - @override - @useResult - $Res call( - {TypedKey superIdentityRecordKey, - TypedSecret identitySecret, - AccountRecordInfo accountRecordInfo, - Timestamp lastActive}); - - @override - $AccountRecordInfoCopyWith<$Res> get accountRecordInfo; -} - -/// @nodoc -class __$UserLoginCopyWithImpl<$Res> implements _$UserLoginCopyWith<$Res> { - __$UserLoginCopyWithImpl(this._self, this._then); - - final _UserLogin _self; - final $Res Function(_UserLogin) _then; - - /// Create a copy of UserLogin - /// with the given fields replaced by the non-null parameter values. + @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - $Res call({ - Object? superIdentityRecordKey = null, - Object? identitySecret = null, - Object? accountRecordInfo = null, - Object? lastActive = null, - }) { - return _then(_UserLogin( - superIdentityRecordKey: null == superIdentityRecordKey - ? _self.superIdentityRecordKey - : superIdentityRecordKey // ignore: cast_nullable_to_non_nullable - as TypedKey, - identitySecret: null == identitySecret - ? _self.identitySecret - : identitySecret // ignore: cast_nullable_to_non_nullable - as TypedSecret, - accountRecordInfo: null == accountRecordInfo - ? _self.accountRecordInfo - : accountRecordInfo // ignore: cast_nullable_to_non_nullable - as AccountRecordInfo, - lastActive: null == lastActive - ? _self.lastActive - : lastActive // ignore: cast_nullable_to_non_nullable - as Timestamp, - )); - } + _$$UserLoginImplCopyWith<_$UserLoginImpl> get copyWith => + __$$UserLoginImplCopyWithImpl<_$UserLoginImpl>(this, _$identity); - /// Create a copy of UserLogin - /// with the given fields replaced by the non-null parameter values. @override - @pragma('vm:prefer-inline') - $AccountRecordInfoCopyWith<$Res> get accountRecordInfo { - return $AccountRecordInfoCopyWith<$Res>(_self.accountRecordInfo, (value) { - return _then(_self.copyWith(accountRecordInfo: value)); - }); + Map toJson() { + return _$$UserLoginImplToJson( + this, + ); } } -// dart format on +abstract class _UserLogin implements UserLogin { + const factory _UserLogin( + {required final Typed superIdentityRecordKey, + required final Typed identitySecret, + required final AccountRecordInfo accountRecordInfo, + required final Timestamp lastActive}) = _$UserLoginImpl; + + factory _UserLogin.fromJson(Map json) = + _$UserLoginImpl.fromJson; + + @override // SuperIdentity record key for the user +// used to index the local accounts table + Typed get superIdentityRecordKey; + @override // The identity secret as unlocked from the local accounts table + Typed get identitySecret; + @override // The account record key, owner key and secret pulled from the identity + AccountRecordInfo get accountRecordInfo; + @override // The time this login was most recently used + Timestamp get lastActive; + @override + @JsonKey(ignore: true) + _$$UserLoginImplCopyWith<_$UserLoginImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/account_manager/models/user_login/user_login.g.dart b/lib/account_manager/models/user_login/user_login.g.dart index fa5314b..173d853 100644 --- a/lib/account_manager/models/user_login/user_login.g.dart +++ b/lib/account_manager/models/user_login/user_login.g.dart @@ -6,7 +6,8 @@ part of 'user_login.dart'; // JsonSerializableGenerator // ************************************************************************** -_UserLogin _$UserLoginFromJson(Map json) => _UserLogin( +_$UserLoginImpl _$$UserLoginImplFromJson(Map json) => + _$UserLoginImpl( superIdentityRecordKey: Typed.fromJson( json['super_identity_record_key']), identitySecret: @@ -16,7 +17,7 @@ _UserLogin _$UserLoginFromJson(Map json) => _UserLogin( lastActive: Timestamp.fromJson(json['last_active']), ); -Map _$UserLoginToJson(_UserLogin instance) => +Map _$$UserLoginImplToJson(_$UserLoginImpl instance) => { 'super_identity_record_key': instance.superIdentityRecordKey.toJson(), 'identity_secret': instance.identitySecret.toJson(), diff --git a/lib/account_manager/repository/account_repository.dart b/lib/account_manager/repository/account_repository.dart index c5058ba..13954ba 100644 --- a/lib/account_manager/repository/account_repository.dart +++ b/lib/account_manager/repository/account_repository.dart @@ -281,33 +281,15 @@ class AccountRepository { parent: parent)) .scope((r) async => r.recordPointer); - final groupChatRecords = await (await DHTShortArray.create( - debugName: 'AccountRepository::_newLocalAccount::GroupChats', - parent: parent)) - .scope((r) async => r.recordPointer); - // Make account object - final profile = proto.Profile() - ..name = accountSpec.name - ..pronouns = accountSpec.pronouns - ..about = accountSpec.about - ..status = accountSpec.status - ..availability = accountSpec.availability - ..timestamp = Veilid.instance.now().toInt64(); - final account = proto.Account() - ..profile = profile - ..invisible = accountSpec.invisible - ..autoAwayTimeoutMin = accountSpec.autoAwayTimeout + ..profile.name = accountSpec.name + ..profile.pronouns = accountSpec.pronouns + ..profile.about = accountSpec.about + ..profile.status = accountSpec.status ..contactList = contactList.toProto() ..contactInvitationRecords = contactInvitationRecords.toProto() - ..chatList = chatRecords.toProto() - ..groupChatList = groupChatRecords.toProto() - ..freeMessage = accountSpec.freeMessage - ..awayMessage = accountSpec.awayMessage - ..busyMessage = accountSpec.busyMessage - ..autodetectAway = accountSpec.autoAway; - + ..chatList = chatRecords.toProto(); return account.writeToBuffer(); }); diff --git a/lib/account_manager/views/edit_account_page.dart b/lib/account_manager/views/edit_account_page.dart index bb14968..e49b48a 100644 --- a/lib/account_manager/views/edit_account_page.dart +++ b/lib/account_manager/views/edit_account_page.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:async_tools/async_tools.dart'; import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -11,18 +10,17 @@ import 'package:veilid_support/veilid_support.dart'; import '../../layout/default_app_bar.dart'; import '../../notifications/notifications.dart'; +import '../../proto/proto.dart' as proto; import '../../theme/theme.dart'; import '../../tools/tools.dart'; import '../../veilid_processor/veilid_processor.dart'; import '../account_manager.dart'; import 'edit_profile_form.dart'; -const _kDoBackArrow = 'doBackArrow'; - class EditAccountPage extends StatefulWidget { const EditAccountPage( {required this.superIdentityRecordKey, - required this.initialValue, + required this.existingAccount, required this.accountRecord, super.key}); @@ -30,7 +28,7 @@ class EditAccountPage extends StatefulWidget { State createState() => _EditAccountPageState(); final TypedKey superIdentityRecordKey; - final AccountSpec initialValue; + final proto.Account existingAccount; final OwnedDHTRecordPointer accountRecord; @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { @@ -38,7 +36,8 @@ class EditAccountPage extends StatefulWidget { properties ..add(DiagnosticsProperty( 'superIdentityRecordKey', superIdentityRecordKey)) - ..add(DiagnosticsProperty('initialValue', initialValue)) + ..add(DiagnosticsProperty( + 'existingAccount', existingAccount)) ..add(DiagnosticsProperty( 'accountRecord', accountRecord)); } @@ -50,31 +49,47 @@ class _EditAccountPageState extends WindowSetupState { titleBarStyle: TitleBarStyle.normal, orientationCapability: OrientationCapability.portraitOnly); - EditProfileForm _editAccountForm(BuildContext context) => EditProfileForm( + Widget _editAccountForm(BuildContext context, + {required Future Function(AccountSpec) onUpdate}) => + EditProfileForm( header: translate('edit_account_page.header'), instructions: translate('edit_account_page.instructions'), - submitText: translate('button.update'), + submitText: translate('edit_account_page.update'), submitDisabledText: translate('button.waiting_for_network'), - onSubmit: _onSubmit, - onModifiedState: _onModifiedState, - initialValue: widget.initialValue, + onUpdate: onUpdate, + initialValueCallback: (key) => switch (key) { + EditProfileForm.formFieldName => widget.existingAccount.profile.name, + EditProfileForm.formFieldPronouns => + widget.existingAccount.profile.pronouns, + EditProfileForm.formFieldAbout => + widget.existingAccount.profile.about, + EditProfileForm.formFieldAvailability => + widget.existingAccount.profile.availability, + EditProfileForm.formFieldFreeMessage => + widget.existingAccount.freeMessage, + EditProfileForm.formFieldAwayMessage => + widget.existingAccount.awayMessage, + EditProfileForm.formFieldBusyMessage => + widget.existingAccount.busyMessage, + EditProfileForm.formFieldAvatar => + widget.existingAccount.profile.avatar, + EditProfileForm.formFieldAutoAway => + widget.existingAccount.autodetectAway, + EditProfileForm.formFieldAutoAwayTimeout => + widget.existingAccount.autoAwayTimeoutMin.toString(), + String() => throw UnimplementedError(), + }, ); Future _onRemoveAccount() async { - // dismiss the keyboard by unfocusing the textfield - FocusScope.of(context).unfocus(); - await asyncSleep(const Duration(milliseconds: 250)); - if (!mounted) { - return; - } - final confirmed = await StyledDialog.show( context: context, title: translate('edit_account_page.remove_account_confirm'), child: Column(mainAxisSize: MainAxisSize.min, children: [ Text(translate('edit_account_page.remove_account_confirm_message')) .paddingLTRB(24, 24, 24, 0), - Text(translate('confirmation.are_you_sure')).paddingAll(8), + Text(translate('edit_account_page.confirm_are_you_sure')) + .paddingAll(8), Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ ElevatedButton( onPressed: () { @@ -82,7 +97,7 @@ class _EditAccountPageState extends WindowSetupState { }, child: Row(mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.cancel, size: 16).paddingLTRB(0, 0, 4, 0), - Text(translate('button.no')).paddingLTRB(0, 0, 4, 0) + Text(translate('button.no_cancel')).paddingLTRB(0, 0, 4, 0) ])), ElevatedButton( onPressed: () { @@ -90,11 +105,14 @@ class _EditAccountPageState extends WindowSetupState { }, child: Row(mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.check, size: 16).paddingLTRB(0, 0, 4, 0), - Text(translate('button.yes')).paddingLTRB(0, 0, 4, 0) + Text(translate('button.yes_proceed')).paddingLTRB(0, 0, 4, 0) ])) ]).paddingAll(24) ])); - if (confirmed != null && confirmed) { + if (confirmed != null && confirmed && mounted) { + // dismiss the keyboard by unfocusing the textfield + FocusScope.of(context).unfocus(); + try { setState(() { _isInAsyncCall = true; @@ -102,40 +120,33 @@ class _EditAccountPageState extends WindowSetupState { try { final success = await AccountRepository.instance.deleteLocalAccount( widget.superIdentityRecordKey, widget.accountRecord); - if (mounted) { - if (success) { - context - .read() - .info(text: translate('edit_account_page.account_removed')); - GoRouterHelper(context).pop(); - } else { - context.read().error( - title: translate('edit_account_page.failed_to_remove_title'), - text: translate('edit_account_page.try_again_network')); - } + if (success && mounted) { + context + .read() + .info(text: translate('edit_account_page.account_removed')); + GoRouterHelper(context).pop(); + } else if (mounted) { + context + .read() + .error(text: translate('edit_account_page.failed_to_remove')); } } finally { - setState(() { - _isInAsyncCall = false; - }); + if (mounted) { + setState(() { + _isInAsyncCall = false; + }); + } } - } on Exception catch (e, st) { + } on Exception catch (e) { if (mounted) { - await showErrorStacktraceModal( - context: context, error: e, stackTrace: st); + await showErrorModal( + context, translate('new_account_page.error'), 'Exception: $e'); } } } } Future _onDestroyAccount() async { - // dismiss the keyboard by unfocusing the textfield - FocusScope.of(context).unfocus(); - await asyncSleep(const Duration(milliseconds: 250)); - if (!mounted) { - return; - } - final confirmed = await StyledDialog.show( context: context, title: translate('edit_account_page.destroy_account_confirm'), @@ -145,7 +156,8 @@ class _EditAccountPageState extends WindowSetupState { Text(translate( 'edit_account_page.destroy_account_confirm_message_details')) .paddingLTRB(24, 24, 24, 0), - Text(translate('confirmation.are_you_sure')).paddingAll(8), + Text(translate('edit_account_page.confirm_are_you_sure')) + .paddingAll(8), Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ ElevatedButton( onPressed: () { @@ -153,7 +165,7 @@ class _EditAccountPageState extends WindowSetupState { }, child: Row(mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.cancel, size: 16).paddingLTRB(0, 0, 4, 0), - Text(translate('button.no')).paddingLTRB(0, 0, 4, 0) + Text(translate('button.no_cancel')).paddingLTRB(0, 0, 4, 0) ])), ElevatedButton( onPressed: () { @@ -161,11 +173,14 @@ class _EditAccountPageState extends WindowSetupState { }, child: Row(mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.check, size: 16).paddingLTRB(0, 0, 4, 0), - Text(translate('button.yes')).paddingLTRB(0, 0, 4, 0) + Text(translate('button.yes_proceed')).paddingLTRB(0, 0, 4, 0) ])) ]).paddingAll(24) ])); - if (confirmed != null && confirmed) { + if (confirmed != null && confirmed && mounted) { + // dismiss the keyboard by unfocusing the textfield + FocusScope.of(context).unfocus(); + try { setState(() { _isInAsyncCall = true; @@ -173,105 +188,67 @@ class _EditAccountPageState extends WindowSetupState { try { final success = await AccountRepository.instance.destroyAccount( widget.superIdentityRecordKey, widget.accountRecord); - if (mounted) { - if (success) { - context - .read() - .info(text: translate('edit_account_page.account_destroyed')); - GoRouterHelper(context).pop(); - } else { - context.read().error( - title: translate('edit_account_page.failed_to_destroy_title'), - text: translate('edit_account_page.try_again_network')); - } + if (success && mounted) { + context + .read() + .info(text: translate('edit_account_page.account_destroyed')); + GoRouterHelper(context).pop(); + } else if (mounted) { + context + .read() + .error(text: translate('edit_account_page.failed_to_destroy')); } } finally { - setState(() { - _isInAsyncCall = false; - }); + if (mounted) { + setState(() { + _isInAsyncCall = false; + }); + } } - } on Exception catch (e, st) { + } on Exception catch (e) { if (mounted) { - await showErrorStacktraceModal( - context: context, error: e, stackTrace: st); + await showErrorModal( + context, translate('new_account_page.error'), 'Exception: $e'); } } } } - void _onModifiedState(bool isModified) { - setState(() { - _isModified = isModified; + Future _onUpdate(AccountSpec accountSpec) async { + // Look up account cubit for this specific account + final perAccountCollectionBlocMapCubit = + context.read(); + final accountRecordCubit = await perAccountCollectionBlocMapCubit.operate( + widget.superIdentityRecordKey, + closure: (c) async => c.accountRecordCubit); + if (accountRecordCubit == null) { + return; + } + + // Update account profile DHT record + // This triggers ConversationCubits to update + accountRecordCubit.updateAccount(accountSpec, () async { + // Update local account profile + await AccountRepository.instance + .updateLocalAccount(widget.superIdentityRecordKey, accountSpec); }); } - Future _onSubmit(AccountSpec accountSpec) async { - try { - setState(() { - _isInAsyncCall = true; - }); - try { - // Look up account cubit for this specific account - final perAccountCollectionBlocMapCubit = - context.read(); - final accountRecordCubit = perAccountCollectionBlocMapCubit - .entry(widget.superIdentityRecordKey) - ?.accountRecordCubit; - if (accountRecordCubit == null) { - return false; - } - - // Update account profile DHT record - // This triggers ConversationCubits to update - accountRecordCubit.updateAccount(accountSpec, () async { - // Update local account profile - await AccountRepository.instance - .updateLocalAccount(widget.superIdentityRecordKey, accountSpec); - }); - - return true; - } finally { - setState(() { - _isInAsyncCall = false; - }); - } - } on Exception catch (e, st) { - if (mounted) { - await showErrorStacktraceModal( - context: context, error: e, stackTrace: st); - } - } - return false; - } - @override Widget build(BuildContext context) { final displayModalHUD = _isInAsyncCall; return StyledScaffold( + // resizeToAvoidBottomInset: false, appBar: DefaultAppBar( title: Text(translate('edit_account_page.titlebar')), leading: Navigator.canPop(context) ? IconButton( icon: const Icon(Icons.arrow_back), onPressed: () { - singleFuture((this, _kDoBackArrow), () async { - if (_isModified) { - final ok = await showConfirmModal( - context: context, - title: - translate('confirmation.discard_changes'), - text: translate( - 'confirmation.are_you_sure_discard')); - if (!ok) { - return; - } - } - if (context.mounted) { - Navigator.pop(context); - } - }); - }) + Navigator.pop(context); + }, + ) : null, actions: [ const SignalStrengthMeterWidget(), @@ -284,7 +261,10 @@ class _EditAccountPageState extends WindowSetupState { ]), body: SingleChildScrollView( child: Column(children: [ - _editAccountForm(context).paddingLTRB(0, 0, 0, 32), + _editAccountForm( + context, + onUpdate: _onUpdate, + ).paddingLTRB(0, 0, 0, 32), OptionBox( instructions: translate('edit_account_page.remove_account_description'), @@ -306,5 +286,4 @@ class _EditAccountPageState extends WindowSetupState { //////////////////////////////////////////////////////////////////////////// bool _isInAsyncCall = false; - bool _isModified = false; } diff --git a/lib/account_manager/views/edit_profile_form.dart b/lib/account_manager/views/edit_profile_form.dart index 4977dc7..05e6ffe 100644 --- a/lib/account_manager/views/edit_profile_form.dart +++ b/lib/account_manager/views/edit_profile_form.dart @@ -2,7 +2,6 @@ import 'package:async_tools/async_tools.dart'; import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_translate/flutter_translate.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; @@ -10,10 +9,9 @@ import 'package:form_builder_validators/form_builder_validators.dart'; import '../../contacts/contacts.dart'; import '../../proto/proto.dart' as proto; import '../../theme/theme.dart'; -import '../../veilid_processor/veilid_processor.dart'; import '../models/models.dart'; -const _kDoSubmitEditProfile = 'doSubmitEditProfile'; +const _kDoUpdateSubmit = 'doUpdateSubmit'; class EditProfileForm extends StatefulWidget { const EditProfileForm({ @@ -21,9 +19,9 @@ class EditProfileForm extends StatefulWidget { required this.instructions, required this.submitText, required this.submitDisabledText, - required this.initialValue, - required this.onSubmit, - this.onModifiedState, + required this.initialValueCallback, + this.onUpdate, + this.onSubmit, super.key, }); @@ -32,11 +30,11 @@ class EditProfileForm extends StatefulWidget { final String header; final String instructions; - final Future Function(AccountSpec) onSubmit; - final void Function(bool)? onModifiedState; + final Future Function(AccountSpec)? onUpdate; + final Future Function(AccountSpec)? onSubmit; final String submitText; final String submitDisabledText; - final AccountSpec initialValue; + final Object Function(String key) initialValueCallback; @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { @@ -44,13 +42,14 @@ class EditProfileForm extends StatefulWidget { properties ..add(StringProperty('header', header)) ..add(StringProperty('instructions', instructions)) + ..add(ObjectFlagProperty Function(AccountSpec)?>.has( + 'onUpdate', onUpdate)) ..add(StringProperty('submitText', submitText)) ..add(StringProperty('submitDisabledText', submitDisabledText)) - ..add(ObjectFlagProperty Function(AccountSpec)>.has( - 'onSubmit', onSubmit)) - ..add(ObjectFlagProperty.has( - 'onModifiedState', onModifiedState)) - ..add(DiagnosticsProperty('initialValue', initialValue)); + ..add(ObjectFlagProperty.has( + 'initialValueCallback', initialValueCallback)) + ..add(ObjectFlagProperty Function(AccountSpec)?>.has( + 'onSubmit', onSubmit)); } static const String formFieldName = 'name'; @@ -70,22 +69,21 @@ class _EditProfileFormState extends State { @override void initState() { - _savedValue = widget.initialValue; - _currentValueName = widget.initialValue.name; - _currentValueAutoAway = widget.initialValue.autoAway; + _autoAwayEnabled = + widget.initialValueCallback(EditProfileForm.formFieldAutoAway) as bool; super.initState(); } FormBuilderDropdown _availabilityDropDown( BuildContext context) { - final theme = Theme.of(context); - final scale = theme.extension()!; - + final initialValueX = + widget.initialValueCallback(EditProfileForm.formFieldAvailability) + as proto.Availability; final initialValue = - _savedValue.availability == proto.Availability.AVAILABILITY_UNSPECIFIED + initialValueX == proto.Availability.AVAILABILITY_UNSPECIFIED ? proto.Availability.AVAILABILITY_FREE - : _savedValue.availability; + : initialValueX; final availabilities = [ proto.Availability.AVAILABILITY_FREE, @@ -105,8 +103,7 @@ class _EditProfileFormState extends State { .map((x) => DropdownMenuItem( value: x, child: Row(mainAxisSize: MainAxisSize.min, children: [ - AvailabilityWidget.availabilityIcon( - x, scale.primaryScale.appText), + AvailabilityWidget.availabilityIcon(x), Text(x == proto.Availability.AVAILABILITY_OFFLINE ? translate('availability.always_show_offline') : AvailabilityWidget.availabilityName(x)) @@ -135,12 +132,6 @@ class _EditProfileFormState extends State { .fields[EditProfileForm.formFieldAwayMessage]!.value as String; final busyMessage = _formKey.currentState! .fields[EditProfileForm.formFieldBusyMessage]!.value as String; - - const proto.DataReference? avatar = null; - // final avatar = _formKey.currentState! - // .fields[EditProfileForm.formFieldAvatar]!.value - //as proto.DataReference?; - final autoAway = _formKey .currentState!.fields[EditProfileForm.formFieldAutoAway]!.value as bool; final autoAwayTimeoutString = _formKey.currentState! @@ -156,21 +147,11 @@ class _EditProfileFormState extends State { freeMessage: freeMessage, awayMessage: awayMessage, busyMessage: busyMessage, - avatar: avatar, + avatar: null, autoAway: autoAway, autoAwayTimeout: autoAwayTimeout); } - // Check if everything is the same and update state - void _onChanged() { - final currentValue = _makeAccountSpec(); - _isModified = currentValue != _savedValue; - final onModifiedState = widget.onModifiedState; - if (onModifiedState != null) { - onModifiedState(_isModified); - } - } - Widget _editProfileForm( BuildContext context, ) { @@ -189,34 +170,24 @@ class _EditProfileFormState extends State { return FormBuilder( key: _formKey, autovalidateMode: AutovalidateMode.onUserInteraction, - onChanged: _onChanged, child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - spacing: 8, children: [ - Row(children: [ - const Spacer(), - AvatarWidget( - name: _currentValueName, - size: 128, - borderColor: border, - foregroundColor: scale.primaryScale.primaryText, - backgroundColor: scale.primaryScale.primary, - scaleConfig: scaleConfig, - textStyle: theme.textTheme.titleLarge!.copyWith(fontSize: 64), - ).paddingLTRB(0, 0, 0, 16), - const Spacer() - ]), + AvatarWidget( + name: _formKey.currentState?.value[EditProfileForm.formFieldName] + as String? ?? + '?', + size: 128, + borderColor: border, + foregroundColor: scale.primaryScale.primaryText, + backgroundColor: scale.primaryScale.primary, + scaleConfig: scaleConfig, + textStyle: theme.textTheme.titleLarge!.copyWith(fontSize: 64), + ).paddingLTRB(0, 0, 0, 16), FormBuilderTextField( autofocus: true, name: EditProfileForm.formFieldName, - initialValue: _savedValue.name, - onChanged: (x) { - setState(() { - _currentValueName = x ?? ''; - }); - }, + initialValue: widget + .initialValueCallback(EditProfileForm.formFieldName) as String, decoration: InputDecoration( floatingLabelBehavior: FloatingLabelBehavior.always, labelText: translate('account.form_name'), @@ -227,20 +198,23 @@ class _EditProfileFormState extends State { FormBuilderValidators.required(), ]), textInputAction: TextInputAction.next, - ), + ).onFocusChange(_onFocusChange), FormBuilderTextField( name: EditProfileForm.formFieldPronouns, - initialValue: _savedValue.pronouns, + initialValue: + widget.initialValueCallback(EditProfileForm.formFieldPronouns) + as String, maxLength: 64, decoration: InputDecoration( floatingLabelBehavior: FloatingLabelBehavior.always, labelText: translate('account.form_pronouns'), hintText: translate('account.empty_pronouns')), textInputAction: TextInputAction.next, - ), + ).onFocusChange(_onFocusChange), FormBuilderTextField( name: EditProfileForm.formFieldAbout, - initialValue: _savedValue.about, + initialValue: widget + .initialValueCallback(EditProfileForm.formFieldAbout) as String, maxLength: 1024, maxLines: 8, minLines: 1, @@ -249,106 +223,114 @@ class _EditProfileFormState extends State { labelText: translate('account.form_about'), hintText: translate('account.empty_about')), textInputAction: TextInputAction.newline, - ), - _availabilityDropDown(context).paddingLTRB(0, 0, 0, 16), + ).onFocusChange(_onFocusChange), + _availabilityDropDown(context) + .paddingLTRB(0, 0, 0, 16) + .onFocusChange(_onFocusChange), FormBuilderTextField( name: EditProfileForm.formFieldFreeMessage, - initialValue: _savedValue.freeMessage, + initialValue: widget.initialValueCallback( + EditProfileForm.formFieldFreeMessage) as String, maxLength: 128, decoration: InputDecoration( floatingLabelBehavior: FloatingLabelBehavior.always, labelText: translate('account.form_free_message'), hintText: translate('account.empty_free_message')), textInputAction: TextInputAction.next, - ), + ).onFocusChange(_onFocusChange), FormBuilderTextField( name: EditProfileForm.formFieldAwayMessage, - initialValue: _savedValue.awayMessage, + initialValue: widget.initialValueCallback( + EditProfileForm.formFieldAwayMessage) as String, maxLength: 128, decoration: InputDecoration( floatingLabelBehavior: FloatingLabelBehavior.always, labelText: translate('account.form_away_message'), hintText: translate('account.empty_away_message')), textInputAction: TextInputAction.next, - ), + ).onFocusChange(_onFocusChange), FormBuilderTextField( name: EditProfileForm.formFieldBusyMessage, - initialValue: _savedValue.busyMessage, + initialValue: widget.initialValueCallback( + EditProfileForm.formFieldBusyMessage) as String, maxLength: 128, decoration: InputDecoration( floatingLabelBehavior: FloatingLabelBehavior.always, labelText: translate('account.form_busy_message'), hintText: translate('account.empty_busy_message')), textInputAction: TextInputAction.next, - ), + ).onFocusChange(_onFocusChange), FormBuilderCheckbox( name: EditProfileForm.formFieldAutoAway, - initialValue: _savedValue.autoAway, + initialValue: + widget.initialValueCallback(EditProfileForm.formFieldAutoAway) + as bool, + side: BorderSide(color: scale.primaryScale.border, width: 2), title: Text(translate('account.form_auto_away'), style: textTheme.labelMedium), onChanged: (v) { setState(() { - _currentValueAutoAway = v ?? false; + _autoAwayEnabled = v ?? false; }); }, - ).paddingLTRB(0, 0, 0, 16), + ).onFocusChange(_onFocusChange), FormBuilderTextField( name: EditProfileForm.formFieldAutoAwayTimeout, - enabled: _currentValueAutoAway, - initialValue: _savedValue.autoAwayTimeout.toString(), + enabled: _autoAwayEnabled, + initialValue: widget.initialValueCallback( + EditProfileForm.formFieldAutoAwayTimeout) as String, decoration: InputDecoration( labelText: translate('account.form_auto_away_timeout'), ), validator: FormBuilderValidators.positiveNumber(), textInputAction: TextInputAction.next, - ), + ).onFocusChange(_onFocusChange), Row(children: [ const Spacer(), Text(widget.instructions).toCenter().flexible(flex: 6), const Spacer(), - ]).paddingSymmetric(vertical: 16), - Row(children: [ - const Spacer(), - Builder(builder: (context) { - final networkReady = context - .watch() - .state - .asData - ?.value - .isPublicInternetReady ?? - false; - - return ElevatedButton( - onPressed: (networkReady && _isModified) ? _doSubmit : null, - child: Row(mainAxisSize: MainAxisSize.min, children: [ - Icon(networkReady ? Icons.check : Icons.hourglass_empty, - size: 16) - .paddingLTRB(0, 0, 4, 0), - Text(networkReady - ? widget.submitText - : widget.submitDisabledText) - .paddingLTRB(0, 0, 4, 0) - ]), - ); - }), - const Spacer() - ]) + ]).paddingSymmetric(vertical: 4), + if (widget.onSubmit != null) + ElevatedButton( + onPressed: widget.onSubmit == null ? null : _doSubmit, + child: Row(mainAxisSize: MainAxisSize.min, children: [ + const Icon(Icons.check, size: 16).paddingLTRB(0, 0, 4, 0), + Text((widget.onSubmit == null) + ? widget.submitDisabledText + : widget.submitText) + .paddingLTRB(0, 0, 4, 0) + ]), + ) ], ), ); } + void _onFocusChange(bool focused) { + if (!focused) { + _doUpdate(); + } + } + + void _doUpdate() { + final onUpdate = widget.onUpdate; + if (onUpdate != null) { + singleFuture((this, _kDoUpdateSubmit), () async { + if (_formKey.currentState?.saveAndValidate() ?? false) { + final aus = _makeAccountSpec(); + await onUpdate(aus); + } + }); + } + } + void _doSubmit() { final onSubmit = widget.onSubmit; - if (_formKey.currentState?.saveAndValidate() ?? false) { - singleFuture((this, _kDoSubmitEditProfile), () async { - final updatedAccountSpec = _makeAccountSpec(); - final saved = await onSubmit(updatedAccountSpec); - if (saved) { - setState(() { - _savedValue = updatedAccountSpec; - }); - _onChanged(); + if (onSubmit != null) { + singleFuture((this, _kDoUpdateSubmit), () async { + if (_formKey.currentState?.saveAndValidate() ?? false) { + final aus = _makeAccountSpec(); + await onSubmit(aus); } }); } @@ -360,8 +342,5 @@ class _EditProfileFormState extends State { ); /////////////////////////////////////////////////////////////////////////// - late AccountSpec _savedValue; - late bool _currentValueAutoAway; - late String _currentValueName; - bool _isModified = false; + late bool _autoAwayEnabled; } diff --git a/lib/account_manager/views/new_account_page.dart b/lib/account_manager/views/new_account_page.dart index 07034df..ee2f62c 100644 --- a/lib/account_manager/views/new_account_page.dart +++ b/lib/account_manager/views/new_account_page.dart @@ -8,6 +8,7 @@ import 'package:go_router/go_router.dart'; import '../../layout/default_app_bar.dart'; import '../../notifications/cubits/notifications_cubit.dart'; +import '../../proto/proto.dart' as proto; import '../../theme/theme.dart'; import '../../tools/tools.dart'; import '../../veilid_processor/veilid_processor.dart'; @@ -27,6 +28,33 @@ class _NewAccountPageState extends WindowSetupState { titleBarStyle: TitleBarStyle.normal, orientationCapability: OrientationCapability.portraitOnly); + Object _defaultAccountValues(String key) { + switch (key) { + case EditProfileForm.formFieldName: + return ''; + case EditProfileForm.formFieldPronouns: + return ''; + case EditProfileForm.formFieldAbout: + return ''; + case EditProfileForm.formFieldAvailability: + return proto.Availability.AVAILABILITY_FREE; + case EditProfileForm.formFieldFreeMessage: + return ''; + case EditProfileForm.formFieldAwayMessage: + return ''; + case EditProfileForm.formFieldBusyMessage: + return ''; + // case EditProfileForm.formFieldAvatar: + // return null; + case EditProfileForm.formFieldAutoAway: + return false; + case EditProfileForm.formFieldAutoAwayTimeout: + return '15'; + default: + throw StateError('missing form element'); + } + } + Widget _newAccountForm( BuildContext context, ) => @@ -35,10 +63,10 @@ class _NewAccountPageState extends WindowSetupState { instructions: translate('new_account_page.instructions'), submitText: translate('new_account_page.create'), submitDisabledText: translate('button.waiting_for_network'), - initialValue: const AccountSpec.empty(), + initialValueCallback: _defaultAccountValues, onSubmit: _onSubmit); - Future _onSubmit(AccountSpec accountSpec) async { + Future _onSubmit(AccountSpec accountSpec) async { // dismiss the keyboard by unfocusing the textfield FocusScope.of(context).unfocus(); @@ -60,18 +88,13 @@ class _NewAccountPageState extends WindowSetupState { context.read().error( text: translate('new_account_page.network_is_offline'), title: translate('new_account_page.error')); - return false; + return; } - final isFirstAccount = - AccountRepository.instance.getLocalAccounts().isEmpty; - final writableSuperIdentity = await AccountRepository.instance .createWithNewSuperIdentity(accountSpec); GoRouterHelper(context).pushReplacement('/new_account/recovery_key', - extra: [writableSuperIdentity, accountSpec.name, isFirstAccount]); - - return true; + extra: [writableSuperIdentity, accountSpec.name]); } finally { if (mounted) { setState(() { @@ -79,13 +102,12 @@ class _NewAccountPageState extends WindowSetupState { }); } } - } on Exception catch (e, st) { + } on Exception catch (e) { if (mounted) { - await showErrorStacktraceModal( - context: context, error: e, stackTrace: st); + await showErrorModal( + context, translate('new_account_page.error'), 'Exception: $e'); } } - return false; } @override @@ -95,15 +117,11 @@ class _NewAccountPageState extends WindowSetupState { return StyledScaffold( appBar: DefaultAppBar( title: Text(translate('new_account_page.titlebar')), - leading: GoRouterHelper(context).canPop() + leading: Navigator.canPop(context) ? IconButton( icon: const Icon(Icons.arrow_back), onPressed: () { - if (GoRouterHelper(context).canPop()) { - GoRouterHelper(context).pop(); - } else { - GoRouterHelper(context).go('/'); - } + Navigator.pop(context); }, ) : null, @@ -117,10 +135,9 @@ class _NewAccountPageState extends WindowSetupState { }) ]), body: SingleChildScrollView( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), child: _newAccountForm( - context, - )).paddingAll(2), + context, + )).paddingSymmetric(horizontal: 24, vertical: 8), ).withModalHUD(context, displayModalHUD); } diff --git a/lib/account_manager/views/profile_widget.dart b/lib/account_manager/views/profile_widget.dart index c026856..672b13a 100644 --- a/lib/account_manager/views/profile_widget.dart +++ b/lib/account_manager/views/profile_widget.dart @@ -43,7 +43,7 @@ class ProfileWidget extends StatelessWidget { : scale.primaryScale.borderText, width: 2), borderRadius: BorderRadius.all( - Radius.circular(8 * scaleConfig.borderRadiusScale))), + Radius.circular(12 * scaleConfig.borderRadiusScale))), ), child: Row(children: [ const Spacer(), @@ -54,7 +54,7 @@ class ProfileWidget extends StatelessWidget { ? scale.primaryScale.border : scale.primaryScale.borderText), textAlign: TextAlign.left, - ).paddingAll(8), + ).paddingAll(12), if (_profile.pronouns.isNotEmpty && _showPronouns) Text('(${_profile.pronouns})', textAlign: TextAlign.right, @@ -62,7 +62,7 @@ class ProfileWidget extends StatelessWidget { color: scaleConfig.preferBorders ? scale.primaryScale.border : scale.primaryScale.primary)) - .paddingAll(8), + .paddingAll(12), const Spacer() ]), ); diff --git a/lib/account_manager/views/show_recovery_key_page.dart b/lib/account_manager/views/show_recovery_key_page.dart index 7c971e0..2649bcf 100644 --- a/lib/account_manager/views/show_recovery_key_page.dart +++ b/lib/account_manager/views/show_recovery_key_page.dart @@ -1,11 +1,11 @@ import 'dart:async'; import 'dart:io'; import 'dart:math'; +import 'dart:typed_data'; import 'package:async_tools/async_tools.dart'; import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:file_saver/file_saver.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_translate/flutter_translate.dart'; import 'package:go_router/go_router.dart'; @@ -25,18 +25,15 @@ class ShowRecoveryKeyPage extends StatefulWidget { const ShowRecoveryKeyPage( {required WritableSuperIdentity writableSuperIdentity, required String name, - required bool isFirstAccount, super.key}) : _writableSuperIdentity = writableSuperIdentity, - _name = name, - _isFirstAccount = isFirstAccount; + _name = name; @override State createState() => _ShowRecoveryKeyPageState(); final WritableSuperIdentity _writableSuperIdentity; final String _name; - final bool _isFirstAccount; } class _ShowRecoveryKeyPageState extends WindowSetupState { @@ -64,7 +61,7 @@ class _ShowRecoveryKeyPageState extends WindowSetupState { _isInAsyncCall = false; }); - if (!kIsWeb && Platform.isLinux) { + if (Platform.isLinux) { // Share plus doesn't do Linux yet await FileSaver.instance.saveFile(name: 'recovery_key.png', bytes: bytes); } else { @@ -163,6 +160,7 @@ class _ShowRecoveryKeyPageState extends WindowSetupState { final displayModalHUD = _isInAsyncCall; return StyledScaffold( + // resizeToAvoidBottomInset: false, appBar: DefaultAppBar( title: Text(translate('show_recovery_key_page.titlebar')), actions: [ @@ -250,13 +248,9 @@ class _ShowRecoveryKeyPageState extends WindowSetupState { child: ElevatedButton( onPressed: () { if (context.mounted) { - if (widget._isFirstAccount) { - GoRouterHelper(context).go('/'); - } else { - GoRouterHelper(context).canPop() - ? GoRouterHelper(context).pop() - : GoRouterHelper(context).go('/'); - } + Navigator.canPop(context) + ? GoRouterHelper(context).pop() + : GoRouterHelper(context).go('/'); } }, child: Text(translate('button.finish')).paddingAll(8)) diff --git a/lib/app.dart b/lib/app.dart index 802b0d7..98e349a 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -1,12 +1,15 @@ import 'package:animated_theme_switcher/animated_theme_switcher.dart'; +import 'package:async_tools/async_tools.dart'; import 'package:fast_immutable_collections/fast_immutable_collections.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_translate/flutter_translate.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:provider/provider.dart'; +import 'package:veilid_support/veilid_support.dart'; import 'account_manager/account_manager.dart'; import 'init.dart'; @@ -16,13 +19,15 @@ import 'router/router.dart'; import 'settings/settings.dart'; import 'theme/theme.dart'; import 'tick.dart'; +import 'tools/loggy.dart'; import 'veilid_processor/veilid_processor.dart'; -class ScrollBehaviorModified extends ScrollBehavior { - const ScrollBehaviorModified(); - @override - ScrollPhysics getScrollPhysics(BuildContext context) => - const ClampingScrollPhysics(); +class ReloadThemeIntent extends Intent { + const ReloadThemeIntent(); +} + +class AttachDetachIntent extends Intent { + const AttachDetachIntent(); } class VeilidChatApp extends StatelessWidget { @@ -35,92 +40,53 @@ class VeilidChatApp extends StatelessWidget { final ThemeData initialThemeData; - Widget appBuilder( - BuildContext context, LocalizationDelegate localizationDelegate) => - ThemeProvider( - initTheme: initialThemeData, - builder: (context, theme) => LocalizationProvider( - state: LocalizationProvider.of(context).state, - child: MultiBlocProvider( - providers: [ - BlocProvider( - create: (context) => - PreferencesCubit(PreferencesRepository.instance), - ), - BlocProvider( - create: (context) => NotificationsCubit( - const NotificationsState(queue: IList.empty()))), - BlocProvider( - create: (context) => - ConnectionStateCubit(ProcessorRepository.instance)), - BlocProvider( - create: (context) => RouterCubit(AccountRepository.instance), - ), - BlocProvider( - create: (context) => - LocalAccountsCubit(AccountRepository.instance), - ), - BlocProvider( - create: (context) => - UserLoginsCubit(AccountRepository.instance), - ), - BlocProvider( - create: (context) => - ActiveLocalAccountCubit(AccountRepository.instance), - ), - BlocProvider( - create: (context) => PerAccountCollectionBlocMapCubit( - accountRepository: AccountRepository.instance, - locator: context.read)), - ], - child: BackgroundTicker(child: Builder(builder: (context) { - final scale = theme.extension()!; - final scaleConfig = theme.extension()!; + void _reloadTheme(BuildContext context) { + log.info('Reloading theme'); + final theme = + PreferencesRepository.instance.value.themePreference.themeData(); + ThemeSwitcher.of(context).changeTheme(theme: theme); - final gradient = LinearGradient( - begin: Alignment.topLeft, - end: Alignment.bottomRight, - colors: scaleConfig.preferBorders && - theme.brightness == Brightness.light - ? [ - scale.grayScale.hoverElementBackground, - scale.grayScale.subtleBackground, - ] - : [ - scale.primaryScale.hoverElementBackground, - scale.primaryScale.subtleBackground, - ]); + // Hack to reload translations + final localizationDelegate = LocalizedApp.of(context).delegate; + singleFuture(this, () async { + await LocalizationDelegate.create( + fallbackLocale: localizationDelegate.fallbackLocale.toString(), + supportedLocales: localizationDelegate.supportedLocales + .map((x) => x.toString()) + .toList()); + }); + } - final wallpaper = PreferencesRepository - .instance.value.themePreference - .wallpaper(); + void _attachDetach(BuildContext context) { + singleFuture(this, () async { + if (ProcessorRepository.instance.processorConnectionState.isAttached) { + log.info('Detaching'); + await Veilid.instance.detach(); + } else if (ProcessorRepository + .instance.processorConnectionState.isDetached) { + log.info('Attaching'); + await Veilid.instance.attach(); + } + }); + } - return Stack( - fit: StackFit.expand, - alignment: Alignment.center, - children: [ - wallpaper ?? - DecoratedBox( - decoration: BoxDecoration(gradient: gradient)), - MaterialApp.router( - scrollBehavior: const ScrollBehaviorModified(), - debugShowCheckedModeBanner: false, - routerConfig: context.read().router(), - title: translate('app.title'), - theme: theme, - localizationsDelegates: [ - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - FormBuilderLocalizations.delegate, - localizationDelegate - ], - supportedLocales: localizationDelegate.supportedLocales, - locale: localizationDelegate.currentLocale, - ) - ]); - })), - )), - ); + Widget _buildShortcuts({required Widget Function(BuildContext) builder}) => + ThemeSwitcher( + builder: (context) => Shortcuts( + shortcuts: { + LogicalKeySet( + LogicalKeyboardKey.alt, LogicalKeyboardKey.keyR): + const ReloadThemeIntent(), + LogicalKeySet( + LogicalKeyboardKey.alt, LogicalKeyboardKey.keyD): + const AttachDetachIntent(), + }, + child: Actions(actions: >{ + ReloadThemeIntent: CallbackAction( + onInvoke: (intent) => _reloadTheme(context)), + AttachDetachIntent: CallbackAction( + onInvoke: (intent) => _attachDetach(context)), + }, child: Focus(autofocus: true, child: builder(context))))); @override Widget build(BuildContext context) => FutureProvider( @@ -134,8 +100,81 @@ class VeilidChatApp extends StatelessWidget { } // Once init is done, we proceed with the app final localizationDelegate = LocalizedApp.of(context).delegate; + return ThemeProvider( + initTheme: initialThemeData, + builder: (context, theme) => LocalizationProvider( + state: LocalizationProvider.of(context).state, + child: MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => + PreferencesCubit(PreferencesRepository.instance), + ), + BlocProvider( + create: (context) => NotificationsCubit( + const NotificationsState(queue: IList.empty()))), + BlocProvider( + create: (context) => + ConnectionStateCubit(ProcessorRepository.instance)), + BlocProvider( + create: (context) => + RouterCubit(AccountRepository.instance), + ), + BlocProvider( + create: (context) => + LocalAccountsCubit(AccountRepository.instance), + ), + BlocProvider( + create: (context) => + UserLoginsCubit(AccountRepository.instance), + ), + BlocProvider( + create: (context) => + ActiveLocalAccountCubit(AccountRepository.instance), + ), + BlocProvider( + create: (context) => PerAccountCollectionBlocMapCubit( + accountRepository: AccountRepository.instance, + locator: context.read)), + ], + child: + BackgroundTicker(child: _buildShortcuts(builder: (context) { + final scale = theme.extension()!; + final scaleConfig = theme.extension()!; - return SafeArea(child: appBuilder(context, localizationDelegate)); + final gradient = LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: scaleConfig.preferBorders && + theme.brightness == Brightness.light + ? [ + scale.grayScale.hoverElementBackground, + scale.grayScale.subtleBackground, + ] + : [ + scale.primaryScale.hoverElementBackground, + scale.primaryScale.subtleBackground, + ]); + + return DecoratedBox( + decoration: BoxDecoration(gradient: gradient), + child: MaterialApp.router( + debugShowCheckedModeBanner: false, + routerConfig: context.read().router(), + title: translate('app.title'), + theme: theme, + localizationsDelegates: [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + FormBuilderLocalizations.delegate, + localizationDelegate + ], + supportedLocales: localizationDelegate.supportedLocales, + locale: localizationDelegate.currentLocale, + )); + })), + )), + ); }); @override diff --git a/lib/chat/cubits/chat_component_cubit.dart b/lib/chat/cubits/chat_component_cubit.dart index 737b1a4..2f2a2d0 100644 --- a/lib/chat/cubits/chat_component_cubit.dart +++ b/lib/chat/cubits/chat_component_cubit.dart @@ -2,21 +2,23 @@ import 'dart:async'; import 'dart:typed_data'; import 'package:async_tools/async_tools.dart'; +import 'package:bloc_advanced_tools/bloc_advanced_tools.dart'; import 'package:fast_immutable_collections/fast_immutable_collections.dart'; import 'package:fixnum/fixnum.dart'; +import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_chat_core/flutter_chat_core.dart' as core; +import 'package:flutter_chat_types/flutter_chat_types.dart' as types; +import 'package:flutter_chat_ui/flutter_chat_ui.dart'; +import 'package:scroll_to_index/scroll_to_index.dart'; import 'package:veilid_support/veilid_support.dart'; import '../../account_manager/account_manager.dart'; import '../../contacts/contacts.dart'; import '../../conversation/conversation.dart'; import '../../proto/proto.dart' as proto; -import '../../tools/tools.dart'; import '../models/chat_component_state.dart'; import '../models/message_state.dart'; import '../models/window_state.dart'; -import '../views/chat_component_widget.dart'; import 'cubits.dart'; const metadataKeyIdentityPublicKey = 'identityPublicKey'; @@ -37,19 +39,19 @@ class ChatComponentCubit extends Cubit { _contactListCubit = contactListCubit, _conversationCubits = conversationCubits, _messagesCubit = messagesCubit, - super(const ChatComponentState( + super(ChatComponentState( + chatKey: GlobalKey(), + scrollController: AutoScrollController(), + textEditingController: InputTextFieldController(), localUser: null, - remoteUsers: IMap.empty(), - historicalRemoteUsers: IMap.empty(), - unknownUsers: IMap.empty(), - messageWindow: AsyncLoading(), + remoteUsers: const IMap.empty(), + historicalRemoteUsers: const IMap.empty(), + unknownUsers: const IMap.empty(), + messageWindow: const AsyncLoading(), title: '', )) { - // Immediate Init - _init(); - // Async Init - _initWait.add(_initAsync); + _initWait.add(_init); } factory ChatComponentCubit.singleContact( @@ -66,7 +68,7 @@ class ChatComponentCubit extends Cubit { messagesCubit: messagesCubit, ); - void _init() { + Future _init(Completer _cancel) async { // Get local user info and account record cubit _localUserIdentityKey = _accountInfo.identityTypedPublicKey; @@ -75,6 +77,9 @@ class ChatComponentCubit extends Cubit { _accountRecordCubit.stream.listen(_onChangedAccountRecord); _onChangedAccountRecord(_accountRecordCubit.state); + // Subscribe to remote user info + await _updateConversationSubscriptions(); + // Subscribe to messages _messagesSubscription = _messagesCubit.stream.listen(_onChangedMessages); _onChangedMessages(_messagesCubit.state); @@ -85,11 +90,6 @@ class ChatComponentCubit extends Cubit { _onChangedContacts(_contactListCubit.state); } - Future _initAsync(Completer _cancel) async { - // Subscribe to remote user info - await _updateConversationSubscriptions(); - } - @override Future close() async { await _initWait(); @@ -97,7 +97,6 @@ class ChatComponentCubit extends Cubit { await _accountRecordSubscription.cancel(); await _messagesSubscription.cancel(); await _conversationSubscriptions.values.map((v) => v.cancel()).wait; - await super.close(); } @@ -118,15 +117,32 @@ class ChatComponentCubit extends Cubit { } // Send a message - void sendMessage( - {required String text, - String? replyToMessageId, - Timestamp? expiration, - int? viewLimit, - List? attachments}) { - final replyId = (replyToMessageId != null) - ? base64UrlNoPadDecode(replyToMessageId) + void sendMessage(types.PartialText message) { + final text = message.text; + + final replyId = (message.repliedMessage != null) + ? base64UrlNoPadDecode(message.repliedMessage!.id) : null; + Timestamp? expiration; + int? viewLimit; + List? attachments; + final metadata = message.metadata; + if (metadata != null) { + final expirationValue = + metadata[metadataKeyExpirationDuration] as TimestampDuration?; + if (expirationValue != null) { + expiration = Veilid.instance.now().offset(expirationValue); + } + final viewLimitValue = metadata[metadataKeyViewLimit] as int?; + if (viewLimitValue != null) { + viewLimit = viewLimitValue; + } + final attachmentsValue = + metadata[metadataKeyAttachments] as List?; + if (attachmentsValue != null) { + attachments = attachmentsValue; + } + } _addTextMessage( text: text, @@ -151,9 +167,9 @@ class ChatComponentCubit extends Cubit { emit(state.copyWith(localUser: null)); return; } - final localUser = core.User( + final localUser = types.User( id: _localUserIdentityKey.toString(), - name: account.profile.name, + firstName: account.profile.name, metadata: {metadataKeyIdentityPublicKey: _localUserIdentityKey}); emit(state.copyWith(localUser: localUser)); } @@ -163,7 +179,9 @@ class ChatComponentCubit extends Cubit { emit(_convertMessages(state, avMessagesState)); } - void _onChangedContacts(DHTShortArrayCubitState bavContacts) { + void _onChangedContacts( + BlocBusyState>>> + bavContacts) { // Rewrite users when contacts change singleFuture((this, _sfChangedContacts), _updateConversationSubscriptions); } @@ -178,12 +196,11 @@ class ChatComponentCubit extends Cubit { // Don't change user information on loading state return; } - - final remoteUser = - _convertRemoteUser(remoteIdentityPublicKey, activeConversationState); - emit(_updateTitle(state.copyWith( - remoteUsers: state.remoteUsers.add(remoteUser.id, remoteUser)))); + remoteUsers: state.remoteUsers.add( + remoteIdentityPublicKey, + _convertRemoteUser( + remoteIdentityPublicKey, activeConversationState))))); } static ChatComponentState _updateTitle(ChatComponentState currentState) { @@ -192,13 +209,13 @@ class ChatComponentCubit extends Cubit { } if (currentState.remoteUsers.length == 1) { final remoteUser = currentState.remoteUsers.values.first; - return currentState.copyWith(title: remoteUser.name ?? ''); + return currentState.copyWith(title: remoteUser.firstName ?? ''); } return currentState.copyWith( title: ''); } - core.User _convertRemoteUser(TypedKey remoteIdentityPublicKey, + types.User _convertRemoteUser(TypedKey remoteIdentityPublicKey, ActiveConversationState activeConversationState) { // See if we have a contact for this remote user final contacts = _contactListCubit.state.state.asData?.value; @@ -207,24 +224,24 @@ class ChatComponentCubit extends Cubit { x.value.identityPublicKey.toVeilid() == remoteIdentityPublicKey); if (contactIdx != -1) { final contact = contacts[contactIdx].value; - return core.User( + return types.User( id: remoteIdentityPublicKey.toString(), - name: contact.displayName, + firstName: contact.displayName, metadata: {metadataKeyIdentityPublicKey: remoteIdentityPublicKey}); } } - return core.User( + return types.User( id: remoteIdentityPublicKey.toString(), - name: activeConversationState.remoteConversation?.profile.name ?? - '', + firstName: activeConversationState.remoteConversation.profile.name, metadata: {metadataKeyIdentityPublicKey: remoteIdentityPublicKey}); } - core.User _convertUnknownUser(TypedKey remoteIdentityPublicKey) => core.User( - id: remoteIdentityPublicKey.toString(), - name: '<$remoteIdentityPublicKey>', - metadata: {metadataKeyIdentityPublicKey: remoteIdentityPublicKey}); + types.User _convertUnknownUser(TypedKey remoteIdentityPublicKey) => + types.User( + id: remoteIdentityPublicKey.toString(), + firstName: '<$remoteIdentityPublicKey>', + metadata: {metadataKeyIdentityPublicKey: remoteIdentityPublicKey}); Future _updateConversationSubscriptions() async { // Get existing subscription keys and state @@ -246,17 +263,16 @@ class ChatComponentCubit extends Cubit { final activeConversationState = cc.state.asData?.value; if (activeConversationState != null) { - final remoteUser = _convertRemoteUser( - remoteIdentityPublicKey, activeConversationState); - currentRemoteUsersState = - currentRemoteUsersState.add(remoteUser.id, remoteUser); + currentRemoteUsersState = currentRemoteUsersState.add( + remoteIdentityPublicKey, + _convertRemoteUser( + remoteIdentityPublicKey, activeConversationState)); } } // Purge remote users we didn't see in the cubit list any more final cancels = >[]; for (final deadUser in existing) { - currentRemoteUsersState = - currentRemoteUsersState.remove(deadUser.toString()); + currentRemoteUsersState = currentRemoteUsersState.remove(deadUser); cancels.add(_conversationSubscriptions.remove(deadUser)!.cancel()); } await cancels.wait; @@ -265,77 +281,63 @@ class ChatComponentCubit extends Cubit { emit(_updateTitle(state.copyWith(remoteUsers: currentRemoteUsersState))); } - (ChatComponentState, core.Message?) _messageStateToChatMessage( + (ChatComponentState, types.Message?) _messageStateToChatMessage( ChatComponentState currentState, MessageState message) { final authorIdentityPublicKey = message.content.author.toVeilid(); - final authorUserId = authorIdentityPublicKey.toString(); - - late final core.User author; + late final types.User author; if (authorIdentityPublicKey == _localUserIdentityKey && currentState.localUser != null) { author = currentState.localUser!; } else { - final remoteUser = currentState.remoteUsers[authorUserId]; + final remoteUser = currentState.remoteUsers[authorIdentityPublicKey]; if (remoteUser != null) { author = remoteUser; } else { final historicalRemoteUser = - currentState.historicalRemoteUsers[authorUserId]; + currentState.historicalRemoteUsers[authorIdentityPublicKey]; if (historicalRemoteUser != null) { author = historicalRemoteUser; } else { - final unknownRemoteUser = currentState.unknownUsers[authorUserId]; + final unknownRemoteUser = + currentState.unknownUsers[authorIdentityPublicKey]; if (unknownRemoteUser != null) { author = unknownRemoteUser; } else { final unknownUser = _convertUnknownUser(authorIdentityPublicKey); currentState = currentState.copyWith( - unknownUsers: - currentState.unknownUsers.add(authorUserId, unknownUser)); + unknownUsers: currentState.unknownUsers + .add(authorIdentityPublicKey, unknownUser)); author = unknownUser; } } } } - // types.Status? status; - // if (message.sendState != null) { - // assert(author.id == _localUserIdentityKey.toString(), - // 'send state should only be on sent messages'); - // switch (message.sendState!) { - // case MessageSendState.sending: - // status = types.Status.sending; - // case MessageSendState.sent: - // status = types.Status.sent; - // case MessageSendState.delivered: - // status = types.Status.delivered; - // } - // } - - final reconciledAt = message.reconciledTimestamp == null - ? null - : DateTime.fromMicrosecondsSinceEpoch( - message.reconciledTimestamp!.value.toInt()); - - // print('message seqid: ${message.seqId}'); + types.Status? status; + if (message.sendState != null) { + assert(author.id == _localUserIdentityKey.toString(), + 'send state should only be on sent messages'); + switch (message.sendState!) { + case MessageSendState.sending: + status = types.Status.sending; + case MessageSendState.sent: + status = types.Status.sent; + case MessageSendState.delivered: + status = types.Status.delivered; + } + } switch (message.content.whichKind()) { case proto.Message_Kind.text: - final reconciledId = message.content.authorUniqueIdString; - final contentText = message.content.text; - final textMessage = core.TextMessage( - authorId: author.id, - createdAt: DateTime.fromMicrosecondsSinceEpoch( - message.sentTimestamp.value.toInt()), - sentAt: reconciledAt, - id: reconciledId, - //text: '${contentText.text} (${message.seqId})', - text: contentText.text, - metadata: { - kSeqId: message.seqId, - kSending: message.sendState == MessageSendState.sending, - if (core.isOnlyEmoji(contentText.text)) 'isOnlyEmoji': true, - }); + final contextText = message.content.text; + final textMessage = types.TextMessage( + author: author, + createdAt: + (message.sentTimestamp.value ~/ BigInt.from(1000)).toInt(), + id: message.content.authorUniqueIdString, + text: contextText.text, + showStatus: status != null, + status: status); return (currentState, textMessage); case proto.Message_Kind.secret: case proto.Message_Kind.delete: @@ -345,7 +347,6 @@ class ChatComponentCubit extends Cubit { case proto.Message_Kind.membership: case proto.Message_Kind.moderation: case proto.Message_Kind.notSet: - case proto.Message_Kind.readReceipt: return (currentState, null); } } @@ -357,7 +358,6 @@ class ChatComponentCubit extends Cubit { final asError = avMessagesState.asError; if (asError != null) { - addError(asError.error, asError.stackTrace); return currentState.copyWith( unknownUsers: const IMap.empty(), messageWindow: AsyncValue.error(asError.error, asError.stackTrace)); @@ -369,7 +369,7 @@ class ChatComponentCubit extends Cubit { final messagesState = avMessagesState.asData!.value; // Convert protobuf messages to chat messages - final chatMessages = []; + final chatMessages = []; final tsSet = {}; for (final message in messagesState.window) { final (newState, chatMessage) = @@ -378,17 +378,17 @@ class ChatComponentCubit extends Cubit { if (chatMessage == null) { continue; } + chatMessages.insert(0, chatMessage); if (!tsSet.add(chatMessage.id)) { - log.error('duplicate id found: ${chatMessage.id}' - // '\nMessages:\n${messagesState.window}' - // '\nChatMessages:\n$chatMessages' - ); - } else { - chatMessages.add(chatMessage); + // ignore: avoid_print + print('duplicate id found: ${chatMessage.id}:\n' + 'Messages:\n${messagesState.window}\n' + 'ChatMessages:\n$chatMessages'); + assert(false, 'should not have duplicate id'); } } return currentState.copyWith( - messageWindow: AsyncValue.data(WindowState( + messageWindow: AsyncValue.data(WindowState( window: chatMessages.toIList(), length: messagesState.length, windowTail: messagesState.windowTail, @@ -433,7 +433,9 @@ class ChatComponentCubit extends Cubit { final Map>> _conversationSubscriptions = {}; late StreamSubscription _messagesSubscription; - late StreamSubscription> + late StreamSubscription< + BlocBusyState< + AsyncValue>>>> _contactListSubscription; double scrollOffset = 0; } diff --git a/lib/chat/cubits/reconciliation/author_input_queue.dart b/lib/chat/cubits/reconciliation/author_input_queue.dart index 9a65e82..b15d92c 100644 --- a/lib/chat/cubits/reconciliation/author_input_queue.dart +++ b/lib/chat/cubits/reconciliation/author_input_queue.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:math'; import 'package:veilid_support/veilid_support.dart'; import '../../../proto/proto.dart' as proto; @@ -7,124 +6,106 @@ import '../../../proto/proto.dart' as proto; import '../../../tools/tools.dart'; import 'author_input_source.dart'; import 'message_integrity.dart'; +import 'output_position.dart'; class AuthorInputQueue { AuthorInputQueue._({ required TypedKey author, required AuthorInputSource inputSource, - required int inputPosition, - required proto.Message? previousMessage, + required OutputPosition? outputPosition, required void Function(Object, StackTrace?) onError, required MessageIntegrity messageIntegrity, }) : _author = author, _onError = onError, _inputSource = inputSource, - _previousMessage = previousMessage, + _outputPosition = outputPosition, + _lastMessage = outputPosition?.message.content, _messageIntegrity = messageIntegrity, - _inputPosition = inputPosition; + _currentPosition = inputSource.currentWindow.last; static Future create({ required TypedKey author, required AuthorInputSource inputSource, - required proto.Message? previousMessage, + required OutputPosition? outputPosition, required void Function(Object, StackTrace?) onError, }) async { - // Get ending input position - final inputPosition = await inputSource.getTailPosition() - 1; - - // Create an input queue for the input source final queue = AuthorInputQueue._( author: author, inputSource: inputSource, - inputPosition: inputPosition, - previousMessage: previousMessage, + outputPosition: outputPosition, onError: onError, messageIntegrity: await MessageIntegrity.create(author: author)); - - // Rewind the queue's 'inputPosition' to the first unreconciled message - if (!await queue._rewindInputToAfterLastMessage()) { + if (!await queue._findStartOfWork()) { return null; } - return queue; } //////////////////////////////////////////////////////////////////////////// // Public interface - /// Get the input source for this queue - AuthorInputSource get inputSource => _inputSource; + // Check if there are no messages left in this queue to reconcile + bool get isDone => _isDone; - /// Get the author of this queue + // Get the current message that needs reconciliation + proto.Message? get current => _currentMessage; + + // Get the earliest output position to start inserting + OutputPosition? get outputPosition => _outputPosition; + + // Get the author of this queue TypedKey get author => _author; - /// Get the current message that needs reconciliation - Future getCurrentMessage() async { - try { - // if we have a current message already, return it - if (_currentMessage != null) { - return _currentMessage; - } - - // Get the window - final currentWindow = await _updateWindow(clampInputPosition: false); - if (currentWindow == null) { - return null; - } - final currentElement = - currentWindow.elements[_inputPosition - currentWindow.firstPosition]; - return _currentMessage = currentElement.value; - // Catch everything so we can avoid ParallelWaitError - // ignore: avoid_catches_without_on_clauses - } catch (e, st) { - log.error('Exception getting current message: $e:\n$st\n'); - _currentMessage = null; - return null; + // Remove a reconciled message and move to the next message + // Returns true if there is more work to do + Future consume() async { + if (_isDone) { + return false; } - } - - /// Move the reconciliation cursor (_inputPosition) forward on the input - /// queue and tees up the next message for processing - /// Returns true if there is more work to do - /// Returns false if there are no more messages to reconcile in this queue - Future advance() async { - // Move current message to previous - _previousMessage = await getCurrentMessage(); - _currentMessage = null; - while (true) { - // Advance to next position - _inputPosition++; + _lastMessage = _currentMessage; + + _currentPosition++; // Get more window if we need to - final currentMessage = await getCurrentMessage(); - if (currentMessage == null) { + if (!await _updateWindow()) { + // Window is not available so this queue can't work right now + _isDone = true; return false; } + final nextMessage = _inputSource.currentWindow + .elements[_currentPosition - _inputSource.currentWindow.first]; - if (_previousMessage != null) { + // Drop the 'offline' elements because we don't reconcile + // anything until it has been confirmed to be committed to the DHT + // if (nextMessage.isOffline) { + // continue; + // } + + if (_lastMessage != null) { // Ensure the timestamp is not moving backward - if (currentMessage.timestamp < _previousMessage!.timestamp) { - log.warning('timestamp backward: ${currentMessage.timestamp}' - ' < ${_previousMessage!.timestamp}'); + if (nextMessage.value.timestamp < _lastMessage!.timestamp) { + log.warning('timestamp backward: ${nextMessage.value.timestamp}' + ' < ${_lastMessage!.timestamp}'); continue; } } // Verify the id chain for the message - final matchId = - await _messageIntegrity.generateMessageId(_previousMessage); - if (matchId.compare(currentMessage.idBytes) != 0) { - log.warning('id chain invalid: $matchId != ${currentMessage.idBytes}'); + final matchId = await _messageIntegrity.generateMessageId(_lastMessage); + if (matchId.compare(nextMessage.value.idBytes) != 0) { + log.warning( + 'id chain invalid: $matchId != ${nextMessage.value.idBytes}'); continue; } // Verify the signature for the message - if (!await _messageIntegrity.verifyMessage(currentMessage)) { - log.warning('invalid message signature: $currentMessage'); + if (!await _messageIntegrity.verifyMessage(nextMessage.value)) { + log.warning('invalid message signature: ${nextMessage.value}'); continue; } + _currentMessage = nextMessage.value; break; } return true; @@ -133,166 +114,106 @@ class AuthorInputQueue { //////////////////////////////////////////////////////////////////////////// // Internal implementation - /// Walk backward from the tail of the input queue to find the first - /// message newer than our last reconciled message from this author - /// Returns false if no work is needed - Future _rewindInputToAfterLastMessage() async { + // Walk backward from the tail of the input queue to find the first + // message newer than our last reconciled message from this author + // Returns false if no work is needed + Future _findStartOfWork() async { // Iterate windows over the inputSource - InputWindow? currentWindow; outer: while (true) { - // Get more window if we need to - currentWindow = await _updateWindow(clampInputPosition: true); - if (currentWindow == null) { - // Window is not available or things are empty so this - // queue can't work right now - return false; - } - // Iterate through current window backward - for (var i = currentWindow.elements.length - 1; - i >= 0 && _inputPosition >= 0; - i--, _inputPosition--) { - final elem = currentWindow.elements[i]; + for (var i = _inputSource.currentWindow.elements.length - 1; + i >= 0 && _currentPosition >= 0; + i--, _currentPosition--) { + final elem = _inputSource.currentWindow.elements[i]; // If we've found an input element that is older or same time as our // last reconciled message for this author, or we find the message // itself then we stop - if (_previousMessage != null) { + if (_lastMessage != null) { if (elem.value.authorUniqueIdBytes - .compare(_previousMessage!.authorUniqueIdBytes) == + .compare(_lastMessage!.authorUniqueIdBytes) == 0 || - elem.value.timestamp <= _previousMessage!.timestamp) { + elem.value.timestamp <= _lastMessage!.timestamp) { break outer; } } } // If we're at the beginning of the inputSource then we stop - if (_inputPosition < 0) { + if (_currentPosition < 0) { break; } + + // Get more window if we need to + if (!await _updateWindow()) { + // Window is not available or things are empty so this + // queue can't work right now + _isDone = true; + return false; + } } - // _inputPosition points to either before the input source starts + // _currentPosition points to either before the input source starts // or the position of the previous element. We still need to set the - // _currentMessage to the previous element so advance() can compare + // _currentMessage to the previous element so consume() can compare // against it if we can. - if (_inputPosition >= 0) { - _currentMessage = currentWindow - .elements[_inputPosition - currentWindow.firstPosition].value; + if (_currentPosition >= 0) { + _currentMessage = _inputSource.currentWindow + .elements[_currentPosition - _inputSource.currentWindow.first].value; } - // After this advance(), the _inputPosition and _currentMessage should + // After this consume(), the currentPosition and _currentMessage should // be equal to the first message to process and the current window to - // process should not be empty if there is work to do - return advance(); + // process should not be empty + return consume(); } - /// Slide the window toward the current position and load the batch around it - Future _updateWindow({required bool clampInputPosition}) async { - final inputTailPosition = await _inputSource.getTailPosition(); - if (inputTailPosition == 0) { - return null; - } - - // Handle out-of-range input position - if (clampInputPosition) { - _inputPosition = min(max(_inputPosition, 0), inputTailPosition - 1); - } else if (_inputPosition < 0 || _inputPosition >= inputTailPosition) { - return null; - } - + // Slide the window toward the current position and load the batch around it + Future _updateWindow() async { // Check if we are still in the window - final currentWindow = _currentWindow; - - int firstPosition; - int lastPosition; - if (currentWindow != null) { - firstPosition = currentWindow.firstPosition; - lastPosition = currentWindow.lastPosition; - - // Slide the window if we need to - if (_inputPosition >= firstPosition && _inputPosition <= lastPosition) { - return currentWindow; - } else if (_inputPosition < firstPosition) { - // Slide it backward, current position is now last - firstPosition = max((_inputPosition - _maxWindowLength) + 1, 0); - lastPosition = _inputPosition; - } else if (_inputPosition > lastPosition) { - // Slide it forward, current position is now first - firstPosition = _inputPosition; - lastPosition = - min((_inputPosition + _maxWindowLength) - 1, inputTailPosition - 1); - } - } else { - // need a new window, start with the input position at the end - lastPosition = _inputPosition; - firstPosition = max((_inputPosition - _maxWindowLength) + 1, 0); + if (_currentPosition >= _inputSource.currentWindow.first && + _currentPosition <= _inputSource.currentWindow.last) { + return true; } // Get another input batch futher back - final avCurrentWindow = await _inputSource.getWindow( - firstPosition, lastPosition - firstPosition + 1); + final avOk = + await _inputSource.updateWindow(_currentPosition, _maxWindowLength); - final asErr = avCurrentWindow.asError; + final asErr = avOk.asError; if (asErr != null) { _onError(asErr.error, asErr.stackTrace); - _currentWindow = null; - return null; + return false; } - final asLoading = avCurrentWindow.asLoading; + final asLoading = avOk.asLoading; if (asLoading != null) { - _currentWindow = null; - return null; + // xxx: no need to block the cubit here for this + // xxx: might want to switch to a 'busy' state though + // xxx: to let the messages view show a spinner at the bottom + // xxx: while we reconcile... + // emit(const AsyncValue.loading()); + return false; } - - final nextWindow = avCurrentWindow.asData!.value; - if (nextWindow == null || nextWindow.length == 0) { - _currentWindow = null; - return null; - } - - // Handle out-of-range input position - // Doing this again because getWindow is allowed to return a smaller - // window than the one requested, possibly due to DHT consistency - // fluctuations and race conditions - if (clampInputPosition) { - _inputPosition = min(max(_inputPosition, nextWindow.firstPosition), - nextWindow.lastPosition); - } else if (_inputPosition < nextWindow.firstPosition || - _inputPosition > nextWindow.lastPosition) { - return null; - } - - return _currentWindow = nextWindow; + return avOk.asData!.value; } //////////////////////////////////////////////////////////////////////////// - /// The author of this messages in the input source final TypedKey _author; - - /// The input source we're pulling messages from final AuthorInputSource _inputSource; - - /// What to call if an error happens + final OutputPosition? _outputPosition; final void Function(Object, StackTrace?) _onError; - - /// The message integrity validator final MessageIntegrity _messageIntegrity; - /// The last message we reconciled/output - proto.Message? _previousMessage; - - /// The current message we're looking at + // The last message we've consumed + proto.Message? _lastMessage; + // The current position in the input log that we are looking at + int _currentPosition; + // The current message we're looking at proto.Message? _currentMessage; + // If we have reached the end + bool _isDone = false; - /// The current position in the input source that we are looking at - int _inputPosition; - - /// The current input window from the InputSource; - InputWindow? _currentWindow; - - /// Desired maximum window length + // Desired maximum window length static const int _maxWindowLength = 256; } diff --git a/lib/chat/cubits/reconciliation/author_input_source.dart b/lib/chat/cubits/reconciliation/author_input_source.dart index f974dae..0bd1afb 100644 --- a/lib/chat/cubits/reconciliation/author_input_source.dart +++ b/lib/chat/cubits/reconciliation/author_input_source.dart @@ -9,68 +9,64 @@ import '../../../proto/proto.dart' as proto; @immutable class InputWindow { - const InputWindow({required this.elements, required this.firstPosition}) - : lastPosition = firstPosition + elements.length - 1, - isEmpty = elements.length == 0, - length = elements.length; - + const InputWindow( + {required this.elements, required this.first, required this.last}); final IList> elements; - final int firstPosition; - final int lastPosition; - final bool isEmpty; - final int length; + final int first; + final int last; } class AuthorInputSource { - AuthorInputSource.fromDHTLog(DHTLog dhtLog) : _dhtLog = dhtLog; + AuthorInputSource.fromCubit( + {required DHTLogStateData cubitState, + required this.cubit}) { + _currentWindow = InputWindow( + elements: cubitState.window, + first: (cubitState.windowTail - cubitState.window.length) % + cubitState.length, + last: (cubitState.windowTail - 1) % cubitState.length); + } //////////////////////////////////////////////////////////////////////////// - Future getTailPosition() async => - _dhtLog.operate((reader) async => reader.length); + InputWindow get currentWindow => _currentWindow; - Future> getWindow( - int startPosition, int windowLength) async => - _dhtLog.operate((reader) async { - // Don't allow negative length - if (windowLength <= 0) { - return const AsyncValue.data(null); + Future> updateWindow( + int currentPosition, int windowLength) async => + cubit.operate((reader) async { + // See if we're beyond the input source + if (currentPosition < 0 || currentPosition >= reader.length) { + return const AsyncValue.data(false); + } + + // Slide the window if we need to + var first = _currentWindow.first; + var last = _currentWindow.last; + if (currentPosition < first) { + // Slide it backward, current position is now last + first = max((currentPosition - windowLength) + 1, 0); + last = currentPosition; + } else if (currentPosition > last) { + // Slide it forward, current position is now first + first = currentPosition; + last = min((currentPosition + windowLength) - 1, reader.length - 1); + } else { + return const AsyncValue.data(true); } - // Trim if we're beyond input source - var endPosition = startPosition + windowLength - 1; - startPosition = max(startPosition, 0); - endPosition = max(endPosition, 0); // Get another input batch futher back - try { - Set? offlinePositions; - if (_dhtLog.writer != null) { - offlinePositions = await reader.getOfflinePositions(); - } - - final messages = await reader.getRangeProtobuf( - proto.Message.fromBuffer, startPosition, - length: endPosition - startPosition + 1); - if (messages == null) { - return const AsyncValue.loading(); - } - - final elements = messages.indexed - .map((x) => OnlineElementState( - value: x.$2, - isOffline: offlinePositions?.contains(x.$1 + startPosition) ?? - false)) - .toIList(); - - final window = - InputWindow(elements: elements, firstPosition: startPosition); - - return AsyncValue.data(window); - } on Exception catch (e, st) { - return AsyncValue.error(e, st); + final nextWindow = await cubit.loadElementsFromReader( + reader, last + 1, (last + 1) - first); + if (nextWindow == null) { + return const AsyncValue.loading(); } + _currentWindow = + InputWindow(elements: nextWindow, first: first, last: last); + return const AsyncValue.data(true); }); //////////////////////////////////////////////////////////////////////////// - final DHTLog _dhtLog; + final DHTLogCubit cubit; + + late InputWindow _currentWindow; } diff --git a/lib/chat/cubits/reconciliation/message_reconciliation.dart b/lib/chat/cubits/reconciliation/message_reconciliation.dart index f6d46c3..f0b8c4c 100644 --- a/lib/chat/cubits/reconciliation/message_reconciliation.dart +++ b/lib/chat/cubits/reconciliation/message_reconciliation.dart @@ -6,7 +6,6 @@ import 'package:sorted_list/sorted_list.dart'; import 'package:veilid_support/veilid_support.dart'; import '../../../proto/proto.dart' as proto; -import '../../../tools/tools.dart'; import 'author_input_queue.dart'; import 'author_input_source.dart'; import 'output_position.dart'; @@ -20,151 +19,96 @@ class MessageReconciliation { //////////////////////////////////////////////////////////////////////////// - void addInputSourceFromDHTLog(TypedKey author, DHTLog inputMessagesDHTLog) { - _inputSources[author] = AuthorInputSource.fromDHTLog(inputMessagesDHTLog); - } + void reconcileMessages( + TypedKey author, + DHTLogStateData inputMessagesCubitState, + DHTLogCubit inputMessagesCubit) { + if (inputMessagesCubitState.window.isEmpty) { + return; + } - void reconcileMessages(TypedKey? author) { - // xxx: can we use 'author' here to optimize _updateAuthorInputQueues? + _inputSources[author] = AuthorInputSource.fromCubit( + cubitState: inputMessagesCubitState, cubit: inputMessagesCubit); singleFuture(this, onError: _onError, () async { - // Update queues - final activeInputQueues = await _updateAuthorInputQueues(); + // Take entire list of input sources we have currently and process them + final inputSources = _inputSources; + _inputSources = {}; + + final inputFuts = >[]; + for (final kv in inputSources.entries) { + final author = kv.key; + final inputSource = kv.value; + inputFuts + .add(_enqueueAuthorInput(author: author, inputSource: inputSource)); + } + final inputQueues = await inputFuts.wait; + + // Make this safe to cast by removing inputs that were rejected or empty + inputQueues.removeNulls(); // Process all input queues together - await _outputCubit.operate((reconciledArray) => _reconcileInputQueues( - reconciledArray: reconciledArray, - activeInputQueues: activeInputQueues, - )); + await _outputCubit + .operate((reconciledArray) async => _reconcileInputQueues( + reconciledArray: reconciledArray, + inputQueues: inputQueues.cast(), + )); }); } //////////////////////////////////////////////////////////////////////////// - // Prepare author input queues by removing dead ones and adding new ones - // Queues that are empty are not added until they have something in them - // Active input queues with a current message are returned in a list - Future> _updateAuthorInputQueues() async { - // Remove any dead input queues - final deadQueues = []; - for (final author in _inputQueues.keys) { - if (!_inputSources.containsKey(author)) { - deadQueues.add(author); - } - } - for (final author in deadQueues) { - _inputQueues.remove(author); - _outputPositions.remove(author); - } - - await _outputCubit.operate((outputArray) async { - final dws = DelayedWaitSet(); - - for (final kv in _inputSources.entries) { - final author = kv.key; - final inputSource = kv.value; - - final iqExisting = _inputQueues[author]; - if (iqExisting == null || iqExisting.inputSource != inputSource) { - dws.add((_) async { - try { - await _enqueueAuthorInput( - author: author, - inputSource: inputSource, - outputArray: outputArray); - // Catch everything so we can avoid ParallelWaitError - // ignore: avoid_catches_without_on_clauses - } catch (e, st) { - log.error('Exception updating author input queue: $e:\n$st\n'); - _inputQueues.remove(author); - _outputPositions.remove(author); - } - }); - } - } - - await dws(); - }); - - // Get the active input queues - final activeInputQueues = await _inputQueues.entries - .map((entry) async { - if (await entry.value.getCurrentMessage() != null) { - return entry.value; - } else { - return null; - } - }) - .toList() - .wait - ..removeNulls(); - - return activeInputQueues.cast(); - } - // Set up a single author's message reconciliation - Future _enqueueAuthorInput( + Future _enqueueAuthorInput( {required TypedKey author, - required AuthorInputSource inputSource, - required TableDBArrayProtobuf - outputArray}) async { + required AuthorInputSource inputSource}) async { // Get the position of our most recent reconciled message from this author - final outputPosition = - await _findLastOutputPosition(author: author, outputArray: outputArray); + final outputPosition = await _findLastOutputPosition(author: author); // Find oldest message we have not yet reconciled final inputQueue = await AuthorInputQueue.create( author: author, inputSource: inputSource, - previousMessage: outputPosition?.message.content, + outputPosition: outputPosition, onError: _onError, ); - - if (inputQueue != null) { - _inputQueues[author] = inputQueue; - _outputPositions[author] = outputPosition; - } else { - _inputQueues.remove(author); - _outputPositions.remove(author); - } + return inputQueue; } // Get the position of our most recent reconciled message from this author // XXX: For a group chat, this should find when the author // was added to the membership so we don't just go back in time forever Future _findLastOutputPosition( - {required TypedKey author, - required TableDBArrayProtobuf - outputArray}) async { - var pos = outputArray.length - 1; - while (pos >= 0) { - final message = await outputArray.get(pos); - if (message.content.author.toVeilid() == author) { - return OutputPosition(message, pos); - } - pos--; - } - return null; - } + {required TypedKey author}) async => + _outputCubit.operate((arr) async { + var pos = arr.length - 1; + while (pos >= 0) { + final message = await arr.get(pos); + if (message.content.author.toVeilid() == author) { + return OutputPosition(message, pos); + } + pos--; + } + return null; + }); // Process a list of author input queues and insert their messages // into the output array, performing validation steps along the way Future _reconcileInputQueues({ required TableDBArrayProtobuf reconciledArray, - required List activeInputQueues, + required List inputQueues, }) async { - // Ensure we have active queues to process - if (activeInputQueues.isEmpty) { + // Ensure queues all have something to do + inputQueues.removeWhere((q) => q.isDone); + if (inputQueues.isEmpty) { return; } // Sort queues from earliest to latest and then by author // to ensure a deterministic insert order - activeInputQueues.sort((a, b) { - final aout = _outputPositions[a.author]; - final bout = _outputPositions[b.author]; - final acmp = aout?.pos ?? -1; - final bcmp = bout?.pos ?? -1; + inputQueues.sort((a, b) { + final acmp = a.outputPosition?.pos ?? -1; + final bcmp = b.outputPosition?.pos ?? -1; if (acmp == bcmp) { return a.author.toString().compareTo(b.author.toString()); } @@ -172,28 +116,21 @@ class MessageReconciliation { }); // Start at the earliest position we know about in all the queues - var currentOutputPosition = - _outputPositions[activeInputQueues.first.author]; + var currentOutputPosition = inputQueues.first.outputPosition; final toInsert = SortedList(proto.MessageExt.compareTimestamp); - while (activeInputQueues.isNotEmpty) { + while (inputQueues.isNotEmpty) { // Get up to '_maxReconcileChunk' of the items from the queues // that we can insert at this location bool added; do { added = false; - - final emptyQueues = {}; - for (final inputQueue in activeInputQueues) { - final inputCurrent = await inputQueue.getCurrentMessage(); - if (inputCurrent == null) { - log.error('Active input queue did not have a current message: ' - '${inputQueue.author}'); - continue; - } + var someQueueEmpty = false; + for (final inputQueue in inputQueues) { + final inputCurrent = inputQueue.current!; if (currentOutputPosition == null || inputCurrent.timestamp < currentOutputPosition.message.content.timestamp) { @@ -201,14 +138,16 @@ class MessageReconciliation { added = true; // Advance this queue - if (!await inputQueue.advance()) { - // Mark queue as empty for removal - emptyQueues.add(inputQueue); + if (!await inputQueue.consume()) { + // Queue is empty now, run a queue purge + someQueueEmpty = true; } } } - // Remove finished queues now that we're done iterating - activeInputQueues.removeWhere(emptyQueues.contains); + // Remove empty queues now that we're done iterating + if (someQueueEmpty) { + inputQueues.removeWhere((q) => q.isDone); + } if (toInsert.length >= _maxReconcileChunk) { break; @@ -226,27 +165,9 @@ class MessageReconciliation { ..content = message) .toList(); - // Figure out where to insert the reconciled messages - final insertPos = currentOutputPosition?.pos ?? reconciledArray.length; - - // Insert them all at once - await reconciledArray.insertAll(insertPos, reconciledInserts); - - // Update output positions for input queues - final updatePositions = _outputPositions.keys.toSet(); - var outputPos = insertPos + reconciledInserts.length; - for (final inserted in reconciledInserts.reversed) { - if (updatePositions.isEmpty) { - // Last seen positions already recorded for each active author - break; - } - outputPos--; - final author = inserted.content.author.toVeilid(); - if (updatePositions.contains(author)) { - _outputPositions[author] = OutputPosition(inserted, outputPos); - updatePositions.remove(author); - } - } + await reconciledArray.insertAll( + currentOutputPosition?.pos ?? reconciledArray.length, + reconciledInserts); toInsert.clear(); } else { @@ -266,11 +187,9 @@ class MessageReconciliation { //////////////////////////////////////////////////////////////////////////// - final Map _inputSources = {}; - final Map _inputQueues = {}; - final Map _outputPositions = {}; + Map _inputSources = {}; final TableDBArrayProtobufCubit _outputCubit; final void Function(Object, StackTrace?) _onError; - static const _maxReconcileChunk = 65536; + static const int _maxReconcileChunk = 65536; } diff --git a/lib/chat/cubits/single_contact_messages_cubit.dart b/lib/chat/cubits/single_contact_messages_cubit.dart index 878858b..97e474f 100644 --- a/lib/chat/cubits/single_contact_messages_cubit.dart +++ b/lib/chat/cubits/single_contact_messages_cubit.dart @@ -2,9 +2,8 @@ import 'dart:async'; import 'package:async_tools/async_tools.dart'; import 'package:fast_immutable_collections/fast_immutable_collections.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:uuid/uuid.dart'; -import 'package:uuid/v4.dart'; import 'package:veilid_support/veilid_support.dart'; import '../../account_manager/account_manager.dart'; @@ -13,12 +12,9 @@ import '../../tools/tools.dart'; import '../models/models.dart'; import 'reconciliation/reconciliation.dart'; -const _sfSendMessageTag = 'sfSendMessageTag'; - class RenderStateElement { RenderStateElement( - {required this.seqId, - required this.message, + {required this.message, required this.isLocal, this.reconciledTimestamp, this.sent = false, @@ -41,7 +37,6 @@ class RenderStateElement { return null; } - int seqId; proto.Message message; bool isLocal; Timestamp? reconciledTimestamp; @@ -60,7 +55,7 @@ class SingleContactMessagesCubit extends Cubit { required TypedKey localConversationRecordKey, required TypedKey localMessagesRecordKey, required TypedKey remoteConversationRecordKey, - required TypedKey? remoteMessagesRecordKey, + required TypedKey remoteMessagesRecordKey, }) : _accountInfo = accountInfo, _remoteIdentityPublicKey = remoteIdentityPublicKey, _localConversationRecordKey = localConversationRecordKey, @@ -77,16 +72,14 @@ class SingleContactMessagesCubit extends Cubit { Future close() async { await _initWait(); - await serialFutureClose((this, _sfSendMessageTag)); - await _commandController.close(); await _commandRunnerFut; await _unsentMessagesQueue.close(); await _sentSubscription?.cancel(); await _rcvdSubscription?.cancel(); await _reconciledSubscription?.cancel(); - await _sentMessagesDHTLog?.close(); - await _rcvdMessagesDHTLog?.close(); + await _sentMessagesCubit?.close(); + await _rcvdMessagesCubit?.close(); await _reconciledMessagesCubit?.close(); // If the local conversation record is gone, then delete the reconciled @@ -102,14 +95,14 @@ class SingleContactMessagesCubit extends Cubit { } // Initialize everything - Future _init(Completer _) async { + Future _init(Completer _cancel) async { _unsentMessagesQueue = PersistentQueue( table: 'SingleContactUnsentMessages', key: _remoteConversationRecordKey.toString(), fromBuffer: proto.Message.fromBuffer, closure: _processUnsentMessages, - onError: (e, st) { - log.error('Exception while processing unsent messages: $e\n$st\n'); + onError: (e, sp) { + log.error('Exception while processing unsent messages: $e\n$sp\n'); }); // Make crypto @@ -119,16 +112,13 @@ class SingleContactMessagesCubit extends Cubit { await _initReconciledMessagesCubit(); // Local messages key - await _initSentMessagesDHTLog(); + await _initSentMessagesCubit(); // Remote messages key - await _initRcvdMessagesDHTLog(); + await _initRcvdMessagesCubit(); // Command execution background process _commandRunnerFut = Future.delayed(Duration.zero, _commandRunner); - - // Run reconciliation once for all input queues - _reconciliation.reconcileMessages(null); } // Make crypto @@ -140,67 +130,36 @@ class SingleContactMessagesCubit extends Cubit { } // Open local messages key - Future _initSentMessagesDHTLog() async { + Future _initSentMessagesCubit() async { final writer = _accountInfo.identityWriter; - final sentMessagesDHTLog = - await DHTLog.openWrite(_localMessagesRecordKey, writer, + _sentMessagesCubit = DHTLogCubit( + open: () async => DHTLog.openWrite(_localMessagesRecordKey, writer, debugName: 'SingleContactMessagesCubit::_initSentMessagesCubit::' 'SentMessages', parent: _localConversationRecordKey, - crypto: _conversationCrypto); - _sentSubscription = await sentMessagesDHTLog.listen(_updateSentMessages); - - _sentMessagesDHTLog = sentMessagesDHTLog; - _reconciliation.addInputSourceFromDHTLog( - _accountInfo.identityTypedPublicKey, sentMessagesDHTLog); + crypto: _conversationCrypto), + decodeElement: proto.Message.fromBuffer); + _sentSubscription = + _sentMessagesCubit!.stream.listen(_updateSentMessagesState); + _updateSentMessagesState(_sentMessagesCubit!.state); } // Open remote messages key - Future _initRcvdMessagesDHTLog() async { - // Don't bother if we don't have a remote messages record key yet - if (_remoteMessagesRecordKey == null) { - return; - } - - // Open new cubit if one is desired - final rcvdMessagesDHTLog = await DHTLog.openRead(_remoteMessagesRecordKey!, - debugName: 'SingleContactMessagesCubit::_initRcvdMessagesCubit::' - 'RcvdMessages', - parent: _remoteConversationRecordKey, - crypto: _conversationCrypto); - _rcvdSubscription = await rcvdMessagesDHTLog.listen(_updateRcvdMessages); - - _rcvdMessagesDHTLog = rcvdMessagesDHTLog; - _reconciliation.addInputSourceFromDHTLog( - _remoteIdentityPublicKey, rcvdMessagesDHTLog); + Future _initRcvdMessagesCubit() async { + _rcvdMessagesCubit = DHTLogCubit( + open: () async => DHTLog.openRead(_remoteMessagesRecordKey, + debugName: 'SingleContactMessagesCubit::_initRcvdMessagesCubit::' + 'RcvdMessages', + parent: _remoteConversationRecordKey, + crypto: _conversationCrypto), + decodeElement: proto.Message.fromBuffer); + _rcvdSubscription = + _rcvdMessagesCubit!.stream.listen(_updateRcvdMessagesState); + _updateRcvdMessagesState(_rcvdMessagesCubit!.state); } - void updateRemoteMessagesRecordKey(TypedKey? remoteMessagesRecordKey) { - _sspRemoteConversationRecordKey.updateState(remoteMessagesRecordKey, - (remoteMessagesRecordKey) async { - await _initWait(); - - // Don't bother if nothing is changing - if (_remoteMessagesRecordKey == remoteMessagesRecordKey) { - return; - } - - // Close existing DHTLog if we have one - final rcvdMessagesDHTLog = _rcvdMessagesDHTLog; - _rcvdMessagesDHTLog = null; - _remoteMessagesRecordKey = null; - await _rcvdSubscription?.cancel(); - _rcvdSubscription = null; - await rcvdMessagesDHTLog?.close(); - - // Init the new DHTLog if we should - _remoteMessagesRecordKey = remoteMessagesRecordKey; - await _initRcvdMessagesDHTLog(); - }); - } - - Future _makeLocalMessagesCrypto() => + Future _makeLocalMessagesCrypto() async => VeilidCryptoPrivate.fromTypedKey( _accountInfo.userLogin!.identitySecret, 'tabledb'); @@ -212,7 +171,7 @@ class SingleContactMessagesCubit extends Cubit { final crypto = await _makeLocalMessagesCrypto(); _reconciledMessagesCubit = TableDBArrayProtobufCubit( - open: () => TableDBArrayProtobuf.make( + open: () async => TableDBArrayProtobuf.make( table: tableName, crypto: crypto, fromBuffer: proto.ReconciledMessage.fromBuffer), @@ -221,7 +180,6 @@ class SingleContactMessagesCubit extends Cubit { _reconciliation = MessageReconciliation( output: _reconciledMessagesCubit!, onError: (e, st) { - addError(e, st); emit(AsyncValue.error(e, st)); }); @@ -259,7 +217,7 @@ class SingleContactMessagesCubit extends Cubit { void runCommand(String command) { final (cmd, rest) = command.splitOnce(' '); - if (kIsDebugMode) { + if (kDebugMode) { if (cmd == '/repeat' && rest != null) { final (countStr, text) = rest.splitOnce(' '); final count = int.tryParse(countStr); @@ -286,15 +244,30 @@ class SingleContactMessagesCubit extends Cubit { //////////////////////////////////////////////////////////////////////////// // Internal implementation - // Called when the sent messages DHTLog gets a change + // Called when the sent messages cubit gets a change // This will re-render when messages are sent from another machine - void _updateSentMessages(DHTLogUpdate upd) { - _reconciliation.reconcileMessages(_accountInfo.identityTypedPublicKey); + void _updateSentMessagesState(DHTLogBusyState avmessages) { + final sentMessages = avmessages.state.asData?.value; + if (sentMessages == null) { + return; + } + + _reconciliation.reconcileMessages( + _accountInfo.identityTypedPublicKey, sentMessages, _sentMessagesCubit!); + + // Update the view + _renderState(); } - // Called when the received messages DHTLog gets a change - void _updateRcvdMessages(DHTLogUpdate upd) { - _reconciliation.reconcileMessages(_remoteIdentityPublicKey); + // Called when the received messages cubit gets a change + void _updateRcvdMessagesState(DHTLogBusyState avmessages) { + final rcvdMessages = avmessages.state.asData?.value; + if (rcvdMessages == null) { + return; + } + + _reconciliation.reconcileMessages( + _remoteIdentityPublicKey, rcvdMessages, _rcvdMessagesCubit!); } // Called when the reconciled messages window gets a change @@ -306,11 +279,14 @@ class SingleContactMessagesCubit extends Cubit { Future _processMessageToSend( proto.Message message, proto.Message? previousMessage) async { - // It's possible we had a signature from a previous - // operateAppendEventual attempt, so clear it and make a new message id too - message - ..clearSignature() - ..id = await _senderMessageIntegrity.generateMessageId(previousMessage); + // Get the previous message if we don't have one + previousMessage ??= await _sentMessagesCubit!.operate((r) async => + r.length == 0 + ? null + : await r.getProtobuf(proto.Message.fromBuffer, r.length - 1)); + + message.id = + await _senderMessageIntegrity.generateMessageId(previousMessage); // Now sign it await _senderMessageIntegrity.signMessage( @@ -319,47 +295,43 @@ class SingleContactMessagesCubit extends Cubit { // Async process to send messages in the background Future _processUnsentMessages(IList messages) async { - try { - await _sentMessagesDHTLog!.operateAppendEventual((writer) async { - // Get the previous message if we have one - var previousMessage = writer.length == 0 - ? null - : await writer.getProtobuf( - proto.Message.fromBuffer, writer.length - 1); - - // Sign all messages - final processedMessages = messages.toList(); - for (final message in processedMessages) { - try { - await _processMessageToSend(message, previousMessage); - previousMessage = message; - } on Exception catch (e, st) { - log.error('Exception processing unsent message: $e:\n$st\n'); - } - } - final byteMessages = messages.map((m) => m.writeToBuffer()).toList(); - - return writer.addAll(byteMessages); - }); - } on Exception catch (e, st) { - log.error('Exception appending unsent messages: $e:\n$st\n'); + // Go through and assign ids to all the messages in order + proto.Message? previousMessage; + final processedMessages = messages.toList(); + for (final message in processedMessages) { + try { + await _processMessageToSend(message, previousMessage); + previousMessage = message; + } on Exception catch (e) { + log.error('Exception processing unsent message: $e'); + } } + + // _sendingMessages = messages; + + // _renderState(); + try { + await _sentMessagesCubit!.operateAppendEventual((writer) => + writer.addAll(messages.map((m) => m.writeToBuffer()).toList())); + } on Exception catch (e) { + log.error('Exception appending unsent messages: $e'); + } + + // _sendingMessages = const IList.empty(); } // Produce a state for this cubit from the input cubits and queues void _renderState() { - // Get all reconciled messages in the cubit window + // Get all reconciled messages final reconciledMessages = _reconciledMessagesCubit?.state.state.asData?.value; - - // Get all sent messages that are still offline - //final sentMessages = _sentMessagesDHTLog. - - // Get all items in the unsent queue - final unsentMessages = _unsentMessagesQueue.queue; + // Get all sent messages + final sentMessages = _sentMessagesCubit?.state.state.asData?.value; + //Get all items in the unsent queue + //final unsentMessages = _unsentMessagesQueue.queue; // If we aren't ready to render a state, say we're loading - if (reconciledMessages == null) { + if (reconciledMessages == null || sentMessages == null) { emit(const AsyncLoading()); return; } @@ -370,78 +342,54 @@ class SingleContactMessagesCubit extends Cubit { // keyMapper: (x) => x.content.authorUniqueIdString, // values: reconciledMessages.windowElements, // ); - // final sentMessagesMap = - // IMap>.fromValues( - // keyMapper: (x) => x.value.authorUniqueIdString, - // values: sentMessages.window, - // ); + final sentMessagesMap = + IMap>.fromValues( + keyMapper: (x) => x.value.authorUniqueIdString, + values: sentMessages.window, + ); // final unsentMessagesMap = IMap.fromValues( // keyMapper: (x) => x.authorUniqueIdString, // values: unsentMessages, // ); - // List of all rendered state elements that we will turn into - // message states final renderedElements = []; - - // Keep track of the ids we have rendered - // because there can be an overlap between the 'unsent messages' - // and the reconciled messages as the async state catches up final renderedIds = {}; - - var seqId = (reconciledMessages.windowTail == 0 - ? reconciledMessages.length - : reconciledMessages.windowTail) - - reconciledMessages.windowElements.length; for (final m in reconciledMessages.windowElements) { final isLocal = m.content.author.toVeilid() == _accountInfo.identityTypedPublicKey; final reconciledTimestamp = Timestamp.fromInt64(m.reconciledTime); - //final sm = - //isLocal ? sentMessagesMap[m.content.authorUniqueIdString] : null; - //final sent = isLocal && sm != null; - //final sentOffline = isLocal && sm != null && sm.isOffline; - final sent = isLocal; - final sentOffline = false; // + final sm = + isLocal ? sentMessagesMap[m.content.authorUniqueIdString] : null; + final sent = isLocal && sm != null; + final sentOffline = isLocal && sm != null && sm.isOffline; - if (renderedIds.contains(m.content.authorUniqueIdString)) { - seqId++; - continue; - } renderedElements.add(RenderStateElement( - seqId: seqId, message: m.content, isLocal: isLocal, reconciledTimestamp: reconciledTimestamp, sent: sent, sentOffline: sentOffline, )); + renderedIds.add(m.content.authorUniqueIdString); - seqId++; } // Render in-flight messages at the bottom - - for (final m in unsentMessages) { - if (renderedIds.contains(m.authorUniqueIdString)) { - seqId++; - continue; - } - renderedElements.add(RenderStateElement( - seqId: seqId, - message: m, - isLocal: true, - sent: true, - sentOffline: true, - )); - renderedIds.add(m.authorUniqueIdString); - seqId++; - } + // for (final m in _sendingMessages) { + // if (renderedIds.contains(m.authorUniqueIdString)) { + // continue; + // } + // renderedElements.add(RenderStateElement( + // message: m, + // isLocal: true, + // sent: true, + // sentOffline: true, + // )); + // } // Render the state final messages = renderedElements .map((x) => MessageState( - seqId: x.seqId, content: x.message, sentTimestamp: Timestamp.fromInt64(x.message.timestamp), reconciledTimestamp: x.reconciledTimestamp, @@ -459,26 +407,20 @@ class SingleContactMessagesCubit extends Cubit { void _sendMessage({required proto.Message message}) { // Add common fields - // real id and signature will get set by _processMessageToSend - // temporary id set here is random and not 'valid' in the eyes - // of reconcilation, noting that reconciled timestamp is not yet set. + // id and signature will get set by _processMessageToSend message ..author = _accountInfo.identityTypedPublicKey.toProto() - ..timestamp = Veilid.instance.now().toInt64() - ..id = Uuid.parse(_uuidGen.generate()); + ..timestamp = Veilid.instance.now().toInt64(); if ((message.writeToBuffer().lengthInBytes + 256) > 4096) { throw const FormatException('message is too long'); } // Put in the queue - serialFuture((this, _sfSendMessageTag), () async { - // Add the message to the persistent queue - await _unsentMessagesQueue.add(message); + _unsentMessagesQueue.addSync(message); - // Update the view - _renderState(); - }); + // Update the view + _renderState(); } Future _commandRunner() async { @@ -509,25 +451,23 @@ class SingleContactMessagesCubit extends Cubit { final TypedKey _localConversationRecordKey; final TypedKey _localMessagesRecordKey; final TypedKey _remoteConversationRecordKey; - TypedKey? _remoteMessagesRecordKey; + final TypedKey _remoteMessagesRecordKey; late final VeilidCrypto _conversationCrypto; late final MessageIntegrity _senderMessageIntegrity; - DHTLog? _sentMessagesDHTLog; - DHTLog? _rcvdMessagesDHTLog; + DHTLogCubit? _sentMessagesCubit; + DHTLogCubit? _rcvdMessagesCubit; TableDBArrayProtobufCubit? _reconciledMessagesCubit; late final MessageReconciliation _reconciliation; late final PersistentQueue _unsentMessagesQueue; - StreamSubscription? _sentSubscription; - StreamSubscription? _rcvdSubscription; + // IList _sendingMessages = const IList.empty(); + StreamSubscription>? _sentSubscription; + StreamSubscription>? _rcvdSubscription; StreamSubscription>? _reconciledSubscription; final StreamController Function()> _commandController; late final Future _commandRunnerFut; - - final _sspRemoteConversationRecordKey = SingleStateProcessor(); - final _uuidGen = const UuidV4(); } diff --git a/lib/chat/models/chat_component_state.dart b/lib/chat/models/chat_component_state.dart index 1a52524..b06b413 100644 --- a/lib/chat/models/chat_component_state.dart +++ b/lib/chat/models/chat_component_state.dart @@ -1,26 +1,41 @@ import 'package:async_tools/async_tools.dart'; import 'package:fast_immutable_collections/fast_immutable_collections.dart'; -import 'package:flutter_chat_core/flutter_chat_core.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_chat_types/flutter_chat_types.dart' show Message, User; +import 'package:flutter_chat_ui/flutter_chat_ui.dart' + show ChatState, InputTextFieldController; import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:scroll_to_index/scroll_to_index.dart'; +import 'package:veilid_support/veilid_support.dart'; import 'window_state.dart'; part 'chat_component_state.freezed.dart'; @freezed -sealed class ChatComponentState with _$ChatComponentState { +class ChatComponentState with _$ChatComponentState { const factory ChatComponentState( { + // GlobalKey for the chat + required GlobalKey chatKey, + // ScrollController for the chat + required AutoScrollController scrollController, + // TextEditingController for the chat + required InputTextFieldController textEditingController, // Local user required User? localUser, // Active remote users - required IMap remoteUsers, + required IMap remoteUsers, // Historical remote users - required IMap historicalRemoteUsers, + required IMap historicalRemoteUsers, // Unknown users - required IMap unknownUsers, + required IMap unknownUsers, // Messages state required AsyncValue> messageWindow, // Title of the chat required String title}) = _ChatComponentState; } + +extension ChatComponentStateExt on ChatComponentState { + // +} diff --git a/lib/chat/models/chat_component_state.freezed.dart b/lib/chat/models/chat_component_state.freezed.dart index bbecc7a..41ab7e2 100644 --- a/lib/chat/models/chat_component_state.freezed.dart +++ b/lib/chat/models/chat_component_state.freezed.dart @@ -1,4 +1,3 @@ -// dart format width=80 // coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND // ignore_for_file: type=lint @@ -10,86 +9,73 @@ part of 'chat_component_state.dart'; // FreezedGenerator // ************************************************************************** -// dart format off T _$identity(T value) => value; +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + /// @nodoc mixin _$ChatComponentState { -// Local user - User? get localUser; // Active remote users - IMap get remoteUsers; // Historical remote users - IMap get historicalRemoteUsers; // Unknown users - IMap get unknownUsers; // Messages state - AsyncValue> get messageWindow; // Title of the chat - String get title; +// GlobalKey for the chat + GlobalKey get chatKey => + throw _privateConstructorUsedError; // ScrollController for the chat + AutoScrollController get scrollController => + throw _privateConstructorUsedError; // TextEditingController for the chat + InputTextFieldController get textEditingController => + throw _privateConstructorUsedError; // Local user + User? get localUser => + throw _privateConstructorUsedError; // Active remote users + IMap, User> get remoteUsers => + throw _privateConstructorUsedError; // Historical remote users + IMap, User> get historicalRemoteUsers => + throw _privateConstructorUsedError; // Unknown users + IMap, User> get unknownUsers => + throw _privateConstructorUsedError; // Messages state + AsyncValue> get messageWindow => + throw _privateConstructorUsedError; // Title of the chat + String get title => throw _privateConstructorUsedError; - /// Create a copy of ChatComponentState - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') + @JsonKey(ignore: true) $ChatComponentStateCopyWith get copyWith => - _$ChatComponentStateCopyWithImpl( - this as ChatComponentState, _$identity); - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is ChatComponentState && - (identical(other.localUser, localUser) || - other.localUser == localUser) && - (identical(other.remoteUsers, remoteUsers) || - other.remoteUsers == remoteUsers) && - (identical(other.historicalRemoteUsers, historicalRemoteUsers) || - other.historicalRemoteUsers == historicalRemoteUsers) && - (identical(other.unknownUsers, unknownUsers) || - other.unknownUsers == unknownUsers) && - (identical(other.messageWindow, messageWindow) || - other.messageWindow == messageWindow) && - (identical(other.title, title) || other.title == title)); - } - - @override - int get hashCode => Object.hash(runtimeType, localUser, remoteUsers, - historicalRemoteUsers, unknownUsers, messageWindow, title); - - @override - String toString() { - return 'ChatComponentState(localUser: $localUser, remoteUsers: $remoteUsers, historicalRemoteUsers: $historicalRemoteUsers, unknownUsers: $unknownUsers, messageWindow: $messageWindow, title: $title)'; - } + throw _privateConstructorUsedError; } /// @nodoc -abstract mixin class $ChatComponentStateCopyWith<$Res> { +abstract class $ChatComponentStateCopyWith<$Res> { factory $ChatComponentStateCopyWith( - ChatComponentState value, $Res Function(ChatComponentState) _then) = - _$ChatComponentStateCopyWithImpl; + ChatComponentState value, $Res Function(ChatComponentState) then) = + _$ChatComponentStateCopyWithImpl<$Res, ChatComponentState>; @useResult $Res call( - {User? localUser, - IMap remoteUsers, - IMap historicalRemoteUsers, - IMap unknownUsers, + {GlobalKey chatKey, + AutoScrollController scrollController, + InputTextFieldController textEditingController, + User? localUser, + IMap, User> remoteUsers, + IMap, User> historicalRemoteUsers, + IMap, User> unknownUsers, AsyncValue> messageWindow, String title}); - $UserCopyWith<$Res>? get localUser; $AsyncValueCopyWith, $Res> get messageWindow; } /// @nodoc -class _$ChatComponentStateCopyWithImpl<$Res> +class _$ChatComponentStateCopyWithImpl<$Res, $Val extends ChatComponentState> implements $ChatComponentStateCopyWith<$Res> { - _$ChatComponentStateCopyWithImpl(this._self, this._then); + _$ChatComponentStateCopyWithImpl(this._value, this._then); - final ChatComponentState _self; - final $Res Function(ChatComponentState) _then; + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; - /// Create a copy of ChatComponentState - /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ + Object? chatKey = null, + Object? scrollController = null, + Object? textEditingController = null, Object? localUser = freezed, Object? remoteUsers = null, Object? historicalRemoteUsers = null, @@ -97,83 +83,176 @@ class _$ChatComponentStateCopyWithImpl<$Res> Object? messageWindow = null, Object? title = null, }) { - return _then(_self.copyWith( + return _then(_value.copyWith( + chatKey: null == chatKey + ? _value.chatKey + : chatKey // ignore: cast_nullable_to_non_nullable + as GlobalKey, + scrollController: null == scrollController + ? _value.scrollController + : scrollController // ignore: cast_nullable_to_non_nullable + as AutoScrollController, + textEditingController: null == textEditingController + ? _value.textEditingController + : textEditingController // ignore: cast_nullable_to_non_nullable + as InputTextFieldController, localUser: freezed == localUser - ? _self.localUser + ? _value.localUser : localUser // ignore: cast_nullable_to_non_nullable as User?, remoteUsers: null == remoteUsers - ? _self.remoteUsers + ? _value.remoteUsers : remoteUsers // ignore: cast_nullable_to_non_nullable - as IMap, + as IMap, User>, historicalRemoteUsers: null == historicalRemoteUsers - ? _self.historicalRemoteUsers + ? _value.historicalRemoteUsers : historicalRemoteUsers // ignore: cast_nullable_to_non_nullable - as IMap, + as IMap, User>, unknownUsers: null == unknownUsers - ? _self.unknownUsers + ? _value.unknownUsers : unknownUsers // ignore: cast_nullable_to_non_nullable - as IMap, + as IMap, User>, messageWindow: null == messageWindow - ? _self.messageWindow + ? _value.messageWindow : messageWindow // ignore: cast_nullable_to_non_nullable as AsyncValue>, title: null == title - ? _self.title + ? _value.title : title // ignore: cast_nullable_to_non_nullable as String, - )); + ) as $Val); } - /// Create a copy of ChatComponentState - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $UserCopyWith<$Res>? get localUser { - if (_self.localUser == null) { - return null; - } - - return $UserCopyWith<$Res>(_self.localUser!, (value) { - return _then(_self.copyWith(localUser: value)); - }); - } - - /// Create a copy of ChatComponentState - /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $AsyncValueCopyWith, $Res> get messageWindow { - return $AsyncValueCopyWith, $Res>(_self.messageWindow, + return $AsyncValueCopyWith, $Res>(_value.messageWindow, (value) { - return _then(_self.copyWith(messageWindow: value)); + return _then(_value.copyWith(messageWindow: value) as $Val); }); } } /// @nodoc +abstract class _$$ChatComponentStateImplCopyWith<$Res> + implements $ChatComponentStateCopyWith<$Res> { + factory _$$ChatComponentStateImplCopyWith(_$ChatComponentStateImpl value, + $Res Function(_$ChatComponentStateImpl) then) = + __$$ChatComponentStateImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {GlobalKey chatKey, + AutoScrollController scrollController, + InputTextFieldController textEditingController, + User? localUser, + IMap, User> remoteUsers, + IMap, User> historicalRemoteUsers, + IMap, User> unknownUsers, + AsyncValue> messageWindow, + String title}); -class _ChatComponentState implements ChatComponentState { - const _ChatComponentState( - {required this.localUser, + @override + $AsyncValueCopyWith, $Res> get messageWindow; +} + +/// @nodoc +class __$$ChatComponentStateImplCopyWithImpl<$Res> + extends _$ChatComponentStateCopyWithImpl<$Res, _$ChatComponentStateImpl> + implements _$$ChatComponentStateImplCopyWith<$Res> { + __$$ChatComponentStateImplCopyWithImpl(_$ChatComponentStateImpl _value, + $Res Function(_$ChatComponentStateImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? chatKey = null, + Object? scrollController = null, + Object? textEditingController = null, + Object? localUser = freezed, + Object? remoteUsers = null, + Object? historicalRemoteUsers = null, + Object? unknownUsers = null, + Object? messageWindow = null, + Object? title = null, + }) { + return _then(_$ChatComponentStateImpl( + chatKey: null == chatKey + ? _value.chatKey + : chatKey // ignore: cast_nullable_to_non_nullable + as GlobalKey, + scrollController: null == scrollController + ? _value.scrollController + : scrollController // ignore: cast_nullable_to_non_nullable + as AutoScrollController, + textEditingController: null == textEditingController + ? _value.textEditingController + : textEditingController // ignore: cast_nullable_to_non_nullable + as InputTextFieldController, + localUser: freezed == localUser + ? _value.localUser + : localUser // ignore: cast_nullable_to_non_nullable + as User?, + remoteUsers: null == remoteUsers + ? _value.remoteUsers + : remoteUsers // ignore: cast_nullable_to_non_nullable + as IMap, User>, + historicalRemoteUsers: null == historicalRemoteUsers + ? _value.historicalRemoteUsers + : historicalRemoteUsers // ignore: cast_nullable_to_non_nullable + as IMap, User>, + unknownUsers: null == unknownUsers + ? _value.unknownUsers + : unknownUsers // ignore: cast_nullable_to_non_nullable + as IMap, User>, + messageWindow: null == messageWindow + ? _value.messageWindow + : messageWindow // ignore: cast_nullable_to_non_nullable + as AsyncValue>, + title: null == title + ? _value.title + : title // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc + +class _$ChatComponentStateImpl implements _ChatComponentState { + const _$ChatComponentStateImpl( + {required this.chatKey, + required this.scrollController, + required this.textEditingController, + required this.localUser, required this.remoteUsers, required this.historicalRemoteUsers, required this.unknownUsers, required this.messageWindow, required this.title}); +// GlobalKey for the chat + @override + final GlobalKey chatKey; +// ScrollController for the chat + @override + final AutoScrollController scrollController; +// TextEditingController for the chat + @override + final InputTextFieldController textEditingController; // Local user @override final User? localUser; // Active remote users @override - final IMap remoteUsers; + final IMap, User> remoteUsers; // Historical remote users @override - final IMap historicalRemoteUsers; + final IMap, User> historicalRemoteUsers; // Unknown users @override - final IMap unknownUsers; + final IMap, User> unknownUsers; // Messages state @override final AsyncValue> messageWindow; @@ -181,19 +260,21 @@ class _ChatComponentState implements ChatComponentState { @override final String title; - /// Create a copy of ChatComponentState - /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') - _$ChatComponentStateCopyWith<_ChatComponentState> get copyWith => - __$ChatComponentStateCopyWithImpl<_ChatComponentState>(this, _$identity); + String toString() { + return 'ChatComponentState(chatKey: $chatKey, scrollController: $scrollController, textEditingController: $textEditingController, localUser: $localUser, remoteUsers: $remoteUsers, historicalRemoteUsers: $historicalRemoteUsers, unknownUsers: $unknownUsers, messageWindow: $messageWindow, title: $title)'; + } @override bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _ChatComponentState && + other is _$ChatComponentStateImpl && + (identical(other.chatKey, chatKey) || other.chatKey == chatKey) && + (identical(other.scrollController, scrollController) || + other.scrollController == scrollController) && + (identical(other.textEditingController, textEditingController) || + other.textEditingController == textEditingController) && (identical(other.localUser, localUser) || other.localUser == localUser) && (identical(other.remoteUsers, remoteUsers) || @@ -208,109 +289,59 @@ class _ChatComponentState implements ChatComponentState { } @override - int get hashCode => Object.hash(runtimeType, localUser, remoteUsers, - historicalRemoteUsers, unknownUsers, messageWindow, title); + int get hashCode => Object.hash( + runtimeType, + chatKey, + scrollController, + textEditingController, + localUser, + remoteUsers, + historicalRemoteUsers, + unknownUsers, + messageWindow, + title); - @override - String toString() { - return 'ChatComponentState(localUser: $localUser, remoteUsers: $remoteUsers, historicalRemoteUsers: $historicalRemoteUsers, unknownUsers: $unknownUsers, messageWindow: $messageWindow, title: $title)'; - } -} - -/// @nodoc -abstract mixin class _$ChatComponentStateCopyWith<$Res> - implements $ChatComponentStateCopyWith<$Res> { - factory _$ChatComponentStateCopyWith( - _ChatComponentState value, $Res Function(_ChatComponentState) _then) = - __$ChatComponentStateCopyWithImpl; - @override - @useResult - $Res call( - {User? localUser, - IMap remoteUsers, - IMap historicalRemoteUsers, - IMap unknownUsers, - AsyncValue> messageWindow, - String title}); - - @override - $UserCopyWith<$Res>? get localUser; - @override - $AsyncValueCopyWith, $Res> get messageWindow; -} - -/// @nodoc -class __$ChatComponentStateCopyWithImpl<$Res> - implements _$ChatComponentStateCopyWith<$Res> { - __$ChatComponentStateCopyWithImpl(this._self, this._then); - - final _ChatComponentState _self; - final $Res Function(_ChatComponentState) _then; - - /// Create a copy of ChatComponentState - /// with the given fields replaced by the non-null parameter values. + @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - $Res call({ - Object? localUser = freezed, - Object? remoteUsers = null, - Object? historicalRemoteUsers = null, - Object? unknownUsers = null, - Object? messageWindow = null, - Object? title = null, - }) { - return _then(_ChatComponentState( - localUser: freezed == localUser - ? _self.localUser - : localUser // ignore: cast_nullable_to_non_nullable - as User?, - remoteUsers: null == remoteUsers - ? _self.remoteUsers - : remoteUsers // ignore: cast_nullable_to_non_nullable - as IMap, - historicalRemoteUsers: null == historicalRemoteUsers - ? _self.historicalRemoteUsers - : historicalRemoteUsers // ignore: cast_nullable_to_non_nullable - as IMap, - unknownUsers: null == unknownUsers - ? _self.unknownUsers - : unknownUsers // ignore: cast_nullable_to_non_nullable - as IMap, - messageWindow: null == messageWindow - ? _self.messageWindow - : messageWindow // ignore: cast_nullable_to_non_nullable - as AsyncValue>, - title: null == title - ? _self.title - : title // ignore: cast_nullable_to_non_nullable - as String, - )); - } - - /// Create a copy of ChatComponentState - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $UserCopyWith<$Res>? get localUser { - if (_self.localUser == null) { - return null; - } - - return $UserCopyWith<$Res>(_self.localUser!, (value) { - return _then(_self.copyWith(localUser: value)); - }); - } - - /// Create a copy of ChatComponentState - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $AsyncValueCopyWith, $Res> get messageWindow { - return $AsyncValueCopyWith, $Res>(_self.messageWindow, - (value) { - return _then(_self.copyWith(messageWindow: value)); - }); - } + _$$ChatComponentStateImplCopyWith<_$ChatComponentStateImpl> get copyWith => + __$$ChatComponentStateImplCopyWithImpl<_$ChatComponentStateImpl>( + this, _$identity); } -// dart format on +abstract class _ChatComponentState implements ChatComponentState { + const factory _ChatComponentState( + {required final GlobalKey chatKey, + required final AutoScrollController scrollController, + required final InputTextFieldController textEditingController, + required final User? localUser, + required final IMap, User> remoteUsers, + required final IMap, User> + historicalRemoteUsers, + required final IMap, User> unknownUsers, + required final AsyncValue> messageWindow, + required final String title}) = _$ChatComponentStateImpl; + + @override // GlobalKey for the chat + GlobalKey get chatKey; + @override // ScrollController for the chat + AutoScrollController get scrollController; + @override // TextEditingController for the chat + InputTextFieldController get textEditingController; + @override // Local user + User? get localUser; + @override // Active remote users + IMap, User> get remoteUsers; + @override // Historical remote users + IMap, User> get historicalRemoteUsers; + @override // Unknown users + IMap, User> get unknownUsers; + @override // Messages state + AsyncValue> get messageWindow; + @override // Title of the chat + String get title; + @override + @JsonKey(ignore: true) + _$$ChatComponentStateImplCopyWith<_$ChatComponentStateImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/chat/models/message_state.dart b/lib/chat/models/message_state.dart index 80852e6..8eacc8e 100644 --- a/lib/chat/models/message_state.dart +++ b/lib/chat/models/message_state.dart @@ -24,11 +24,8 @@ enum MessageSendState { } @freezed -sealed class MessageState with _$MessageState { - @JsonSerializable() +class MessageState with _$MessageState { const factory MessageState({ - // Sequence number of the message for display purposes - required int seqId, // Content of the message @JsonKey(fromJson: messageFromJson, toJson: messageToJson) required proto.Message content, diff --git a/lib/chat/models/message_state.freezed.dart b/lib/chat/models/message_state.freezed.dart index 342b305..96c98e2 100644 --- a/lib/chat/models/message_state.freezed.dart +++ b/lib/chat/models/message_state.freezed.dart @@ -1,4 +1,3 @@ -// dart format width=80 // coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND // ignore_for_file: type=lint @@ -10,76 +9,41 @@ part of 'message_state.dart'; // FreezedGenerator // ************************************************************************** -// dart format off T _$identity(T value) => value; -/// @nodoc -mixin _$MessageState implements DiagnosticableTreeMixin { -// Sequence number of the message for display purposes - int get seqId; // Content of the message - @JsonKey(fromJson: messageFromJson, toJson: messageToJson) - proto.Message get content; // Sent timestamp - Timestamp get sentTimestamp; // Reconciled timestamp - Timestamp? get reconciledTimestamp; // The state of the message - MessageSendState? get sendState; +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - /// Create a copy of MessageState - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') - $MessageStateCopyWith get copyWith => - _$MessageStateCopyWithImpl( - this as MessageState, _$identity); - - /// Serializes this MessageState to a JSON map. - Map toJson(); - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - properties - ..add(DiagnosticsProperty('type', 'MessageState')) - ..add(DiagnosticsProperty('seqId', seqId)) - ..add(DiagnosticsProperty('content', content)) - ..add(DiagnosticsProperty('sentTimestamp', sentTimestamp)) - ..add(DiagnosticsProperty('reconciledTimestamp', reconciledTimestamp)) - ..add(DiagnosticsProperty('sendState', sendState)); - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is MessageState && - (identical(other.seqId, seqId) || other.seqId == seqId) && - (identical(other.content, content) || other.content == content) && - (identical(other.sentTimestamp, sentTimestamp) || - other.sentTimestamp == sentTimestamp) && - (identical(other.reconciledTimestamp, reconciledTimestamp) || - other.reconciledTimestamp == reconciledTimestamp) && - (identical(other.sendState, sendState) || - other.sendState == sendState)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash(runtimeType, seqId, content, sentTimestamp, - reconciledTimestamp, sendState); - - @override - String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { - return 'MessageState(seqId: $seqId, content: $content, sentTimestamp: $sentTimestamp, reconciledTimestamp: $reconciledTimestamp, sendState: $sendState)'; - } +MessageState _$MessageStateFromJson(Map json) { + return _MessageState.fromJson(json); } /// @nodoc -abstract mixin class $MessageStateCopyWith<$Res> { +mixin _$MessageState { +// Content of the message + @JsonKey(fromJson: messageFromJson, toJson: messageToJson) + proto.Message get content => + throw _privateConstructorUsedError; // Sent timestamp + Timestamp get sentTimestamp => + throw _privateConstructorUsedError; // Reconciled timestamp + Timestamp? get reconciledTimestamp => + throw _privateConstructorUsedError; // The state of the message + MessageSendState? get sendState => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $MessageStateCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $MessageStateCopyWith<$Res> { factory $MessageStateCopyWith( - MessageState value, $Res Function(MessageState) _then) = - _$MessageStateCopyWithImpl; + MessageState value, $Res Function(MessageState) then) = + _$MessageStateCopyWithImpl<$Res, MessageState>; @useResult $Res call( - {int seqId, - @JsonKey(fromJson: messageFromJson, toJson: messageToJson) + {@JsonKey(fromJson: messageFromJson, toJson: messageToJson) proto.Message content, Timestamp sentTimestamp, Timestamp? reconciledTimestamp, @@ -87,42 +51,91 @@ abstract mixin class $MessageStateCopyWith<$Res> { } /// @nodoc -class _$MessageStateCopyWithImpl<$Res> implements $MessageStateCopyWith<$Res> { - _$MessageStateCopyWithImpl(this._self, this._then); +class _$MessageStateCopyWithImpl<$Res, $Val extends MessageState> + implements $MessageStateCopyWith<$Res> { + _$MessageStateCopyWithImpl(this._value, this._then); - final MessageState _self; - final $Res Function(MessageState) _then; + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; - /// Create a copy of MessageState - /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ - Object? seqId = null, Object? content = null, Object? sentTimestamp = null, Object? reconciledTimestamp = freezed, Object? sendState = freezed, }) { - return _then(_self.copyWith( - seqId: null == seqId - ? _self.seqId - : seqId // ignore: cast_nullable_to_non_nullable - as int, + return _then(_value.copyWith( content: null == content - ? _self.content + ? _value.content : content // ignore: cast_nullable_to_non_nullable as proto.Message, sentTimestamp: null == sentTimestamp - ? _self.sentTimestamp + ? _value.sentTimestamp : sentTimestamp // ignore: cast_nullable_to_non_nullable as Timestamp, reconciledTimestamp: freezed == reconciledTimestamp - ? _self.reconciledTimestamp + ? _value.reconciledTimestamp : reconciledTimestamp // ignore: cast_nullable_to_non_nullable as Timestamp?, sendState: freezed == sendState - ? _self.sendState + ? _value.sendState + : sendState // ignore: cast_nullable_to_non_nullable + as MessageSendState?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$MessageStateImplCopyWith<$Res> + implements $MessageStateCopyWith<$Res> { + factory _$$MessageStateImplCopyWith( + _$MessageStateImpl value, $Res Function(_$MessageStateImpl) then) = + __$$MessageStateImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {@JsonKey(fromJson: messageFromJson, toJson: messageToJson) + proto.Message content, + Timestamp sentTimestamp, + Timestamp? reconciledTimestamp, + MessageSendState? sendState}); +} + +/// @nodoc +class __$$MessageStateImplCopyWithImpl<$Res> + extends _$MessageStateCopyWithImpl<$Res, _$MessageStateImpl> + implements _$$MessageStateImplCopyWith<$Res> { + __$$MessageStateImplCopyWithImpl( + _$MessageStateImpl _value, $Res Function(_$MessageStateImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? content = null, + Object? sentTimestamp = null, + Object? reconciledTimestamp = freezed, + Object? sendState = freezed, + }) { + return _then(_$MessageStateImpl( + content: null == content + ? _value.content + : content // ignore: cast_nullable_to_non_nullable + as proto.Message, + sentTimestamp: null == sentTimestamp + ? _value.sentTimestamp + : sentTimestamp // ignore: cast_nullable_to_non_nullable + as Timestamp, + reconciledTimestamp: freezed == reconciledTimestamp + ? _value.reconciledTimestamp + : reconciledTimestamp // ignore: cast_nullable_to_non_nullable + as Timestamp?, + sendState: freezed == sendState + ? _value.sendState : sendState // ignore: cast_nullable_to_non_nullable as MessageSendState?, )); @@ -130,22 +143,18 @@ class _$MessageStateCopyWithImpl<$Res> implements $MessageStateCopyWith<$Res> { } /// @nodoc - @JsonSerializable() -class _MessageState with DiagnosticableTreeMixin implements MessageState { - const _MessageState( - {required this.seqId, - @JsonKey(fromJson: messageFromJson, toJson: messageToJson) +class _$MessageStateImpl with DiagnosticableTreeMixin implements _MessageState { + const _$MessageStateImpl( + {@JsonKey(fromJson: messageFromJson, toJson: messageToJson) required this.content, required this.sentTimestamp, required this.reconciledTimestamp, required this.sendState}); - factory _MessageState.fromJson(Map json) => - _$MessageStateFromJson(json); -// Sequence number of the message for display purposes - @override - final int seqId; + factory _$MessageStateImpl.fromJson(Map json) => + _$$MessageStateImplFromJson(json); + // Content of the message @override @JsonKey(fromJson: messageFromJson, toJson: messageToJson) @@ -160,26 +169,16 @@ class _MessageState with DiagnosticableTreeMixin implements MessageState { @override final MessageSendState? sendState; - /// Create a copy of MessageState - /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') - _$MessageStateCopyWith<_MessageState> get copyWith => - __$MessageStateCopyWithImpl<_MessageState>(this, _$identity); - - @override - Map toJson() { - return _$MessageStateToJson( - this, - ); + String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { + return 'MessageState(content: $content, sentTimestamp: $sentTimestamp, reconciledTimestamp: $reconciledTimestamp, sendState: $sendState)'; } @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); properties ..add(DiagnosticsProperty('type', 'MessageState')) - ..add(DiagnosticsProperty('seqId', seqId)) ..add(DiagnosticsProperty('content', content)) ..add(DiagnosticsProperty('sentTimestamp', sentTimestamp)) ..add(DiagnosticsProperty('reconciledTimestamp', reconciledTimestamp)) @@ -190,8 +189,7 @@ class _MessageState with DiagnosticableTreeMixin implements MessageState { bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _MessageState && - (identical(other.seqId, seqId) || other.seqId == seqId) && + other is _$MessageStateImpl && (identical(other.content, content) || other.content == content) && (identical(other.sentTimestamp, sentTimestamp) || other.sentTimestamp == sentTimestamp) && @@ -201,76 +199,47 @@ class _MessageState with DiagnosticableTreeMixin implements MessageState { other.sendState == sendState)); } - @JsonKey(includeFromJson: false, includeToJson: false) + @JsonKey(ignore: true) @override - int get hashCode => Object.hash(runtimeType, seqId, content, sentTimestamp, - reconciledTimestamp, sendState); + int get hashCode => Object.hash( + runtimeType, content, sentTimestamp, reconciledTimestamp, sendState); - @override - String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { - return 'MessageState(seqId: $seqId, content: $content, sentTimestamp: $sentTimestamp, reconciledTimestamp: $reconciledTimestamp, sendState: $sendState)'; - } -} - -/// @nodoc -abstract mixin class _$MessageStateCopyWith<$Res> - implements $MessageStateCopyWith<$Res> { - factory _$MessageStateCopyWith( - _MessageState value, $Res Function(_MessageState) _then) = - __$MessageStateCopyWithImpl; - @override - @useResult - $Res call( - {int seqId, - @JsonKey(fromJson: messageFromJson, toJson: messageToJson) - proto.Message content, - Timestamp sentTimestamp, - Timestamp? reconciledTimestamp, - MessageSendState? sendState}); -} - -/// @nodoc -class __$MessageStateCopyWithImpl<$Res> - implements _$MessageStateCopyWith<$Res> { - __$MessageStateCopyWithImpl(this._self, this._then); - - final _MessageState _self; - final $Res Function(_MessageState) _then; - - /// Create a copy of MessageState - /// with the given fields replaced by the non-null parameter values. + @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - $Res call({ - Object? seqId = null, - Object? content = null, - Object? sentTimestamp = null, - Object? reconciledTimestamp = freezed, - Object? sendState = freezed, - }) { - return _then(_MessageState( - seqId: null == seqId - ? _self.seqId - : seqId // ignore: cast_nullable_to_non_nullable - as int, - content: null == content - ? _self.content - : content // ignore: cast_nullable_to_non_nullable - as proto.Message, - sentTimestamp: null == sentTimestamp - ? _self.sentTimestamp - : sentTimestamp // ignore: cast_nullable_to_non_nullable - as Timestamp, - reconciledTimestamp: freezed == reconciledTimestamp - ? _self.reconciledTimestamp - : reconciledTimestamp // ignore: cast_nullable_to_non_nullable - as Timestamp?, - sendState: freezed == sendState - ? _self.sendState - : sendState // ignore: cast_nullable_to_non_nullable - as MessageSendState?, - )); + _$$MessageStateImplCopyWith<_$MessageStateImpl> get copyWith => + __$$MessageStateImplCopyWithImpl<_$MessageStateImpl>(this, _$identity); + + @override + Map toJson() { + return _$$MessageStateImplToJson( + this, + ); } } -// dart format on +abstract class _MessageState implements MessageState { + const factory _MessageState( + {@JsonKey(fromJson: messageFromJson, toJson: messageToJson) + required final proto.Message content, + required final Timestamp sentTimestamp, + required final Timestamp? reconciledTimestamp, + required final MessageSendState? sendState}) = _$MessageStateImpl; + + factory _MessageState.fromJson(Map json) = + _$MessageStateImpl.fromJson; + + @override // Content of the message + @JsonKey(fromJson: messageFromJson, toJson: messageToJson) + proto.Message get content; + @override // Sent timestamp + Timestamp get sentTimestamp; + @override // Reconciled timestamp + Timestamp? get reconciledTimestamp; + @override // The state of the message + MessageSendState? get sendState; + @override + @JsonKey(ignore: true) + _$$MessageStateImplCopyWith<_$MessageStateImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/chat/models/message_state.g.dart b/lib/chat/models/message_state.g.dart index 2eee78d..99899a7 100644 --- a/lib/chat/models/message_state.g.dart +++ b/lib/chat/models/message_state.g.dart @@ -6,9 +6,8 @@ part of 'message_state.dart'; // JsonSerializableGenerator // ************************************************************************** -_MessageState _$MessageStateFromJson(Map json) => - _MessageState( - seqId: (json['seq_id'] as num).toInt(), +_$MessageStateImpl _$$MessageStateImplFromJson(Map json) => + _$MessageStateImpl( content: messageFromJson(json['content'] as Map), sentTimestamp: Timestamp.fromJson(json['sent_timestamp']), reconciledTimestamp: json['reconciled_timestamp'] == null @@ -19,9 +18,8 @@ _MessageState _$MessageStateFromJson(Map json) => : MessageSendState.fromJson(json['send_state']), ); -Map _$MessageStateToJson(_MessageState instance) => +Map _$$MessageStateImplToJson(_$MessageStateImpl instance) => { - 'seq_id': instance.seqId, 'content': messageToJson(instance.content), 'sent_timestamp': instance.sentTimestamp.toJson(), 'reconciled_timestamp': instance.reconciledTimestamp?.toJson(), diff --git a/lib/chat/models/window_state.dart b/lib/chat/models/window_state.dart index 14a94a5..91cde8a 100644 --- a/lib/chat/models/window_state.dart +++ b/lib/chat/models/window_state.dart @@ -5,7 +5,7 @@ import 'package:freezed_annotation/freezed_annotation.dart'; part 'window_state.freezed.dart'; @freezed -sealed class WindowState with _$WindowState { +class WindowState with _$WindowState { const factory WindowState({ // List of objects in the window required IList window, diff --git a/lib/chat/models/window_state.freezed.dart b/lib/chat/models/window_state.freezed.dart index 38a2ec1..604931d 100644 --- a/lib/chat/models/window_state.freezed.dart +++ b/lib/chat/models/window_state.freezed.dart @@ -1,4 +1,3 @@ -// dart format width=80 // coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND // ignore_for_file: type=lint @@ -10,71 +9,34 @@ part of 'window_state.dart'; // FreezedGenerator // ************************************************************************** -// dart format off T _$identity(T value) => value; +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + /// @nodoc -mixin _$WindowState implements DiagnosticableTreeMixin { +mixin _$WindowState { // List of objects in the window - IList get window; // Total number of objects (windowTail max) - int get length; // One past the end of the last element - int get windowTail; // The total number of elements to try to keep in the window - int get windowCount; // If we should have the tail following the array - bool get follow; + IList get window => + throw _privateConstructorUsedError; // Total number of objects (windowTail max) + int get length => + throw _privateConstructorUsedError; // One past the end of the last element + int get windowTail => + throw _privateConstructorUsedError; // The total number of elements to try to keep in the window + int get windowCount => + throw _privateConstructorUsedError; // If we should have the tail following the array + bool get follow => throw _privateConstructorUsedError; - /// Create a copy of WindowState - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') + @JsonKey(ignore: true) $WindowStateCopyWith> get copyWith => - _$WindowStateCopyWithImpl>( - this as WindowState, _$identity); - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - properties - ..add(DiagnosticsProperty('type', 'WindowState<$T>')) - ..add(DiagnosticsProperty('window', window)) - ..add(DiagnosticsProperty('length', length)) - ..add(DiagnosticsProperty('windowTail', windowTail)) - ..add(DiagnosticsProperty('windowCount', windowCount)) - ..add(DiagnosticsProperty('follow', follow)); - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is WindowState && - const DeepCollectionEquality().equals(other.window, window) && - (identical(other.length, length) || other.length == length) && - (identical(other.windowTail, windowTail) || - other.windowTail == windowTail) && - (identical(other.windowCount, windowCount) || - other.windowCount == windowCount) && - (identical(other.follow, follow) || other.follow == follow)); - } - - @override - int get hashCode => Object.hash( - runtimeType, - const DeepCollectionEquality().hash(window), - length, - windowTail, - windowCount, - follow); - - @override - String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { - return 'WindowState<$T>(window: $window, length: $length, windowTail: $windowTail, windowCount: $windowCount, follow: $follow)'; - } + throw _privateConstructorUsedError; } /// @nodoc -abstract mixin class $WindowStateCopyWith { +abstract class $WindowStateCopyWith { factory $WindowStateCopyWith( - WindowState value, $Res Function(WindowState) _then) = - _$WindowStateCopyWithImpl; + WindowState value, $Res Function(WindowState) then) = + _$WindowStateCopyWithImpl>; @useResult $Res call( {IList window, @@ -85,15 +47,15 @@ abstract mixin class $WindowStateCopyWith { } /// @nodoc -class _$WindowStateCopyWithImpl +class _$WindowStateCopyWithImpl> implements $WindowStateCopyWith { - _$WindowStateCopyWithImpl(this._self, this._then); + _$WindowStateCopyWithImpl(this._value, this._then); - final WindowState _self; - final $Res Function(WindowState) _then; + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; - /// Create a copy of WindowState - /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -103,25 +65,83 @@ class _$WindowStateCopyWithImpl Object? windowCount = null, Object? follow = null, }) { - return _then(_self.copyWith( + return _then(_value.copyWith( window: null == window - ? _self.window + ? _value.window : window // ignore: cast_nullable_to_non_nullable as IList, length: null == length - ? _self.length + ? _value.length : length // ignore: cast_nullable_to_non_nullable as int, windowTail: null == windowTail - ? _self.windowTail + ? _value.windowTail : windowTail // ignore: cast_nullable_to_non_nullable as int, windowCount: null == windowCount - ? _self.windowCount + ? _value.windowCount : windowCount // ignore: cast_nullable_to_non_nullable as int, follow: null == follow - ? _self.follow + ? _value.follow + : follow // ignore: cast_nullable_to_non_nullable + as bool, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$WindowStateImplCopyWith + implements $WindowStateCopyWith { + factory _$$WindowStateImplCopyWith(_$WindowStateImpl value, + $Res Function(_$WindowStateImpl) then) = + __$$WindowStateImplCopyWithImpl; + @override + @useResult + $Res call( + {IList window, + int length, + int windowTail, + int windowCount, + bool follow}); +} + +/// @nodoc +class __$$WindowStateImplCopyWithImpl + extends _$WindowStateCopyWithImpl> + implements _$$WindowStateImplCopyWith { + __$$WindowStateImplCopyWithImpl( + _$WindowStateImpl _value, $Res Function(_$WindowStateImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? window = null, + Object? length = null, + Object? windowTail = null, + Object? windowCount = null, + Object? follow = null, + }) { + return _then(_$WindowStateImpl( + window: null == window + ? _value.window + : window // ignore: cast_nullable_to_non_nullable + as IList, + length: null == length + ? _value.length + : length // ignore: cast_nullable_to_non_nullable + as int, + windowTail: null == windowTail + ? _value.windowTail + : windowTail // ignore: cast_nullable_to_non_nullable + as int, + windowCount: null == windowCount + ? _value.windowCount + : windowCount // ignore: cast_nullable_to_non_nullable + as int, + follow: null == follow + ? _value.follow : follow // ignore: cast_nullable_to_non_nullable as bool, )); @@ -130,8 +150,10 @@ class _$WindowStateCopyWithImpl /// @nodoc -class _WindowState with DiagnosticableTreeMixin implements WindowState { - const _WindowState( +class _$WindowStateImpl + with DiagnosticableTreeMixin + implements _WindowState { + const _$WindowStateImpl( {required this.window, required this.length, required this.windowTail, @@ -154,16 +176,14 @@ class _WindowState with DiagnosticableTreeMixin implements WindowState { @override final bool follow; - /// Create a copy of WindowState - /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') - _$WindowStateCopyWith> get copyWith => - __$WindowStateCopyWithImpl>(this, _$identity); + String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { + return 'WindowState<$T>(window: $window, length: $length, windowTail: $windowTail, windowCount: $windowCount, follow: $follow)'; + } @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); properties ..add(DiagnosticsProperty('type', 'WindowState<$T>')) ..add(DiagnosticsProperty('window', window)) @@ -177,7 +197,7 @@ class _WindowState with DiagnosticableTreeMixin implements WindowState { bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _WindowState && + other is _$WindowStateImpl && const DeepCollectionEquality().equals(other.window, window) && (identical(other.length, length) || other.length == length) && (identical(other.windowTail, windowTail) || @@ -196,70 +216,34 @@ class _WindowState with DiagnosticableTreeMixin implements WindowState { windowCount, follow); - @override - String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { - return 'WindowState<$T>(window: $window, length: $length, windowTail: $windowTail, windowCount: $windowCount, follow: $follow)'; - } -} - -/// @nodoc -abstract mixin class _$WindowStateCopyWith - implements $WindowStateCopyWith { - factory _$WindowStateCopyWith( - _WindowState value, $Res Function(_WindowState) _then) = - __$WindowStateCopyWithImpl; - @override - @useResult - $Res call( - {IList window, - int length, - int windowTail, - int windowCount, - bool follow}); -} - -/// @nodoc -class __$WindowStateCopyWithImpl - implements _$WindowStateCopyWith { - __$WindowStateCopyWithImpl(this._self, this._then); - - final _WindowState _self; - final $Res Function(_WindowState) _then; - - /// Create a copy of WindowState - /// with the given fields replaced by the non-null parameter values. + @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - $Res call({ - Object? window = null, - Object? length = null, - Object? windowTail = null, - Object? windowCount = null, - Object? follow = null, - }) { - return _then(_WindowState( - window: null == window - ? _self.window - : window // ignore: cast_nullable_to_non_nullable - as IList, - length: null == length - ? _self.length - : length // ignore: cast_nullable_to_non_nullable - as int, - windowTail: null == windowTail - ? _self.windowTail - : windowTail // ignore: cast_nullable_to_non_nullable - as int, - windowCount: null == windowCount - ? _self.windowCount - : windowCount // ignore: cast_nullable_to_non_nullable - as int, - follow: null == follow - ? _self.follow - : follow // ignore: cast_nullable_to_non_nullable - as bool, - )); - } + _$$WindowStateImplCopyWith> get copyWith => + __$$WindowStateImplCopyWithImpl>( + this, _$identity); } -// dart format on +abstract class _WindowState implements WindowState { + const factory _WindowState( + {required final IList window, + required final int length, + required final int windowTail, + required final int windowCount, + required final bool follow}) = _$WindowStateImpl; + + @override // List of objects in the window + IList get window; + @override // Total number of objects (windowTail max) + int get length; + @override // One past the end of the last element + int get windowTail; + @override // The total number of elements to try to keep in the window + int get windowCount; + @override // If we should have the tail following the array + bool get follow; + @override + @JsonKey(ignore: true) + _$$WindowStateImplCopyWith> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/chat/views/chat_builders/chat_builders.dart b/lib/chat/views/chat_builders/chat_builders.dart deleted file mode 100644 index 529341f..0000000 --- a/lib/chat/views/chat_builders/chat_builders.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'vc_composer_widget.dart'; -export 'vc_text_message_widget.dart'; diff --git a/lib/chat/views/chat_builders/vc_composer_widget.dart b/lib/chat/views/chat_builders/vc_composer_widget.dart deleted file mode 100644 index b3eb1e5..0000000 --- a/lib/chat/views/chat_builders/vc_composer_widget.dart +++ /dev/null @@ -1,431 +0,0 @@ -import 'dart:convert'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_chat_ui/flutter_chat_ui.dart'; -// Typedefs need to come out -// ignore: implementation_imports -import 'package:flutter_chat_ui/src/utils/typedefs.dart'; -import 'package:provider/provider.dart'; - -import '../../../theme/theme.dart'; -import '../../chat.dart'; - -enum ShiftEnterAction { newline, send } - -/// The message composer widget positioned at the bottom of the chat screen. -/// -/// Includes a text input field, an optional attachment button, -/// and a send button. -class VcComposerWidget extends StatefulWidget { - /// Creates a message composer widget. - const VcComposerWidget({ - super.key, - this.textEditingController, - this.left = 0, - this.right = 0, - this.top, - this.bottom = 0, - this.sigmaX = 20, - this.sigmaY = 20, - this.padding = const EdgeInsets.all(8), - this.attachmentIcon = const Icon(Icons.attachment), - this.sendIcon = const Icon(Icons.send), - this.gap = 8, - this.inputBorder, - this.filled, - this.topWidget, - this.handleSafeArea = true, - this.backgroundColor, - this.attachmentIconColor, - this.sendIconColor, - this.hintColor, - this.textColor, - this.inputFillColor, - this.hintText = 'Type a message', - this.keyboardAppearance, - this.autocorrect, - this.autofocus = false, - this.textCapitalization = TextCapitalization.sentences, - this.keyboardType, - this.textInputAction = TextInputAction.newline, - this.shiftEnterAction = ShiftEnterAction.send, - this.focusNode, - this.maxLength, - this.minLines = 1, - this.maxLines = 3, - }); - - /// Optional controller for the text input field. - final TextEditingController? textEditingController; - - /// Optional left position. - final double? left; - - /// Optional right position. - final double? right; - - /// Optional top position. - final double? top; - - /// Optional bottom position. - final double? bottom; - - /// Optional X blur value for the background (if using glassmorphism). - final double? sigmaX; - - /// Optional Y blur value for the background (if using glassmorphism). - final double? sigmaY; - - /// Padding around the composer content. - final EdgeInsetsGeometry? padding; - - /// Icon for the attachment button. Defaults to [Icons.attachment]. - final Widget? attachmentIcon; - - /// Icon for the send button. Defaults to [Icons.send]. - final Widget? sendIcon; - - /// Horizontal gap between elements (attachment icon, text field, send icon). - final double? gap; - - /// Border style for the text input field. - final InputBorder? inputBorder; - - /// Whether the text input field should be filled. - final bool? filled; - - /// Optional widget to display above the main composer row. - final Widget? topWidget; - - /// Whether to adjust padding for the bottom safe area. - final bool handleSafeArea; - - /// Background color of the composer container. - final Color? backgroundColor; - - /// Color of the attachment icon. - final Color? attachmentIconColor; - - /// Color of the send icon. - final Color? sendIconColor; - - /// Color of the hint text in the input field. - final Color? hintColor; - - /// Color of the text entered in the input field. - final Color? textColor; - - /// Fill color for the text input field when [filled] is true. - final Color? inputFillColor; - - /// Placeholder text for the input field. - final String? hintText; - - /// Appearance of the keyboard. - final Brightness? keyboardAppearance; - - /// Whether to enable autocorrect for the input field. - final bool? autocorrect; - - /// Whether the input field should autofocus. - final bool autofocus; - - /// Capitalization behavior for the input field. - final TextCapitalization textCapitalization; - - /// Type of keyboard to display. - final TextInputType? keyboardType; - - /// Action button type for the keyboard (e.g., newline, send). - final TextInputAction textInputAction; - - /// Action when shift-enter is pressed (e.g., newline, send). - final ShiftEnterAction shiftEnterAction; - - /// Focus node for the text input field. - final FocusNode? focusNode; - - /// Maximum character length for the input field. - final int? maxLength; - - /// Minimum number of lines for the input field. - final int? minLines; - - /// Maximum number of lines the input field can expand to. - final int? maxLines; - - @override - State createState() => _VcComposerState(); - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(DiagnosticsProperty( - 'textEditingController', textEditingController)) - ..add(DoubleProperty('left', left)) - ..add(DoubleProperty('right', right)) - ..add(DoubleProperty('top', top)) - ..add(DoubleProperty('bottom', bottom)) - ..add(DoubleProperty('sigmaX', sigmaX)) - ..add(DoubleProperty('sigmaY', sigmaY)) - ..add(DiagnosticsProperty('padding', padding)) - ..add(DoubleProperty('gap', gap)) - ..add(DiagnosticsProperty('inputBorder', inputBorder)) - ..add(DiagnosticsProperty('filled', filled)) - ..add(DiagnosticsProperty('handleSafeArea', handleSafeArea)) - ..add(ColorProperty('backgroundColor', backgroundColor)) - ..add(ColorProperty('attachmentIconColor', attachmentIconColor)) - ..add(ColorProperty('sendIconColor', sendIconColor)) - ..add(ColorProperty('hintColor', hintColor)) - ..add(ColorProperty('textColor', textColor)) - ..add(ColorProperty('inputFillColor', inputFillColor)) - ..add(StringProperty('hintText', hintText)) - ..add(EnumProperty('keyboardAppearance', keyboardAppearance)) - ..add(DiagnosticsProperty('autocorrect', autocorrect)) - ..add(DiagnosticsProperty('autofocus', autofocus)) - ..add(EnumProperty( - 'textCapitalization', textCapitalization)) - ..add(DiagnosticsProperty('keyboardType', keyboardType)) - ..add(EnumProperty('textInputAction', textInputAction)) - ..add( - EnumProperty('shiftEnterAction', shiftEnterAction)) - ..add(DiagnosticsProperty('focusNode', focusNode)) - ..add(IntProperty('maxLength', maxLength)) - ..add(IntProperty('minLines', minLines)) - ..add(IntProperty('maxLines', maxLines)); - } -} - -class _VcComposerState extends State { - final _key = GlobalKey(); - late final TextEditingController _textController; - late final FocusNode _focusNode; - late String _suffixText; - - @override - void initState() { - super.initState(); - _textController = widget.textEditingController ?? TextEditingController(); - _focusNode = widget.focusNode ?? FocusNode(); - _focusNode.onKeyEvent = _handleKeyEvent; - _updateSuffixText(); - WidgetsBinding.instance.addPostFrameCallback((_) => _measure()); - } - - void _updateSuffixText() { - final utf8Length = utf8.encode(_textController.text).length; - _suffixText = '$utf8Length/${widget.maxLength}'; - } - - KeyEventResult _handleKeyEvent(FocusNode node, KeyEvent event) { - // Check for Shift+Enter - if (event is KeyDownEvent && - event.logicalKey == LogicalKeyboardKey.enter && - HardwareKeyboard.instance.isShiftPressed) { - if (widget.shiftEnterAction == ShiftEnterAction.send) { - _handleSubmitted(_textController.text); - return KeyEventResult.handled; - } else if (widget.shiftEnterAction == ShiftEnterAction.newline) { - final val = _textController.value; - final insertOffset = val.selection.extent.offset; - final messageWithNewLine = - '${_textController.text.substring(0, insertOffset)}\n' - '${_textController.text.substring(insertOffset)}'; - _textController.value = TextEditingValue( - text: messageWithNewLine, - selection: TextSelection.fromPosition( - TextPosition(offset: insertOffset + 1), - ), - ); - return KeyEventResult.handled; - } - } - return KeyEventResult.ignored; - } - - @override - void didUpdateWidget(covariant VcComposerWidget oldWidget) { - super.didUpdateWidget(oldWidget); - WidgetsBinding.instance.addPostFrameCallback((_) => _measure()); - } - - @override - void dispose() { - // Only try to dispose text controller if it's not provided, let - // user handle disposing it how they want. - if (widget.textEditingController == null) { - _textController.dispose(); - } - if (widget.focusNode == null) { - _focusNode.dispose(); - } - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final bottomSafeArea = - widget.handleSafeArea ? MediaQuery.of(context).padding.bottom : 0.0; - final onAttachmentTap = context.read(); - final theme = Theme.of(context); - final scaleTheme = theme.extension()!; - final config = scaleTheme.config; - final scheme = scaleTheme.scheme; - final scale = scaleTheme.scheme.scale(ScaleKind.primary); - final textTheme = theme.textTheme; - final scaleChatTheme = scaleTheme.chatTheme(); - final chatTheme = scaleChatTheme.chatTheme; - - final suffixTextStyle = - textTheme.bodySmall!.copyWith(color: scale.subtleText); - - return Positioned( - left: widget.left, - right: widget.right, - top: widget.top, - bottom: widget.bottom, - child: ClipRect( - child: DecoratedBox( - key: _key, - decoration: BoxDecoration( - border: config.preferBorders - ? Border(top: BorderSide(color: scale.border, width: 2)) - : null, - color: config.preferBorders - ? scale.elementBackground - : scale.border), - child: Column( - children: [ - if (widget.topWidget != null) widget.topWidget!, - Padding( - padding: widget.handleSafeArea - ? (widget.padding?.add( - EdgeInsets.only(bottom: bottomSafeArea), - ) ?? - EdgeInsets.only(bottom: bottomSafeArea)) - : (widget.padding ?? EdgeInsets.zero), - child: Row( - children: [ - if (widget.attachmentIcon != null && - onAttachmentTap != null) - IconButton( - icon: widget.attachmentIcon!, - color: widget.attachmentIconColor ?? - chatTheme.colors.onSurface.withValues(alpha: 0.5), - onPressed: onAttachmentTap, - ) - else - const SizedBox.shrink(), - SizedBox(width: widget.gap), - Expanded( - child: TextField( - controller: _textController, - decoration: InputDecoration( - filled: widget.filled ?? !config.preferBorders, - fillColor: widget.inputFillColor ?? - scheme.primaryScale.subtleBackground, - isDense: true, - contentPadding: - const EdgeInsets.fromLTRB(8, 8, 8, 8), - disabledBorder: OutlineInputBorder( - borderSide: config.preferBorders - ? BorderSide( - color: scheme.grayScale.border, - width: 2) - : BorderSide.none, - borderRadius: BorderRadius.all(Radius.circular( - 8 * config.borderRadiusScale))), - enabledBorder: OutlineInputBorder( - borderSide: config.preferBorders - ? BorderSide( - color: scheme.primaryScale.border, - width: 2) - : BorderSide.none, - borderRadius: BorderRadius.all(Radius.circular( - 8 * config.borderRadiusScale))), - focusedBorder: OutlineInputBorder( - borderSide: config.preferBorders - ? BorderSide( - color: scheme.primaryScale.border, - width: 2) - : BorderSide.none, - borderRadius: BorderRadius.all(Radius.circular( - 8 * config.borderRadiusScale))), - hintText: widget.hintText, - hintStyle: chatTheme.typography.bodyMedium.copyWith( - color: widget.hintColor ?? - chatTheme.colors.onSurface - .withValues(alpha: 0.5), - ), - border: widget.inputBorder, - hoverColor: Colors.transparent, - suffix: Text(_suffixText, style: suffixTextStyle)), - onSubmitted: _handleSubmitted, - onChanged: (value) { - setState(_updateSuffixText); - }, - textInputAction: widget.textInputAction, - keyboardAppearance: widget.keyboardAppearance, - autocorrect: widget.autocorrect ?? true, - autofocus: widget.autofocus, - textCapitalization: widget.textCapitalization, - keyboardType: widget.keyboardType, - focusNode: _focusNode, - //maxLength: widget.maxLength, - minLines: widget.minLines, - maxLines: widget.maxLines, - maxLengthEnforcement: MaxLengthEnforcement.none, - inputFormatters: [ - Utf8LengthLimitingTextInputFormatter( - maxLength: widget.maxLength), - ], - ), - ), - SizedBox(width: widget.gap), - if ((widget.sendIcon ?? scaleChatTheme.sendButtonIcon) != - null) - IconButton( - icon: - (widget.sendIcon ?? scaleChatTheme.sendButtonIcon)!, - color: widget.sendIconColor, - onPressed: () => _handleSubmitted(_textController.text), - ) - else - const SizedBox.shrink(), - ], - ), - ), - ], - ), - ), - ), - ); - } - - void _measure() { - if (!mounted) { - return; - } - - final renderBox = _key.currentContext?.findRenderObject() as RenderBox?; - if (renderBox != null) { - final height = renderBox.size.height; - final bottomSafeArea = MediaQuery.of(context).padding.bottom; - - context.read().setHeight( - // only set real height of the composer, ignoring safe area - widget.handleSafeArea ? height - bottomSafeArea : height, - ); - } - } - - void _handleSubmitted(String text) { - if (text.isNotEmpty) { - context.read()?.call(text); - _textController.clear(); - } - } -} diff --git a/lib/chat/views/chat_builders/vc_text_message_widget.dart b/lib/chat/views/chat_builders/vc_text_message_widget.dart deleted file mode 100644 index fc1fe80..0000000 --- a/lib/chat/views/chat_builders/vc_text_message_widget.dart +++ /dev/null @@ -1,269 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_chat_core/flutter_chat_core.dart'; -import 'package:provider/provider.dart'; - -import '../../../theme/theme.dart'; -import '../date_formatter.dart'; - -/// A widget that displays a text message. -class VcTextMessageWidget extends StatelessWidget { - /// Creates a widget to display a simple text message. - const VcTextMessageWidget({ - required this.message, - required this.index, - this.padding = const EdgeInsets.symmetric(horizontal: 16, vertical: 10), - this.borderRadius, - this.onlyEmojiFontSize, - this.sentBackgroundColor, - this.receivedBackgroundColor, - this.sentTextStyle, - this.receivedTextStyle, - this.timeStyle, - this.showTime = true, - this.showStatus = true, - this.timeAndStatusPosition = TimeAndStatusPosition.end, - super.key, - }); - - /// The text message data model. - final TextMessage message; - - /// The index of the message in the list. - final int index; - - /// Padding around the message bubble content. - final EdgeInsetsGeometry? padding; - - /// Border radius of the message bubble. - final BorderRadiusGeometry? borderRadius; - - /// Font size for messages containing only emojis. - final double? onlyEmojiFontSize; - - /// Background color for messages sent by the current user. - final Color? sentBackgroundColor; - - /// Background color for messages received from other users. - final Color? receivedBackgroundColor; - - /// Text style for messages sent by the current user. - final TextStyle? sentTextStyle; - - /// Text style for messages received from other users. - final TextStyle? receivedTextStyle; - - /// Text style for the message timestamp and status. - final TextStyle? timeStyle; - - /// Whether to display the message timestamp. - final bool showTime; - - /// Whether to display the message status (sent, delivered, seen) - /// for sent messages. - final bool showStatus; - - /// Position of the timestamp and status indicator relative to the text. - final TimeAndStatusPosition timeAndStatusPosition; - - bool get _isOnlyEmoji => message.metadata?['isOnlyEmoji'] == true; - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - final scaleTheme = theme.extension()!; - final config = scaleTheme.config; - final scheme = scaleTheme.scheme; - final scale = scaleTheme.scheme.scale(ScaleKind.primary); - final textTheme = theme.textTheme; - final scaleChatTheme = scaleTheme.chatTheme(); - final chatTheme = scaleChatTheme.chatTheme; - - final isSentByMe = context.watch() == message.authorId; - final backgroundColor = _resolveBackgroundColor(isSentByMe, scaleChatTheme); - final textStyle = _resolveTextStyle(isSentByMe, scaleChatTheme); - final timeStyle = _resolveTimeStyle(isSentByMe, scaleChatTheme); - final emojiFontSize = onlyEmojiFontSize ?? scaleChatTheme.onlyEmojiFontSize; - - final timeAndStatus = showTime || (isSentByMe && showStatus) - ? TimeAndStatus( - time: message.time, - status: message.status, - showTime: showTime, - showStatus: isSentByMe && showStatus, - textStyle: timeStyle, - ) - : null; - - final textContent = Text( - message.text, - style: _isOnlyEmoji - ? textStyle.copyWith(fontSize: emojiFontSize) - : textStyle, - ); - - return Container( - padding: _isOnlyEmoji - ? EdgeInsets.symmetric( - horizontal: (padding?.horizontal ?? 0) / 2, - // vertical: 0, - ) - : padding, - decoration: _isOnlyEmoji - ? null - : BoxDecoration( - color: backgroundColor, - borderRadius: borderRadius ?? chatTheme.shape, - ), - child: _buildContentBasedOnPosition( - context: context, - textContent: textContent, - timeAndStatus: timeAndStatus, - textStyle: textStyle, - ), - ); - } - - Widget _buildContentBasedOnPosition({ - required BuildContext context, - required Widget textContent, - TimeAndStatus? timeAndStatus, - TextStyle? textStyle, - }) { - if (timeAndStatus == null) { - return textContent; - } - - switch (timeAndStatusPosition) { - case TimeAndStatusPosition.start: - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [textContent, timeAndStatus], - ); - case TimeAndStatusPosition.inline: - return Row( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Flexible(child: textContent), - const SizedBox(width: 4), - timeAndStatus, - ], - ); - case TimeAndStatusPosition.end: - return Column( - crossAxisAlignment: CrossAxisAlignment.end, - mainAxisSize: MainAxisSize.min, - children: [textContent, timeAndStatus], - ); - } - } - - Color _resolveBackgroundColor(bool isSentByMe, ScaleChatTheme theme) { - if (isSentByMe) { - return sentBackgroundColor ?? theme.primaryColor; - } - return receivedBackgroundColor ?? theme.secondaryColor; - } - - TextStyle _resolveTextStyle(bool isSentByMe, ScaleChatTheme theme) { - if (isSentByMe) { - return sentTextStyle ?? theme.sentMessageBodyTextStyle; - } - return receivedTextStyle ?? theme.receivedMessageBodyTextStyle; - } - - TextStyle _resolveTimeStyle(bool isSentByMe, ScaleChatTheme theme) { - final ts = _resolveTextStyle(isSentByMe, theme); - - return theme.timeStyle.copyWith(color: ts.color); - } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(DiagnosticsProperty('message', message)) - ..add(IntProperty('index', index)) - ..add(DiagnosticsProperty('padding', padding)) - ..add(DiagnosticsProperty( - 'borderRadius', borderRadius)) - ..add(DoubleProperty('onlyEmojiFontSize', onlyEmojiFontSize)) - ..add(ColorProperty('sentBackgroundColor', sentBackgroundColor)) - ..add(ColorProperty('receivedBackgroundColor', receivedBackgroundColor)) - ..add(DiagnosticsProperty('sentTextStyle', sentTextStyle)) - ..add(DiagnosticsProperty( - 'receivedTextStyle', receivedTextStyle)) - ..add(DiagnosticsProperty('timeStyle', timeStyle)) - ..add(DiagnosticsProperty('showTime', showTime)) - ..add(DiagnosticsProperty('showStatus', showStatus)) - ..add(EnumProperty( - 'timeAndStatusPosition', timeAndStatusPosition)); - } -} - -/// A widget to display the message timestamp and status indicator. -class TimeAndStatus extends StatelessWidget { - /// Creates a widget for displaying time and status. - const TimeAndStatus({ - required this.time, - this.status, - this.showTime = true, - this.showStatus = true, - this.textStyle, - super.key, - }); - - /// The time the message was created. - final DateTime? time; - - /// The status of the message. - final MessageStatus? status; - - /// Whether to display the timestamp. - final bool showTime; - - /// Whether to display the status indicator. - final bool showStatus; - - /// The text style for the time and status. - final TextStyle? textStyle; - - @override - Widget build(BuildContext context) { - final dformat = DateFormatter(); - - return Row( - spacing: 2, - mainAxisSize: MainAxisSize.min, - children: [ - if (showTime && time != null) - Text(dformat.chatDateTimeFormat(time!.toLocal()), style: textStyle), - if (showStatus && status != null) - if (status == MessageStatus.sending) - SizedBox( - width: 6, - height: 6, - child: CircularProgressIndicator( - color: textStyle?.color, - strokeWidth: 2, - ), - ) - else - Icon(getIconForStatus(status!), color: textStyle?.color, size: 12), - ], - ); - } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(DiagnosticsProperty('time', time)) - ..add(EnumProperty('status', status)) - ..add(DiagnosticsProperty('showTime', showTime)) - ..add(DiagnosticsProperty('showStatus', showStatus)) - ..add(DiagnosticsProperty('textStyle', textStyle)); - } -} diff --git a/lib/chat/views/chat_component_widget.dart b/lib/chat/views/chat_component_widget.dart index 7d90d89..5de02a7 100644 --- a/lib/chat/views/chat_component_widget.dart +++ b/lib/chat/views/chat_component_widget.dart @@ -1,13 +1,11 @@ -import 'dart:async'; import 'dart:convert'; import 'dart:math'; import 'package:async_tools/async_tools.dart'; import 'package:awesome_extensions/awesome_extensions.dart'; -import 'package:fast_immutable_collections/fast_immutable_collections.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_chat_core/flutter_chat_core.dart' as core; +import 'package:flutter_chat_types/flutter_chat_types.dart' as types; import 'package:flutter_chat_ui/flutter_chat_ui.dart'; import 'package:flutter_translate/flutter_translate.dart'; import 'package:veilid_support/veilid_support.dart'; @@ -18,32 +16,23 @@ import '../../conversation/conversation.dart'; import '../../notifications/notifications.dart'; import '../../theme/theme.dart'; import '../chat.dart'; -import 'chat_builders/chat_builders.dart'; const onEndReachedThreshold = 0.75; -const _kScrollTag = 'kScrollTag'; -const kSeqId = 'seqId'; -const kSending = 'sending'; -const maxMessageLength = 2048; -class ChatComponentWidget extends StatefulWidget { - const ChatComponentWidget._({ - required super.key, - required TypedKey localConversationRecordKey, - required void Function() onCancel, - required void Function() onClose, - }) : _localConversationRecordKey = localConversationRecordKey, - _onCancel = onCancel, - _onClose = onClose; +class ChatComponentWidget extends StatelessWidget { + const ChatComponentWidget( + {required super.key, required TypedKey localConversationRecordKey}) + : _localConversationRecordKey = localConversationRecordKey; + + ///////////////////////////////////////////////////////////////////// + + @override + Widget build(BuildContext context) { + // final theme = Theme.of(context); + // final scale = theme.extension()!; + // final scaleConfig = theme.extension()!; + // final textTheme = theme.textTheme; - // Create a single-contact chat and its associated state - static Widget singleContact({ - required BuildContext context, - required TypedKey localConversationRecordKey, - required void Function() onCancel, - required void Function() onClose, - Key? key, - }) { // Get the account info final accountInfo = context.watch().state; @@ -54,19 +43,21 @@ class ChatComponentWidget extends StatefulWidget { final contactListCubit = context.watch(); // Get the active conversation cubit - final activeConversationCubit = context.select< - ActiveConversationsBlocMapCubit, - ActiveConversationCubit?>((x) => x.entry(localConversationRecordKey)); + final activeConversationCubit = context + .select( + (x) => x.tryOperateSync(_localConversationRecordKey, + closure: (cubit) => cubit)); if (activeConversationCubit == null) { - return waitingPage(onCancel: onCancel); + return waitingPage(); } // Get the messages cubit final messagesCubit = context.select( - (x) => x.entry(localConversationRecordKey)); + (x) => x.tryOperateSync(_localConversationRecordKey, + closure: (cubit) => cubit)); if (messagesCubit == null) { - return waitingPage(onCancel: onCancel); + return waitingPage(); } // Make chat component state @@ -79,67 +70,25 @@ class ChatComponentWidget extends StatefulWidget { activeConversationCubit: activeConversationCubit, messagesCubit: messagesCubit, ), - child: ChatComponentWidget._( - key: ValueKey(localConversationRecordKey), - localConversationRecordKey: localConversationRecordKey, - onCancel: onCancel, - onClose: onClose)); + child: Builder(builder: _buildChatComponent)); } - @override - State createState() => _ChatComponentWidgetState(); + ///////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////// - final TypedKey _localConversationRecordKey; - final void Function() _onCancel; - final void Function() _onClose; -} - -class _ChatComponentWidgetState extends State { - //////////////////////////////////////////////////////////////////// - - @override - void initState() { - _chatController = core.InMemoryChatController(); - _textEditingController = TextEditingController(); - _scrollController = ScrollController(); - _chatStateProcessor = SingleStateProcessor(); - _focusNode = FocusNode(); - - final chatComponentCubit = context.read(); - _chatStateProcessor.follow( - chatComponentCubit.stream, chatComponentCubit.state, _updateChatState); - - super.initState(); - } - - @override - void dispose() { - unawaited(_chatStateProcessor.close()); - - _focusNode.dispose(); - _chatController.dispose(); - _scrollController.dispose(); - _textEditingController.dispose(); - - super.dispose(); - } - - @override - Widget build(BuildContext context) { + Widget _buildChatComponent(BuildContext context) { final theme = Theme.of(context); - final scaleTheme = theme.extension()!; - final scale = scaleTheme.scheme.scale(ScaleKind.primary); + final scale = theme.extension()!; + final scaleConfig = theme.extension()!; final textTheme = theme.textTheme; - final scaleChatTheme = scaleTheme.chatTheme(); - // final errorChatTheme = chatTheme.copyWith(color:) - // ..inputTextColor = scaleScheme.errorScale.primary - // ..sendButtonIcon = Image.asset( - // 'assets/icon-send.png', - // color: scaleScheme.errorScale.primary, - // package: 'flutter_chat_ui', - // )) - // .commit(); + final chatTheme = makeChatTheme(scale, scaleConfig, textTheme); + final errorChatTheme = (ChatThemeEditor(chatTheme) + ..inputTextColor = scale.errorScale.primary + ..sendButtonIcon = Image.asset( + 'assets/icon-send.png', + color: scale.errorScale.primary, + package: 'flutter_chat_ui', + )) + .commit(); // Get the enclosing chat component cubit that contains our state // (created by ChatComponentWidget.builder()) @@ -148,7 +97,7 @@ class _ChatComponentWidgetState extends State { final localUser = chatComponentState.localUser; if (localUser == null) { - return const EmptyChatWidget(); + return waitingPage(); } final messageWindow = chatComponentState.messageWindow.asData?.value; @@ -160,8 +109,9 @@ class _ChatComponentWidgetState extends State { final title = chatComponentState.title; if (chatComponentCubit.scrollOffset != 0) { - _scrollController.position.correctPixels( - _scrollController.position.pixels + chatComponentCubit.scrollOffset); + chatComponentState.scrollController.position.correctPixels( + chatComponentState.scrollController.position.pixels + + chatComponentCubit.scrollOffset); chatComponentCubit.scrollOffset = 0; } @@ -171,7 +121,7 @@ class _ChatComponentWidgetState extends State { Container( height: 48, decoration: BoxDecoration( - color: scale.border, + color: scale.primaryScale.subtleBorder, ), child: Row(children: [ Align( @@ -181,18 +131,19 @@ class _ChatComponentWidgetState extends State { child: Text(title, textAlign: TextAlign.start, style: textTheme.titleMedium! - .copyWith(color: scale.borderText)), + .copyWith(color: scale.primaryScale.borderText)), )), const Spacer(), IconButton( - iconSize: 24, - icon: Icon(Icons.close, color: scale.borderText), - onPressed: widget._onClose) - .paddingLTRB(0, 0, 8, 0) + icon: Icon(Icons.close, color: scale.primaryScale.borderText), + onPressed: () async { + context.read().setActiveChat(null); + }).paddingLTRB(16, 0, 16, 0) ]), ), DecoratedBox( - decoration: const BoxDecoration(color: Colors.transparent), + decoration: + BoxDecoration(color: scale.primaryScale.subtleBackground), child: NotificationListener( onNotification: (notification) { if (chatComponentCubit.scrollOffset != 0) { @@ -213,7 +164,7 @@ class _ChatComponentWidgetState extends State { chatComponentCubit.scrollOffset = scrollOffset; // - singleFuture((chatComponentCubit, _kScrollTag), () async { + singleFuture(chatComponentState.chatKey, () async { await _handlePageForward( chatComponentCubit, messageWindow, notification); }); @@ -231,7 +182,7 @@ class _ChatComponentWidgetState extends State { chatComponentCubit.scrollOffset = scrollOffset; // - singleFuture((chatComponentCubit, _kScrollTag), () async { + singleFuture(chatComponentState.chatKey, () async { await _handlePageBackward( chatComponentCubit, messageWindow, notification); }); @@ -239,188 +190,84 @@ class _ChatComponentWidgetState extends State { return false; }, child: ValueListenableBuilder( - valueListenable: _textEditingController, + valueListenable: chatComponentState.textEditingController, builder: (context, textEditingValue, __) { final messageIsValid = - _messageIsValid(textEditingValue.text); - var sendIconColor = scaleTheme.config.preferBorders - ? scale.border - : scale.borderText; - - if (!messageIsValid || - _textEditingController.text.isEmpty) { - sendIconColor = sendIconColor.withAlpha(128); - } + utf8.encode(textEditingValue.text).lengthInBytes < + 2048; return Chat( - currentUserId: localUser.id, - resolveUser: (id) async { - if (id == localUser.id) { - return localUser; - } - return chatComponentState.remoteUsers.get(id); - }, - chatController: _chatController, - onMessageSend: (text) => - _handleSendPressed(chatComponentCubit, text), - theme: scaleChatTheme.chatTheme, - builders: core.Builders( - // Chat list builder - chatAnimatedListBuilder: (context, itemBuilder) => - ChatAnimatedListReversed( - scrollController: _scrollController, - itemBuilder: itemBuilder), - // Text message builder - textMessageBuilder: (context, message, index) => - VcTextMessageWidget( - message: message, - index: index, - // showTime: true, - // showStatus: true, - ), - // Composer builder - composerBuilder: (ctx) => VcComposerWidget( - autofocus: true, - focusNode: _focusNode, - textInputAction: isAnyMobile - ? TextInputAction.newline - : TextInputAction.send, - shiftEnterAction: isAnyMobile - ? ShiftEnterAction.send - : ShiftEnterAction.newline, - textEditingController: _textEditingController, - maxLength: maxMessageLength, - keyboardType: TextInputType.multiline, - sendIconColor: sendIconColor, - topWidget: messageIsValid - ? null - : Text(translate('chat.message_too_long'), - style: TextStyle( - color: scaleTheme - .scheme.errorScale.primary)) - .toCenter(), - ), - ), - timeFormat: core.DateFormat.jm(), - ); + key: chatComponentState.chatKey, + theme: + messageIsValid ? chatTheme : errorChatTheme, + messages: messageWindow.window.toList(), + scrollToBottomOnSend: isFirstPage, + scrollController: + chatComponentState.scrollController, + inputOptions: InputOptions( + inputClearMode: messageIsValid + ? InputClearMode.always + : InputClearMode.never, + textEditingController: + chatComponentState.textEditingController), + // isLastPage: isLastPage, + // onEndReached: () async { + // await _handlePageBackward( + // chatComponentCubit, messageWindow); + // }, + //onEndReachedThreshold: onEndReachedThreshold, + //onAttachmentPressed: _handleAttachmentPressed, + //onMessageTap: _handleMessageTap, + //onPreviewDataFetched: _handlePreviewDataFetched, + onSendPressed: (pt) { + try { + if (!messageIsValid) { + context.read().error( + text: + translate('chat.message_too_long')); + return; + } + _handleSendPressed(chatComponentCubit, pt); + } on FormatException { + context.read().error( + text: translate('chat.message_too_long')); + } + }, + listBottomWidget: messageIsValid + ? null + : Text(translate('chat.message_too_long'), + style: TextStyle( + color: scale.errorScale.primary)) + .toCenter(), + //showUserAvatars: false, + //showUserNames: true, + user: localUser, + emptyState: const EmptyChatWidget()) + .paddingLTRB(0, 2, 0, 0); }))).expanded(), ], ); } - ///////////////////////////////////////////////////////////////////// - - bool _messageIsValid(String text) => - utf8.encode(text).lengthInBytes < maxMessageLength; - - Future _updateChatState(ChatComponentState chatComponentState) async { - // Update message window state - final data = chatComponentState.messageWindow.asData; - if (data == null) { - await _chatController.setMessages([]); - return; - } - - final windowState = data.value; - - // await _chatController.setMessages(windowState.window.toList()); - - final newMessagesSet = windowState.window.toSet(); - final newMessagesById = - Map.fromEntries(newMessagesSet.map((m) => MapEntry(m.id, m))); - final newMessagesBySeqId = Map.fromEntries( - newMessagesSet.map((m) => MapEntry(m.metadata![kSeqId], m))); - final oldMessagesSet = _chatController.messages.toSet(); - - if (oldMessagesSet.isEmpty) { - await _chatController.setMessages(windowState.window.toList()); - return; - } - - // See how many messages differ by equality (not identity) - // If there are more than `replaceAllMessagesThreshold` differences - // just replace the whole list of messages - final diffs = newMessagesSet.diffAndIntersect(oldMessagesSet, - diffThisMinusOther: true, diffOtherMinusThis: true); - final addedMessages = diffs.diffThisMinusOther!; - final removedMessages = diffs.diffOtherMinusThis!; - - final replaceAllPaginationLimit = windowState.windowCount / 3; - - if ((addedMessages.length >= replaceAllPaginationLimit) || - removedMessages.length >= replaceAllPaginationLimit) { - await _chatController.setMessages(windowState.window.toList()); - return; - } - - // Remove messages that are gone, and replace the ones that have changed - for (final m in removedMessages) { - final newm = newMessagesById[m.id]; - if (newm != null) { - await _chatController.updateMessage(m, newm); - } else { - final newm = newMessagesBySeqId[m.metadata![kSeqId]]; - if (newm != null) { - await _chatController.updateMessage(m, newm); - addedMessages.remove(newm); - } else { - await _chatController.removeMessage(m); - } - } - } - - if (addedMessages.isNotEmpty) { - await _chatController.setMessages(windowState.window.toList()); - } - - // // // Check for append - // if (addedMessages.isNotEmpty) { - // if (_chatController.messages.isNotEmpty && - // (addedMessages.first.metadata![kSeqId] as int) > - // (_chatController.messages.reversed.last.metadata![kSeqId] - // as int)) { - // await _chatController.insertAllMessages(addedMessages.reversedView, - // index: 0); - // } - // // Check for prepend - // else if (_chatController.messages.isNotEmpty && - // (addedMessages.last.metadata![kSeqId] as int) < - // (_chatController.messages.reversed.first.metadata![kSeqId] - // as int)) { - // await _chatController.insertAllMessages( - // addedMessages.reversedView, - // ); - // } - // // Otherwise just replace - // // xxx could use a better algorithm here to merge added messages in - // else { - // await _chatController.setMessages(windowState.window.toList()); - // } - // } - } - - void _handleSendPressed(ChatComponentCubit chatComponentCubit, String text) { - _focusNode.requestFocus(); + void _handleSendPressed( + ChatComponentCubit chatComponentCubit, types.PartialText message) { + final text = message.text; if (text.startsWith('/')) { chatComponentCubit.runCommand(text); return; } - if (!_messageIsValid(text)) { - context - .read() - .error(text: translate('chat.message_too_long')); - return; - } - - chatComponentCubit.sendMessage(text: text); + chatComponentCubit.sendMessage(message); } // void _handleAttachmentPressed() async { + // // + // } + Future _handlePageForward( ChatComponentCubit chatComponentCubit, - WindowState messageWindow, + WindowState messageWindow, ScrollNotification notification) async { debugPrint( '_handlePageForward: messagesState.length=${messageWindow.length} ' @@ -454,7 +301,7 @@ class _ChatComponentWidgetState extends State { Future _handlePageBackward( ChatComponentCubit chatComponentCubit, - WindowState messageWindow, + WindowState messageWindow, ScrollNotification notification, ) async { debugPrint( @@ -490,9 +337,6 @@ class _ChatComponentWidgetState extends State { //chatComponentCubit.scrollOffset = 0; } - late final core.ChatController _chatController; - late final TextEditingController _textEditingController; - late final ScrollController _scrollController; - late final SingleStateProcessor _chatStateProcessor; - late final FocusNode _focusNode; + //////////////////////////////////////////////////////////////////////////// + final TypedKey _localConversationRecordKey; } diff --git a/lib/chat/views/date_formatter.dart b/lib/chat/views/date_formatter.dart deleted file mode 100644 index 2835b70..0000000 --- a/lib/chat/views/date_formatter.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:flutter_translate/flutter_translate.dart'; -import 'package:intl/intl.dart'; - -class DateFormatter { - DateFormatter(); - - String chatDateTimeFormat(DateTime dateTime) { - final now = DateTime.now(); - - final justNow = now.subtract(const Duration(minutes: 1)); - - final localDateTime = dateTime.toLocal(); - - if (!localDateTime.difference(justNow).isNegative) { - return translate('date_formatter.just_now'); - } - - final roughTimeString = DateFormat.jm().format(dateTime); - - if (localDateTime.day == now.day && - localDateTime.month == now.month && - localDateTime.year == now.year) { - return roughTimeString; - } - - final yesterday = now.subtract(const Duration(days: 1)); - - if (localDateTime.day == yesterday.day && - localDateTime.month == now.month && - localDateTime.year == now.year) { - return translate('date_formatter.yesterday'); - } - - if (now.difference(localDateTime).inDays < 4) { - final weekday = DateFormat(DateFormat.WEEKDAY).format(localDateTime); - - return '$weekday, $roughTimeString'; - } - return '${DateFormat.yMd().format(dateTime)}, $roughTimeString'; - } -} diff --git a/lib/chat/views/empty_chat_widget.dart b/lib/chat/views/empty_chat_widget.dart index 946fc3f..c975722 100644 --- a/lib/chat/views/empty_chat_widget.dart +++ b/lib/chat/views/empty_chat_widget.dart @@ -7,6 +7,7 @@ class EmptyChatWidget extends StatelessWidget { const EmptyChatWidget({super.key}); @override + // ignore: prefer_expression_function_bodies Widget build( BuildContext context, ) { @@ -17,7 +18,7 @@ class EmptyChatWidget extends StatelessWidget { width: double.infinity, height: double.infinity, decoration: BoxDecoration( - color: scale.primaryScale.appBackground, + color: Theme.of(context).scaffoldBackgroundColor, ), child: Column( mainAxisAlignment: MainAxisAlignment.center, diff --git a/lib/chat/views/new_chat_bottom_sheet.dart b/lib/chat/views/new_chat_bottom_sheet.dart new file mode 100644 index 0000000..646a3ec --- /dev/null +++ b/lib/chat/views/new_chat_bottom_sheet.dart @@ -0,0 +1,31 @@ +import 'package:awesome_extensions/awesome_extensions.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_translate/flutter_translate.dart'; + +import '../../theme/theme.dart'; + +Widget newChatBottomSheetBuilder( + BuildContext sheetContext, BuildContext context) { + //final theme = Theme.of(sheetContext); + //final scale = theme.extension()!; + + return KeyboardListener( + focusNode: FocusNode(), + onKeyEvent: (ke) { + if (ke.logicalKey == LogicalKeyboardKey.escape) { + Navigator.pop(sheetContext); + } + }, + child: styledBottomSheet( + context: context, + title: translate('add_chat_sheet.new_chat'), + child: SizedBox( + height: 160, + child: const Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Text( + 'Group and custom chat functionality is not available yet') + ]).paddingAll(16)))); +} diff --git a/lib/chat/views/no_conversation_widget.dart b/lib/chat/views/no_conversation_widget.dart index 3269bea..77502e1 100644 --- a/lib/chat/views/no_conversation_widget.dart +++ b/lib/chat/views/no_conversation_widget.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_translate/flutter_translate.dart'; -import '../../theme/theme.dart'; +import '../../theme/models/scale_scheme.dart'; class NoConversationWidget extends StatelessWidget { const NoConversationWidget({super.key}); @@ -12,31 +12,30 @@ class NoConversationWidget extends StatelessWidget { BuildContext context, ) { final theme = Theme.of(context); - final scaleScheme = theme.extension()!; - final scaleConfig = theme.extension()!; - final scale = scaleScheme.scale(ScaleKind.primary); + final scale = theme.extension()!; return DecoratedBox( - decoration: BoxDecoration( - color: scale.appBackground.withAlpha(scaleConfig.wallpaperAlpha), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.diversity_3, - color: scale.appText.withAlpha(127), - size: 48, - ), - Text( - textAlign: TextAlign.center, - translate('chat.start_a_conversation'), - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: scale.appText.withAlpha(127), - ), - ), - ], - )); + decoration: BoxDecoration( + color: scale.primaryScale.appBackground, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.diversity_3, + color: scale.primaryScale.subtleBorder, + size: 48, + ), + Text( + textAlign: TextAlign.center, + translate('chat.start_a_conversation'), + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: scale.primaryScale.subtleBorder, + ), + ), + ], + ), + ); } } diff --git a/lib/chat/views/utf8_length_limiting_text_input_formatter.dart b/lib/chat/views/utf8_length_limiting_text_input_formatter.dart deleted file mode 100644 index f037ca8..0000000 --- a/lib/chat/views/utf8_length_limiting_text_input_formatter.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'dart:convert'; -import 'dart:math'; - -import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; - -class Utf8LengthLimitingTextInputFormatter extends TextInputFormatter { - Utf8LengthLimitingTextInputFormatter({this.maxLength}) - : assert(maxLength != null || maxLength! >= 0, 'maxLength is invalid'); - - final int? maxLength; - - @override - TextEditingValue formatEditUpdate( - TextEditingValue oldValue, - TextEditingValue newValue, - ) { - if (maxLength != null && _bytesLength(newValue.text) > maxLength!) { - // If already at the maximum and tried to enter even more, - // keep the old value. - if (_bytesLength(oldValue.text) == maxLength) { - return oldValue; - } - return _truncate(newValue, maxLength!); - } - return newValue; - } - - static TextEditingValue _truncate(TextEditingValue value, int maxLength) { - var newValue = ''; - if (_bytesLength(value.text) > maxLength) { - var length = 0; - - value.text.characters.takeWhile((char) { - final nbBytes = _bytesLength(char); - if (length + nbBytes <= maxLength) { - newValue += char; - length += nbBytes; - return true; - } - return false; - }); - } - return TextEditingValue( - text: newValue, - selection: value.selection.copyWith( - baseOffset: min(value.selection.start, newValue.length), - extentOffset: min(value.selection.end, newValue.length), - ), - ); - } - - static int _bytesLength(String value) => utf8.encode(value).length; -} diff --git a/lib/chat/views/views.dart b/lib/chat/views/views.dart index 41b1936..7e8adce 100644 --- a/lib/chat/views/views.dart +++ b/lib/chat/views/views.dart @@ -1,4 +1,4 @@ export 'chat_component_widget.dart'; export 'empty_chat_widget.dart'; +export 'new_chat_bottom_sheet.dart'; export 'no_conversation_widget.dart'; -export 'utf8_length_limiting_text_input_formatter.dart'; diff --git a/lib/chat_list/cubits/chat_list_cubit.dart b/lib/chat_list/cubits/chat_list_cubit.dart index ae31f29..6bb88c1 100644 --- a/lib/chat_list/cubits/chat_list_cubit.dart +++ b/lib/chat_list/cubits/chat_list_cubit.dart @@ -13,7 +13,7 @@ import '../../proto/proto.dart' as proto; ////////////////////////////////////////////////// // Mutable state for per-account chat list -typedef ChatListCubitState = DHTShortArrayCubitState; +typedef ChatListCubitState = DHTShortArrayBusyState; class ChatListCubit extends DHTShortArrayCubit with StateMapFollowable { @@ -86,12 +86,14 @@ class ChatListCubit extends DHTShortArrayCubit // Nothing to do here return; } + break; case proto.Chat_Kind.group: if (c.group.localConversationRecordKey == contact.localConversationRecordKey) { throw StateError('direct conversation record key should' ' not be used for group chats!'); } + break; case proto.Chat_Kind.notSet: throw StateError('unknown chat kind'); } diff --git a/lib/chat_list/views/chat_list_widget.dart b/lib/chat_list/views/chat_list_widget.dart index cce416c..8fc57cf 100644 --- a/lib/chat_list/views/chat_list_widget.dart +++ b/lib/chat_list/views/chat_list_widget.dart @@ -8,6 +8,7 @@ import 'package:veilid_support/veilid_support.dart'; import '../../contacts/contacts.dart'; import '../../proto/proto.dart' as proto; +import '../../proto/proto.dart'; import '../../theme/theme.dart'; import '../chat_list.dart'; @@ -17,15 +18,15 @@ class ChatListWidget extends StatelessWidget { Widget _itemBuilderDirect( proto.DirectChat direct, IMap contactMap) { final contact = contactMap[direct.localConversationRecordKey]; - return ChatSingleContactItemWidget( - localConversationRecordKey: - direct.localConversationRecordKey.toVeilid(), - contact: contact) + if (contact == null) { + return const Text('...'); + } + return ChatSingleContactItemWidget(contact: contact) .paddingLTRB(0, 4, 0, 0); } List _itemFilter(IMap contactMap, - IList> chatList, String filter) { + IList> chatList, String filter) { final lowerValue = filter.toLowerCase(); return chatList.map((x) => x.value).where((c) { switch (c.whichKind()) { @@ -47,6 +48,7 @@ class ChatListWidget extends StatelessWidget { } @override + // ignore: prefer_expression_function_bodies Widget build(BuildContext context) { final contactListV = context.watch().state; @@ -81,8 +83,7 @@ class ChatListWidget extends StatelessWidget { }, filter: (value) => _itemFilter(contactMap, chatList, value), - searchFieldPadding: - const EdgeInsets.fromLTRB(0, 0, 0, 4), + spaceBetweenSearchAndList: 4, inputDecoration: InputDecoration( labelText: translate('chat_list.search'), ), diff --git a/lib/chat_list/views/chat_single_contact_item_widget.dart b/lib/chat_list/views/chat_single_contact_item_widget.dart index 5191fdd..1bd5f64 100644 --- a/lib/chat_list/views/chat_single_contact_item_widget.dart +++ b/lib/chat_list/views/chat_single_contact_item_widget.dart @@ -2,7 +2,6 @@ import 'package:async_tools/async_tools.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_translate/flutter_translate.dart'; -import 'package:veilid_support/veilid_support.dart'; import '../../chat/cubits/active_chat_cubit.dart'; import '../../contacts/contacts.dart'; import '../../proto/proto.dart' as proto; @@ -11,16 +10,13 @@ import '../chat_list.dart'; class ChatSingleContactItemWidget extends StatelessWidget { const ChatSingleContactItemWidget({ - required TypedKey localConversationRecordKey, - required proto.Contact? contact, + required proto.Contact contact, bool disabled = false, super.key, - }) : _localConversationRecordKey = localConversationRecordKey, - _contact = contact, + }) : _contact = contact, _disabled = disabled; - final TypedKey _localConversationRecordKey; - final proto.Contact? _contact; + final proto.Contact _contact; final bool _disabled; @override @@ -29,67 +25,56 @@ class ChatSingleContactItemWidget extends StatelessWidget { BuildContext context, ) { final theme = Theme.of(context); - final scaleTheme = Theme.of(context).extension()!; + final scale = theme.extension()!; + final scaleConfig = theme.extension()!; final activeChatCubit = context.watch(); - final selected = activeChatCubit.state == _localConversationRecordKey; + final localConversationRecordKey = + _contact.localConversationRecordKey.toVeilid(); + final selected = activeChatCubit.state == localConversationRecordKey; - final name = _contact == null ? '?' : _contact.nameOrNickname; - final title = _contact == null - ? translate('chat_list.deleted_contact') - : _contact.displayName; - final subtitle = _contact == null ? '' : _contact.profile.status; - final availability = _contact == null - ? proto.Availability.AVAILABILITY_UNSPECIFIED - : _contact.profile.availability; - - final scaleTileTheme = scaleTheme.tileTheme( - disabled: _disabled, - selected: selected, - ); + final name = _contact.nameOrNickname; + final title = _contact.displayName; + final subtitle = _contact.profile.status; final avatar = AvatarWidget( name: name, - size: 32, - borderColor: scaleTheme.config.useVisualIndicators - ? scaleTheme.scheme.primaryScale.primaryText - : scaleTheme.scheme.primaryScale.subtleBorder, + size: 34, + borderColor: _disabled + ? scale.grayScale.primaryText + : scale.secondaryScale.primaryText, foregroundColor: _disabled - ? scaleTheme.scheme.grayScale.primaryText - : scaleTheme.scheme.primaryScale.primaryText, - backgroundColor: _disabled - ? scaleTheme.scheme.grayScale.primary - : scaleTheme.scheme.primaryScale.primary, - scaleConfig: scaleTheme.config, + ? scale.grayScale.primaryText + : scale.secondaryScale.primaryText, + backgroundColor: + _disabled ? scale.grayScale.primary : scale.secondaryScale.primary, + scaleConfig: scaleConfig, textStyle: theme.textTheme.titleLarge!, ); return SliderTile( - key: ValueKey(_localConversationRecordKey), + key: ObjectKey(_contact), disabled: _disabled, selected: selected, - tileScale: ScaleKind.primary, + tileScale: ScaleKind.secondary, title: title, subtitle: subtitle, leading: avatar, - trailing: AvailabilityWidget( - availability: availability, - color: scaleTileTheme.textColor, - ).fit(fit: BoxFit.scaleDown), + trailing: AvailabilityWidget(availability: _contact.profile.availability), onTap: () { singleFuture(activeChatCubit, () async { - activeChatCubit.setActiveChat(_localConversationRecordKey); + activeChatCubit.setActiveChat(localConversationRecordKey); }); }, endActions: [ SliderTileAction( - //icon: Icons.delete, + icon: Icons.delete, label: translate('button.delete'), actionScale: ScaleKind.tertiary, onPressed: (context) async { final chatListCubit = context.read(); await chatListCubit.deleteChat( - localConversationRecordKey: _localConversationRecordKey); + localConversationRecordKey: localConversationRecordKey); }) ], ); diff --git a/lib/contact_invitation/cubits/contact_invitation_list_cubit.dart b/lib/contact_invitation/cubits/contact_invitation_list_cubit.dart index 332341d..f5663af 100644 --- a/lib/contact_invitation/cubits/contact_invitation_list_cubit.dart +++ b/lib/contact_invitation/cubits/contact_invitation_list_cubit.dart @@ -27,7 +27,7 @@ typedef GetEncryptionKeyCallback = Future Function( ////////////////////////////////////////////////// typedef ContactInvitiationListState - = DHTShortArrayCubitState; + = DHTShortArrayBusyState; ////////////////////////////////////////////////// // Mutable state for per-account contact invitations @@ -59,7 +59,6 @@ class ContactInvitationListCubit {required proto.Profile profile, required EncryptionKeyType encryptionKeyType, required String encryptionKey, - required String recipient, required String message, required Timestamp? expiration}) async { final pool = DHTRecordPool.instance; @@ -155,8 +154,7 @@ class ContactInvitationListCubit ..localConversationRecordKey = localConversation.key.toProto() ..expiration = expiration?.toInt64() ?? Int64.ZERO ..invitation = signedContactInvitationBytes - ..message = message - ..recipient = recipient; + ..message = message; // Add ContactInvitationRecord to account's list await operateWriteEventual((writer) async { @@ -254,7 +252,7 @@ class ContactInvitationListCubit .openRecordRead(contactRequestInboxKey, debugName: 'ContactInvitationListCubit::validateInvitation::' 'ContactRequestInbox', - parent: await pool.getParentRecordKey(contactRequestInboxKey) ?? + parent: pool.getParentRecordKey(contactRequestInboxKey) ?? _accountInfo.accountRecordKey) .withCancel(cancelRequest)) .maybeDeleteScope(!isSelf, (contactRequestInbox) async { diff --git a/lib/contact_invitation/cubits/contact_request_inbox_cubit.dart b/lib/contact_invitation/cubits/contact_request_inbox_cubit.dart index 198ae85..714201b 100644 --- a/lib/contact_invitation/cubits/contact_request_inbox_cubit.dart +++ b/lib/contact_invitation/cubits/contact_request_inbox_cubit.dart @@ -1,12 +1,9 @@ -import 'package:async_tools/async_tools.dart'; import 'package:veilid_support/veilid_support.dart'; import '../../account_manager/account_manager.dart'; import '../../proto/proto.dart' as proto; // Watch subkey #1 of the ContactRequest record for accept/reject -typedef ContactRequestInboxState = AsyncValue; - class ContactRequestInboxCubit extends DefaultDHTRecordCubit { ContactRequestInboxCubit( diff --git a/lib/contact_invitation/cubits/invitation_generator_cubit.dart b/lib/contact_invitation/cubits/invitation_generator_cubit.dart index 8d2226c..5c0fa15 100644 --- a/lib/contact_invitation/cubits/invitation_generator_cubit.dart +++ b/lib/contact_invitation/cubits/invitation_generator_cubit.dart @@ -5,5 +5,5 @@ import 'package:veilid_support/veilid_support.dart'; class InvitationGeneratorCubit extends FutureCubit<(Uint8List, TypedKey)> { InvitationGeneratorCubit(super.fut); - InvitationGeneratorCubit.value(super.state) : super.value(); + InvitationGeneratorCubit.value(super.v) : super.value(); } diff --git a/lib/contact_invitation/cubits/waiting_invitation_cubit.dart b/lib/contact_invitation/cubits/waiting_invitation_cubit.dart index b712546..47addc2 100644 --- a/lib/contact_invitation/cubits/waiting_invitation_cubit.dart +++ b/lib/contact_invitation/cubits/waiting_invitation_cubit.dart @@ -9,62 +9,10 @@ import 'package:veilid_support/veilid_support.dart'; import '../../account_manager/account_manager.dart'; import '../../conversation/conversation.dart'; import '../../proto/proto.dart' as proto; +import '../../tools/tools.dart'; import '../models/accepted_contact.dart'; import 'contact_request_inbox_cubit.dart'; -/// State of WaitingInvitationCubit -sealed class WaitingInvitationState - implements StateMachineState { - WaitingInvitationState({required this.global}); - final WaitingInvitationStateGlobal global; -} - -class WaitingInvitationStateGlobal { - WaitingInvitationStateGlobal( - {required this.accountInfo, - required this.accountRecordCubit, - required this.contactInvitationRecord}); - final AccountInfo accountInfo; - final AccountRecordCubit accountRecordCubit; - final proto.ContactInvitationRecord contactInvitationRecord; -} - -/// State of WaitingInvitationCubit: -/// Signature was invalid -class WaitingInvitationStateInvalidSignature - with StateMachineEndState - implements WaitingInvitationState { - const WaitingInvitationStateInvalidSignature({required this.global}); - - @override - final WaitingInvitationStateGlobal global; -} - -/// State of WaitingInvitationCubit: -/// Failed to initialize -class WaitingInvitationStateInitFailed - with StateMachineEndState - implements WaitingInvitationState { - const WaitingInvitationStateInitFailed( - {required this.global, required this.exception}); - - @override - final WaitingInvitationStateGlobal global; - final Exception exception; -} - -/// State of WaitingInvitationCubit: -/// Finished normally with an invitation status -class WaitingInvitationStateInvitationStatus - with StateMachineEndState - implements WaitingInvitationState { - const WaitingInvitationStateInvitationStatus( - {required this.global, required this.status}); - @override - final WaitingInvitationStateGlobal global; - final InvitationStatus status; -} - @immutable class InvitationStatus extends Equatable { const InvitationStatus({required this.acceptedContact}); @@ -74,160 +22,94 @@ class InvitationStatus extends Equatable { List get props => [acceptedContact]; } -/// State of WaitingInvitationCubit: -/// Waiting for the invited contact to accept/reject the invitation -class WaitingInvitationStateWaitForContactResponse - extends AsyncCubitReactorState< - WaitingInvitationState, - ContactRequestInboxState, - ContactRequestInboxCubit> implements WaitingInvitationState { - WaitingInvitationStateWaitForContactResponse(super.create, - {required this.global}) - : super(onState: (ctx) async { - final signedContactResponse = ctx.state.asData?.value; - if (signedContactResponse == null) { - return null; - } - - final contactResponse = proto.ContactResponse.fromBuffer( - signedContactResponse.contactResponse); - final contactSuperRecordKey = - contactResponse.superIdentityRecordKey.toVeilid(); - - // Fetch the remote contact's account superidentity - return WaitingInvitationStateWaitForContactSuperIdentity( - () => SuperIdentityCubit(superRecordKey: contactSuperRecordKey), - global: global, - signedContactResponse: signedContactResponse); - }); - - @override - final WaitingInvitationStateGlobal global; -} - -/// State of WaitingInvitationCubit: -/// Once an accept/reject happens, get the SuperIdentity of the recipient -class WaitingInvitationStateWaitForContactSuperIdentity - extends AsyncCubitReactorState implements WaitingInvitationState { - WaitingInvitationStateWaitForContactSuperIdentity(super.create, - {required this.global, - required proto.SignedContactResponse signedContactResponse}) - : super(onState: (ctx) async { - final contactSuperIdentity = ctx.state.asData?.value; - if (contactSuperIdentity == null) { - return null; - } - - final contactResponseBytes = - Uint8List.fromList(signedContactResponse.contactResponse); - final contactResponse = - proto.ContactResponse.fromBuffer(contactResponseBytes); - - // Verify - final idcs = await contactSuperIdentity.currentInstance.cryptoSystem; - final signature = signedContactResponse.identitySignature.toVeilid(); - if (!await idcs.verify(contactSuperIdentity.currentInstance.publicKey, - contactResponseBytes, signature)) { - // Could not verify signature of contact response - return WaitingInvitationStateInvalidSignature( - global: global, - ); - } - - // Check for rejection - if (!contactResponse.accept) { - // Rejection - return WaitingInvitationStateInvitationStatus( - global: global, - status: const InvitationStatus(acceptedContact: null), - ); - } - - // Pull profile from remote conversation key - final remoteConversationRecordKey = - contactResponse.remoteConversationRecordKey.toVeilid(); - - return WaitingInvitationStateWaitForConversation( - () => ConversationCubit( - accountInfo: global.accountInfo, - remoteIdentityPublicKey: - contactSuperIdentity.currentInstance.typedPublicKey, - remoteConversationRecordKey: remoteConversationRecordKey), - global: global, - remoteConversationRecordKey: remoteConversationRecordKey, - contactSuperIdentity: contactSuperIdentity, - ); - }); - - @override - final WaitingInvitationStateGlobal global; -} - -/// State of WaitingInvitationCubit: -/// Wait for the conversation cubit to initialize so we can return the -/// accepted invitation -class WaitingInvitationStateWaitForConversation extends AsyncCubitReactorState< - WaitingInvitationState, - AsyncValue, - ConversationCubit> implements WaitingInvitationState { - WaitingInvitationStateWaitForConversation(super.create, - {required this.global, - required TypedKey remoteConversationRecordKey, - required SuperIdentity contactSuperIdentity}) - : super(onState: (ctx) async { - final remoteConversation = ctx.state.asData?.value.remoteConversation; - final localConversation = ctx.state.asData?.value.localConversation; - if (remoteConversation == null || localConversation != null) { - return null; - } - - // Stop reacting to the conversation cubit - ctx.stop(); - - // Complete the local conversation now that we have the remote profile - final remoteProfile = remoteConversation.profile; - final localConversationRecordKey = global - .contactInvitationRecord.localConversationRecordKey - .toVeilid(); - - try { - await ctx.cubit.initLocalConversation( - profile: global.accountRecordCubit.state.asData!.value.profile, - existingConversationRecordKey: localConversationRecordKey); - } on Exception catch (e) { - return WaitingInvitationStateInitFailed( - global: global, exception: e); - } - - return WaitingInvitationStateInvitationStatus( - global: global, - status: InvitationStatus( - acceptedContact: AcceptedContact( - remoteProfile: remoteProfile, - remoteIdentity: contactSuperIdentity, - remoteConversationRecordKey: remoteConversationRecordKey, - localConversationRecordKey: localConversationRecordKey))); - }); - - @override - final WaitingInvitationStateGlobal global; -} - -/// Invitation state processor for sent invitations -class WaitingInvitationCubit extends StateMachineCubit { - WaitingInvitationCubit({ - required ContactRequestInboxCubit Function() initialStateCreate, +class WaitingInvitationCubit extends AsyncTransformerCubit { + WaitingInvitationCubit( + ContactRequestInboxCubit super.input, { required AccountInfo accountInfo, required AccountRecordCubit accountRecordCubit, required proto.ContactInvitationRecord contactInvitationRecord, }) : super( - WaitingInvitationStateWaitForContactResponse( - initialStateCreate, - global: WaitingInvitationStateGlobal( + transform: (signedContactResponse) => _transform( + signedContactResponse, accountInfo: accountInfo, accountRecordCubit: accountRecordCubit, - contactInvitationRecord: contactInvitationRecord), - ), - ); + contactInvitationRecord: contactInvitationRecord)); + + static Future> _transform( + proto.SignedContactResponse? signedContactResponse, + {required AccountInfo accountInfo, + required AccountRecordCubit accountRecordCubit, + required proto.ContactInvitationRecord contactInvitationRecord}) async { + if (signedContactResponse == null) { + return const AsyncValue.loading(); + } + + final contactResponseBytes = + Uint8List.fromList(signedContactResponse.contactResponse); + final contactResponse = + proto.ContactResponse.fromBuffer(contactResponseBytes); + final contactIdentityMasterRecordKey = + contactResponse.superIdentityRecordKey.toVeilid(); + + // Fetch the remote contact's account master + final contactSuperIdentity = await SuperIdentity.open( + superRecordKey: contactIdentityMasterRecordKey); + + // Verify + final idcs = await contactSuperIdentity.currentInstance.cryptoSystem; + final signature = signedContactResponse.identitySignature.toVeilid(); + if (!await idcs.verify(contactSuperIdentity.currentInstance.publicKey, + contactResponseBytes, signature)) { + // Could not verify signature of contact response + return AsyncValue.error('Invalid signature on contact response.'); + } + + // Check for rejection + if (!contactResponse.accept) { + // Rejection + return const AsyncValue.data(InvitationStatus(acceptedContact: null)); + } + + // Pull profile from remote conversation key + final remoteConversationRecordKey = + contactResponse.remoteConversationRecordKey.toVeilid(); + + final conversation = ConversationCubit( + accountInfo: accountInfo, + remoteIdentityPublicKey: + contactSuperIdentity.currentInstance.typedPublicKey, + remoteConversationRecordKey: remoteConversationRecordKey); + + // wait for remote conversation for up to 20 seconds + proto.Conversation? remoteConversation; + var retryCount = 20; + do { + await conversation.refresh(); + remoteConversation = conversation.state.asData?.value.remoteConversation; + if (remoteConversation != null) { + break; + } + log.info('Remote conversation could not be read. Waiting...'); + await Future.delayed(const Duration(seconds: 1)); + retryCount--; + } while (retryCount > 0); + if (remoteConversation == null) { + return AsyncValue.error('Invitation accept timed out.'); + } + + // Complete the local conversation now that we have the remote profile + final remoteProfile = remoteConversation.profile; + final localConversationRecordKey = + contactInvitationRecord.localConversationRecordKey.toVeilid(); + return conversation.initLocalConversation( + profile: accountRecordCubit.state.asData!.value.profile, + existingConversationRecordKey: localConversationRecordKey, + callback: (localConversation) async => AsyncValue.data(InvitationStatus( + acceptedContact: AcceptedContact( + remoteProfile: remoteProfile, + remoteIdentity: contactSuperIdentity, + remoteConversationRecordKey: remoteConversationRecordKey, + localConversationRecordKey: localConversationRecordKey)))); + } } diff --git a/lib/contact_invitation/cubits/waiting_invitations_bloc_map_cubit.dart b/lib/contact_invitation/cubits/waiting_invitations_bloc_map_cubit.dart index f125e71..97e1c76 100644 --- a/lib/contact_invitation/cubits/waiting_invitations_bloc_map_cubit.dart +++ b/lib/contact_invitation/cubits/waiting_invitations_bloc_map_cubit.dart @@ -10,15 +10,15 @@ import '../../proto/proto.dart' as proto; import 'cubits.dart'; typedef WaitingInvitationsBlocMapState - = BlocMapState; + = BlocMapState>; // Map of contactRequestInboxRecordKey to WaitingInvitationCubit // Wraps a contact invitation cubit to watch for accept/reject // Automatically follows the state of a ContactInvitationListCubit. class WaitingInvitationsBlocMapCubit extends BlocMapCubit + AsyncValue, WaitingInvitationCubit> with - StateMapFollower, + StateMapFollower, TypedKey, proto.ContactInvitationRecord> { WaitingInvitationsBlocMapCubit( {required AccountInfo accountInfo, @@ -41,94 +41,66 @@ class WaitingInvitationsBlocMapCubit extends BlocMapCubit close() async { - await _singleInvitationStatusProcessor.close(); + await _singleInvitationStatusProcessor.unfollow(); await super.close(); } - void _addWaitingInvitation( - {required proto.ContactInvitationRecord contactInvitationRecord}) => - add( + Future _addWaitingInvitation( + {required proto.ContactInvitationRecord + contactInvitationRecord}) async => + add(() => MapEntry( contactInvitationRecord.contactRequestInbox.recordKey.toVeilid(), - () => WaitingInvitationCubit( - initialStateCreate: () => ContactRequestInboxCubit( + WaitingInvitationCubit( + ContactRequestInboxCubit( accountInfo: _accountInfo, contactInvitationRecord: contactInvitationRecord), accountInfo: _accountInfo, accountRecordCubit: _accountRecordCubit, - contactInvitationRecord: contactInvitationRecord)); + contactInvitationRecord: contactInvitationRecord))); // Process all accepted or rejected invitations Future _invitationStatusListener( WaitingInvitationsBlocMapState newState) async { for (final entry in newState.entries) { final contactRequestInboxRecordKey = entry.key; + final invStatus = entry.value.asData?.value; + // Skip invitations that have not yet been accepted or rejected + if (invStatus == null) { + continue; + } - switch (entry.value) { - case WaitingInvitationStateInvalidSignature(): - // Signature was invalid, display an error and treat like a rejection - await _contactInvitationListCubit.deleteInvitation( - accepted: false, - contactRequestInboxRecordKey: contactRequestInboxRecordKey); + // Delete invitation and process the accepted or rejected contact + final acceptedContact = invStatus.acceptedContact; + if (acceptedContact != null) { + await _contactInvitationListCubit.deleteInvitation( + accepted: true, + contactRequestInboxRecordKey: contactRequestInboxRecordKey); - // Notify about error state - _notificationsCubit.error( - text: translate('waiting_invitation.invalid')); - case final WaitingInvitationStateInitFailed st: - // Initialization error, display an error and treat like a rejection - await _contactInvitationListCubit.deleteInvitation( - accepted: false, - contactRequestInboxRecordKey: contactRequestInboxRecordKey); + // Accept + await _contactListCubit.createContact( + profile: acceptedContact.remoteProfile, + remoteSuperIdentity: acceptedContact.remoteIdentity, + remoteConversationRecordKey: + acceptedContact.remoteConversationRecordKey, + localConversationRecordKey: + acceptedContact.localConversationRecordKey, + ); - // Notify about error state - _notificationsCubit.error( - text: '${translate('waiting_invitation.init_failed')}\n' - '${st.exception}'); + // Notify about acceptance + _notificationsCubit.info( + text: translate('waiting_invitation.accepted', + args: {'name': acceptedContact.remoteProfile.name})); + } else { + // Reject + await _contactInvitationListCubit.deleteInvitation( + accepted: false, + contactRequestInboxRecordKey: contactRequestInboxRecordKey); - case final WaitingInvitationStateInvitationStatus st: - final invStatus = st.status; - - // Delete invitation and process the accepted or rejected contact - final acceptedContact = invStatus.acceptedContact; - if (acceptedContact != null) { - await _contactInvitationListCubit.deleteInvitation( - accepted: true, - contactRequestInboxRecordKey: contactRequestInboxRecordKey); - - // Accept - await _contactListCubit.createContact( - profile: acceptedContact.remoteProfile, - remoteSuperIdentity: acceptedContact.remoteIdentity, - remoteConversationRecordKey: - acceptedContact.remoteConversationRecordKey, - localConversationRecordKey: - acceptedContact.localConversationRecordKey, - ); - - // Notify about acceptance - _notificationsCubit.info( - text: translate('waiting_invitation.accepted', - args: {'name': acceptedContact.remoteProfile.name})); - } else { - // Reject - await _contactInvitationListCubit.deleteInvitation( - accepted: false, - contactRequestInboxRecordKey: contactRequestInboxRecordKey); - - // Notify about rejection - _notificationsCubit.info( - text: translate( - 'waiting_invitation.rejected', - )); - } - case WaitingInvitationStateWaitForContactResponse(): - // Do nothing, still waiting for contact response - break; - case WaitingInvitationStateWaitForContactSuperIdentity(): - // Do nothing, still waiting for contact SuperIdentity - break; - case WaitingInvitationStateWaitForConversation(): - // Do nothing, still waiting for conversation - break; + // Notify about rejection + _notificationsCubit.info( + text: translate( + 'waiting_invitation.rejected', + )); } } } @@ -136,12 +108,14 @@ class WaitingInvitationsBlocMapCubit extends BlocMapCubit remove(key); + Future removeFromState(TypedKey key) => remove(key); @override - void updateState(TypedKey key, proto.ContactInvitationRecord? oldValue, - proto.ContactInvitationRecord newValue) { - _addWaitingInvitation(contactInvitationRecord: newValue); + Future updateState( + TypedKey key, + proto.ContactInvitationRecord? oldValue, + proto.ContactInvitationRecord newValue) async { + await _addWaitingInvitation(contactInvitationRecord: newValue); } //// diff --git a/lib/contact_invitation/models/valid_contact_invitation.dart b/lib/contact_invitation/models/valid_contact_invitation.dart index c39692c..fb8b8de 100644 --- a/lib/contact_invitation/models/valid_contact_invitation.dart +++ b/lib/contact_invitation/models/valid_contact_invitation.dart @@ -37,7 +37,7 @@ class ValidContactInvitation { return (await pool.openRecordWrite(_contactRequestInboxKey, _writer, debugName: 'ValidContactInvitation::accept::' 'ContactRequestInbox', - parent: await pool.getParentRecordKey(_contactRequestInboxKey) ?? + parent: pool.getParentRecordKey(_contactRequestInboxKey) ?? _accountInfo.accountRecordKey)) // ignore: prefer_expression_function_bodies .maybeDeleteScope(!isSelf, (contactRequestInbox) async { @@ -47,35 +47,36 @@ class ValidContactInvitation { accountInfo: _accountInfo, remoteIdentityPublicKey: _contactSuperIdentity.currentInstance.typedPublicKey); - final localConversationRecordKey = - await conversation.initLocalConversation(profile: profile); + return conversation.initLocalConversation( + profile: profile, + callback: (localConversation) async { + final contactResponse = proto.ContactResponse() + ..accept = true + ..remoteConversationRecordKey = localConversation.key.toProto() + ..superIdentityRecordKey = + _accountInfo.superIdentityRecordKey.toProto(); + final contactResponseBytes = contactResponse.writeToBuffer(); - final contactResponse = proto.ContactResponse() - ..accept = true - ..remoteConversationRecordKey = localConversationRecordKey.toProto() - ..superIdentityRecordKey = - _accountInfo.superIdentityRecordKey.toProto(); - final contactResponseBytes = contactResponse.writeToBuffer(); + final cs = await _accountInfo.identityCryptoSystem; + final identitySignature = await cs.signWithKeyPair( + _accountInfo.identityWriter, contactResponseBytes); - final cs = await _accountInfo.identityCryptoSystem; - final identitySignature = await cs.signWithKeyPair( - _accountInfo.identityWriter, contactResponseBytes); + final signedContactResponse = proto.SignedContactResponse() + ..contactResponse = contactResponseBytes + ..identitySignature = identitySignature.toProto(); - final signedContactResponse = proto.SignedContactResponse() - ..contactResponse = contactResponseBytes - ..identitySignature = identitySignature.toProto(); + // Write the acceptance to the inbox + await contactRequestInbox + .eventualWriteProtobuf(signedContactResponse, subkey: 1); - // Write the acceptance to the inbox - await contactRequestInbox.eventualWriteProtobuf(signedContactResponse, - subkey: 1); - - return AcceptedContact( - remoteProfile: _contactRequestPrivate.profile, - remoteIdentity: _contactSuperIdentity, - remoteConversationRecordKey: - _contactRequestPrivate.chatRecordKey.toVeilid(), - localConversationRecordKey: localConversationRecordKey, - ); + return AcceptedContact( + remoteProfile: _contactRequestPrivate.profile, + remoteIdentity: _contactSuperIdentity, + remoteConversationRecordKey: + _contactRequestPrivate.chatRecordKey.toVeilid(), + localConversationRecordKey: localConversation.key, + ); + }); }); } on Exception catch (e) { log.debug('exception: $e', e); diff --git a/lib/contact_invitation/views/contact_invitation_display.dart b/lib/contact_invitation/views/contact_invitation_display.dart index b3f048a..83b80d0 100644 --- a/lib/contact_invitation/views/contact_invitation_display.dart +++ b/lib/contact_invitation/views/contact_invitation_display.dart @@ -1,6 +1,5 @@ import 'dart:math'; -import 'package:auto_size_text/auto_size_text.dart'; import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:basic_utils/basic_utils.dart'; import 'package:flutter/foundation.dart'; @@ -21,13 +20,11 @@ import '../contact_invitation.dart'; class ContactInvitationDisplayDialog extends StatelessWidget { const ContactInvitationDisplayDialog._({ required this.locator, - required this.recipient, required this.message, required this.fingerprint, }); final Locator locator; - final String recipient; final String message; final String fingerprint; @@ -35,22 +32,18 @@ class ContactInvitationDisplayDialog extends StatelessWidget { void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(StringProperty('recipient', recipient)) ..add(StringProperty('message', message)) ..add(DiagnosticsProperty('locator', locator)) ..add(StringProperty('fingerprint', fingerprint)); } - String makeTextInvite(String recipient, String message, Uint8List data) { + String makeTextInvite(String message, Uint8List data) { final invite = StringUtils.addCharAtPosition( base64UrlNoPadEncode(data), '\n', 40, repeat: true); - final to = recipient.isNotEmpty - ? '${translate('invitiation_dialog.to')}: $recipient\n' - : ''; final msg = message.isNotEmpty ? '$message\n' : ''; - return '$to' - '$msg' + + return '$msg' '--- BEGIN VEILIDCHAT CONTACT INVITE ----\n' '$invite\n' '---- END VEILIDCHAT CONTACT INVITE -----\n' @@ -69,10 +62,6 @@ class ContactInvitationDisplayDialog extends StatelessWidget { final cardsize = min(MediaQuery.of(context).size.shortestSide - 48.0, 400); - final fingerprintText = - '${translate('create_invitation_dialog.fingerprint')}\n' - '$fingerprint'; - return BlocListener( bloc: locator(), @@ -121,21 +110,14 @@ class ContactInvitationDisplayDialog extends StatelessWidget { errorCorrectLevel: QrErrorCorrectLevel.L)), ).expanded(), - if (recipient.isNotEmpty) - AutoSizeText(recipient, - softWrap: true, - maxLines: 2, - style: textTheme.labelLarge! - .copyWith(color: Colors.black)) - .paddingAll(8), - if (message.isNotEmpty) - Text(message, - softWrap: true, - maxLines: 2, - style: textTheme.labelMedium! - .copyWith(color: Colors.black)) - .paddingAll(8), - Text(fingerprintText, + Text(message, + softWrap: true, + style: textTheme.labelLarge! + .copyWith(color: Colors.black)) + .paddingAll(8), + Text( + '${translate('create_invitation_dialog.fingerprint')}\n' + '$fingerprint', softWrap: true, textAlign: TextAlign.center, style: textTheme.labelSmall!.copyWith( @@ -155,8 +137,7 @@ class ContactInvitationDisplayDialog extends StatelessWidget { text: translate('create_invitation_dialog' '.invitation_copied')); await Clipboard.setData(ClipboardData( - text: makeTextInvite( - recipient, message, data.$1))); + text: makeTextInvite(message, data.$1))); }, ).paddingAll(16), ]), @@ -167,7 +148,6 @@ class ContactInvitationDisplayDialog extends StatelessWidget { required BuildContext context, required Locator locator, required InvitationGeneratorCubit Function(BuildContext) create, - required String recipient, required String message, }) async { final fingerprint = @@ -179,7 +159,6 @@ class ContactInvitationDisplayDialog extends StatelessWidget { create: create, child: ContactInvitationDisplayDialog._( locator: locator, - recipient: recipient, message: message, fingerprint: fingerprint, ))); diff --git a/lib/contact_invitation/views/contact_invitation_item_widget.dart b/lib/contact_invitation/views/contact_invitation_item_widget.dart index 544e5db..6e6dfcf 100644 --- a/lib/contact_invitation/views/contact_invitation_item_widget.dart +++ b/lib/contact_invitation/views/contact_invitation_item_widget.dart @@ -37,19 +37,14 @@ class ContactInvitationItemWidget extends StatelessWidget { final tileDisabled = disabled || context.watch().isBusy; - var title = translate('contact_list.invitation'); - if (contactInvitationRecord.recipient.isNotEmpty) { - title = contactInvitationRecord.recipient; - } else if (contactInvitationRecord.message.isNotEmpty) { - title = contactInvitationRecord.message; - } - return SliderTile( key: ObjectKey(contactInvitationRecord), disabled: tileDisabled, selected: selected, - tileScale: ScaleKind.secondary, - title: title, + tileScale: ScaleKind.primary, + title: contactInvitationRecord.message.isEmpty + ? translate('contact_list.invitation') + : contactInvitationRecord.message, leading: const Icon(Icons.person_add), onTap: () async { if (!context.mounted) { @@ -58,7 +53,6 @@ class ContactInvitationItemWidget extends StatelessWidget { await ContactInvitationDisplayDialog.show( context: context, locator: context.read, - recipient: contactInvitationRecord.recipient, message: contactInvitationRecord.message, create: (context) => InvitationGeneratorCubit.value(( Uint8List.fromList(contactInvitationRecord.invitation), @@ -68,7 +62,7 @@ class ContactInvitationItemWidget extends StatelessWidget { }, endActions: [ SliderTileAction( - // icon: Icons.delete, + icon: Icons.delete, label: translate('button.delete'), actionScale: ScaleKind.tertiary, onPressed: (context) async { diff --git a/lib/contact_invitation/views/create_invitation_dialog.dart b/lib/contact_invitation/views/create_invitation_dialog.dart index 581e8d6..f1115d7 100644 --- a/lib/contact_invitation/views/create_invitation_dialog.dart +++ b/lib/contact_invitation/views/create_invitation_dialog.dart @@ -18,7 +18,7 @@ class CreateInvitationDialog extends StatefulWidget { const CreateInvitationDialog._({required this.locator}); @override - State createState() => _CreateInvitationDialogState(); + CreateInvitationDialogState createState() => CreateInvitationDialogState(); static Future show(BuildContext context) async { await StyledDialog.show( @@ -36,9 +36,8 @@ class CreateInvitationDialog extends StatefulWidget { } } -class _CreateInvitationDialogState extends State { +class CreateInvitationDialogState extends State { late final TextEditingController _messageTextController; - late final TextEditingController _recipientTextController; EncryptionKeyType _encryptionKeyType = EncryptionKeyType.none; String _encryptionKey = ''; @@ -52,7 +51,6 @@ class _CreateInvitationDialogState extends State { _messageTextController = TextEditingController( text: translate('create_invitation_dialog.connect_with_me', args: {'name': name})); - _recipientTextController = TextEditingController(); super.initState(); } @@ -156,7 +154,6 @@ class _CreateInvitationDialogState extends State { profile: profile, encryptionKeyType: _encryptionKeyType, encryptionKey: _encryptionKey, - recipient: _recipientTextController.text, message: _messageTextController.text, expiration: _expiration); @@ -165,7 +162,6 @@ class _CreateInvitationDialogState extends State { await ContactInvitationDisplayDialog.show( context: context, locator: widget.locator, - recipient: _recipientTextController.text, message: _messageTextController.text, create: (context) => InvitationGeneratorCubit(generator)); } @@ -180,7 +176,6 @@ class _CreateInvitationDialogState extends State { final theme = Theme.of(context); //final scale = theme.extension()!; final textTheme = theme.textTheme; - return ConstrainedBox( constraints: BoxConstraints(maxHeight: maxDialogHeight, maxWidth: maxDialogWidth), @@ -190,35 +185,19 @@ class _CreateInvitationDialogState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - TextField( - autofocus: true, - controller: _recipientTextController, - onChanged: (value) { - setState(() {}); - }, - inputFormatters: [ - LengthLimitingTextInputFormatter(128), - ], - decoration: InputDecoration( - hintText: - translate('create_invitation_dialog.recipient_hint'), - labelText: - translate('create_invitation_dialog.recipient_name'), - helperText: - translate('create_invitation_dialog.recipient_helper')), + Text( + translate('create_invitation_dialog.message_to_contact'), ).paddingAll(8), - const SizedBox(height: 10), TextField( controller: _messageTextController, inputFormatters: [ LengthLimitingTextInputFormatter(128), ], decoration: InputDecoration( - hintText: translate('create_invitation_dialog.message_hint'), - labelText: - translate('create_invitation_dialog.message_label'), - helperText: - translate('create_invitation_dialog.message_helper')), + //border: const OutlineInputBorder(), + hintText: + translate('create_invitation_dialog.enter_message_hint'), + labelText: translate('create_invitation_dialog.message')), ).paddingAll(8), const SizedBox(height: 10), Text(translate('create_invitation_dialog.protect_this_invitation'), @@ -249,9 +228,7 @@ class _CreateInvitationDialogState extends State { Container( padding: const EdgeInsets.all(8), child: ElevatedButton( - onPressed: _recipientTextController.text.isNotEmpty - ? _onGenerateButtonPressed - : null, + onPressed: _onGenerateButtonPressed, child: Text( translate('create_invitation_dialog.generate'), ).paddingAll(16), @@ -267,4 +244,11 @@ class _CreateInvitationDialogState extends State { ), ); } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(DiagnosticsProperty( + 'messageTextController', _messageTextController)); + } } diff --git a/lib/contact_invitation/views/new_contact_bottom_sheet.dart b/lib/contact_invitation/views/new_contact_bottom_sheet.dart new file mode 100644 index 0000000..a79a07f --- /dev/null +++ b/lib/contact_invitation/views/new_contact_bottom_sheet.dart @@ -0,0 +1,71 @@ +import 'package:awesome_extensions/awesome_extensions.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_translate/flutter_translate.dart'; + +import '../../theme/theme.dart'; +import 'create_invitation_dialog.dart'; +import 'paste_invitation_dialog.dart'; +import 'scan_invitation_dialog.dart'; + +Widget newContactBottomSheetBuilder( + BuildContext sheetContext, BuildContext context) { + final theme = Theme.of(sheetContext); + final scale = theme.extension()!; + + return KeyboardListener( + focusNode: FocusNode(), + onKeyEvent: (ke) { + if (ke.logicalKey == LogicalKeyboardKey.escape) { + Navigator.pop(sheetContext); + } + }, + child: styledBottomSheet( + context: context, + title: translate('add_contact_sheet.new_contact'), + child: SizedBox( + height: 160, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Column(children: [ + IconButton( + onPressed: () async { + Navigator.pop(sheetContext); + await CreateInvitationDialog.show(context); + }, + iconSize: 64, + icon: const Icon(Icons.contact_page), + color: scale.primaryScale.hoverBorder), + Text( + translate('add_contact_sheet.create_invite'), + ) + ]), + Column(children: [ + IconButton( + onPressed: () async { + Navigator.pop(sheetContext); + await ScanInvitationDialog.show(context); + }, + iconSize: 64, + icon: const Icon(Icons.qr_code_scanner), + color: scale.primaryScale.hoverBorder), + Text( + translate('add_contact_sheet.scan_invite'), + ) + ]), + Column(children: [ + IconButton( + onPressed: () async { + Navigator.pop(sheetContext); + await PasteInvitationDialog.show(context); + }, + iconSize: 64, + icon: const Icon(Icons.paste), + color: scale.primaryScale.hoverBorder), + Text( + translate('add_contact_sheet.paste_invite'), + ) + ]) + ]).paddingAll(16)))); +} diff --git a/lib/contact_invitation/views/paste_invitation_dialog.dart b/lib/contact_invitation/views/paste_invitation_dialog.dart index b014fc2..9cd9efc 100644 --- a/lib/contact_invitation/views/paste_invitation_dialog.dart +++ b/lib/contact_invitation/views/paste_invitation_dialog.dart @@ -112,8 +112,8 @@ class PasteInvitationDialogState extends State { constraints: const BoxConstraints(maxHeight: 200), child: TextField( enabled: !dialogState.isValidating, - autofocus: true, - onChanged: (text) => _onPasteChanged(text, validateInviteData), + onChanged: (text) async => + _onPasteChanged(text, validateInviteData), style: monoStyle, keyboardType: TextInputType.multiline, maxLines: null, @@ -129,11 +129,14 @@ class PasteInvitationDialogState extends State { } @override - Widget build(BuildContext context) => InvitationDialog( - locator: widget._locator, - onValidationCancelled: onValidationCancelled, - onValidationSuccess: onValidationSuccess, - onValidationFailed: onValidationFailed, - inviteControlIsValid: inviteControlIsValid, - buildInviteControl: buildInviteControl); + // ignore: prefer_expression_function_bodies + Widget build(BuildContext context) { + return InvitationDialog( + locator: widget._locator, + onValidationCancelled: onValidationCancelled, + onValidationSuccess: onValidationSuccess, + onValidationFailed: onValidationFailed, + inviteControlIsValid: inviteControlIsValid, + buildInviteControl: buildInviteControl); + } } diff --git a/lib/contact_invitation/views/scan_invitation_dialog.dart b/lib/contact_invitation/views/scan_invitation_dialog.dart index 8fbdf5c..645640e 100644 --- a/lib/contact_invitation/views/scan_invitation_dialog.dart +++ b/lib/contact_invitation/views/scan_invitation_dialog.dart @@ -86,7 +86,7 @@ class ScannerOverlay extends CustomPainter { final cutoutPath = Path()..addRect(scanWindow); final backgroundPaint = Paint() - ..color = Colors.black.withAlpha(127) + ..color = Colors.black.withOpacity(0.5) ..style = PaintingStyle.fill ..blendMode = BlendMode.dstOut; @@ -169,7 +169,7 @@ class ScanInvitationDialogState extends State { fit: BoxFit.contain, scanWindow: scanWindow, controller: cameraController, - errorBuilder: (context, error) => + errorBuilder: (context, error, child) => ScannerErrorWidget(error: error), onDetect: (c) { final barcode = c.barcodes.firstOrNull; @@ -188,7 +188,7 @@ class ScanInvitationDialogState extends State { child: Container( alignment: Alignment.bottomCenter, height: 100, - color: Colors.black.withAlpha(127), + color: Colors.black.withOpacity(0.4), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ @@ -242,10 +242,6 @@ class ScanInvitationDialogState extends State { return const Icon(Icons.camera_front); case CameraFacing.back: return const Icon(Icons.camera_rear); - case CameraFacing.external: - return const Icon(Icons.camera_alt); - case CameraFacing.unknown: - return const Icon(Icons.question_mark); } }, ), diff --git a/lib/contact_invitation/views/views.dart b/lib/contact_invitation/views/views.dart index 241513d..726f0b9 100644 --- a/lib/contact_invitation/views/views.dart +++ b/lib/contact_invitation/views/views.dart @@ -3,5 +3,6 @@ export 'contact_invitation_item_widget.dart'; export 'contact_invitation_list_widget.dart'; export 'create_invitation_dialog.dart'; export 'invitation_dialog.dart'; +export 'new_contact_bottom_sheet.dart'; export 'paste_invitation_dialog.dart'; export 'scan_invitation_dialog.dart'; diff --git a/lib/contacts/contacts.dart b/lib/contacts/contacts.dart index 08ae2e7..6acdd43 100644 --- a/lib/contacts/contacts.dart +++ b/lib/contacts/contacts.dart @@ -1,3 +1,2 @@ export 'cubits/cubits.dart'; -export 'models/models.dart'; export 'views/views.dart'; diff --git a/lib/contacts/cubits/contact_list_cubit.dart b/lib/contacts/cubits/contact_list_cubit.dart index a0591ad..d3c6483 100644 --- a/lib/contacts/cubits/contact_list_cubit.dart +++ b/lib/contacts/cubits/contact_list_cubit.dart @@ -8,7 +8,6 @@ import 'package:veilid_support/veilid_support.dart'; import '../../account_manager/account_manager.dart'; import '../../proto/proto.dart' as proto; import '../../tools/tools.dart'; -import '../models/models.dart'; ////////////////////////////////////////////////// // Mutable state for per-account contacts @@ -82,7 +81,9 @@ class ContactListCubit extends DHTShortArrayCubit { Future updateContactFields({ required TypedKey localConversationRecordKey, - required ContactSpec updatedContactSpec, + String? nickname, + String? notes, + bool? showAvailability, }) async { // Update contact's locally-modifiable fields await operateWriteEventual((writer) async { @@ -91,7 +92,17 @@ class ContactListCubit extends DHTShortArrayCubit { if (c != null && c.localConversationRecordKey.toVeilid() == localConversationRecordKey) { - final newContact = await updatedContactSpec.updateProto(c); + final newContact = c.deepCopy(); + + if (nickname != null) { + newContact.nickname = nickname; + } + if (notes != null) { + newContact.notes = notes; + } + if (showAvailability != null) { + newContact.showAvailability = showAvailability; + } final updated = await writer.tryWriteItemProtobuf( proto.Contact.fromBuffer, pos, newContact); @@ -149,14 +160,10 @@ class ContactListCubit extends DHTShortArrayCubit { // Mark the conversation records for deletion await DHTRecordPool.instance .deleteRecord(deletedItem.localConversationRecordKey.toVeilid()); - } on Exception catch (e) { - log.debug('error deleting local conversation record: $e', e); - } - try { await DHTRecordPool.instance .deleteRecord(deletedItem.remoteConversationRecordKey.toVeilid()); } on Exception catch (e) { - log.debug('error deleting remote conversation record: $e', e); + log.debug('error deleting conversation records: $e', e); } } } diff --git a/lib/contacts/models/contact_spec.dart b/lib/contacts/models/contact_spec.dart deleted file mode 100644 index 1596434..0000000 --- a/lib/contacts/models/contact_spec.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:flutter/foundation.dart'; -import 'package:protobuf/protobuf.dart'; - -import '../../proto/proto.dart' as proto; - -@immutable -class ContactSpec extends Equatable { - const ContactSpec({ - required this.nickname, - required this.notes, - required this.showAvailability, - }); - - ContactSpec.fromProto(proto.Contact p) - : nickname = p.nickname, - notes = p.notes, - showAvailability = p.showAvailability; - - Future updateProto(proto.Contact old) async { - final newProto = old.deepCopy() - ..nickname = nickname - ..notes = notes - ..showAvailability = showAvailability; - - return newProto; - } - - //////////////////////////////////////////////////////////////////////////// - - final String nickname; - final String notes; - final bool showAvailability; - - @override - List get props => [nickname, notes, showAvailability]; -} diff --git a/lib/contacts/models/models.dart b/lib/contacts/models/models.dart deleted file mode 100644 index d489632..0000000 --- a/lib/contacts/models/models.dart +++ /dev/null @@ -1 +0,0 @@ -export 'contact_spec.dart'; diff --git a/lib/contacts/views/availability_widget.dart b/lib/contacts/views/availability_widget.dart index 75ef0a8..d50e323 100644 --- a/lib/contacts/views/availability_widget.dart +++ b/lib/contacts/views/availability_widget.dart @@ -1,39 +1,34 @@ import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_translate/flutter_translate.dart'; import '../../proto/proto.dart' as proto; -import '../../theme/theme.dart'; class AvailabilityWidget extends StatelessWidget { const AvailabilityWidget( {required this.availability, - required this.color, this.vertical = true, - this.size = 32, + this.iconSize = 32, super.key}); - static Widget availabilityIcon(proto.Availability availability, Color color, - {double size = 24}) { - late final Widget icon; + static Widget availabilityIcon(proto.Availability availability, + {double size = 32}) { + late final Widget iconData; switch (availability) { case proto.Availability.AVAILABILITY_AWAY: - icon = SvgPicture.asset('assets/images/toilet.svg', - width: size, - height: size, - colorFilter: ColorFilter.mode(color, BlendMode.srcATop)); + iconData = + ImageIcon(const AssetImage('assets/images/toilet.png'), size: size); case proto.Availability.AVAILABILITY_BUSY: - icon = Icon(Icons.event_busy, size: size); + iconData = Icon(Icons.event_busy, size: size); case proto.Availability.AVAILABILITY_FREE: - icon = Icon(Icons.event_available, size: size); + iconData = Icon(Icons.event_available, size: size); case proto.Availability.AVAILABILITY_OFFLINE: - icon = Icon(Icons.cloud_off, size: size); + iconData = Icon(Icons.cloud_off, size: size); case proto.Availability.AVAILABILITY_UNSPECIFIED: - icon = Icon(Icons.question_mark, size: size); + iconData = Icon(Icons.question_mark, size: size); } - return icon; + return iconData; } static String availabilityName(proto.Availability availability) { @@ -57,33 +52,31 @@ class AvailabilityWidget extends StatelessWidget { Widget build(BuildContext context) { final theme = Theme.of(context); final textTheme = theme.textTheme; + // final scale = theme.extension()!; + // final scaleConfig = theme.extension()!; final name = availabilityName(availability); - final icon = availabilityIcon(availability, color, size: size * 2 / 3); + final icon = availabilityIcon(availability, size: iconSize); return vertical - ? ConstrainedBox( - constraints: BoxConstraints.tightFor(width: size), - child: Column(mainAxisSize: MainAxisSize.min, children: [ - icon, - Text(name, style: textTheme.labelSmall!.copyWith(color: color)) - .fit(fit: BoxFit.scaleDown) - ])) - : ConstrainedBox( - constraints: BoxConstraints.tightFor(height: size), - child: Row(mainAxisSize: MainAxisSize.min, children: [ - icon, - Text(name, style: textTheme.labelLarge!.copyWith(color: color)) - .paddingLTRB(size / 4, 0, 0, 0) - ])); + ? Column( + mainAxisSize: MainAxisSize.min, + //mainAxisAlignment: MainAxisAlignment.center, + children: [ + icon, + Text(name, style: textTheme.labelSmall).paddingLTRB(0, 0, 0, 0) + ]) + : Row(mainAxisSize: MainAxisSize.min, children: [ + icon, + Text(name, style: textTheme.labelSmall).paddingLTRB(8, 0, 0, 0) + ]); } //////////////////////////////////////////////////////////////////////////// final proto.Availability availability; - final Color color; final bool vertical; - final double size; + final double iconSize; @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { @@ -92,7 +85,6 @@ class AvailabilityWidget extends StatelessWidget { ..add( DiagnosticsProperty('availability', availability)) ..add(DiagnosticsProperty('vertical', vertical)) - ..add(DoubleProperty('size', size)) - ..add(ColorProperty('color', color)); + ..add(DoubleProperty('iconSize', iconSize)); } } diff --git a/lib/contacts/views/contact_details_widget.dart b/lib/contacts/views/contact_details_widget.dart index 707dc1e..bd4376f 100644 --- a/lib/contacts/views/contact_details_widget.dart +++ b/lib/contacts/views/contact_details_widget.dart @@ -1,15 +1,13 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_translate/flutter_translate.dart'; import '../../proto/proto.dart' as proto; -import '../../tools/tools.dart'; import '../contacts.dart'; class ContactDetailsWidget extends StatefulWidget { - const ContactDetailsWidget( - {required this.contact, this.onModifiedState, super.key}); + const ContactDetailsWidget({required this.contact, super.key}); + final proto.Contact contact; @override State createState() => _ContactDetailsWidgetState(); @@ -17,35 +15,27 @@ class ContactDetailsWidget extends StatefulWidget { @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); - properties - ..add(DiagnosticsProperty('contact', contact)) - ..add(ObjectFlagProperty.has( - 'onModifiedState', onModifiedState)); + properties.add(DiagnosticsProperty('contact', contact)); } - - final proto.Contact contact; - final void Function(bool)? onModifiedState; } -class _ContactDetailsWidgetState extends State { +class _ContactDetailsWidgetState extends State + with SingleTickerProviderStateMixin { @override Widget build(BuildContext context) => SingleChildScrollView( child: EditContactForm( + formKey: GlobalKey(), contact: widget.contact, - submitText: translate('button.update'), - submitDisabledText: translate('button.waiting_for_network'), - onModifiedState: widget.onModifiedState, - onSubmit: (updatedContactSpec) async { + onSubmit: (fbs) async { final contactList = context.read(); - try { - await contactList.updateContactFields( - localConversationRecordKey: - widget.contact.localConversationRecordKey.toVeilid(), - updatedContactSpec: updatedContactSpec); - } on Exception catch (e) { - log.debug('error updating contact: $e', e); - return false; - } - return true; + await contactList.updateContactFields( + localConversationRecordKey: + widget.contact.localConversationRecordKey.toVeilid(), + nickname: fbs.currentState + ?.value[EditContactForm.formFieldNickname] as String, + notes: fbs.currentState?.value[EditContactForm.formFieldNotes] + as String, + showAvailability: fbs.currentState + ?.value[EditContactForm.formFieldShowAvailability] as bool); })); } diff --git a/lib/contacts/views/contact_item_widget.dart b/lib/contacts/views/contact_item_widget.dart index e206570..46b658a 100644 --- a/lib/contacts/views/contact_item_widget.dart +++ b/lib/contacts/views/contact_item_widget.dart @@ -5,6 +5,7 @@ import '../../proto/proto.dart' as proto; import '../../theme/theme.dart'; const _kOnTap = 'onTap'; +const _kOnDelete = 'onDelete'; class ContactItemWidget extends StatelessWidget { const ContactItemWidget( @@ -23,6 +24,7 @@ class ContactItemWidget extends StatelessWidget { _onDelete = onDelete; @override + // ignore: prefer_expression_function_bodies Widget build( BuildContext context, ) { @@ -39,7 +41,7 @@ class ContactItemWidget extends StatelessWidget { size: 34, borderColor: _disabled ? scale.grayScale.primaryText - : scale.primaryScale.subtleBorder, + : scale.primaryScale.primaryText, foregroundColor: _disabled ? scale.grayScale.primaryText : scale.primaryScale.primaryText, @@ -67,36 +69,14 @@ class ContactItemWidget extends StatelessWidget { : () => singleFuture((this, _kOnTap), () async { await _onTap(_contact); }), - startActions: [ - if (_onDoubleTap != null) - SliderTileAction( - //icon: Icons.edit, - label: translate('button.chat'), - actionScale: ScaleKind.secondary, - onPressed: (_context) => - singleFuture((this, _kOnTap), () async { - await _onDoubleTap(_contact); - }), - ), - ], endActions: [ - if (_onTap != null) - SliderTileAction( - //icon: Icons.edit, - label: translate('button.edit'), - actionScale: ScaleKind.secondary, - onPressed: (_context) => - singleFuture((this, _kOnTap), () async { - await _onTap(_contact); - }), - ), if (_onDelete != null) SliderTileAction( - //icon: Icons.delete, + icon: Icons.delete, label: translate('button.delete'), actionScale: ScaleKind.tertiary, onPressed: (_context) => - singleFuture((this, _kOnTap), () async { + singleFuture((this, _kOnDelete), () async { await _onDelete(_contact); }), ), diff --git a/lib/contacts/views/contacts_browser.dart b/lib/contacts/views/contacts_browser.dart index 84a2601..2eea880 100644 --- a/lib/contacts/views/contacts_browser.dart +++ b/lib/contacts/views/contacts_browser.dart @@ -8,6 +8,7 @@ import 'package:searchable_listview/searchable_listview.dart'; import 'package:star_menu/star_menu.dart'; import 'package:veilid_support/veilid_support.dart'; +import '../../chat_list/chat_list.dart'; import '../../contact_invitation/contact_invitation.dart'; import '../../proto/proto.dart' as proto; import '../../theme/theme.dart'; @@ -16,25 +17,19 @@ import 'contact_item_widget.dart'; import 'empty_contact_list_widget.dart'; enum ContactsBrowserElementKind { - contact, invitation, + contact, } class ContactsBrowserElement { - ContactsBrowserElement.contact(proto.Contact c) - : kind = ContactsBrowserElementKind.contact, - invitation = null, - contact = c; ContactsBrowserElement.invitation(proto.ContactInvitationRecord i) : kind = ContactsBrowserElementKind.invitation, contact = null, invitation = i; - - String get sortKey => switch (kind) { - ContactsBrowserElementKind.contact => contact!.displayName, - ContactsBrowserElementKind.invitation => - invitation!.recipient + invitation!.message - }; + ContactsBrowserElement.contact(proto.Contact c) + : kind = ContactsBrowserElementKind.contact, + invitation = null, + contact = c; final ContactsBrowserElementKind kind; final proto.ContactInvitationRecord? invitation; @@ -44,16 +39,14 @@ class ContactsBrowserElement { class ContactsBrowser extends StatefulWidget { const ContactsBrowser( {required this.onContactSelected, - required this.onContactDeleted, - required this.onStartChat, + required this.onChatStarted, this.selectedContactRecordKey, super.key}); @override State createState() => _ContactsBrowserState(); final Future Function(proto.Contact? contact) onContactSelected; - final Future Function(proto.Contact contact) onContactDeleted; - final Future Function(proto.Contact contact) onStartChat; + final Future Function(proto.Contact contact) onChatStarted; final TypedKey? selectedContactRecordKey; @override @@ -67,32 +60,36 @@ class ContactsBrowser extends StatefulWidget { 'onContactSelected', onContactSelected)) ..add( ObjectFlagProperty Function(proto.Contact contact)>.has( - 'onStartChat', onStartChat)) - ..add( - ObjectFlagProperty Function(proto.Contact contact)>.has( - 'onContactDeleted', onContactDeleted)); + 'onChatStarted', onChatStarted)); } } class _ContactsBrowserState extends State with SingleTickerProviderStateMixin { - Widget buildInvitationButton(BuildContext context) { + Widget buildInvitationBar(BuildContext context) { final theme = Theme.of(context); - final scaleScheme = theme.extension()!; + final textTheme = theme.textTheme; + final scale = theme.extension()!; final scaleConfig = theme.extension()!; final menuIconColor = scaleConfig.preferBorders - ? scaleScheme.primaryScale.hoverBorder - : scaleScheme.primaryScale.hoverBorder; + ? scale.primaryScale.hoverBorder + : scale.primaryScale.borderText; final menuBackgroundColor = scaleConfig.preferBorders - ? scaleScheme.primaryScale.activeElementBackground - : scaleScheme.primaryScale.activeElementBackground; + ? scale.primaryScale.elementBackground + : scale.primaryScale.border; + // final menuHoverColor = scaleConfig.preferBorders + // ? scale.primaryScale.hoverElementBackground + // : scale.primaryScale.hoverBorder; - final menuBorderColor = scaleScheme.primaryScale.hoverBorder; + final menuBorderColor = scale.primaryScale.hoverBorder; final menuParams = StarMenuParameters( - shape: MenuShape.linear, + shape: MenuShape.grid, + checkItemsScreenBoundaries: true, centerOffset: const Offset(0, 64), + backgroundParams: + BackgroundParams(backgroundColor: theme.shadowColor.withAlpha(128)), boundaryBackground: BoundaryBackground( color: menuBackgroundColor, decoration: ShapeDecoration( @@ -105,173 +102,225 @@ class _ContactsBrowserState extends State borderRadius: BorderRadius.circular( 8 * scaleConfig.borderRadiusScale))))); - ElevatedButton makeMenuButton( - {required IconData iconData, - required String text, - required void Function()? onPressed}) => - ElevatedButton.icon( - onPressed: onPressed, - icon: Icon( - iconData, - size: 32, - ).paddingSTEB(0, 8, 0, 8), - label: Text( - text, - maxLines: 2, - textAlign: TextAlign.center, - ).paddingSTEB(0, 8, 0, 8)); - - final inviteMenuItems = [ - makeMenuButton( - iconData: Icons.paste, - text: translate('add_contact_sheet.paste_invite'), + final receiveInviteMenuItems = [ + Column(mainAxisSize: MainAxisSize.min, children: [ + IconButton( onPressed: () async { - _invitationMenuController.closeMenu!(); - await PasteInvitationDialog.show(context); - }), - makeMenuButton( - iconData: Icons.qr_code_scanner, - text: translate('add_contact_sheet.scan_invite'), - onPressed: () async { - _invitationMenuController.closeMenu!(); + _receiveInviteMenuController.closeMenu!(); await ScanInvitationDialog.show(context); - }).paddingLTRB(0, 0, 0, 8), - makeMenuButton( - iconData: Icons.contact_page, - text: translate('add_contact_sheet.create_invite'), + }, + iconSize: 32, + icon: Icon( + Icons.qr_code_scanner, + size: 32, + color: menuIconColor, + ), + ), + Text(translate('add_contact_sheet.scan_invite'), + maxLines: 2, + textAlign: TextAlign.center, + style: textTheme.labelSmall!.copyWith(color: menuIconColor)) + ]).paddingAll(4), + Column(mainAxisSize: MainAxisSize.min, children: [ + IconButton( onPressed: () async { - _invitationMenuController.closeMenu!(); - await CreateInvitationDialog.show(context); - }).paddingLTRB(0, 0, 0, 8), + _receiveInviteMenuController.closeMenu!(); + await PasteInvitationDialog.show(context); + }, + iconSize: 32, + icon: Icon( + Icons.paste, + size: 32, + color: menuIconColor, + ), + ), + Text(translate('add_contact_sheet.paste_invite'), + maxLines: 2, + textAlign: TextAlign.center, + style: textTheme.labelSmall!.copyWith(color: menuIconColor)) + ]).paddingAll(4) ]; - return StarMenu( - items: inviteMenuItems, - onItemTapped: (_, controller) { - controller.closeMenu!(); - }, - controller: _invitationMenuController, - params: menuParams, - child: IconButton( - onPressed: () {}, - iconSize: 24, - icon: Icon(Icons.person_add, color: menuIconColor), - tooltip: translate('add_contact_sheet.add_contact')), - ); + return Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ + Column(mainAxisSize: MainAxisSize.min, children: [ + IconButton( + onPressed: () async { + await CreateInvitationDialog.show(context); + }, + iconSize: 32, + icon: const Icon(Icons.contact_page), + color: scale.primaryScale.hoverBorder, + ), + Text(translate('add_contact_sheet.create_invite'), + maxLines: 2, + textAlign: TextAlign.center, + style: textTheme.labelSmall! + .copyWith(color: scale.primaryScale.hoverBorder)) + ]), + StarMenu( + items: receiveInviteMenuItems, + onItemTapped: (_index, controller) { + controller.closeMenu!(); + }, + controller: _receiveInviteMenuController, + params: menuParams, + child: Column(mainAxisSize: MainAxisSize.min, children: [ + IconButton( + onPressed: () {}, + iconSize: 32, + icon: ImageIcon( + const AssetImage('assets/images/handshake.png'), + size: 32, + color: scale.primaryScale.hoverBorder, + )), + Text(translate('add_contact_sheet.receive_invite'), + maxLines: 2, + textAlign: TextAlign.center, + style: textTheme.labelSmall! + .copyWith(color: scale.primaryScale.hoverBorder)) + ]), + ), + ]).paddingAll(16); } @override Widget build(BuildContext context) { final theme = Theme.of(context); + final textTheme = theme.textTheme; final scale = theme.extension()!; + //final scaleConfig = theme.extension()!; final cilState = context.watch().state; + final cilBusy = cilState.busy; final contactInvitationRecordList = cilState.state.asData?.value.map((x) => x.value).toIList() ?? const IListConst([]); final ciState = context.watch().state; + final ciBusy = ciState.busy; final contactList = ciState.state.asData?.value.map((x) => x.value).toIList(); - final initialList = []; - if (contactList != null) { - initialList - .addAll(contactList.toList().map(ContactsBrowserElement.contact)); - } + final expansionListData = + >{}; if (contactInvitationRecordList.isNotEmpty) { - initialList.addAll(contactInvitationRecordList - .toList() - .map(ContactsBrowserElement.invitation)); + expansionListData[ContactsBrowserElementKind.invitation] = + contactInvitationRecordList + .toList() + .map(ContactsBrowserElement.invitation) + .toList(); + } + if (contactList != null) { + expansionListData[ContactsBrowserElementKind.contact] = + contactList.toList().map(ContactsBrowserElement.contact).toList(); } - - initialList.sort((a, b) => a.sortKey.compareTo(b.sortKey)); return Column(children: [ - SearchableList( - initialList: initialList, - itemBuilder: (element) { + buildInvitationBar(context), + SearchableList.expansion( + expansionListData: expansionListData, + expansionTitleBuilder: (k) { + final kind = k as ContactsBrowserElementKind; + late final String title; + switch (kind) { + case ContactsBrowserElementKind.contact: + title = translate('contacts_dialog.contacts'); + case ContactsBrowserElementKind.invitation: + title = translate('contacts_dialog.invitations'); + } + + return Center( + child: Text(title, style: textTheme.titleSmall), + ); + }, + expansionInitiallyExpanded: (k) => true, + expansionListBuilder: (_index, element) { + switch (element.kind) { + case ContactsBrowserElementKind.contact: + final contact = element.contact!; + return ContactItemWidget( + contact: contact, + selected: widget.selectedContactRecordKey == + contact.localConversationRecordKey.toVeilid(), + disabled: false, + onTap: _onTapContact, + onDoubleTap: _onStartChat, + onDelete: _onDeleteContact) + .paddingLTRB(0, 4, 0, 0); + case ContactsBrowserElementKind.invitation: + final invitation = element.invitation!; + return ContactInvitationItemWidget( + contactInvitationRecord: invitation, disabled: false) + .paddingLTRB(0, 4, 0, 0); + } + }, + filterExpansionData: (value) { + final lowerValue = value.toLowerCase(); + final filteredMap = { + for (final entry in expansionListData.entries) + entry.key: (expansionListData[entry.key] ?? []).where((element) { switch (element.kind) { case ContactsBrowserElementKind.contact: final contact = element.contact!; - return ContactItemWidget( - contact: contact, - selected: widget.selectedContactRecordKey == - contact.localConversationRecordKey.toVeilid(), - disabled: false, - onDoubleTap: _onStartChat, - onTap: onContactSelected, - onDelete: _onContactDeleted) - .paddingLTRB(0, 4, 0, 0); + return contact.nickname + .toLowerCase() + .contains(lowerValue) || + contact.profile.name + .toLowerCase() + .contains(lowerValue) || + contact.profile.pronouns + .toLowerCase() + .contains(lowerValue); case ContactsBrowserElementKind.invitation: final invitation = element.invitation!; - return ContactInvitationItemWidget( - contactInvitationRecord: invitation, - disabled: false) - .paddingLTRB(0, 4, 0, 0); + return invitation.message + .toLowerCase() + .contains(lowerValue); } - }, - filter: (value) { - final lowerValue = value.toLowerCase(); - - final filtered = []; - for (final element in initialList) { - switch (element.kind) { - case ContactsBrowserElementKind.contact: - final contact = element.contact!; - if (contact.nickname.toLowerCase().contains(lowerValue) || - contact.profile.name - .toLowerCase() - .contains(lowerValue) || - contact.profile.pronouns - .toLowerCase() - .contains(lowerValue)) { - filtered.add(element); - } - case ContactsBrowserElementKind.invitation: - final invitation = element.invitation!; - if (invitation.message - .toLowerCase() - .contains(lowerValue) || - invitation.recipient - .toLowerCase() - .contains(lowerValue)) { - filtered.add(element); - } - } - } - return filtered; - }, - searchFieldHeight: 40, - listViewPadding: const EdgeInsets.fromLTRB(4, 0, 4, 4), - searchFieldPadding: const EdgeInsets.fromLTRB(4, 8, 4, 4), - emptyWidget: contactList == null - ? waitingPage( - text: translate('contact_list.loading_contacts')) - : const EmptyContactListWidget(), - defaultSuffixIconColor: scale.primaryScale.border, - closeKeyboardWhenScrolling: true, - searchFieldEnabled: contactList != null, - inputDecoration: - InputDecoration(labelText: translate('contact_list.search')), - secondaryWidget: - buildInvitationButton(context).paddingLTRB(4, 0, 0, 0)) - .expanded() + }).toList() + }; + return filteredMap; + }, + hideEmptyExpansionItems: true, + searchFieldHeight: 40, + listViewPadding: const EdgeInsets.all(4), + spaceBetweenSearchAndList: 4, + emptyWidget: contactList == null + ? waitingPage(text: translate('contact_list.loading_contacts')) + : const EmptyContactListWidget(), + defaultSuffixIconColor: scale.primaryScale.border, + closeKeyboardWhenScrolling: true, + searchFieldEnabled: contactList != null, + inputDecoration: + InputDecoration(labelText: translate('contact_list.search')), + ).expanded() ]); } - Future onContactSelected(proto.Contact contact) async { + Future _onTapContact(proto.Contact contact) async { await widget.onContactSelected(contact); } Future _onStartChat(proto.Contact contact) async { - await widget.onStartChat(contact); + await widget.onChatStarted(contact); } - Future _onContactDeleted(proto.Contact contact) async { - await widget.onContactDeleted(contact); + Future _onDeleteContact(proto.Contact contact) async { + final localConversationRecordKey = + contact.localConversationRecordKey.toVeilid(); + + final contactListCubit = context.read(); + final chatListCubit = context.read(); + + // Delete the contact itself + await contactListCubit.deleteContact( + localConversationRecordKey: localConversationRecordKey); + + // Remove any chats for this contact + await chatListCubit.deleteChat( + localConversationRecordKey: localConversationRecordKey); } //////////////////////////////////////////////////////////////////////////// - final _invitationMenuController = StarMenuController(); + final _receiveInviteMenuController = StarMenuController(); } diff --git a/lib/contacts/views/contacts_dialog.dart b/lib/contacts/views/contacts_dialog.dart new file mode 100644 index 0000000..f994148 --- /dev/null +++ b/lib/contacts/views/contacts_dialog.dart @@ -0,0 +1,136 @@ +import 'package:awesome_extensions/awesome_extensions.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_translate/flutter_translate.dart'; +import 'package:provider/provider.dart'; + +import '../../chat/chat.dart'; +import '../../chat_list/chat_list.dart'; +import '../../layout/layout.dart'; +import '../../proto/proto.dart' as proto; +import '../../theme/theme.dart'; +import '../contacts.dart'; + +class ContactsDialog extends StatefulWidget { + const ContactsDialog._({required this.modalContext}); + + @override + State createState() => _ContactsDialogState(); + + static Future show(BuildContext modalContext) async { + await showDialog( + context: modalContext, + barrierDismissible: false, + useRootNavigator: false, + builder: (context) => ContactsDialog._(modalContext: modalContext)); + } + + final BuildContext modalContext; + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + .add(DiagnosticsProperty('modalContext', modalContext)); + } +} + +class _ContactsDialogState extends State { + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + // final textTheme = theme.textTheme; + final scale = theme.extension()!; + // final scaleConfig = theme.extension()!; + + final enableSplit = !isMobileWidth(context); + final enableLeft = enableSplit || _selectedContact == null; + final enableRight = enableSplit || _selectedContact != null; + + return SizedBox( + width: MediaQuery.of(context).size.width, + child: StyledScaffold( + appBar: DefaultAppBar( + title: Text(!enableSplit && enableRight + ? translate('contacts_dialog.edit_contact') + : translate('contacts_dialog.contacts')), + leading: Navigator.canPop(context) + ? IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: () { + if (!enableSplit && enableRight) { + setState(() { + _selectedContact = null; + }); + } else { + Navigator.pop(context); + } + }, + ) + : null, + actions: [ + if (_selectedContact != null) + IconButton( + icon: const Icon(Icons.chat_bubble), + tooltip: translate('contacts_dialog.new_chat'), + onPressed: () async { + await onChatStarted(_selectedContact!); + }) + ]), + body: LayoutBuilder(builder: (context, constraint) { + final maxWidth = constraint.maxWidth; + + return Row(children: [ + Offstage( + offstage: !enableLeft, + child: SizedBox( + width: enableLeft && !enableRight + ? maxWidth + : (maxWidth / 3).clamp(200, 500), + child: DecoratedBox( + decoration: BoxDecoration( + color: scale.primaryScale.subtleBackground), + child: ContactsBrowser( + selectedContactRecordKey: _selectedContact + ?.localConversationRecordKey + .toVeilid(), + onContactSelected: onContactSelected, + onChatStarted: onChatStarted, + ).paddingLTRB(8, 0, 8, 8)))), + if (enableRight) + if (_selectedContact == null) + const NoContactWidget().expanded() + else + ContactDetailsWidget(contact: _selectedContact!) + .paddingAll(8) + .expanded(), + ]); + }))); + } + + Future onContactSelected(proto.Contact? contact) async { + setState(() { + _selectedContact = contact; + }); + } + + Future onChatStarted(proto.Contact contact) async { + final chatListCubit = context.read(); + await chatListCubit.getOrCreateChatSingleContact(contact: contact); + + if (mounted) { + context + .read() + .setActiveChat(contact.localConversationRecordKey.toVeilid()); + + Navigator.pop(context); + } + } + + proto.Contact? _selectedContact; +} diff --git a/lib/contacts/views/contacts_page.dart b/lib/contacts/views/contacts_page.dart deleted file mode 100644 index 26a6f0d..0000000 --- a/lib/contacts/views/contacts_page.dart +++ /dev/null @@ -1,197 +0,0 @@ -import 'package:async_tools/async_tools.dart'; -import 'package:awesome_extensions/awesome_extensions.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_translate/flutter_translate.dart'; -import 'package:provider/provider.dart'; - -import '../../chat/chat.dart'; -import '../../chat_list/chat_list.dart'; -import '../../layout/layout.dart'; -import '../../proto/proto.dart' as proto; -import '../../theme/theme.dart'; -import '../contacts.dart'; - -const _kDoBackArrow = 'doBackArrow'; - -class ContactsPage extends StatefulWidget { - const ContactsPage({super.key}); - - @override - State createState() => _ContactsPageState(); -} - -class _ContactsPageState extends State { - @override - void initState() { - super.initState(); - } - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - final scaleTheme = theme.extension()!; - final appBarTheme = scaleTheme.appBarTheme(); - final scaleScheme = theme.extension()!; - final scale = scaleScheme.scale(ScaleKind.primary); - - final enableSplit = !isMobileSize(context); - final enableLeft = enableSplit || _selectedContact == null; - final enableRight = enableSplit || _selectedContact != null; - - return StyledScaffold( - appBar: DefaultAppBar( - title: Text( - !enableSplit && enableRight - ? translate('contacts_dialog.edit_contact') - : translate('contacts_dialog.contacts'), - ), - leading: IconButton( - icon: const Icon(Icons.arrow_back), - onPressed: () { - singleFuture((this, _kDoBackArrow), () async { - final confirmed = await _onContactSelected(null); - if (!enableSplit && enableRight) { - } else { - if (confirmed) { - if (context.mounted) { - Navigator.pop(context); - } - } - } - }); - }, - ), - actions: [ - if (_selectedContact != null) - IconButton( - icon: const Icon(Icons.chat_bubble), - iconSize: 24, - color: appBarTheme.iconColor, - tooltip: translate('contacts_dialog.new_chat'), - onPressed: () async { - await _onChatStarted(_selectedContact!); - }).paddingLTRB(8, 0, 8, 0), - if (enableSplit && _selectedContact != null) - IconButton( - icon: const Icon(Icons.close), - iconSize: 24, - color: appBarTheme.iconColor, - tooltip: translate('contacts_dialog.close_contact'), - onPressed: () async { - await _onContactSelected(null); - }).paddingLTRB(8, 0, 8, 0), - ]), - body: LayoutBuilder(builder: (context, constraint) { - final maxWidth = constraint.maxWidth; - - return ColoredBox( - color: scale.appBackground, - child: - Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Offstage( - offstage: !enableLeft, - child: SizedBox( - width: enableLeft && !enableRight - ? maxWidth - : (maxWidth / 3).clamp(200, 500), - child: DecoratedBox( - decoration: - BoxDecoration(color: scale.subtleBackground), - child: ContactsBrowser( - selectedContactRecordKey: _selectedContact - ?.localConversationRecordKey - .toVeilid(), - onContactSelected: _onContactSelected, - onStartChat: _onChatStarted, - onContactDeleted: _onContactDeleted, - ).paddingLTRB(4, 0, 4, 8)))), - if (enableRight && enableLeft) - Container( - constraints: - const BoxConstraints(minWidth: 1, maxWidth: 1), - color: scale.subtleBorder), - if (enableRight) - if (_selectedContact == null) - const NoContactWidget().expanded() - else - ContactDetailsWidget( - contact: _selectedContact!, - onModifiedState: _onModifiedState) - .paddingLTRB(16, 16, 16, 16) - .expanded(), - ])); - })); - } - - void _onModifiedState(bool isModified) { - setState(() { - _isModified = isModified; - }); - } - - Future _onContactSelected(proto.Contact? contact) async { - if (contact != _selectedContact && _isModified) { - final ok = await showConfirmModal( - context: context, - title: translate('confirmation.discard_changes'), - text: translate('confirmation.are_you_sure_discard')); - if (!ok) { - return false; - } - } - setState(() { - _selectedContact = contact; - _isModified = false; - }); - return true; - } - - Future _onChatStarted(proto.Contact contact) async { - final chatListCubit = context.read(); - await chatListCubit.getOrCreateChatSingleContact(contact: contact); - - if (mounted) { - context - .read() - .setActiveChat(contact.localConversationRecordKey.toVeilid()); - Navigator.pop(context); - } - } - - Future _onContactDeleted(proto.Contact contact) async { - if (contact == _selectedContact && _isModified) { - final ok = await showConfirmModal( - context: context, - title: translate('confirmation.discard_changes'), - text: translate('confirmation.are_you_sure_discard')); - if (!ok) { - return false; - } - } - setState(() { - _selectedContact = null; - _isModified = false; - }); - - if (mounted) { - final localConversationRecordKey = - contact.localConversationRecordKey.toVeilid(); - - final contactListCubit = context.read(); - final chatListCubit = context.read(); - - // Delete the contact itself - await contactListCubit.deleteContact( - localConversationRecordKey: localConversationRecordKey); - - // Remove any chats for this contact - await chatListCubit.deleteChat( - localConversationRecordKey: localConversationRecordKey); - } - - return true; - } - - proto.Contact? _selectedContact; - var _isModified = false; -} diff --git a/lib/contacts/views/edit_contact_form.dart b/lib/contacts/views/edit_contact_form.dart index 514f019..0e8acc1 100644 --- a/lib/contacts/views/edit_contact_form.dart +++ b/lib/contacts/views/edit_contact_form.dart @@ -1,4 +1,3 @@ -import 'package:async_tools/async_tools.dart'; import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -7,18 +6,13 @@ import 'package:flutter_translate/flutter_translate.dart'; import '../../proto/proto.dart' as proto; import '../../theme/theme.dart'; -import '../models/contact_spec.dart'; import 'availability_widget.dart'; -const _kDoSubmitEditContact = 'doSubmitEditContact'; - class EditContactForm extends StatefulWidget { const EditContactForm({ + required this.formKey, required this.contact, - required this.onSubmit, - required this.submitText, - required this.submitDisabledText, - this.onModifiedState, + this.onSubmit, super.key, }); @@ -26,22 +20,19 @@ class EditContactForm extends StatefulWidget { State createState() => _EditContactFormState(); final proto.Contact contact; - final String submitText; - final String submitDisabledText; - final Future Function(ContactSpec) onSubmit; - final void Function(bool)? onModifiedState; + final Future Function(GlobalKey)? onSubmit; + final GlobalKey formKey; @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(ObjectFlagProperty Function(ContactSpec p1)>.has( - 'onSubmit', onSubmit)) - ..add(ObjectFlagProperty.has( - 'onModifiedState', onModifiedState)) + ..add(ObjectFlagProperty< + Future Function( + GlobalKey p1)?>.has('onSubmit', onSubmit)) ..add(DiagnosticsProperty('contact', contact)) - ..add(StringProperty('submitText', submitText)) - ..add(StringProperty('submitDisabledText', submitDisabledText)); + ..add( + DiagnosticsProperty>('formKey', formKey)); } static const String formFieldNickname = 'nickname'; @@ -50,46 +41,17 @@ class EditContactForm extends StatefulWidget { } class _EditContactFormState extends State { - final _formKey = GlobalKey(); - @override void initState() { - _savedValue = ContactSpec.fromProto(widget.contact); - _currentValueNickname = _savedValue.nickname; - super.initState(); } - ContactSpec _makeContactSpec() { - final nickname = _formKey.currentState! - .fields[EditContactForm.formFieldNickname]!.value as String; - final notes = _formKey - .currentState!.fields[EditContactForm.formFieldNotes]!.value as String; - final showAvailability = _formKey.currentState! - .fields[EditContactForm.formFieldShowAvailability]!.value as bool; + Widget _availabilityWidget( + BuildContext context, proto.Availability availability) => + AvailabilityWidget(availability: availability); - return ContactSpec( - nickname: nickname, notes: notes, showAvailability: showAvailability); - } - - // Check if everything is the same and update state - void _onChanged() { - final currentValue = _makeContactSpec(); - _isModified = currentValue != _savedValue; - final onModifiedState = widget.onModifiedState; - if (onModifiedState != null) { - onModifiedState(_isModified); - } - } - - Widget _availabilityWidget(proto.Availability availability, Color color) => - AvailabilityWidget( - availability: availability, - color: color, - vertical: false, - ); - - Widget _editContactForm(BuildContext context) { + @override + Widget build(BuildContext context) { final theme = Theme.of(context); final scale = theme.extension()!; final scaleConfig = theme.extension()!; @@ -99,94 +61,73 @@ class _EditContactFormState extends State { if (scaleConfig.useVisualIndicators && !scaleConfig.preferBorders) { border = scale.primaryScale.elementBackground; } else { - border = scale.primaryScale.subtleBorder; + border = scale.primaryScale.border; } return FormBuilder( - key: _formKey, - autovalidateMode: AutovalidateMode.onUserInteraction, - onChanged: _onChanged, + key: widget.formKey, child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - styledCard( - context: context, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Row(children: [ - const Spacer(), - AvatarWidget( - name: _currentValueNickname.isNotEmpty - ? _currentValueNickname - : widget.contact.profile.name, - size: 128, - borderColor: border, - foregroundColor: scale.primaryScale.primaryText, - backgroundColor: scale.primaryScale.primary, - scaleConfig: scaleConfig, - textStyle: theme.textTheme.titleLarge! - .copyWith(fontSize: 64), - ).paddingLTRB(0, 0, 0, 16), - const Spacer() - ]), - SelectableText(widget.contact.profile.name, - style: textTheme.bodyLarge) - .noEditDecoratorLabel( - context, - translate('contact_form.form_name'), - ) - .paddingSymmetric(vertical: 4), - SelectableText(widget.contact.profile.pronouns, - style: textTheme.bodyLarge) - .noEditDecoratorLabel( - context, - translate('contact_form.form_pronouns'), - ) - .paddingSymmetric(vertical: 4), - Row(mainAxisSize: MainAxisSize.min, children: [ - _availabilityWidget( - widget.contact.profile.availability, - scale.primaryScale.appText), - SelectableText(widget.contact.profile.status, - style: textTheme.bodyMedium) - .paddingSymmetric(horizontal: 8) - ]) - .noEditDecoratorLabel( - context, - translate('contact_form.form_status'), - ) - .paddingSymmetric(vertical: 4), - SelectableText(widget.contact.profile.about, - minLines: 1, - maxLines: 8, - style: textTheme.bodyMedium) - .noEditDecoratorLabel( - context, - translate('contact_form.form_about'), - ) - .paddingSymmetric(vertical: 4), - SelectableText( - widget.contact.identityPublicKey.value - .toVeilid() - .toString(), - style: textTheme.bodyMedium! - .copyWith(fontFamily: 'Source Code Pro')) - .noEditDecoratorLabel( - context, - translate('contact_form.form_fingerprint'), - ) - .paddingSymmetric(vertical: 4), - ]).paddingAll(16)) - .paddingLTRB(0, 0, 0, 16), + AvatarWidget( + name: widget.contact.profile.name, + size: 128, + borderColor: border, + foregroundColor: scale.primaryScale.primaryText, + backgroundColor: scale.primaryScale.primary, + scaleConfig: scaleConfig, + textStyle: theme.textTheme.titleLarge!.copyWith(fontSize: 64), + ).paddingLTRB(0, 0, 0, 16), + SelectableText(widget.contact.profile.name, + style: textTheme.headlineMedium) + .decoratorLabel( + context, + translate('contact_form.form_name'), + scale: scale.secondaryScale, + ) + .paddingSymmetric(vertical: 8), + SelectableText(widget.contact.profile.pronouns, + style: textTheme.headlineSmall) + .decoratorLabel( + context, + translate('contact_form.form_pronouns'), + scale: scale.secondaryScale, + ) + .paddingSymmetric(vertical: 8), + Row(children: [ + _availabilityWidget(context, widget.contact.profile.availability), + SelectableText(widget.contact.profile.status, + style: textTheme.bodyMedium) + .paddingSymmetric(horizontal: 8) + ]) + .decoratorLabel( + context, + translate('contact_form.form_status'), + scale: scale.secondaryScale, + ) + .paddingSymmetric(vertical: 8), + SelectableText(widget.contact.profile.about, + minLines: 1, maxLines: 8, style: textTheme.bodyMedium) + .decoratorLabel( + context, + translate('contact_form.form_about'), + scale: scale.secondaryScale, + ) + .paddingSymmetric(vertical: 8), + SelectableText( + widget.contact.identityPublicKey.value.toVeilid().toString(), + style: textTheme.labelMedium! + .copyWith(fontFamily: 'Source Code Pro')) + .decoratorLabel( + context, + translate('contact_form.form_fingerprint'), + scale: scale.secondaryScale, + ) + .paddingSymmetric(vertical: 8), + Divider(color: border).paddingLTRB(8, 0, 8, 8), FormBuilderTextField( + autofocus: true, name: EditContactForm.formFieldNickname, - initialValue: _currentValueNickname, - onChanged: (x) { - setState(() { - _currentValueNickname = x ?? ''; - }); - }, + initialValue: widget.contact.nickname, decoration: InputDecoration( labelText: translate('contact_form.form_nickname')), maxLength: 64, @@ -194,13 +135,14 @@ class _EditContactFormState extends State { ), FormBuilderCheckbox( name: EditContactForm.formFieldShowAvailability, - initialValue: _savedValue.showAvailability, + initialValue: widget.contact.showAvailability, + side: BorderSide(color: scale.primaryScale.border, width: 2), title: Text(translate('contact_form.form_show_availability'), style: textTheme.labelMedium), ), FormBuilderTextField( name: EditContactForm.formFieldNotes, - initialValue: _savedValue.notes, + initialValue: widget.contact.notes, minLines: 1, maxLines: 8, maxLength: 1024, @@ -209,38 +151,24 @@ class _EditContactFormState extends State { textInputAction: TextInputAction.newline, ), ElevatedButton( - onPressed: _isModified ? _doSubmit : null, + onPressed: widget.onSubmit == null + ? null + : () async { + if (widget.formKey.currentState?.saveAndValidate() ?? + false) { + await widget.onSubmit!(widget.formKey); + } + }, child: Row(mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.check, size: 16).paddingLTRB(0, 0, 4, 0), - Text(widget.submitText).paddingLTRB(0, 0, 4, 0) + Text((widget.onSubmit == null) + ? translate('contact_form.save') + : translate('contact_form.save')) + .paddingLTRB(0, 0, 4, 0) ]), - ).paddingSymmetric(vertical: 4).alignAtCenter(), + ).paddingSymmetric(vertical: 4).alignAtCenterRight(), ], ), ); } - - void _doSubmit() { - final onSubmit = widget.onSubmit; - if (_formKey.currentState?.saveAndValidate() ?? false) { - singleFuture((this, _kDoSubmitEditContact), () async { - final updatedContactSpec = _makeContactSpec(); - final saved = await onSubmit(updatedContactSpec); - if (saved) { - setState(() { - _savedValue = updatedContactSpec; - }); - _onChanged(); - } - }); - } - } - - @override - Widget build(BuildContext context) => _editContactForm(context); - - /////////////////////////////////////////////////////////////////////////// - late ContactSpec _savedValue; - late String _currentValueNickname; - bool _isModified = false; } diff --git a/lib/contacts/views/empty_contact_list_widget.dart b/lib/contacts/views/empty_contact_list_widget.dart index e6912fd..ae8a852 100644 --- a/lib/contacts/views/empty_contact_list_widget.dart +++ b/lib/contacts/views/empty_contact_list_widget.dart @@ -20,15 +20,14 @@ class EmptyContactListWidget extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisAlignment: MainAxisAlignment.center, children: [ - // Icon( - // Icons.person_add_sharp, - // color: scale.primaryScale.subtleBorder, - // size: 48, - // ), + Icon( + Icons.person_add_sharp, + color: scale.primaryScale.subtleBorder, + size: 48, + ), Text( textAlign: TextAlign.center, translate('contact_list.invite_people'), - //maxLines: 3, style: textTheme.bodyMedium?.copyWith( color: scale.primaryScale.subtleBorder, ), diff --git a/lib/contacts/views/no_contact_widget.dart b/lib/contacts/views/no_contact_widget.dart index 8edb5d5..c559d8b 100644 --- a/lib/contacts/views/no_contact_widget.dart +++ b/lib/contacts/views/no_contact_widget.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_translate/flutter_translate.dart'; -import '../../theme/models/scale_theme/scale_scheme.dart'; +import '../../theme/models/scale_scheme.dart'; class NoContactWidget extends StatelessWidget { const NoContactWidget({super.key}); diff --git a/lib/contacts/views/views.dart b/lib/contacts/views/views.dart index b74aff3..55b96d0 100644 --- a/lib/contacts/views/views.dart +++ b/lib/contacts/views/views.dart @@ -2,7 +2,7 @@ export 'availability_widget.dart'; export 'contact_details_widget.dart'; export 'contact_item_widget.dart'; export 'contacts_browser.dart'; -export 'contacts_page.dart'; +export 'contacts_dialog.dart'; export 'edit_contact_form.dart'; export 'empty_contact_list_widget.dart'; export 'no_contact_widget.dart'; diff --git a/lib/conversation/cubits/active_conversations_bloc_map_cubit.dart b/lib/conversation/cubits/active_conversations_bloc_map_cubit.dart index 3c00eba..b983265 100644 --- a/lib/conversation/cubits/active_conversations_bloc_map_cubit.dart +++ b/lib/conversation/cubits/active_conversations_bloc_map_cubit.dart @@ -24,7 +24,7 @@ class ActiveConversationState extends Equatable { final TypedKey localConversationRecordKey; final TypedKey remoteConversationRecordKey; final proto.Conversation localConversation; - final proto.Conversation? remoteConversation; + final proto.Conversation remoteConversation; @override List get props => [ @@ -50,7 +50,7 @@ typedef ActiveConversationsBlocMapState // We currently only build the cubits for the chats that are active, not // archived chats or contacts that are not actively in a chat. // -// TODO(crioux): Polling contacts for new inactive chats is yet to be done +// TODO: Polling contacts for new inactive chats is yet to be done // class ActiveConversationsBlocMapCubit extends BlocMapCubit, ActiveConversationCubit> @@ -74,11 +74,11 @@ class ActiveConversationsBlocMapCubit extends BlocMapCubit _addDirectConversation( {required TypedKey remoteIdentityPublicKey, required TypedKey localConversationRecordKey, - required TypedKey remoteConversationRecordKey}) => - add(localConversationRecordKey, () { + required TypedKey remoteConversationRecordKey}) async => + add(() { // Conversation cubit the tracks the state between the local // and remote halves of a contact's relationship with this account final conversationCubit = ConversationCubit( @@ -102,8 +102,7 @@ class ActiveConversationsBlocMapCubit extends BlocMapCubit, ConversationCubit>(conversationCubit, transform: (avstate) => avstate.when( - data: (data) => (data.localConversation == null) + data: (data) => (data.localConversation == null || + data.remoteConversation == null) ? const AsyncValue.loading() : AsyncValue.data(ActiveConversationState( localConversation: data.localConversation!, - remoteConversation: data.remoteConversation, + remoteConversation: data.remoteConversation!, remoteIdentityPublicKey: remoteIdentityPublicKey, localConversationRecordKey: localConversationRecordKey, remoteConversationRecordKey: @@ -123,16 +123,17 @@ class ActiveConversationsBlocMapCubit extends BlocMapCubit remove(key); + Future removeFromState(TypedKey key) => remove(key); @override - void updateState(TypedKey key, proto.Chat? oldValue, proto.Chat newValue) { + Future updateState( + TypedKey key, proto.Chat? oldValue, proto.Chat newValue) async { switch (newValue.whichKind()) { case proto.Chat_Kind.notSet: throw StateError('unknown chat kind'); @@ -160,11 +161,12 @@ class ActiveConversationsBlocMapCubit extends BlocMapCubit get props => [ @@ -51,19 +53,17 @@ class ActiveSingleContactChatBlocMapCubit extends BlocMapCubit - cubit.updateRemoteMessagesRecordKey(state.remoteMessagesRecordKey), - onCreate: () => SingleContactMessagesCubit( - accountInfo: _accountInfo, - remoteIdentityPublicKey: state.remoteIdentityPublicKey, - localConversationRecordKey: state.localConversationRecordKey, - remoteConversationRecordKey: state.remoteConversationRecordKey, - localMessagesRecordKey: state.localMessagesRecordKey, - remoteMessagesRecordKey: state.remoteMessagesRecordKey, - )); - } + Future _addConversationMessages(_SingleContactChatState state) async => + add(() => MapEntry( + state.localConversationRecordKey, + SingleContactMessagesCubit( + accountInfo: _accountInfo, + remoteIdentityPublicKey: state.remoteIdentityPublicKey, + localConversationRecordKey: state.localConversationRecordKey, + remoteConversationRecordKey: state.remoteConversationRecordKey, + localMessagesRecordKey: state.localMessagesRecordKey, + remoteMessagesRecordKey: state.remoteMessagesRecordKey, + ))); _SingleContactChatState? _mapStateValue( AsyncValue avInputState) { @@ -78,17 +78,19 @@ class ActiveSingleContactChatBlocMapCubit extends BlocMapCubit remove(key); + Future removeFromState(TypedKey key) => remove(key); @override - void updateState(TypedKey key, AsyncValue? oldValue, - AsyncValue newValue) { + Future updateState( + TypedKey key, + AsyncValue? oldValue, + AsyncValue newValue) async { final newState = _mapStateValue(newValue); if (oldValue != null) { final oldState = _mapStateValue(oldValue); @@ -97,14 +99,13 @@ class ActiveSingleContactChatBlocMapCubit extends BlocMapCubit get props => [localConversation, remoteConversation]; - - @override - String toString() => 'ConversationState(' - 'localConversation: ${DynamicDebug.toDebug(localConversation)}, ' - 'remoteConversation: ${DynamicDebug.toDebug(remoteConversation)}' - ')'; } /// Represents the control channel between two contacts @@ -45,11 +39,13 @@ class ConversationCubit extends Cubit> { TypedKey? localConversationRecordKey, TypedKey? remoteConversationRecordKey}) : _accountInfo = accountInfo, + _localConversationRecordKey = localConversationRecordKey, _remoteIdentityPublicKey = remoteIdentityPublicKey, + _remoteConversationRecordKey = remoteConversationRecordKey, super(const AsyncValue.loading()) { _identityWriter = _accountInfo.identityWriter; - if (localConversationRecordKey != null) { + if (_localConversationRecordKey != null) { _initWait.add((_) async { await _setLocalConversation(() async { // Open local record key if it is specified @@ -58,7 +54,7 @@ class ConversationCubit extends Cubit> { final writer = _identityWriter; final record = await pool.openRecordWrite( - localConversationRecordKey, writer, + _localConversationRecordKey!, writer, debugName: 'ConversationCubit::LocalConversation', parent: accountInfo.accountRecordKey, crypto: crypto); @@ -68,18 +64,17 @@ class ConversationCubit extends Cubit> { }); } - if (remoteConversationRecordKey != null) { + if (_remoteConversationRecordKey != null) { _initWait.add((cancel) async { await _setRemoteConversation(() async { // Open remote record key if it is specified final pool = DHTRecordPool.instance; final crypto = await _cachedConversationCrypto(); - final record = await pool.openRecordRead(remoteConversationRecordKey, + final record = await pool.openRecordRead(_remoteConversationRecordKey, debugName: 'ConversationCubit::RemoteConversation', - parent: - await pool.getParentRecordKey(remoteConversationRecordKey) ?? - accountInfo.accountRecordKey, + parent: pool.getParentRecordKey(_remoteConversationRecordKey) ?? + accountInfo.accountRecordKey, crypto: crypto); return record; @@ -108,11 +103,13 @@ class ConversationCubit extends Cubit> { /// incomplete 'existingConversationRecord' that we need to fill /// in now that we have the remote identity key /// The ConversationCubit must not already have a local conversation - /// Returns the local conversation record key that was initialized - Future initLocalConversation( + /// The callback allows for more initialization to occur and for + /// cleanup to delete records upon failure of the callback + Future initLocalConversation( {required proto.Profile profile, + required FutureOr Function(DHTRecord) callback, TypedKey? existingConversationRecordKey}) async { - assert(_localConversationCubit == null, + assert(_localConversationRecordKey == null, 'must not have a local conversation yet'); final pool = DHTRecordPool.instance; @@ -140,8 +137,11 @@ class ConversationCubit extends Cubit> { schema: DHTSchema.smpl( oCnt: 0, members: [DHTSchemaMember(mKey: writer.key, mCnt: 1)])); } - await localConversationRecord.deleteScope((localConversation) async { - await _initLocalMessages( + final out = localConversationRecord + // ignore: prefer_expression_function_bodies + .deleteScope((localConversation) async { + // Make messages log + return _initLocalMessages( localConversationKey: localConversation.key, callback: (messages) async { // Create initial local conversation key contents @@ -157,31 +157,36 @@ class ConversationCubit extends Cubit> { if (update != null) { throw Exception('Failed to write local conversation'); } + final out = await callback(localConversation); - // If success, save the new local conversation - // record key in this object - localConversation.ref(); - await _setLocalConversation(() async => localConversation); + // Upon success emit the local conversation record to the state + _updateLocalConversationState(AsyncValue.data(conversation)); + + return out; }); }); - return localConversationRecord.key; + // If success, save the new local conversation record key in this object + _localConversationRecordKey = localConversationRecord.key; + await _setLocalConversation(() async => localConversationRecord); + + return out; } /// Force refresh of conversation keys - // Future refresh() async { - // await _initWait(); + Future refresh() async { + await _initWait(); - // final lcc = _localConversationCubit; - // final rcc = _remoteConversationCubit; + final lcc = _localConversationCubit; + final rcc = _remoteConversationCubit; - // if (lcc != null) { - // await lcc.refreshDefault(); - // } - // if (rcc != null) { - // await rcc.refreshDefault(); - // } - // } + if (lcc != null) { + await lcc.refreshDefault(); + } + if (rcc != null) { + await rcc.refreshDefault(); + } + } /// Watch for account record changes and update the conversation void watchAccountChanges(Stream> accountStream, @@ -204,7 +209,7 @@ class ConversationCubit extends Cubit> { return; } serialFuture((this, _sfUpdateAccountChange), () async { - await cubit.record?.eventualUpdateProtobuf(proto.Conversation.fromBuffer, + await cubit.record.eventualUpdateProtobuf(proto.Conversation.fromBuffer, (old) async { if (old == null || old.profile == account.profile) { return null; @@ -220,6 +225,14 @@ class ConversationCubit extends Cubit> { _incrementalState = ConversationState( localConversation: conv, remoteConversation: _incrementalState.remoteConversation); + // return loading still if state isn't complete + if ((_localConversationRecordKey != null && + _incrementalState.localConversation == null) || + (_remoteConversationRecordKey != null && + _incrementalState.remoteConversation == null)) { + return const AsyncValue.loading(); + } + // state is complete, all required keys are open return AsyncValue.data(_incrementalState); }, loading: AsyncValue.loading, @@ -234,6 +247,14 @@ class ConversationCubit extends Cubit> { _incrementalState = ConversationState( localConversation: _incrementalState.localConversation, remoteConversation: conv); + // return loading still if state isn't complete + if ((_localConversationRecordKey != null && + _incrementalState.localConversation == null) || + (_remoteConversationRecordKey != null && + _incrementalState.remoteConversation == null)) { + return const AsyncValue.loading(); + } + // state is complete, all required keys are open return AsyncValue.data(_incrementalState); }, loading: AsyncValue.loading, @@ -245,12 +266,9 @@ class ConversationCubit extends Cubit> { // Open local converation key Future _setLocalConversation(Future Function() open) async { assert(_localConversationCubit == null, - 'should not set local conversation twice'); + 'shoud not set local conversation twice'); _localConversationCubit = DefaultDHTRecordCubit( open: open, decodeState: proto.Conversation.fromBuffer); - - await _localConversationCubit!.ready(); - _localSubscription = _localConversationCubit!.stream.listen(_updateLocalConversationState); _updateLocalConversationState(_localConversationCubit!.state); @@ -259,12 +277,9 @@ class ConversationCubit extends Cubit> { // Open remote converation key Future _setRemoteConversation(Future Function() open) async { assert(_remoteConversationCubit == null, - 'should not set remote conversation twice'); + 'shoud not set remote conversation twice'); _remoteConversationCubit = DefaultDHTRecordCubit( open: open, decodeState: proto.Conversation.fromBuffer); - - await _remoteConversationCubit!.ready(); - _remoteSubscription = _remoteConversationCubit!.stream.listen(_updateRemoteConversationState); _updateRemoteConversationState(_remoteConversationCubit!.state); @@ -304,12 +319,14 @@ class ConversationCubit extends Cubit> { final AccountInfo _accountInfo; late final KeyPair _identityWriter; final TypedKey _remoteIdentityPublicKey; + TypedKey? _localConversationRecordKey; + final TypedKey? _remoteConversationRecordKey; DefaultDHTRecordCubit? _localConversationCubit; DefaultDHTRecordCubit? _remoteConversationCubit; StreamSubscription>? _localSubscription; StreamSubscription>? _remoteSubscription; StreamSubscription>? _accountSubscription; - var _incrementalState = const ConversationState( + ConversationState _incrementalState = const ConversationState( localConversation: null, remoteConversation: null); VeilidCrypto? _conversationCrypto; final WaitSet _initWait = WaitSet(); diff --git a/lib/init.dart b/lib/init.dart index 958cd08..fc836a5 100644 --- a/lib/init.dart +++ b/lib/init.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; import 'package:veilid_support/veilid_support.dart'; import 'account_manager/account_manager.dart'; @@ -9,23 +8,17 @@ import 'app.dart'; import 'tools/tools.dart'; import 'veilid_processor/veilid_processor.dart'; -List rootAssets = []; - class VeilidChatGlobalInit { VeilidChatGlobalInit._(); // Initialize Veilid Future _initializeVeilid() async { // Init Veilid - try { - Veilid.instance.initializeVeilidCore( - await getDefaultVeilidPlatformConfig(kIsWeb, VeilidChatApp.name)); - } on VeilidAPIExceptionAlreadyInitialized { - log.debug('Already initialized, not reinitializing veilid-core'); - } + Veilid.instance.initializeVeilidCore( + await getDefaultVeilidPlatformConfig(kIsWeb, VeilidChatApp.name)); // Veilid logging - initVeilidLog(kIsDebugMode); + initVeilidLog(kDebugMode); // Startup Veilid await ProcessorRepository.instance.startup(); @@ -35,22 +28,14 @@ class VeilidChatGlobalInit { logger: (message) => log.debug('DHTRecordPool: $message')); } - // Initialize repositories +// Initialize repositories Future _initializeRepositories() async { await AccountRepository.instance.init(); } - // Initialize asset manifest - static Future loadAssetManifest() async { - final assetManifest = await AssetManifest.loadFromAssetBundle(rootBundle); - rootAssets = assetManifest.listAssets(); - } - static Future initialize() async { final veilidChatGlobalInit = VeilidChatGlobalInit._(); - await loadAssetManifest(); - log.info('Initializing Veilid'); await veilidChatGlobalInit._initializeVeilid(); log.info('Initializing Repositories'); diff --git a/lib/keyboard_shortcuts.dart b/lib/keyboard_shortcuts.dart deleted file mode 100644 index 6708d72..0000000 --- a/lib/keyboard_shortcuts.dart +++ /dev/null @@ -1,171 +0,0 @@ -import 'package:animated_theme_switcher/animated_theme_switcher.dart'; -import 'package:async_tools/async_tools.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; -import 'package:flutter_translate/flutter_translate.dart'; -import 'package:veilid_support/veilid_support.dart'; - -import 'init.dart'; -import 'router/router.dart'; -import 'settings/settings.dart'; -import 'theme/theme.dart'; -import 'tools/tools.dart'; -import 'veilid_processor/veilid_processor.dart'; - -class ReloadThemeIntent extends Intent { - const ReloadThemeIntent(); -} - -class ChangeBrightnessIntent extends Intent { - const ChangeBrightnessIntent(); -} - -class ChangeColorIntent extends Intent { - const ChangeColorIntent(); -} - -class AttachDetachIntent extends Intent { - const AttachDetachIntent(); -} - -class DeveloperPageIntent extends Intent { - const DeveloperPageIntent(); -} - -class KeyboardShortcuts extends StatelessWidget { - const KeyboardShortcuts({required this.child, super.key}); - - void reloadTheme(BuildContext context) { - singleFuture(this, () async { - log.info('Reloading theme'); - - await VeilidChatGlobalInit.loadAssetManifest(); - - final theme = - PreferencesRepository.instance.value.themePreference.themeData(); - if (context.mounted) { - ThemeSwitcher.of(context).changeTheme(theme: theme); - - // Hack to reload translations - final localizationDelegate = LocalizedApp.of(context).delegate; - await LocalizationDelegate.create( - fallbackLocale: localizationDelegate.fallbackLocale.toString(), - supportedLocales: localizationDelegate.supportedLocales - .map((x) => x.toString()) - .toList()); - } - }); - } - - void changeBrightness(BuildContext context) { - singleFuture(this, () async { - final prefs = PreferencesRepository.instance.value; - - final oldBrightness = prefs.themePreference.brightnessPreference; - final newBrightness = BrightnessPreference.values[ - (oldBrightness.index + 1) % BrightnessPreference.values.length]; - - log.info('Changing brightness to $newBrightness'); - - final newPrefs = prefs.copyWith( - themePreference: prefs.themePreference - .copyWith(brightnessPreference: newBrightness)); - await PreferencesRepository.instance.set(newPrefs); - - if (context.mounted) { - ThemeSwitcher.of(context) - .changeTheme(theme: newPrefs.themePreference.themeData()); - } - }); - } - - void changeColor(BuildContext context) { - singleFuture(this, () async { - final prefs = PreferencesRepository.instance.value; - final oldColor = prefs.themePreference.colorPreference; - final newColor = ColorPreference - .values[(oldColor.index + 1) % ColorPreference.values.length]; - - log.info('Changing color to $newColor'); - - final newPrefs = prefs.copyWith( - themePreference: - prefs.themePreference.copyWith(colorPreference: newColor)); - await PreferencesRepository.instance.set(newPrefs); - - if (context.mounted) { - ThemeSwitcher.of(context) - .changeTheme(theme: newPrefs.themePreference.themeData()); - } - }); - } - - void _attachDetach(BuildContext context) { - singleFuture(this, () async { - if (ProcessorRepository.instance.processorConnectionState.isAttached) { - log.info('Detaching'); - await Veilid.instance.detach(); - } else if (ProcessorRepository - .instance.processorConnectionState.isDetached) { - log.info('Attaching'); - await Veilid.instance.attach(); - } - }); - } - - void _developerPage(BuildContext context) { - singleFuture(this, () async { - final path = GoRouter.of(context).location(); - if (path != '/developer') { - await GoRouterHelper(context).push('/developer'); - } - }); - } - - @override - Widget build(BuildContext context) => ThemeSwitcher( - builder: (context) => Shortcuts( - shortcuts: const { - SingleActivator( - LogicalKeyboardKey.keyR, - control: true, - alt: true, - ): ReloadThemeIntent(), - SingleActivator( - LogicalKeyboardKey.keyB, - control: true, - alt: true, - ): ChangeBrightnessIntent(), - SingleActivator( - LogicalKeyboardKey.keyC, - control: true, - alt: true, - ): ChangeColorIntent(), - SingleActivator( - LogicalKeyboardKey.keyA, - control: true, - alt: true, - ): AttachDetachIntent(), - SingleActivator( - LogicalKeyboardKey.keyD, - control: true, - alt: true, - ): DeveloperPageIntent(), - }, - child: Actions(actions: >{ - ReloadThemeIntent: CallbackAction( - onInvoke: (intent) => reloadTheme(context)), - ChangeBrightnessIntent: CallbackAction( - onInvoke: (intent) => changeBrightness(context)), - ChangeColorIntent: CallbackAction( - onInvoke: (intent) => changeColor(context)), - AttachDetachIntent: CallbackAction( - onInvoke: (intent) => _attachDetach(context)), - DeveloperPageIntent: CallbackAction( - onInvoke: (intent) => _developerPage(context)), - }, child: Focus(autofocus: true, child: child)))); - - ///////////////////////////////////////////////////////// - - final Widget child; -} diff --git a/lib/layout/default_app_bar.dart b/lib/layout/default_app_bar.dart index b9c0b41..41e3601 100644 --- a/lib/layout/default_app_bar.dart +++ b/lib/layout/default_app_bar.dart @@ -4,11 +4,7 @@ import 'package:flutter_svg/flutter_svg.dart'; class DefaultAppBar extends AppBar { DefaultAppBar( - {super.title, - super.flexibleSpace, - super.key, - Widget? leading, - super.actions}) + {required super.title, super.key, Widget? leading, super.actions}) : super( leading: leading ?? Container( @@ -17,6 +13,6 @@ class DefaultAppBar extends AppBar { color: Colors.black.withAlpha(32), shape: BoxShape.circle), child: - SvgPicture.asset('assets/images/vlogo.svg', height: 24) + SvgPicture.asset('assets/images/vlogo.svg', height: 32) .paddingAll(4))); } diff --git a/lib/layout/home/drawer_menu/drawer_menu.dart b/lib/layout/home/drawer_menu/drawer_menu.dart index 0863b1f..0821bbb 100644 --- a/lib/layout/home/drawer_menu/drawer_menu.dart +++ b/lib/layout/home/drawer_menu/drawer_menu.dart @@ -9,14 +9,12 @@ import 'package:go_router/go_router.dart'; import 'package:veilid_support/veilid_support.dart'; import '../../../account_manager/account_manager.dart'; -import '../../../keyboard_shortcuts.dart'; +import '../../../proto/proto.dart' as proto; import '../../../theme/theme.dart'; import '../../../tools/tools.dart'; import '../../../veilid_processor/veilid_processor.dart'; import 'menu_item_widget.dart'; -const _scaleKind = ScaleKind.secondary; - class DrawerMenu extends StatefulWidget { const DrawerMenu({super.key}); @@ -42,7 +40,7 @@ class _DrawerMenuState extends State { } void _doEditClick(TypedKey superIdentityRecordKey, - AccountSpec existingAccount, OwnedDHTRecordPointer accountRecord) { + proto.Account existingAccount, OwnedDHTRecordPointer accountRecord) { singleFuture(this, () async { await GoRouterHelper(context).push('/edit_account', extra: [superIdentityRecordKey, existingAccount, accountRecord]); @@ -60,6 +58,45 @@ class _DrawerMenuState extends State { borderRadius: BorderRadius.circular(borderRadius))), child: child); + Widget _makeAvatarWidget({ + required String name, + required double size, + required Color borderColor, + required Color foregroundColor, + required Color backgroundColor, + required ScaleConfig scaleConfig, + required TextStyle textStyle, + ImageProvider? imageProvider, + }) { + final abbrev = name.split(' ').map((s) => s.isEmpty ? '' : s[0]).join(); + late final String shortname; + if (abbrev.length >= 3) { + shortname = abbrev[0] + abbrev[1] + abbrev[abbrev.length - 1]; + } else { + shortname = abbrev; + } + + return Container( + height: size, + width: size, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: scaleConfig.preferBorders + ? Border.all( + color: borderColor, + width: 2 * (size ~/ 32 + 1), + strokeAlign: BorderSide.strokeAlignOutside) + : null, + color: Colors.blue, + ), + child: AvatarImage( + //size: 32, + backgroundImage: imageProvider, + backgroundColor: backgroundColor, + foregroundColor: foregroundColor, + child: Text(shortname, style: textStyle))); + } + Widget _makeAccountWidget( {required String name, required bool selected, @@ -85,9 +122,8 @@ class _DrawerMenuState extends State { hoverBorder = border; activeBorder = border; } else { - background = selected - ? scale.elementBackground - : scale.elementBackground.withAlpha(128); + background = + selected ? scale.activeElementBackground : scale.elementBackground; hoverBackground = scale.hoverElementBackground; activeBackground = scale.activeElementBackground; border = loggedIn ? scale.border : scale.subtleBorder; @@ -134,17 +170,9 @@ class _DrawerMenuState extends State { callback: callback, footerButtonIcon: loggedIn ? Icons.edit_outlined : null, footerCallback: footerCallback, - footerButtonIconColor: - scaleConfig.preferBorders ? scale.border : scale.borderText, - footerButtonIconHoverColor: - (scaleConfig.preferBorders || scaleConfig.useVisualIndicators) - ? null - : hoverBorder, - footerButtonIconFocusColor: - (scaleConfig.preferBorders || scaleConfig.useVisualIndicators) - ? null - : activeBorder, - minHeight: 48, + footerButtonIconColor: border, + footerButtonIconHoverColor: hoverBackground, + footerButtonIconFocusColor: activeBackground, )); } @@ -156,7 +184,6 @@ class _DrawerMenuState extends State { final theme = Theme.of(context); final scaleScheme = theme.extension()!; final scaleConfig = theme.extension()!; - final scale = scaleScheme.scale(_scaleKind); final loggedInAccounts = []; final loggedOutAccounts = []; @@ -170,6 +197,9 @@ class _DrawerMenuState extends State { final avAccountRecordState = perAccountState?.avAccountRecordState; if (perAccountState != null && avAccountRecordState != null) { // Account is logged in + final scale = scaleConfig.useVisualIndicators + ? theme.extension()!.primaryScale + : theme.extension()!.tertiaryScale; final loggedInAccount = avAccountRecordState.when( data: (value) => _makeAccountWidget( name: value.profile.name, @@ -183,7 +213,7 @@ class _DrawerMenuState extends State { footerCallback: () { _doEditClick( superIdentityRecordKey, - AccountSpec.fromProto(value), + value, perAccountState.accountInfo.userLogin!.accountRecordInfo .accountRecord); }), @@ -281,14 +311,13 @@ class _DrawerMenuState extends State { Widget _getBottomButtons() { final theme = Theme.of(context); - final scaleScheme = theme.extension()!; + final scale = theme.extension()!; final scaleConfig = theme.extension()!; - final scale = scaleScheme.scale(_scaleKind); final settingsButton = _getButton( icon: const Icon(Icons.settings), tooltip: translate('menu.settings_tooltip'), - scale: scale, + scale: scale.tertiaryScale, scaleConfig: scaleConfig, onPressed: () async { await GoRouterHelper(context).push('/settings'); @@ -297,7 +326,7 @@ class _DrawerMenuState extends State { final addButton = _getButton( icon: const Icon(Icons.add), tooltip: translate('menu.add_account_tooltip'), - scale: scale, + scale: scale.tertiaryScale, scaleConfig: scaleConfig, onPressed: () async { await GoRouterHelper(context).push('/new_account'); @@ -311,9 +340,8 @@ class _DrawerMenuState extends State { @override Widget build(BuildContext context) { final theme = Theme.of(context); - final scaleScheme = theme.extension()!; + final scale = theme.extension()!; final scaleConfig = theme.extension()!; - final scale = scaleScheme.scale(_scaleKind); //final textTheme = theme.textTheme; final localAccounts = context.watch().state; final perAccountCollectionBlocMapState = @@ -323,22 +351,43 @@ class _DrawerMenuState extends State { begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ - scale.border, - scale.subtleBorder, + scale.tertiaryScale.border, + scale.tertiaryScale.subtleBorder, ]); - Widget menu = DecoratedBox( + return DecoratedBox( decoration: ShapeDecoration( - shadows: themedShadow(scaleConfig, scale), + shadows: [ + if (scaleConfig.useVisualIndicators && !scaleConfig.preferBorders) + BoxShadow( + color: scale.tertiaryScale.primary.darken(80), + spreadRadius: 2, + ) + else if (scaleConfig.useVisualIndicators && + scaleConfig.preferBorders) + BoxShadow( + color: scale.tertiaryScale.border, + spreadRadius: 2, + ) + else + BoxShadow( + color: scale.tertiaryScale.primary.darken(40), + blurRadius: 6, + offset: const Offset( + 0, + 4, + ), + ), + ], gradient: scaleConfig.useVisualIndicators ? null : gradient, color: scaleConfig.useVisualIndicators ? (scaleConfig.preferBorders - ? scale.appBackground - : scale.subtleBorder) + ? scale.tertiaryScale.appBackground + : scale.tertiaryScale.subtleBorder) : null, shape: RoundedRectangleBorder( side: scaleConfig.preferBorders - ? BorderSide(color: scale.primary, width: 2) + ? BorderSide(color: scale.tertiaryScale.primary, width: 2) : BorderSide.none, borderRadius: BorderRadius.only( topRight: Radius.circular(16 * scaleConfig.borderRadiusScale), @@ -350,37 +399,31 @@ class _DrawerMenuState extends State { child: ColorFiltered( colorFilter: ColorFilter.mode( theme.brightness == Brightness.light - ? scale.primary - : scale.border, + ? scale.tertiaryScale.primary + : scale.tertiaryScale.border, scaleConfig.preferBorders ? BlendMode.modulate : BlendMode.dst), child: Row(children: [ - // SvgPicture.asset( - // height: 48, - // 'assets/images/icon.svg', - // colorFilter: scaleConfig.useVisualIndicators - // ? grayColorFilter - // : null) - // .paddingLTRB(0, 0, 16, 0), - GestureDetector( - onLongPress: () async { - context - .findAncestorWidgetOfExactType()! - .reloadTheme(context); - }, - child: SvgPicture.asset( + SvgPicture.asset( height: 48, - 'assets/images/title.svg', + 'assets/images/icon.svg', colorFilter: scaleConfig.useVisualIndicators ? grayColorFilter - : src96StencilFilter)), + : null) + .paddingLTRB(0, 0, 16, 0), + SvgPicture.asset( + height: 48, + 'assets/images/title.svg', + colorFilter: scaleConfig.useVisualIndicators + ? grayColorFilter + : null), ]))), Text(translate('menu.accounts'), style: theme.textTheme.titleMedium!.copyWith( color: scaleConfig.preferBorders - ? scale.border - : scale.borderText)) + ? scale.tertiaryScale.border + : scale.tertiaryScale.borderText)) .paddingLTRB(0, 16, 0, 16), ListView( shrinkWrap: true, @@ -395,25 +438,19 @@ class _DrawerMenuState extends State { Text('${translate('menu.version')} $packageInfoVersion', style: theme.textTheme.labelMedium!.copyWith( color: scaleConfig.preferBorders - ? scale.hoverBorder - : scale.subtleBackground)), + ? scale.tertiaryScale.hoverBorder + : scale.tertiaryScale.subtleBackground)), const Spacer(), SignalStrengthMeterWidget( color: scaleConfig.preferBorders - ? scale.hoverBorder - : scale.subtleBackground, + ? scale.tertiaryScale.hoverBorder + : scale.tertiaryScale.subtleBackground, inactiveColor: scaleConfig.preferBorders - ? scale.border - : scale.elementBackground, + ? scale.tertiaryScale.border + : scale.tertiaryScale.elementBackground, ), ]) ]).paddingAll(16), - ); - - if (scaleConfig.preferBorders || scaleConfig.useVisualIndicators) { - menu = menu.paddingLTRB(0, 2, 2, 2); - } - - return menu; + ).paddingLTRB(0, 2, 2, 2); } } diff --git a/lib/layout/home/drawer_menu/menu_item_widget.dart b/lib/layout/home/drawer_menu/menu_item_widget.dart index 1255458..e538b15 100644 --- a/lib/layout/home/drawer_menu/menu_item_widget.dart +++ b/lib/layout/home/drawer_menu/menu_item_widget.dart @@ -22,44 +22,39 @@ class MenuItemWidget extends StatelessWidget { this.footerButtonIconHoverColor, this.footerButtonIconFocusColor, this.footerCallback, - this.minHeight = 0, super.key, }); @override Widget build(BuildContext context) => TextButton( - onPressed: callback, - style: TextButton.styleFrom(foregroundColor: foregroundColor).copyWith( - backgroundColor: WidgetStateProperty.resolveWith((states) { - if (states.contains(WidgetState.hovered)) { - return backgroundHoverColor; - } - if (states.contains(WidgetState.focused)) { - return backgroundFocusColor; - } - return backgroundColor; - }), - overlayColor: - WidgetStateProperty.resolveWith((states) => backgroundHoverColor), - side: WidgetStateBorderSide.resolveWith((states) { - if (states.contains(WidgetState.hovered)) { + onPressed: callback, + style: TextButton.styleFrom(foregroundColor: foregroundColor).copyWith( + backgroundColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.hovered)) { + return backgroundHoverColor; + } + if (states.contains(WidgetState.focused)) { + return backgroundFocusColor; + } + return backgroundColor; + }), + side: WidgetStateBorderSide.resolveWith((states) { + if (states.contains(WidgetState.hovered)) { + return borderColor != null + ? BorderSide(width: 2, color: borderHoverColor!) + : null; + } + if (states.contains(WidgetState.focused)) { + return borderColor != null + ? BorderSide(width: 2, color: borderFocusColor!) + : null; + } return borderColor != null - ? BorderSide(width: 2, color: borderHoverColor!) + ? BorderSide(width: 2, color: borderColor!) : null; - } - if (states.contains(WidgetState.focused)) { - return borderColor != null - ? BorderSide(width: 2, color: borderFocusColor!) - : null; - } - return borderColor != null - ? BorderSide(width: 2, color: borderColor!) - : null; - }), - shape: WidgetStateProperty.all(RoundedRectangleBorder( - borderRadius: BorderRadius.circular(borderRadius ?? 0)))), - child: ConstrainedBox( - constraints: BoxConstraints(minHeight: minHeight), + }), + shape: WidgetStateProperty.all(RoundedRectangleBorder( + borderRadius: BorderRadius.circular(borderRadius ?? 0)))), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -85,8 +80,8 @@ class MenuItemWidget extends StatelessWidget { ), onPressed: footerCallback), ], - ).paddingAll(2), - )); + ), + ); @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { @@ -111,8 +106,7 @@ class MenuItemWidget extends StatelessWidget { ..add(ColorProperty('borderColor', borderColor)) ..add(DoubleProperty('borderRadius', borderRadius)) ..add(ColorProperty('borderHoverColor', borderHoverColor)) - ..add(ColorProperty('borderFocusColor', borderFocusColor)) - ..add(DoubleProperty('minHeight', minHeight)); + ..add(ColorProperty('borderFocusColor', borderFocusColor)); } //////////////////////////////////////////////////////////////////////////// @@ -135,5 +129,4 @@ class MenuItemWidget extends StatelessWidget { final Color? footerButtonIconColor; final Color? footerButtonIconHoverColor; final Color? footerButtonIconFocusColor; - final double minHeight; } diff --git a/lib/layout/home/home_account_ready.dart b/lib/layout/home/home_account_ready.dart index a2966a6..5a6ab2b 100644 --- a/lib/layout/home/home_account_ready.dart +++ b/lib/layout/home/home_account_ready.dart @@ -33,7 +33,7 @@ class _HomeAccountReadyState extends State { color: scaleConfig.preferBorders ? scale.primaryScale.border : scale.primaryScale.borderText, - constraints: const BoxConstraints.expand(height: 40, width: 40), + constraints: const BoxConstraints.expand(height: 48, width: 48), style: ButtonStyle( backgroundColor: WidgetStateProperty.all( scaleConfig.preferBorders @@ -50,7 +50,7 @@ class _HomeAccountReadyState extends State { : scale.primaryScale.borderText, width: 2), borderRadius: BorderRadius.all( - Radius.circular(8 * scaleConfig.borderRadiusScale))), + Radius.circular(12 * scaleConfig.borderRadiusScale))), )), tooltip: translate('menu.accounts_menu_tooltip'), onPressed: () async { @@ -68,7 +68,7 @@ class _HomeAccountReadyState extends State { color: scaleConfig.preferBorders ? scale.primaryScale.border : scale.primaryScale.borderText, - constraints: const BoxConstraints.expand(height: 40, width: 40), + constraints: const BoxConstraints.expand(height: 48, width: 48), style: ButtonStyle( backgroundColor: WidgetStateProperty.all( scaleConfig.preferBorders @@ -85,74 +85,63 @@ class _HomeAccountReadyState extends State { : scale.primaryScale.borderText, width: 2), borderRadius: BorderRadius.all( - Radius.circular(8 * scaleConfig.borderRadiusScale))), + Radius.circular(12 * scaleConfig.borderRadiusScale))), )), tooltip: translate('menu.contacts_tooltip'), onPressed: () async { - await Navigator.of(context).push( - MaterialPageRoute( - builder: (_) => const ContactsPage(), - ), - ); + await ContactsDialog.show(context); }); }); - Widget buildLeftPane(BuildContext context) => Builder( - builder: (context) => Material( - color: Colors.transparent, - child: Builder(builder: (context) { - final profile = context.select( - (c) => c.state.asData!.value.profile); - final theme = Theme.of(context); - final scale = theme.extension()!; - final scaleConfig = theme.extension()!; + Widget buildUserPanel() => Builder(builder: (context) { + final profile = context.select( + (c) => c.state.asData!.value.profile); + final theme = Theme.of(context); + final scale = theme.extension()!; + final scaleConfig = theme.extension()!; - return ColoredBox( - color: scaleConfig.preferBorders - ? scale.primaryScale.subtleBackground - : scale.primaryScale.subtleBorder, - child: Column(children: [ - Row(children: [ - buildMenuButton().paddingLTRB(0, 0, 8, 0), - ProfileWidget( - profile: profile, - showPronouns: false, - ).expanded(), - buildContactsButton().paddingLTRB(8, 0, 0, 0), - ]).paddingAll(8), - const ChatListWidget().expanded() - ])); - }))); + return ColoredBox( + color: scaleConfig.preferBorders + ? scale.primaryScale.subtleBackground + : scale.primaryScale.subtleBorder, + child: Column(children: [ + Row(children: [ + buildMenuButton().paddingLTRB(0, 0, 8, 0), + ProfileWidget( + profile: profile, + showPronouns: false, + ).expanded(), + buildContactsButton().paddingLTRB(8, 0, 0, 0), + ]).paddingAll(8), + const ChatListWidget().expanded() + ])); + }); + + Widget buildLeftPane(BuildContext context) => Builder( + builder: (context) => + Material(color: Colors.transparent, child: buildUserPanel())); Widget buildRightPane(BuildContext context) { - final activeChatCubit = context.watch(); - final activeChatLocalConversationKey = activeChatCubit.state; + final activeChatLocalConversationKey = + context.watch().state; if (activeChatLocalConversationKey == null) { return const NoConversationWidget(); } - return Material( - color: Colors.transparent, - child: Builder( - builder: (context) => ChatComponentWidget.singleContact( - context: context, - localConversationRecordKey: activeChatLocalConversationKey, - onCancel: () { - activeChatCubit.setActiveChat(null); - }, - onClose: () { - activeChatCubit.setActiveChat(null); - }, - key: ValueKey(activeChatLocalConversationKey)))); + return ChatComponentWidget( + localConversationRecordKey: activeChatLocalConversationKey, + key: ValueKey(activeChatLocalConversationKey)); } @override Widget build(BuildContext context) { - final isSmallScreen = isMobileSize(context); + final isLarge = responsiveVisibility( + context: context, + phone: false, + ); final theme = Theme.of(context); - final scaleScheme = theme.extension()!; + final scale = theme.extension()!; final scaleConfig = theme.extension()!; - final scale = scaleScheme.scale(ScaleKind.primary); final activeChat = context.watch().state; final hasActiveChat = activeChat != null; @@ -164,7 +153,12 @@ class _HomeAccountReadyState extends State { late final bool visibleRight; late final double leftWidth; late final double rightWidth; - if (isSmallScreen) { + if (isLarge) { + visibleLeft = true; + visibleRight = true; + leftWidth = leftColumnSize; + rightWidth = constraints.maxWidth - leftColumnSize - 2; + } else { if (hasActiveChat) { visibleLeft = false; visibleRight = true; @@ -176,32 +170,23 @@ class _HomeAccountReadyState extends State { leftWidth = constraints.maxWidth; rightWidth = 400; // whatever } - } else { - visibleLeft = true; - visibleRight = true; - leftWidth = leftColumnSize; - rightWidth = constraints.maxWidth - - leftColumnSize - - (scaleConfig.useVisualIndicators ? 2 : 0); } return Row(crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Offstage( - offstage: !visibleLeft, - child: ConstrainedBox( - constraints: BoxConstraints(maxWidth: leftWidth), - child: buildLeftPane(context))) - .withThemedShadow(scaleConfig, scale), - if (scaleConfig.useVisualIndicators) - Offstage( - offstage: !(visibleLeft && visibleRight), - child: SizedBox( - width: 2, - height: double.infinity, - child: ColoredBox( - color: scaleConfig.preferBorders - ? scale.subtleBorder - : scale.subtleBackground))), + offstage: !visibleLeft, + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: leftWidth), + child: buildLeftPane(context))), + Offstage( + offstage: !(visibleLeft && visibleRight), + child: SizedBox( + width: 2, + height: double.infinity, + child: ColoredBox( + color: scaleConfig.preferBorders + ? scale.primaryScale.subtleBorder + : scale.primaryScale.subtleBackground))), Offstage( offstage: !visibleRight, child: ConstrainedBox( diff --git a/lib/layout/home/home_screen.dart b/lib/layout/home/home_screen.dart index b4c3b58..893ca97 100644 --- a/lib/layout/home/home_screen.dart +++ b/lib/layout/home/home_screen.dart @@ -5,8 +5,8 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_translate/flutter_translate.dart'; import 'package:flutter_zoom_drawer/flutter_zoom_drawer.dart'; -import 'package:keyboard_avoider/keyboard_avoider.dart'; import 'package:provider/provider.dart'; +import 'package:quickalert/quickalert.dart'; import 'package:transitioned_indexed_stack/transitioned_indexed_stack.dart'; import 'package:url_launcher/url_launcher_string.dart'; import 'package:veilid_support/veilid_support.dart'; @@ -63,42 +63,37 @@ class HomeScreenState extends State Future _doBetaDialog(BuildContext context) async { var displayBetaWarning = true; - final theme = Theme.of(context); - final scale = theme.extension()!; - final scaleConfig = theme.extension()!; - await showAlertWidgetModal( + await QuickAlert.show( context: context, title: translate('splash.beta_title'), - child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ - const Icon(Icons.warning, size: 64), + widget: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ RichText( textAlign: TextAlign.center, text: TextSpan( children: [ TextSpan( text: translate('splash.beta_text'), - style: theme.textTheme.bodyMedium! - .copyWith(color: scale.primaryScale.appText), + style: const TextStyle( + color: Colors.black87, + ), ), TextSpan( - text: 'https://veilid.com/chat/knownissues', - style: theme.textTheme.bodyMedium!.copyWith( - color: scaleConfig.useVisualIndicators - ? scale.secondaryScale.primaryText - : scale.secondaryScale.primary, + text: 'https://veilid.com/chat/beta', + style: const TextStyle( + color: Colors.blue, decoration: TextDecoration.underline, ), recognizer: TapGestureRecognizer() - ..onTap = () => - launchUrlString('https://veilid.com/chat/knownissues'), + ..onTap = + () => launchUrlString('https://veilid.com/chat/beta'), ), ], ), ), Row(mainAxisSize: MainAxisSize.min, children: [ StatefulBuilder( - builder: (context, setState) => Checkbox( + builder: (context, setState) => Checkbox.adaptive( value: displayBetaWarning, onChanged: (value) { setState(() { @@ -106,13 +101,11 @@ class HomeScreenState extends State }); }, )), - Text( - translate('settings_page.display_beta_warning'), - style: theme.textTheme.bodyMedium! - .copyWith(color: scale.primaryScale.appText), - ), + Text(translate('settings_page.display_beta_warning'), + style: const TextStyle(color: Colors.black)), ]), ]), + type: QuickAlertType.warning, ); final preferencesInstance = PreferencesRepository.instance; @@ -138,11 +131,15 @@ class HomeScreenState extends State } // Re-export all ready blocs to the account display subtree - final pages = >[ - const MaterialPage(child: HomeAccountReady()) - ]; - return perAccountCollectionState.provideReady( - child: Navigator(onDidRemovePage: pages.remove, pages: pages)); + return perAccountCollectionState.provide( + child: Navigator( + onPopPage: (route, result) { + if (!route.didPop(result)) { + return false; + } + return true; + }, + pages: const [MaterialPage(child: HomeAccountReady())])); } } @@ -206,12 +203,12 @@ class HomeScreenState extends State .indexWhere((x) => x.superIdentity.recordKey == activeLocalAccount); final canClose = activeIndex != -1; - return DefaultTextStyle( - style: theme.textTheme.bodySmall!, - child: KeyboardAvoider( - curve: Curves.ease, + return SafeArea( + child: DefaultTextStyle( + style: theme.textTheme.bodySmall!, child: ZoomDrawer( controller: _zoomDrawerController, + //menuBackgroundColor: Colors.transparent, menuScreen: Builder(builder: (context) { final zoomDrawer = ZoomDrawer.of(context); zoomDrawer!.stateNotifier.addListener(() { @@ -226,8 +223,10 @@ class HomeScreenState extends State child: Builder(builder: _buildAccountPageView)), borderRadius: 0, angle: 0, + mainScreenOverlayColor: theme.shadowColor.withAlpha(0x2F), openCurve: Curves.fastEaseInToSlowEaseOut, - closeCurve: Curves.fastEaseInToSlowEaseOut, + // duration: const Duration(milliseconds: 250), + // reverseDuration: const Duration(milliseconds: 250), menuScreenTapClose: canClose, mainScreenTapClose: canClose, disableDragGesture: !canClose, diff --git a/lib/notifications/models/notifications_preference.dart b/lib/notifications/models/notifications_preference.dart index 913cb25..35385c6 100644 --- a/lib/notifications/models/notifications_preference.dart +++ b/lib/notifications/models/notifications_preference.dart @@ -5,7 +5,7 @@ part 'notifications_preference.freezed.dart'; part 'notifications_preference.g.dart'; @freezed -sealed class NotificationsPreference with _$NotificationsPreference { +class NotificationsPreference with _$NotificationsPreference { const factory NotificationsPreference({ @Default(true) bool displayBetaWarning, @Default(true) bool enableBadge, diff --git a/lib/notifications/models/notifications_preference.freezed.dart b/lib/notifications/models/notifications_preference.freezed.dart index 55f700a..b2cbc67 100644 --- a/lib/notifications/models/notifications_preference.freezed.dart +++ b/lib/notifications/models/notifications_preference.freezed.dart @@ -1,4 +1,3 @@ -// dart format width=80 // coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND // ignore_for_file: type=lint @@ -10,86 +9,43 @@ part of 'notifications_preference.dart'; // FreezedGenerator // ************************************************************************** -// dart format off T _$identity(T value) => value; -/// @nodoc -mixin _$NotificationsPreference { - bool get displayBetaWarning; - bool get enableBadge; - bool get enableNotifications; - MessageNotificationContent get messageNotificationContent; - NotificationMode get onInvitationAcceptedMode; - SoundEffect get onInvitationAcceptedSound; - NotificationMode get onMessageReceivedMode; - SoundEffect get onMessageReceivedSound; - SoundEffect get onMessageSentSound; +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - /// Create a copy of NotificationsPreference - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') - $NotificationsPreferenceCopyWith get copyWith => - _$NotificationsPreferenceCopyWithImpl( - this as NotificationsPreference, _$identity); - - /// Serializes this NotificationsPreference to a JSON map. - Map toJson(); - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is NotificationsPreference && - (identical(other.displayBetaWarning, displayBetaWarning) || - other.displayBetaWarning == displayBetaWarning) && - (identical(other.enableBadge, enableBadge) || - other.enableBadge == enableBadge) && - (identical(other.enableNotifications, enableNotifications) || - other.enableNotifications == enableNotifications) && - (identical(other.messageNotificationContent, - messageNotificationContent) || - other.messageNotificationContent == - messageNotificationContent) && - (identical( - other.onInvitationAcceptedMode, onInvitationAcceptedMode) || - other.onInvitationAcceptedMode == onInvitationAcceptedMode) && - (identical(other.onInvitationAcceptedSound, - onInvitationAcceptedSound) || - other.onInvitationAcceptedSound == onInvitationAcceptedSound) && - (identical(other.onMessageReceivedMode, onMessageReceivedMode) || - other.onMessageReceivedMode == onMessageReceivedMode) && - (identical(other.onMessageReceivedSound, onMessageReceivedSound) || - other.onMessageReceivedSound == onMessageReceivedSound) && - (identical(other.onMessageSentSound, onMessageSentSound) || - other.onMessageSentSound == onMessageSentSound)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash( - runtimeType, - displayBetaWarning, - enableBadge, - enableNotifications, - messageNotificationContent, - onInvitationAcceptedMode, - onInvitationAcceptedSound, - onMessageReceivedMode, - onMessageReceivedSound, - onMessageSentSound); - - @override - String toString() { - return 'NotificationsPreference(displayBetaWarning: $displayBetaWarning, enableBadge: $enableBadge, enableNotifications: $enableNotifications, messageNotificationContent: $messageNotificationContent, onInvitationAcceptedMode: $onInvitationAcceptedMode, onInvitationAcceptedSound: $onInvitationAcceptedSound, onMessageReceivedMode: $onMessageReceivedMode, onMessageReceivedSound: $onMessageReceivedSound, onMessageSentSound: $onMessageSentSound)'; - } +NotificationsPreference _$NotificationsPreferenceFromJson( + Map json) { + return _NotificationsPreference.fromJson(json); } /// @nodoc -abstract mixin class $NotificationsPreferenceCopyWith<$Res> { +mixin _$NotificationsPreference { + bool get displayBetaWarning => throw _privateConstructorUsedError; + bool get enableBadge => throw _privateConstructorUsedError; + bool get enableNotifications => throw _privateConstructorUsedError; + MessageNotificationContent get messageNotificationContent => + throw _privateConstructorUsedError; + NotificationMode get onInvitationAcceptedMode => + throw _privateConstructorUsedError; + SoundEffect get onInvitationAcceptedSound => + throw _privateConstructorUsedError; + NotificationMode get onMessageReceivedMode => + throw _privateConstructorUsedError; + SoundEffect get onMessageReceivedSound => throw _privateConstructorUsedError; + SoundEffect get onMessageSentSound => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $NotificationsPreferenceCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $NotificationsPreferenceCopyWith<$Res> { factory $NotificationsPreferenceCopyWith(NotificationsPreference value, - $Res Function(NotificationsPreference) _then) = - _$NotificationsPreferenceCopyWithImpl; + $Res Function(NotificationsPreference) then) = + _$NotificationsPreferenceCopyWithImpl<$Res, NotificationsPreference>; @useResult $Res call( {bool displayBetaWarning, @@ -104,15 +60,16 @@ abstract mixin class $NotificationsPreferenceCopyWith<$Res> { } /// @nodoc -class _$NotificationsPreferenceCopyWithImpl<$Res> +class _$NotificationsPreferenceCopyWithImpl<$Res, + $Val extends NotificationsPreference> implements $NotificationsPreferenceCopyWith<$Res> { - _$NotificationsPreferenceCopyWithImpl(this._self, this._then); + _$NotificationsPreferenceCopyWithImpl(this._value, this._then); - final NotificationsPreference _self; - final $Res Function(NotificationsPreference) _then; + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; - /// Create a copy of NotificationsPreference - /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -126,41 +83,126 @@ class _$NotificationsPreferenceCopyWithImpl<$Res> Object? onMessageReceivedSound = null, Object? onMessageSentSound = null, }) { - return _then(_self.copyWith( + return _then(_value.copyWith( displayBetaWarning: null == displayBetaWarning - ? _self.displayBetaWarning + ? _value.displayBetaWarning : displayBetaWarning // ignore: cast_nullable_to_non_nullable as bool, enableBadge: null == enableBadge - ? _self.enableBadge + ? _value.enableBadge : enableBadge // ignore: cast_nullable_to_non_nullable as bool, enableNotifications: null == enableNotifications - ? _self.enableNotifications + ? _value.enableNotifications : enableNotifications // ignore: cast_nullable_to_non_nullable as bool, messageNotificationContent: null == messageNotificationContent - ? _self.messageNotificationContent + ? _value.messageNotificationContent : messageNotificationContent // ignore: cast_nullable_to_non_nullable as MessageNotificationContent, onInvitationAcceptedMode: null == onInvitationAcceptedMode - ? _self.onInvitationAcceptedMode + ? _value.onInvitationAcceptedMode : onInvitationAcceptedMode // ignore: cast_nullable_to_non_nullable as NotificationMode, onInvitationAcceptedSound: null == onInvitationAcceptedSound - ? _self.onInvitationAcceptedSound + ? _value.onInvitationAcceptedSound : onInvitationAcceptedSound // ignore: cast_nullable_to_non_nullable as SoundEffect, onMessageReceivedMode: null == onMessageReceivedMode - ? _self.onMessageReceivedMode + ? _value.onMessageReceivedMode : onMessageReceivedMode // ignore: cast_nullable_to_non_nullable as NotificationMode, onMessageReceivedSound: null == onMessageReceivedSound - ? _self.onMessageReceivedSound + ? _value.onMessageReceivedSound : onMessageReceivedSound // ignore: cast_nullable_to_non_nullable as SoundEffect, onMessageSentSound: null == onMessageSentSound - ? _self.onMessageSentSound + ? _value.onMessageSentSound + : onMessageSentSound // ignore: cast_nullable_to_non_nullable + as SoundEffect, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$NotificationsPreferenceImplCopyWith<$Res> + implements $NotificationsPreferenceCopyWith<$Res> { + factory _$$NotificationsPreferenceImplCopyWith( + _$NotificationsPreferenceImpl value, + $Res Function(_$NotificationsPreferenceImpl) then) = + __$$NotificationsPreferenceImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {bool displayBetaWarning, + bool enableBadge, + bool enableNotifications, + MessageNotificationContent messageNotificationContent, + NotificationMode onInvitationAcceptedMode, + SoundEffect onInvitationAcceptedSound, + NotificationMode onMessageReceivedMode, + SoundEffect onMessageReceivedSound, + SoundEffect onMessageSentSound}); +} + +/// @nodoc +class __$$NotificationsPreferenceImplCopyWithImpl<$Res> + extends _$NotificationsPreferenceCopyWithImpl<$Res, + _$NotificationsPreferenceImpl> + implements _$$NotificationsPreferenceImplCopyWith<$Res> { + __$$NotificationsPreferenceImplCopyWithImpl( + _$NotificationsPreferenceImpl _value, + $Res Function(_$NotificationsPreferenceImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? displayBetaWarning = null, + Object? enableBadge = null, + Object? enableNotifications = null, + Object? messageNotificationContent = null, + Object? onInvitationAcceptedMode = null, + Object? onInvitationAcceptedSound = null, + Object? onMessageReceivedMode = null, + Object? onMessageReceivedSound = null, + Object? onMessageSentSound = null, + }) { + return _then(_$NotificationsPreferenceImpl( + displayBetaWarning: null == displayBetaWarning + ? _value.displayBetaWarning + : displayBetaWarning // ignore: cast_nullable_to_non_nullable + as bool, + enableBadge: null == enableBadge + ? _value.enableBadge + : enableBadge // ignore: cast_nullable_to_non_nullable + as bool, + enableNotifications: null == enableNotifications + ? _value.enableNotifications + : enableNotifications // ignore: cast_nullable_to_non_nullable + as bool, + messageNotificationContent: null == messageNotificationContent + ? _value.messageNotificationContent + : messageNotificationContent // ignore: cast_nullable_to_non_nullable + as MessageNotificationContent, + onInvitationAcceptedMode: null == onInvitationAcceptedMode + ? _value.onInvitationAcceptedMode + : onInvitationAcceptedMode // ignore: cast_nullable_to_non_nullable + as NotificationMode, + onInvitationAcceptedSound: null == onInvitationAcceptedSound + ? _value.onInvitationAcceptedSound + : onInvitationAcceptedSound // ignore: cast_nullable_to_non_nullable + as SoundEffect, + onMessageReceivedMode: null == onMessageReceivedMode + ? _value.onMessageReceivedMode + : onMessageReceivedMode // ignore: cast_nullable_to_non_nullable + as NotificationMode, + onMessageReceivedSound: null == onMessageReceivedSound + ? _value.onMessageReceivedSound + : onMessageReceivedSound // ignore: cast_nullable_to_non_nullable + as SoundEffect, + onMessageSentSound: null == onMessageSentSound + ? _value.onMessageSentSound : onMessageSentSound // ignore: cast_nullable_to_non_nullable as SoundEffect, )); @@ -169,8 +211,8 @@ class _$NotificationsPreferenceCopyWithImpl<$Res> /// @nodoc @JsonSerializable() -class _NotificationsPreference implements NotificationsPreference { - const _NotificationsPreference( +class _$NotificationsPreferenceImpl implements _NotificationsPreference { + const _$NotificationsPreferenceImpl( {this.displayBetaWarning = true, this.enableBadge = true, this.enableNotifications = true, @@ -181,8 +223,9 @@ class _NotificationsPreference implements NotificationsPreference { this.onMessageReceivedMode = NotificationMode.inAppOrPush, this.onMessageReceivedSound = SoundEffect.boop, this.onMessageSentSound = SoundEffect.bonk}); - factory _NotificationsPreference.fromJson(Map json) => - _$NotificationsPreferenceFromJson(json); + + factory _$NotificationsPreferenceImpl.fromJson(Map json) => + _$$NotificationsPreferenceImplFromJson(json); @override @JsonKey() @@ -212,27 +255,16 @@ class _NotificationsPreference implements NotificationsPreference { @JsonKey() final SoundEffect onMessageSentSound; - /// Create a copy of NotificationsPreference - /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') - _$NotificationsPreferenceCopyWith<_NotificationsPreference> get copyWith => - __$NotificationsPreferenceCopyWithImpl<_NotificationsPreference>( - this, _$identity); - - @override - Map toJson() { - return _$NotificationsPreferenceToJson( - this, - ); + String toString() { + return 'NotificationsPreference(displayBetaWarning: $displayBetaWarning, enableBadge: $enableBadge, enableNotifications: $enableNotifications, messageNotificationContent: $messageNotificationContent, onInvitationAcceptedMode: $onInvitationAcceptedMode, onInvitationAcceptedSound: $onInvitationAcceptedSound, onMessageReceivedMode: $onMessageReceivedMode, onMessageReceivedSound: $onMessageReceivedSound, onMessageSentSound: $onMessageSentSound)'; } @override bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _NotificationsPreference && + other is _$NotificationsPreferenceImpl && (identical(other.displayBetaWarning, displayBetaWarning) || other.displayBetaWarning == displayBetaWarning) && (identical(other.enableBadge, enableBadge) || @@ -257,7 +289,7 @@ class _NotificationsPreference implements NotificationsPreference { other.onMessageSentSound == onMessageSentSound)); } - @JsonKey(includeFromJson: false, includeToJson: false) + @JsonKey(ignore: true) @override int get hashCode => Object.hash( runtimeType, @@ -271,94 +303,56 @@ class _NotificationsPreference implements NotificationsPreference { onMessageReceivedSound, onMessageSentSound); - @override - String toString() { - return 'NotificationsPreference(displayBetaWarning: $displayBetaWarning, enableBadge: $enableBadge, enableNotifications: $enableNotifications, messageNotificationContent: $messageNotificationContent, onInvitationAcceptedMode: $onInvitationAcceptedMode, onInvitationAcceptedSound: $onInvitationAcceptedSound, onMessageReceivedMode: $onMessageReceivedMode, onMessageReceivedSound: $onMessageReceivedSound, onMessageSentSound: $onMessageSentSound)'; - } -} - -/// @nodoc -abstract mixin class _$NotificationsPreferenceCopyWith<$Res> - implements $NotificationsPreferenceCopyWith<$Res> { - factory _$NotificationsPreferenceCopyWith(_NotificationsPreference value, - $Res Function(_NotificationsPreference) _then) = - __$NotificationsPreferenceCopyWithImpl; - @override - @useResult - $Res call( - {bool displayBetaWarning, - bool enableBadge, - bool enableNotifications, - MessageNotificationContent messageNotificationContent, - NotificationMode onInvitationAcceptedMode, - SoundEffect onInvitationAcceptedSound, - NotificationMode onMessageReceivedMode, - SoundEffect onMessageReceivedSound, - SoundEffect onMessageSentSound}); -} - -/// @nodoc -class __$NotificationsPreferenceCopyWithImpl<$Res> - implements _$NotificationsPreferenceCopyWith<$Res> { - __$NotificationsPreferenceCopyWithImpl(this._self, this._then); - - final _NotificationsPreference _self; - final $Res Function(_NotificationsPreference) _then; - - /// Create a copy of NotificationsPreference - /// with the given fields replaced by the non-null parameter values. + @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - $Res call({ - Object? displayBetaWarning = null, - Object? enableBadge = null, - Object? enableNotifications = null, - Object? messageNotificationContent = null, - Object? onInvitationAcceptedMode = null, - Object? onInvitationAcceptedSound = null, - Object? onMessageReceivedMode = null, - Object? onMessageReceivedSound = null, - Object? onMessageSentSound = null, - }) { - return _then(_NotificationsPreference( - displayBetaWarning: null == displayBetaWarning - ? _self.displayBetaWarning - : displayBetaWarning // ignore: cast_nullable_to_non_nullable - as bool, - enableBadge: null == enableBadge - ? _self.enableBadge - : enableBadge // ignore: cast_nullable_to_non_nullable - as bool, - enableNotifications: null == enableNotifications - ? _self.enableNotifications - : enableNotifications // ignore: cast_nullable_to_non_nullable - as bool, - messageNotificationContent: null == messageNotificationContent - ? _self.messageNotificationContent - : messageNotificationContent // ignore: cast_nullable_to_non_nullable - as MessageNotificationContent, - onInvitationAcceptedMode: null == onInvitationAcceptedMode - ? _self.onInvitationAcceptedMode - : onInvitationAcceptedMode // ignore: cast_nullable_to_non_nullable - as NotificationMode, - onInvitationAcceptedSound: null == onInvitationAcceptedSound - ? _self.onInvitationAcceptedSound - : onInvitationAcceptedSound // ignore: cast_nullable_to_non_nullable - as SoundEffect, - onMessageReceivedMode: null == onMessageReceivedMode - ? _self.onMessageReceivedMode - : onMessageReceivedMode // ignore: cast_nullable_to_non_nullable - as NotificationMode, - onMessageReceivedSound: null == onMessageReceivedSound - ? _self.onMessageReceivedSound - : onMessageReceivedSound // ignore: cast_nullable_to_non_nullable - as SoundEffect, - onMessageSentSound: null == onMessageSentSound - ? _self.onMessageSentSound - : onMessageSentSound // ignore: cast_nullable_to_non_nullable - as SoundEffect, - )); + _$$NotificationsPreferenceImplCopyWith<_$NotificationsPreferenceImpl> + get copyWith => __$$NotificationsPreferenceImplCopyWithImpl< + _$NotificationsPreferenceImpl>(this, _$identity); + + @override + Map toJson() { + return _$$NotificationsPreferenceImplToJson( + this, + ); } } -// dart format on +abstract class _NotificationsPreference implements NotificationsPreference { + const factory _NotificationsPreference( + {final bool displayBetaWarning, + final bool enableBadge, + final bool enableNotifications, + final MessageNotificationContent messageNotificationContent, + final NotificationMode onInvitationAcceptedMode, + final SoundEffect onInvitationAcceptedSound, + final NotificationMode onMessageReceivedMode, + final SoundEffect onMessageReceivedSound, + final SoundEffect onMessageSentSound}) = _$NotificationsPreferenceImpl; + + factory _NotificationsPreference.fromJson(Map json) = + _$NotificationsPreferenceImpl.fromJson; + + @override + bool get displayBetaWarning; + @override + bool get enableBadge; + @override + bool get enableNotifications; + @override + MessageNotificationContent get messageNotificationContent; + @override + NotificationMode get onInvitationAcceptedMode; + @override + SoundEffect get onInvitationAcceptedSound; + @override + NotificationMode get onMessageReceivedMode; + @override + SoundEffect get onMessageReceivedSound; + @override + SoundEffect get onMessageSentSound; + @override + @JsonKey(ignore: true) + _$$NotificationsPreferenceImplCopyWith<_$NotificationsPreferenceImpl> + get copyWith => throw _privateConstructorUsedError; +} diff --git a/lib/notifications/models/notifications_preference.g.dart b/lib/notifications/models/notifications_preference.g.dart index d1d5e89..d22b4b5 100644 --- a/lib/notifications/models/notifications_preference.g.dart +++ b/lib/notifications/models/notifications_preference.g.dart @@ -6,9 +6,9 @@ part of 'notifications_preference.dart'; // JsonSerializableGenerator // ************************************************************************** -_NotificationsPreference _$NotificationsPreferenceFromJson( +_$NotificationsPreferenceImpl _$$NotificationsPreferenceImplFromJson( Map json) => - _NotificationsPreference( + _$NotificationsPreferenceImpl( displayBetaWarning: json['display_beta_warning'] as bool? ?? true, enableBadge: json['enable_badge'] as bool? ?? true, enableNotifications: json['enable_notifications'] as bool? ?? true, @@ -33,8 +33,8 @@ _NotificationsPreference _$NotificationsPreferenceFromJson( : SoundEffect.fromJson(json['on_message_sent_sound']), ); -Map _$NotificationsPreferenceToJson( - _NotificationsPreference instance) => +Map _$$NotificationsPreferenceImplToJson( + _$NotificationsPreferenceImpl instance) => { 'display_beta_warning': instance.displayBetaWarning, 'enable_badge': instance.enableBadge, diff --git a/lib/notifications/models/notifications_state.dart b/lib/notifications/models/notifications_state.dart index 6248bba..d001ce2 100644 --- a/lib/notifications/models/notifications_state.dart +++ b/lib/notifications/models/notifications_state.dart @@ -9,7 +9,7 @@ enum NotificationType { } @freezed -sealed class NotificationItem with _$NotificationItem { +class NotificationItem with _$NotificationItem { const factory NotificationItem( {required NotificationType type, required String text, @@ -17,7 +17,7 @@ sealed class NotificationItem with _$NotificationItem { } @freezed -sealed class NotificationsState with _$NotificationsState { +class NotificationsState with _$NotificationsState { const factory NotificationsState({required IList queue}) = _NotificationsState; } diff --git a/lib/notifications/models/notifications_state.freezed.dart b/lib/notifications/models/notifications_state.freezed.dart index 8633702..90893e6 100644 --- a/lib/notifications/models/notifications_state.freezed.dart +++ b/lib/notifications/models/notifications_state.freezed.dart @@ -1,4 +1,3 @@ -// dart format width=80 // coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND // ignore_for_file: type=lint @@ -10,61 +9,41 @@ part of 'notifications_state.dart'; // FreezedGenerator // ************************************************************************** -// dart format off T _$identity(T value) => value; +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + /// @nodoc mixin _$NotificationItem { - NotificationType get type; - String get text; - String? get title; + NotificationType get type => throw _privateConstructorUsedError; + String get text => throw _privateConstructorUsedError; + String? get title => throw _privateConstructorUsedError; - /// Create a copy of NotificationItem - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') + @JsonKey(ignore: true) $NotificationItemCopyWith get copyWith => - _$NotificationItemCopyWithImpl( - this as NotificationItem, _$identity); - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is NotificationItem && - (identical(other.type, type) || other.type == type) && - (identical(other.text, text) || other.text == text) && - (identical(other.title, title) || other.title == title)); - } - - @override - int get hashCode => Object.hash(runtimeType, type, text, title); - - @override - String toString() { - return 'NotificationItem(type: $type, text: $text, title: $title)'; - } + throw _privateConstructorUsedError; } /// @nodoc -abstract mixin class $NotificationItemCopyWith<$Res> { +abstract class $NotificationItemCopyWith<$Res> { factory $NotificationItemCopyWith( - NotificationItem value, $Res Function(NotificationItem) _then) = - _$NotificationItemCopyWithImpl; + NotificationItem value, $Res Function(NotificationItem) then) = + _$NotificationItemCopyWithImpl<$Res, NotificationItem>; @useResult $Res call({NotificationType type, String text, String? title}); } /// @nodoc -class _$NotificationItemCopyWithImpl<$Res> +class _$NotificationItemCopyWithImpl<$Res, $Val extends NotificationItem> implements $NotificationItemCopyWith<$Res> { - _$NotificationItemCopyWithImpl(this._self, this._then); + _$NotificationItemCopyWithImpl(this._value, this._then); - final NotificationItem _self; - final $Res Function(NotificationItem) _then; + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; - /// Create a copy of NotificationItem - /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -72,17 +51,60 @@ class _$NotificationItemCopyWithImpl<$Res> Object? text = null, Object? title = freezed, }) { - return _then(_self.copyWith( + return _then(_value.copyWith( type: null == type - ? _self.type + ? _value.type : type // ignore: cast_nullable_to_non_nullable as NotificationType, text: null == text - ? _self.text + ? _value.text : text // ignore: cast_nullable_to_non_nullable as String, title: freezed == title - ? _self.title + ? _value.title + : title // ignore: cast_nullable_to_non_nullable + as String?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$NotificationItemImplCopyWith<$Res> + implements $NotificationItemCopyWith<$Res> { + factory _$$NotificationItemImplCopyWith(_$NotificationItemImpl value, + $Res Function(_$NotificationItemImpl) then) = + __$$NotificationItemImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({NotificationType type, String text, String? title}); +} + +/// @nodoc +class __$$NotificationItemImplCopyWithImpl<$Res> + extends _$NotificationItemCopyWithImpl<$Res, _$NotificationItemImpl> + implements _$$NotificationItemImplCopyWith<$Res> { + __$$NotificationItemImplCopyWithImpl(_$NotificationItemImpl _value, + $Res Function(_$NotificationItemImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? type = null, + Object? text = null, + Object? title = freezed, + }) { + return _then(_$NotificationItemImpl( + type: null == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as NotificationType, + text: null == text + ? _value.text + : text // ignore: cast_nullable_to_non_nullable + as String, + title: freezed == title + ? _value.title : title // ignore: cast_nullable_to_non_nullable as String?, )); @@ -91,8 +113,9 @@ class _$NotificationItemCopyWithImpl<$Res> /// @nodoc -class _NotificationItem implements NotificationItem { - const _NotificationItem({required this.type, required this.text, this.title}); +class _$NotificationItemImpl implements _NotificationItem { + const _$NotificationItemImpl( + {required this.type, required this.text, this.title}); @override final NotificationType type; @@ -101,19 +124,16 @@ class _NotificationItem implements NotificationItem { @override final String? title; - /// Create a copy of NotificationItem - /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') - _$NotificationItemCopyWith<_NotificationItem> get copyWith => - __$NotificationItemCopyWithImpl<_NotificationItem>(this, _$identity); + String toString() { + return 'NotificationItem(type: $type, text: $text, title: $title)'; + } @override bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _NotificationItem && + other is _$NotificationItemImpl && (identical(other.type, type) || other.type == type) && (identical(other.text, text) || other.text == text) && (identical(other.title, title) || other.title == title)); @@ -122,114 +142,101 @@ class _NotificationItem implements NotificationItem { @override int get hashCode => Object.hash(runtimeType, type, text, title); - @override - String toString() { - return 'NotificationItem(type: $type, text: $text, title: $title)'; - } -} - -/// @nodoc -abstract mixin class _$NotificationItemCopyWith<$Res> - implements $NotificationItemCopyWith<$Res> { - factory _$NotificationItemCopyWith( - _NotificationItem value, $Res Function(_NotificationItem) _then) = - __$NotificationItemCopyWithImpl; - @override - @useResult - $Res call({NotificationType type, String text, String? title}); -} - -/// @nodoc -class __$NotificationItemCopyWithImpl<$Res> - implements _$NotificationItemCopyWith<$Res> { - __$NotificationItemCopyWithImpl(this._self, this._then); - - final _NotificationItem _self; - final $Res Function(_NotificationItem) _then; - - /// Create a copy of NotificationItem - /// with the given fields replaced by the non-null parameter values. + @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - $Res call({ - Object? type = null, - Object? text = null, - Object? title = freezed, - }) { - return _then(_NotificationItem( - type: null == type - ? _self.type - : type // ignore: cast_nullable_to_non_nullable - as NotificationType, - text: null == text - ? _self.text - : text // ignore: cast_nullable_to_non_nullable - as String, - title: freezed == title - ? _self.title - : title // ignore: cast_nullable_to_non_nullable - as String?, - )); - } + _$$NotificationItemImplCopyWith<_$NotificationItemImpl> get copyWith => + __$$NotificationItemImplCopyWithImpl<_$NotificationItemImpl>( + this, _$identity); +} + +abstract class _NotificationItem implements NotificationItem { + const factory _NotificationItem( + {required final NotificationType type, + required final String text, + final String? title}) = _$NotificationItemImpl; + + @override + NotificationType get type; + @override + String get text; + @override + String? get title; + @override + @JsonKey(ignore: true) + _$$NotificationItemImplCopyWith<_$NotificationItemImpl> get copyWith => + throw _privateConstructorUsedError; } /// @nodoc mixin _$NotificationsState { - IList get queue; + IList get queue => throw _privateConstructorUsedError; - /// Create a copy of NotificationsState - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') + @JsonKey(ignore: true) $NotificationsStateCopyWith get copyWith => - _$NotificationsStateCopyWithImpl( - this as NotificationsState, _$identity); - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is NotificationsState && - const DeepCollectionEquality().equals(other.queue, queue)); - } - - @override - int get hashCode => - Object.hash(runtimeType, const DeepCollectionEquality().hash(queue)); - - @override - String toString() { - return 'NotificationsState(queue: $queue)'; - } + throw _privateConstructorUsedError; } /// @nodoc -abstract mixin class $NotificationsStateCopyWith<$Res> { +abstract class $NotificationsStateCopyWith<$Res> { factory $NotificationsStateCopyWith( - NotificationsState value, $Res Function(NotificationsState) _then) = - _$NotificationsStateCopyWithImpl; + NotificationsState value, $Res Function(NotificationsState) then) = + _$NotificationsStateCopyWithImpl<$Res, NotificationsState>; @useResult $Res call({IList queue}); } /// @nodoc -class _$NotificationsStateCopyWithImpl<$Res> +class _$NotificationsStateCopyWithImpl<$Res, $Val extends NotificationsState> implements $NotificationsStateCopyWith<$Res> { - _$NotificationsStateCopyWithImpl(this._self, this._then); + _$NotificationsStateCopyWithImpl(this._value, this._then); - final NotificationsState _self; - final $Res Function(NotificationsState) _then; + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; - /// Create a copy of NotificationsState - /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ Object? queue = null, }) { - return _then(_self.copyWith( + return _then(_value.copyWith( queue: null == queue - ? _self.queue + ? _value.queue + : queue // ignore: cast_nullable_to_non_nullable + as IList, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$NotificationsStateImplCopyWith<$Res> + implements $NotificationsStateCopyWith<$Res> { + factory _$$NotificationsStateImplCopyWith(_$NotificationsStateImpl value, + $Res Function(_$NotificationsStateImpl) then) = + __$$NotificationsStateImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({IList queue}); +} + +/// @nodoc +class __$$NotificationsStateImplCopyWithImpl<$Res> + extends _$NotificationsStateCopyWithImpl<$Res, _$NotificationsStateImpl> + implements _$$NotificationsStateImplCopyWith<$Res> { + __$$NotificationsStateImplCopyWithImpl(_$NotificationsStateImpl _value, + $Res Function(_$NotificationsStateImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? queue = null, + }) { + return _then(_$NotificationsStateImpl( + queue: null == queue + ? _value.queue : queue // ignore: cast_nullable_to_non_nullable as IList, )); @@ -238,25 +245,22 @@ class _$NotificationsStateCopyWithImpl<$Res> /// @nodoc -class _NotificationsState implements NotificationsState { - const _NotificationsState({required this.queue}); +class _$NotificationsStateImpl implements _NotificationsState { + const _$NotificationsStateImpl({required this.queue}); @override final IList queue; - /// Create a copy of NotificationsState - /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') - _$NotificationsStateCopyWith<_NotificationsState> get copyWith => - __$NotificationsStateCopyWithImpl<_NotificationsState>(this, _$identity); + String toString() { + return 'NotificationsState(queue: $queue)'; + } @override bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _NotificationsState && + other is _$NotificationsStateImpl && const DeepCollectionEquality().equals(other.queue, queue)); } @@ -264,45 +268,23 @@ class _NotificationsState implements NotificationsState { int get hashCode => Object.hash(runtimeType, const DeepCollectionEquality().hash(queue)); - @override - String toString() { - return 'NotificationsState(queue: $queue)'; - } -} - -/// @nodoc -abstract mixin class _$NotificationsStateCopyWith<$Res> - implements $NotificationsStateCopyWith<$Res> { - factory _$NotificationsStateCopyWith( - _NotificationsState value, $Res Function(_NotificationsState) _then) = - __$NotificationsStateCopyWithImpl; - @override - @useResult - $Res call({IList queue}); -} - -/// @nodoc -class __$NotificationsStateCopyWithImpl<$Res> - implements _$NotificationsStateCopyWith<$Res> { - __$NotificationsStateCopyWithImpl(this._self, this._then); - - final _NotificationsState _self; - final $Res Function(_NotificationsState) _then; - - /// Create a copy of NotificationsState - /// with the given fields replaced by the non-null parameter values. + @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - $Res call({ - Object? queue = null, - }) { - return _then(_NotificationsState( - queue: null == queue - ? _self.queue - : queue // ignore: cast_nullable_to_non_nullable - as IList, - )); - } + _$$NotificationsStateImplCopyWith<_$NotificationsStateImpl> get copyWith => + __$$NotificationsStateImplCopyWithImpl<_$NotificationsStateImpl>( + this, _$identity); } -// dart format on +abstract class _NotificationsState implements NotificationsState { + const factory _NotificationsState( + {required final IList queue}) = + _$NotificationsStateImpl; + + @override + IList get queue; + @override + @JsonKey(ignore: true) + _$$NotificationsStateImplCopyWith<_$NotificationsStateImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/notifications/views/notifications_preferences.dart b/lib/notifications/views/notifications_preferences.dart index 82f3555..5967522 100644 --- a/lib/notifications/views/notifications_preferences.dart +++ b/lib/notifications/views/notifications_preferences.dart @@ -129,6 +129,7 @@ Widget buildSettingsPageNotificationPreferences( // Display Beta Warning FormBuilderCheckbox( name: formFieldDisplayBetaWarning, + side: BorderSide(color: scale.primaryScale.border, width: 2), title: Text(translate('settings_page.display_beta_warning'), style: textTheme.labelMedium), initialValue: notificationsPreference.displayBetaWarning, @@ -144,6 +145,7 @@ Widget buildSettingsPageNotificationPreferences( // Enable Badge FormBuilderCheckbox( name: formFieldEnableBadge, + side: BorderSide(color: scale.primaryScale.border, width: 2), title: Text(translate('settings_page.enable_badge'), style: textTheme.labelMedium), initialValue: notificationsPreference.enableBadge, @@ -158,6 +160,7 @@ Widget buildSettingsPageNotificationPreferences( // Enable Notifications FormBuilderCheckbox( name: formFieldEnableNotifications, + side: BorderSide(color: scale.primaryScale.border, width: 2), title: Text(translate('settings_page.enable_notifications'), style: textTheme.labelMedium), initialValue: notificationsPreference.enableNotifications, diff --git a/lib/notifications/views/notifications_widget.dart b/lib/notifications/views/notifications_widget.dart index 73fac5f..246a570 100644 --- a/lib/notifications/views/notifications_widget.dart +++ b/lib/notifications/views/notifications_widget.dart @@ -1,7 +1,6 @@ -import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:toastification/toastification.dart'; +import 'package:motion_toast/motion_toast.dart'; import '../../theme/theme.dart'; import '../notifications.dart'; @@ -44,47 +43,46 @@ class NotificationsWidget extends StatelessWidget { //////////////////////////////////////////////////////////////////////////// // Private Implementation - void _toast( - {required BuildContext context, - required String text, - required ScaleToastTheme toastTheme, - String? title}) { - toastification.show( - context: context, - title: title != null - ? Text(title) - .copyWith(style: toastTheme.titleTextStyle) - .paddingLTRB(0, 0, 0, 8) - : null, - description: Text(text).copyWith(style: toastTheme.descriptionTextStyle), - icon: toastTheme.icon, - primaryColor: toastTheme.primaryColor, - backgroundColor: toastTheme.backgroundColor, - foregroundColor: toastTheme.foregroundColor, - padding: toastTheme.padding, - borderRadius: toastTheme.borderRadius, - borderSide: toastTheme.borderSide, - autoCloseDuration: const Duration(seconds: 2), - animationDuration: const Duration(milliseconds: 500), - ); - } - void _info( {required BuildContext context, required String text, String? title}) { final theme = Theme.of(context); - final toastTheme = - theme.extension()!.toastTheme(ScaleToastKind.info); + final scale = theme.extension()!; + final scaleConfig = theme.extension()!; - _toast(context: context, text: text, toastTheme: toastTheme, title: title); + MotionToast( + title: title != null ? Text(title) : null, + description: Text(text), + constraints: BoxConstraints.loose(const Size(400, 100)), + contentPadding: const EdgeInsets.all(16), + primaryColor: scale.tertiaryScale.elementBackground, + secondaryColor: scale.tertiaryScale.calloutBackground, + borderRadius: 12 * scaleConfig.borderRadiusScale, + toastDuration: const Duration(seconds: 2), + animationDuration: const Duration(milliseconds: 500), + displayBorder: scaleConfig.useVisualIndicators, + icon: Icons.info, + ).show(context); } void _error( {required BuildContext context, required String text, String? title}) { final theme = Theme.of(context); - final toastTheme = - theme.extension()!.toastTheme(ScaleToastKind.error); + final scale = theme.extension()!; + final scaleConfig = theme.extension()!; - _toast(context: context, text: text, toastTheme: toastTheme, title: title); + MotionToast( + title: title != null ? Text(title) : null, + description: Text(text), + constraints: BoxConstraints.loose(const Size(400, 100)), + contentPadding: const EdgeInsets.all(16), + primaryColor: scale.errorScale.elementBackground, + secondaryColor: scale.errorScale.calloutBackground, + borderRadius: 12 * scaleConfig.borderRadiusScale, + toastDuration: const Duration(seconds: 4), + animationDuration: const Duration(milliseconds: 1000), + displayBorder: scaleConfig.useVisualIndicators, + icon: Icons.error, + ).show(context); } //////////////////////////////////////////////////////////////////////////// diff --git a/lib/proto/proto.dart b/lib/proto/proto.dart index 21c988a..6ad8432 100644 --- a/lib/proto/proto.dart +++ b/lib/proto/proto.dart @@ -1,6 +1,3 @@ -import 'package:veilid_support/veilid_support.dart'; -import 'veilidchat.pb.dart' as vcproto; - export 'package:veilid_support/dht_support/proto/proto.dart'; export 'package:veilid_support/proto/proto.dart'; @@ -9,292 +6,3 @@ export 'veilidchat.pb.dart'; export 'veilidchat.pbenum.dart'; export 'veilidchat.pbjson.dart'; export 'veilidchat.pbserver.dart'; - -void registerVeilidchatProtoToDebug() { - dynamic toDebug(dynamic obj) { - if (obj is vcproto.DHTDataReference) { - return { - 'dhtData': obj.dhtData, - 'hash': obj.hash, - }; - } - if (obj is vcproto.BlockStoreDataReference) { - return { - 'block': obj.block, - }; - } - if (obj is vcproto.DataReference) { - return { - 'kind': obj.whichKind(), - if (obj.whichKind() == vcproto.DataReference_Kind.dhtData) - 'dhtData': obj.dhtData, - if (obj.whichKind() == vcproto.DataReference_Kind.blockStoreData) - 'blockStoreData': obj.blockStoreData, - }; - } - if (obj is vcproto.Attachment) { - return { - 'kind': obj.whichKind(), - if (obj.whichKind() == vcproto.Attachment_Kind.media) - 'media': obj.media, - 'signature': obj.signature, - }; - } - if (obj is vcproto.AttachmentMedia) { - return { - 'mime': obj.mime, - 'name': obj.name, - 'content': obj.content, - }; - } - if (obj is vcproto.Permissions) { - return { - 'canAddMembers': obj.canAddMembers, - 'canEditInfo': obj.canEditInfo, - 'moderated': obj.moderated, - }; - } - if (obj is vcproto.Membership) { - return { - 'watchers': obj.watchers, - 'moderated': obj.moderated, - 'talkers': obj.talkers, - 'moderators': obj.moderators, - 'admins': obj.admins, - }; - } - if (obj is vcproto.ChatSettings) { - return { - 'title': obj.title, - 'description': obj.description, - 'icon': obj.icon, - 'defaultExpiration': obj.defaultExpiration, - }; - } - if (obj is vcproto.ChatSettings) { - return { - 'title': obj.title, - 'description': obj.description, - 'icon': obj.icon, - 'defaultExpiration': obj.defaultExpiration, - }; - } - if (obj is vcproto.Message) { - return { - 'id': obj.id, - 'author': obj.author, - 'timestamp': obj.timestamp, - 'kind': obj.whichKind(), - if (obj.whichKind() == vcproto.Message_Kind.text) 'text': obj.text, - if (obj.whichKind() == vcproto.Message_Kind.secret) - 'secret': obj.secret, - if (obj.whichKind() == vcproto.Message_Kind.delete) - 'delete': obj.delete, - if (obj.whichKind() == vcproto.Message_Kind.erase) 'erase': obj.erase, - if (obj.whichKind() == vcproto.Message_Kind.settings) - 'settings': obj.settings, - if (obj.whichKind() == vcproto.Message_Kind.permissions) - 'permissions': obj.permissions, - if (obj.whichKind() == vcproto.Message_Kind.membership) - 'membership': obj.membership, - if (obj.whichKind() == vcproto.Message_Kind.moderation) - 'moderation': obj.moderation, - 'signature': obj.signature, - }; - } - if (obj is vcproto.Message_Text) { - return { - 'text': obj.text, - 'topic': obj.topic, - 'replyId': obj.replyId, - 'expiration': obj.expiration, - 'viewLimit': obj.viewLimit, - 'attachments': obj.attachments, - }; - } - if (obj is vcproto.Message_Secret) { - return { - 'ciphertext': obj.ciphertext, - 'expiration': obj.expiration, - }; - } - if (obj is vcproto.Message_ControlDelete) { - return { - 'ids': obj.ids, - }; - } - if (obj is vcproto.Message_ControlErase) { - return { - 'timestamp': obj.timestamp, - }; - } - if (obj is vcproto.Message_ControlSettings) { - return { - 'settings': obj.settings, - }; - } - if (obj is vcproto.Message_ControlPermissions) { - return { - 'permissions': obj.permissions, - }; - } - if (obj is vcproto.Message_ControlMembership) { - return { - 'membership': obj.membership, - }; - } - if (obj is vcproto.Message_ControlModeration) { - return { - 'acceptedIds': obj.acceptedIds, - 'rejectdIds': obj.rejectedIds, - }; - } - if (obj is vcproto.Message_ControlModeration) { - return { - 'acceptedIds': obj.acceptedIds, - 'rejectdIds': obj.rejectedIds, - }; - } - if (obj is vcproto.Message_ControlReadReceipt) { - return { - 'readIds': obj.readIds, - }; - } - if (obj is vcproto.ReconciledMessage) { - return { - 'content': obj.content, - 'reconciledTime': obj.reconciledTime, - }; - } - if (obj is vcproto.Conversation) { - return { - 'profile': obj.profile, - 'superIdentityJson': obj.superIdentityJson, - 'messages': obj.messages - }; - } - if (obj is vcproto.ChatMember) { - return { - 'remoteIdentityPublicKey': obj.remoteIdentityPublicKey, - 'remoteConversationRecordKey': obj.remoteConversationRecordKey, - }; - } - if (obj is vcproto.DirectChat) { - return { - 'settings': obj.settings, - 'localConversationRecordKey': obj.localConversationRecordKey, - 'remoteMember': obj.remoteMember, - }; - } - if (obj is vcproto.GroupChat) { - return { - 'settings': obj.settings, - 'membership': obj.membership, - 'permissions': obj.permissions, - 'localConversationRecordKey': obj.localConversationRecordKey, - 'remoteMembers': obj.remoteMembers, - }; - } - if (obj is vcproto.Chat) { - return { - 'kind': obj.whichKind(), - if (obj.whichKind() == vcproto.Chat_Kind.direct) 'direct': obj.direct, - if (obj.whichKind() == vcproto.Chat_Kind.group) 'group': obj.group, - }; - } - if (obj is vcproto.Profile) { - return { - 'name': obj.name, - 'pronouns': obj.pronouns, - 'about': obj.about, - 'status': obj.status, - 'availability': obj.availability, - 'avatar': obj.avatar, - 'timestamp': obj.timestamp, - }; - } - if (obj is vcproto.Account) { - return { - 'profile': obj.profile, - 'invisible': obj.invisible, - 'autoAwayTimeoutMin': obj.autoAwayTimeoutMin, - 'contact_list': obj.contactList, - 'contactInvitationRecords': obj.contactInvitationRecords, - 'chatList': obj.chatList, - 'groupChatList': obj.groupChatList, - 'freeMessage': obj.freeMessage, - 'busyMessage': obj.busyMessage, - 'awayMessage': obj.awayMessage, - 'autodetectAway': obj.autodetectAway, - }; - } - if (obj is vcproto.Contact) { - return { - 'nickname': obj.nickname, - 'profile': obj.profile, - 'superIdentityJson': obj.superIdentityJson, - 'identityPublicKey': obj.identityPublicKey, - 'remoteConversationRecordKey': obj.remoteConversationRecordKey, - 'localConversationRecordKey': obj.localConversationRecordKey, - 'showAvailability': obj.showAvailability, - 'notes': obj.notes, - }; - } - if (obj is vcproto.ContactInvitation) { - return { - 'contactRequestInboxKey': obj.contactRequestInboxKey, - 'writerSecret': obj.writerSecret, - }; - } - if (obj is vcproto.SignedContactInvitation) { - return { - 'contactInvitation': obj.contactInvitation, - 'identitySignature': obj.identitySignature, - }; - } - if (obj is vcproto.ContactRequest) { - return { - 'encryptionKeyType': obj.encryptionKeyType, - 'private': obj.private, - }; - } - if (obj is vcproto.ContactRequestPrivate) { - return { - 'writerKey': obj.writerKey, - 'profile': obj.profile, - 'superIdentityRecordKey': obj.superIdentityRecordKey, - 'chatRecordKey': obj.chatRecordKey, - 'expiration': obj.expiration, - }; - } - if (obj is vcproto.ContactResponse) { - return { - 'accept': obj.accept, - 'superIdentityRecordKey': obj.superIdentityRecordKey, - 'remoteConversationRecordKey': obj.remoteConversationRecordKey, - }; - } - if (obj is vcproto.SignedContactResponse) { - return { - 'contactResponse': obj.contactResponse, - 'identitySignature': obj.identitySignature, - }; - } - if (obj is vcproto.ContactInvitationRecord) { - return { - 'contactRequestInbox': obj.contactRequestInbox, - 'writerKey': obj.writerKey, - 'writerSecret': obj.writerSecret, - 'localConversationRecordKey': obj.localConversationRecordKey, - 'expiration': obj.expiration, - 'invitation': obj.invitation, - 'message': obj.message, - 'recipient': obj.recipient, - }; - } - - return obj; - } - - DynamicDebug.registerToDebug(toDebug); -} diff --git a/lib/proto/veilidchat.pb.dart b/lib/proto/veilidchat.pb.dart index 67805ae..5152594 100644 --- a/lib/proto/veilidchat.pb.dart +++ b/lib/proto/veilidchat.pb.dart @@ -4,7 +4,7 @@ // // @dart = 2.12 -// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: annotate_overrides, camel_case_types // ignore_for_file: constant_identifier_names, library_prefixes // ignore_for_file: non_constant_identifier_names, prefer_final_fields // ignore_for_file: unnecessary_import, unnecessary_this, unused_import @@ -20,21 +20,8 @@ import 'veilidchat.pbenum.dart'; export 'veilidchat.pbenum.dart'; -/// Reference to data on the DHT class DHTDataReference extends $pb.GeneratedMessage { - factory DHTDataReference({ - $0.TypedKey? dhtData, - $0.TypedKey? hash, - }) { - final $result = create(); - if (dhtData != null) { - $result.dhtData = dhtData; - } - if (hash != null) { - $result.hash = hash; - } - return $result; - } + factory DHTDataReference() => create(); DHTDataReference._() : super(); factory DHTDataReference.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory DHTDataReference.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -89,17 +76,8 @@ class DHTDataReference extends $pb.GeneratedMessage { $0.TypedKey ensureHash() => $_ensure(1); } -/// Reference to data on the BlockStore class BlockStoreDataReference extends $pb.GeneratedMessage { - factory BlockStoreDataReference({ - $0.TypedKey? block, - }) { - final $result = create(); - if (block != null) { - $result.block = block; - } - return $result; - } + factory BlockStoreDataReference() => create(); BlockStoreDataReference._() : super(); factory BlockStoreDataReference.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory BlockStoreDataReference.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -148,23 +126,8 @@ enum DataReference_Kind { notSet } -/// DataReference -/// Pointer to data somewhere in Veilid -/// Abstraction over DHTData and BlockStore class DataReference extends $pb.GeneratedMessage { - factory DataReference({ - DHTDataReference? dhtData, - BlockStoreDataReference? blockStoreData, - }) { - final $result = create(); - if (dhtData != null) { - $result.dhtData = dhtData; - } - if (blockStoreData != null) { - $result.blockStoreData = blockStoreData; - } - return $result; - } + factory DataReference() => create(); DataReference._() : super(); factory DataReference.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory DataReference.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -233,21 +196,8 @@ enum Attachment_Kind { notSet } -/// A single attachment class Attachment extends $pb.GeneratedMessage { - factory Attachment({ - AttachmentMedia? media, - $0.Signature? signature, - }) { - final $result = create(); - if (media != null) { - $result.media = media; - } - if (signature != null) { - $result.signature = signature; - } - return $result; - } + factory Attachment() => create(); Attachment._() : super(); factory Attachment.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory Attachment.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -298,7 +248,6 @@ class Attachment extends $pb.GeneratedMessage { @$pb.TagNumber(1) AttachmentMedia ensureMedia() => $_ensure(0); - /// Author signature over all attachment fields and content fields and bytes @$pb.TagNumber(2) $0.Signature get signature => $_getN(1); @$pb.TagNumber(2) @@ -311,25 +260,8 @@ class Attachment extends $pb.GeneratedMessage { $0.Signature ensureSignature() => $_ensure(1); } -/// A file, audio, image, or video attachment class AttachmentMedia extends $pb.GeneratedMessage { - factory AttachmentMedia({ - $core.String? mime, - $core.String? name, - DataReference? content, - }) { - final $result = create(); - if (mime != null) { - $result.mime = mime; - } - if (name != null) { - $result.name = name; - } - if (content != null) { - $result.content = content; - } - return $result; - } + factory AttachmentMedia() => create(); AttachmentMedia._() : super(); factory AttachmentMedia.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory AttachmentMedia.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -362,7 +294,6 @@ class AttachmentMedia extends $pb.GeneratedMessage { static AttachmentMedia getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static AttachmentMedia? _defaultInstance; - /// MIME type of the data @$pb.TagNumber(1) $core.String get mime => $_getSZ(0); @$pb.TagNumber(1) @@ -372,7 +303,6 @@ class AttachmentMedia extends $pb.GeneratedMessage { @$pb.TagNumber(1) void clearMime() => clearField(1); - /// Title or filename @$pb.TagNumber(2) $core.String get name => $_getSZ(1); @$pb.TagNumber(2) @@ -382,7 +312,6 @@ class AttachmentMedia extends $pb.GeneratedMessage { @$pb.TagNumber(2) void clearName() => clearField(2); - /// Pointer to the data content @$pb.TagNumber(3) DataReference get content => $_getN(2); @$pb.TagNumber(3) @@ -395,25 +324,8 @@ class AttachmentMedia extends $pb.GeneratedMessage { DataReference ensureContent() => $_ensure(2); } -/// Permissions of a chat class Permissions extends $pb.GeneratedMessage { - factory Permissions({ - Scope? canAddMembers, - Scope? canEditInfo, - $core.bool? moderated, - }) { - final $result = create(); - if (canAddMembers != null) { - $result.canAddMembers = canAddMembers; - } - if (canEditInfo != null) { - $result.canEditInfo = canEditInfo; - } - if (moderated != null) { - $result.moderated = moderated; - } - return $result; - } + factory Permissions() => create(); Permissions._() : super(); factory Permissions.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory Permissions.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -446,7 +358,6 @@ class Permissions extends $pb.GeneratedMessage { static Permissions getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static Permissions? _defaultInstance; - /// Parties in this scope or higher can add members to their own group or lower @$pb.TagNumber(1) Scope get canAddMembers => $_getN(0); @$pb.TagNumber(1) @@ -456,7 +367,6 @@ class Permissions extends $pb.GeneratedMessage { @$pb.TagNumber(1) void clearCanAddMembers() => clearField(1); - /// Parties in this scope or higher can change the 'info' of a group @$pb.TagNumber(2) Scope get canEditInfo => $_getN(1); @$pb.TagNumber(2) @@ -466,7 +376,6 @@ class Permissions extends $pb.GeneratedMessage { @$pb.TagNumber(2) void clearCanEditInfo() => clearField(2); - /// If moderation is enabled or not. @$pb.TagNumber(3) $core.bool get moderated => $_getBF(2); @$pb.TagNumber(3) @@ -477,33 +386,8 @@ class Permissions extends $pb.GeneratedMessage { void clearModerated() => clearField(3); } -/// The membership of a chat class Membership extends $pb.GeneratedMessage { - factory Membership({ - $core.Iterable<$0.TypedKey>? watchers, - $core.Iterable<$0.TypedKey>? moderated, - $core.Iterable<$0.TypedKey>? talkers, - $core.Iterable<$0.TypedKey>? moderators, - $core.Iterable<$0.TypedKey>? admins, - }) { - final $result = create(); - if (watchers != null) { - $result.watchers.addAll(watchers); - } - if (moderated != null) { - $result.moderated.addAll(moderated); - } - if (talkers != null) { - $result.talkers.addAll(talkers); - } - if (moderators != null) { - $result.moderators.addAll(moderators); - } - if (admins != null) { - $result.admins.addAll(admins); - } - return $result; - } + factory Membership() => create(); Membership._() : super(); factory Membership.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory Membership.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -538,50 +422,24 @@ class Membership extends $pb.GeneratedMessage { static Membership getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static Membership? _defaultInstance; - /// Conversation keys for parties in the 'watchers' group @$pb.TagNumber(1) $core.List<$0.TypedKey> get watchers => $_getList(0); - /// Conversation keys for parties in the 'moderated' group @$pb.TagNumber(2) $core.List<$0.TypedKey> get moderated => $_getList(1); - /// Conversation keys for parties in the 'talkers' group @$pb.TagNumber(3) $core.List<$0.TypedKey> get talkers => $_getList(2); - /// Conversation keys for parties in the 'moderators' group @$pb.TagNumber(4) $core.List<$0.TypedKey> get moderators => $_getList(3); - /// Conversation keys for parties in the 'admins' group @$pb.TagNumber(5) $core.List<$0.TypedKey> get admins => $_getList(4); } -/// The chat settings class ChatSettings extends $pb.GeneratedMessage { - factory ChatSettings({ - $core.String? title, - $core.String? description, - DataReference? icon, - $fixnum.Int64? defaultExpiration, - }) { - final $result = create(); - if (title != null) { - $result.title = title; - } - if (description != null) { - $result.description = description; - } - if (icon != null) { - $result.icon = icon; - } - if (defaultExpiration != null) { - $result.defaultExpiration = defaultExpiration; - } - return $result; - } + factory ChatSettings() => create(); ChatSettings._() : super(); factory ChatSettings.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ChatSettings.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -615,7 +473,6 @@ class ChatSettings extends $pb.GeneratedMessage { static ChatSettings getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static ChatSettings? _defaultInstance; - /// Title for the chat @$pb.TagNumber(1) $core.String get title => $_getSZ(0); @$pb.TagNumber(1) @@ -625,7 +482,6 @@ class ChatSettings extends $pb.GeneratedMessage { @$pb.TagNumber(1) void clearTitle() => clearField(1); - /// Description for the chat @$pb.TagNumber(2) $core.String get description => $_getSZ(1); @$pb.TagNumber(2) @@ -635,7 +491,6 @@ class ChatSettings extends $pb.GeneratedMessage { @$pb.TagNumber(2) void clearDescription() => clearField(2); - /// Icon for the chat @$pb.TagNumber(3) DataReference get icon => $_getN(2); @$pb.TagNumber(3) @@ -647,7 +502,6 @@ class ChatSettings extends $pb.GeneratedMessage { @$pb.TagNumber(3) DataReference ensureIcon() => $_ensure(2); - /// Default message expiration duration (in us) @$pb.TagNumber(4) $fixnum.Int64 get defaultExpiration => $_getI64(3); @$pb.TagNumber(4) @@ -658,37 +512,8 @@ class ChatSettings extends $pb.GeneratedMessage { void clearDefaultExpiration() => clearField(4); } -/// A text message class Message_Text extends $pb.GeneratedMessage { - factory Message_Text({ - $core.String? text, - $core.String? topic, - $core.List<$core.int>? replyId, - $fixnum.Int64? expiration, - $core.int? viewLimit, - $core.Iterable? attachments, - }) { - final $result = create(); - if (text != null) { - $result.text = text; - } - if (topic != null) { - $result.topic = topic; - } - if (replyId != null) { - $result.replyId = replyId; - } - if (expiration != null) { - $result.expiration = expiration; - } - if (viewLimit != null) { - $result.viewLimit = viewLimit; - } - if (attachments != null) { - $result.attachments.addAll(attachments); - } - return $result; - } + factory Message_Text() => create(); Message_Text._() : super(); factory Message_Text.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory Message_Text.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -724,7 +549,6 @@ class Message_Text extends $pb.GeneratedMessage { static Message_Text getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static Message_Text? _defaultInstance; - /// Text of the message @$pb.TagNumber(1) $core.String get text => $_getSZ(0); @$pb.TagNumber(1) @@ -734,7 +558,6 @@ class Message_Text extends $pb.GeneratedMessage { @$pb.TagNumber(1) void clearText() => clearField(1); - /// Topic of the message / Content warning @$pb.TagNumber(2) $core.String get topic => $_getSZ(1); @$pb.TagNumber(2) @@ -744,7 +567,6 @@ class Message_Text extends $pb.GeneratedMessage { @$pb.TagNumber(2) void clearTopic() => clearField(2); - /// Message id replied to (author id + message id) @$pb.TagNumber(3) $core.List<$core.int> get replyId => $_getN(2); @$pb.TagNumber(3) @@ -754,7 +576,6 @@ class Message_Text extends $pb.GeneratedMessage { @$pb.TagNumber(3) void clearReplyId() => clearField(3); - /// Message expiration timestamp @$pb.TagNumber(4) $fixnum.Int64 get expiration => $_getI64(3); @$pb.TagNumber(4) @@ -764,7 +585,6 @@ class Message_Text extends $pb.GeneratedMessage { @$pb.TagNumber(4) void clearExpiration() => clearField(4); - /// Message view limit before deletion @$pb.TagNumber(5) $core.int get viewLimit => $_getIZ(4); @$pb.TagNumber(5) @@ -774,26 +594,12 @@ class Message_Text extends $pb.GeneratedMessage { @$pb.TagNumber(5) void clearViewLimit() => clearField(5); - /// Attachments on the message @$pb.TagNumber(6) $core.List get attachments => $_getList(5); } -/// A secret message class Message_Secret extends $pb.GeneratedMessage { - factory Message_Secret({ - $core.List<$core.int>? ciphertext, - $fixnum.Int64? expiration, - }) { - final $result = create(); - if (ciphertext != null) { - $result.ciphertext = ciphertext; - } - if (expiration != null) { - $result.expiration = expiration; - } - return $result; - } + factory Message_Secret() => create(); Message_Secret._() : super(); factory Message_Secret.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory Message_Secret.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -825,7 +631,6 @@ class Message_Secret extends $pb.GeneratedMessage { static Message_Secret getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static Message_Secret? _defaultInstance; - /// Text message protobuf encrypted by a key @$pb.TagNumber(1) $core.List<$core.int> get ciphertext => $_getN(0); @$pb.TagNumber(1) @@ -835,8 +640,6 @@ class Message_Secret extends $pb.GeneratedMessage { @$pb.TagNumber(1) void clearCiphertext() => clearField(1); - /// Secret expiration timestamp - /// This is the time after which an un-revealed secret will get deleted @$pb.TagNumber(2) $fixnum.Int64 get expiration => $_getI64(1); @$pb.TagNumber(2) @@ -847,18 +650,8 @@ class Message_Secret extends $pb.GeneratedMessage { void clearExpiration() => clearField(2); } -/// A 'delete' control message -/// Deletes a set of messages by their ids class Message_ControlDelete extends $pb.GeneratedMessage { - factory Message_ControlDelete({ - $core.Iterable<$core.List<$core.int>>? ids, - }) { - final $result = create(); - if (ids != null) { - $result.ids.addAll(ids); - } - return $result; - } + factory Message_ControlDelete() => create(); Message_ControlDelete._() : super(); factory Message_ControlDelete.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory Message_ControlDelete.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -893,18 +686,8 @@ class Message_ControlDelete extends $pb.GeneratedMessage { $core.List<$core.List<$core.int>> get ids => $_getList(0); } -/// An 'erase' control message -/// Deletes a set of messages from before some timestamp class Message_ControlErase extends $pb.GeneratedMessage { - factory Message_ControlErase({ - $fixnum.Int64? timestamp, - }) { - final $result = create(); - if (timestamp != null) { - $result.timestamp = timestamp; - } - return $result; - } + factory Message_ControlErase() => create(); Message_ControlErase._() : super(); factory Message_ControlErase.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory Message_ControlErase.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -935,8 +718,6 @@ class Message_ControlErase extends $pb.GeneratedMessage { static Message_ControlErase getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static Message_ControlErase? _defaultInstance; - /// The latest timestamp to delete messages before - /// If this is zero then all messages are cleared @$pb.TagNumber(1) $fixnum.Int64 get timestamp => $_getI64(0); @$pb.TagNumber(1) @@ -947,17 +728,8 @@ class Message_ControlErase extends $pb.GeneratedMessage { void clearTimestamp() => clearField(1); } -/// A 'change settings' control message class Message_ControlSettings extends $pb.GeneratedMessage { - factory Message_ControlSettings({ - ChatSettings? settings, - }) { - final $result = create(); - if (settings != null) { - $result.settings = settings; - } - return $result; - } + factory Message_ControlSettings() => create(); Message_ControlSettings._() : super(); factory Message_ControlSettings.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory Message_ControlSettings.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -1000,18 +772,8 @@ class Message_ControlSettings extends $pb.GeneratedMessage { ChatSettings ensureSettings() => $_ensure(0); } -/// A 'change permissions' control message -/// Changes the permissions of a chat class Message_ControlPermissions extends $pb.GeneratedMessage { - factory Message_ControlPermissions({ - Permissions? permissions, - }) { - final $result = create(); - if (permissions != null) { - $result.permissions = permissions; - } - return $result; - } + factory Message_ControlPermissions() => create(); Message_ControlPermissions._() : super(); factory Message_ControlPermissions.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory Message_ControlPermissions.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -1054,18 +816,8 @@ class Message_ControlPermissions extends $pb.GeneratedMessage { Permissions ensurePermissions() => $_ensure(0); } -/// A 'change membership' control message -/// Changes the class Message_ControlMembership extends $pb.GeneratedMessage { - factory Message_ControlMembership({ - Membership? membership, - }) { - final $result = create(); - if (membership != null) { - $result.membership = membership; - } - return $result; - } + factory Message_ControlMembership() => create(); Message_ControlMembership._() : super(); factory Message_ControlMembership.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory Message_ControlMembership.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -1108,22 +860,8 @@ class Message_ControlMembership extends $pb.GeneratedMessage { Membership ensureMembership() => $_ensure(0); } -/// A 'moderation' control message -/// Accepts or rejects a set of messages class Message_ControlModeration extends $pb.GeneratedMessage { - factory Message_ControlModeration({ - $core.Iterable<$core.List<$core.int>>? acceptedIds, - $core.Iterable<$core.List<$core.int>>? rejectedIds, - }) { - final $result = create(); - if (acceptedIds != null) { - $result.acceptedIds.addAll(acceptedIds); - } - if (rejectedIds != null) { - $result.rejectedIds.addAll(rejectedIds); - } - return $result; - } + factory Message_ControlModeration() => create(); Message_ControlModeration._() : super(); factory Message_ControlModeration.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory Message_ControlModeration.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -1162,17 +900,8 @@ class Message_ControlModeration extends $pb.GeneratedMessage { $core.List<$core.List<$core.int>> get rejectedIds => $_getList(1); } -/// A 'read receipt' control message class Message_ControlReadReceipt extends $pb.GeneratedMessage { - factory Message_ControlReadReceipt({ - $core.Iterable<$core.List<$core.int>>? readIds, - }) { - final $result = create(); - if (readIds != null) { - $result.readIds.addAll(readIds); - } - return $result; - } + factory Message_ControlReadReceipt() => create(); Message_ControlReadReceipt._() : super(); factory Message_ControlReadReceipt.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory Message_ControlReadReceipt.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -1216,69 +945,11 @@ enum Message_Kind { permissions, membership, moderation, - readReceipt, notSet } -/// A single message as part of a series of messages class Message extends $pb.GeneratedMessage { - factory Message({ - $core.List<$core.int>? id, - $0.TypedKey? author, - $fixnum.Int64? timestamp, - Message_Text? text, - Message_Secret? secret, - Message_ControlDelete? delete, - Message_ControlErase? erase, - Message_ControlSettings? settings, - Message_ControlPermissions? permissions, - Message_ControlMembership? membership, - Message_ControlModeration? moderation, - $0.Signature? signature, - Message_ControlReadReceipt? readReceipt, - }) { - final $result = create(); - if (id != null) { - $result.id = id; - } - if (author != null) { - $result.author = author; - } - if (timestamp != null) { - $result.timestamp = timestamp; - } - if (text != null) { - $result.text = text; - } - if (secret != null) { - $result.secret = secret; - } - if (delete != null) { - $result.delete = delete; - } - if (erase != null) { - $result.erase = erase; - } - if (settings != null) { - $result.settings = settings; - } - if (permissions != null) { - $result.permissions = permissions; - } - if (membership != null) { - $result.membership = membership; - } - if (moderation != null) { - $result.moderation = moderation; - } - if (signature != null) { - $result.signature = signature; - } - if (readReceipt != null) { - $result.readReceipt = readReceipt; - } - return $result; - } + factory Message() => create(); Message._() : super(); factory Message.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory Message.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -1292,11 +963,10 @@ class Message extends $pb.GeneratedMessage { 9 : Message_Kind.permissions, 10 : Message_Kind.membership, 11 : Message_Kind.moderation, - 13 : Message_Kind.readReceipt, 0 : Message_Kind.notSet }; static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Message', package: const $pb.PackageName(_omitMessageNames ? '' : 'veilidchat'), createEmptyInstance: create) - ..oo(0, [4, 5, 6, 7, 8, 9, 10, 11, 13]) + ..oo(0, [4, 5, 6, 7, 8, 9, 10, 11]) ..a<$core.List<$core.int>>(1, _omitFieldNames ? '' : 'id', $pb.PbFieldType.OY) ..aOM<$0.TypedKey>(2, _omitFieldNames ? '' : 'author', subBuilder: $0.TypedKey.create) ..a<$fixnum.Int64>(3, _omitFieldNames ? '' : 'timestamp', $pb.PbFieldType.OU6, defaultOrMaker: $fixnum.Int64.ZERO) @@ -1309,7 +979,6 @@ class Message extends $pb.GeneratedMessage { ..aOM(10, _omitFieldNames ? '' : 'membership', subBuilder: Message_ControlMembership.create) ..aOM(11, _omitFieldNames ? '' : 'moderation', subBuilder: Message_ControlModeration.create) ..aOM<$0.Signature>(12, _omitFieldNames ? '' : 'signature', subBuilder: $0.Signature.create) - ..aOM(13, _omitFieldNames ? '' : 'readReceipt', protoName: 'readReceipt', subBuilder: Message_ControlReadReceipt.create) ..hasRequiredFields = false ; @@ -1337,8 +1006,6 @@ class Message extends $pb.GeneratedMessage { Message_Kind whichKind() => _Message_KindByTag[$_whichOneof(0)]!; void clearKind() => clearField($_whichOneof(0)); - /// Unique id for this author stream - /// Calculated from the hash of the previous message from this author @$pb.TagNumber(1) $core.List<$core.int> get id => $_getN(0); @$pb.TagNumber(1) @@ -1348,7 +1015,6 @@ class Message extends $pb.GeneratedMessage { @$pb.TagNumber(1) void clearId() => clearField(1); - /// Author of the message (identity public key) @$pb.TagNumber(2) $0.TypedKey get author => $_getN(1); @$pb.TagNumber(2) @@ -1360,7 +1026,6 @@ class Message extends $pb.GeneratedMessage { @$pb.TagNumber(2) $0.TypedKey ensureAuthor() => $_ensure(1); - /// Time the message was sent according to sender @$pb.TagNumber(3) $fixnum.Int64 get timestamp => $_getI64(2); @$pb.TagNumber(3) @@ -1458,7 +1123,6 @@ class Message extends $pb.GeneratedMessage { @$pb.TagNumber(11) Message_ControlModeration ensureModeration() => $_ensure(10); - /// Author signature over all of the fields and attachment signatures @$pb.TagNumber(12) $0.Signature get signature => $_getN(11); @$pb.TagNumber(12) @@ -1469,34 +1133,10 @@ class Message extends $pb.GeneratedMessage { void clearSignature() => clearField(12); @$pb.TagNumber(12) $0.Signature ensureSignature() => $_ensure(11); - - @$pb.TagNumber(13) - Message_ControlReadReceipt get readReceipt => $_getN(12); - @$pb.TagNumber(13) - set readReceipt(Message_ControlReadReceipt v) { setField(13, v); } - @$pb.TagNumber(13) - $core.bool hasReadReceipt() => $_has(12); - @$pb.TagNumber(13) - void clearReadReceipt() => clearField(13); - @$pb.TagNumber(13) - Message_ControlReadReceipt ensureReadReceipt() => $_ensure(12); } -/// Locally stored messages for chats class ReconciledMessage extends $pb.GeneratedMessage { - factory ReconciledMessage({ - Message? content, - $fixnum.Int64? reconciledTime, - }) { - final $result = create(); - if (content != null) { - $result.content = content; - } - if (reconciledTime != null) { - $result.reconciledTime = reconciledTime; - } - return $result; - } + factory ReconciledMessage() => create(); ReconciledMessage._() : super(); factory ReconciledMessage.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ReconciledMessage.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -1528,7 +1168,6 @@ class ReconciledMessage extends $pb.GeneratedMessage { static ReconciledMessage getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static ReconciledMessage? _defaultInstance; - /// The message as sent @$pb.TagNumber(1) Message get content => $_getN(0); @$pb.TagNumber(1) @@ -1540,7 +1179,6 @@ class ReconciledMessage extends $pb.GeneratedMessage { @$pb.TagNumber(1) Message ensureContent() => $_ensure(0); - /// The timestamp the message was reconciled @$pb.TagNumber(2) $fixnum.Int64 get reconciledTime => $_getI64(1); @$pb.TagNumber(2) @@ -1551,36 +1189,8 @@ class ReconciledMessage extends $pb.GeneratedMessage { void clearReconciledTime() => clearField(2); } -/// The means of direct communications that is synchronized between -/// two users. Visible and encrypted for the other party. -/// Includes communications for: -/// * Profile changes -/// * Identity changes -/// * 1-1 chat messages -/// * Group chat messages -/// -/// DHT Schema: SMPL(0,1,[identityPublicKey]) -/// DHT Key (UnicastOutbox): localConversation -/// DHT Secret: None -/// Encryption: DH(IdentityA, IdentityB) class Conversation extends $pb.GeneratedMessage { - factory Conversation({ - Profile? profile, - $core.String? superIdentityJson, - $0.TypedKey? messages, - }) { - final $result = create(); - if (profile != null) { - $result.profile = profile; - } - if (superIdentityJson != null) { - $result.superIdentityJson = superIdentityJson; - } - if (messages != null) { - $result.messages = messages; - } - return $result; - } + factory Conversation() => create(); Conversation._() : super(); factory Conversation.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory Conversation.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -1613,7 +1223,6 @@ class Conversation extends $pb.GeneratedMessage { static Conversation getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static Conversation? _defaultInstance; - /// Profile to publish to friend @$pb.TagNumber(1) Profile get profile => $_getN(0); @$pb.TagNumber(1) @@ -1625,7 +1234,6 @@ class Conversation extends $pb.GeneratedMessage { @$pb.TagNumber(1) Profile ensureProfile() => $_ensure(0); - /// SuperIdentity (JSON) to publish to friend or chat room @$pb.TagNumber(2) $core.String get superIdentityJson => $_getSZ(1); @$pb.TagNumber(2) @@ -1635,7 +1243,6 @@ class Conversation extends $pb.GeneratedMessage { @$pb.TagNumber(2) void clearSuperIdentityJson() => clearField(2); - /// Messages DHTLog @$pb.TagNumber(3) $0.TypedKey get messages => $_getN(2); @$pb.TagNumber(3) @@ -1648,21 +1255,8 @@ class Conversation extends $pb.GeneratedMessage { $0.TypedKey ensureMessages() => $_ensure(2); } -/// A member of chat which may or may not be associated with a contact class ChatMember extends $pb.GeneratedMessage { - factory ChatMember({ - $0.TypedKey? remoteIdentityPublicKey, - $0.TypedKey? remoteConversationRecordKey, - }) { - final $result = create(); - if (remoteIdentityPublicKey != null) { - $result.remoteIdentityPublicKey = remoteIdentityPublicKey; - } - if (remoteConversationRecordKey != null) { - $result.remoteConversationRecordKey = remoteConversationRecordKey; - } - return $result; - } + factory ChatMember() => create(); ChatMember._() : super(); factory ChatMember.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ChatMember.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -1694,7 +1288,6 @@ class ChatMember extends $pb.GeneratedMessage { static ChatMember getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static ChatMember? _defaultInstance; - /// The identity public key most recently associated with the chat member @$pb.TagNumber(1) $0.TypedKey get remoteIdentityPublicKey => $_getN(0); @$pb.TagNumber(1) @@ -1706,7 +1299,6 @@ class ChatMember extends $pb.GeneratedMessage { @$pb.TagNumber(1) $0.TypedKey ensureRemoteIdentityPublicKey() => $_ensure(0); - /// Conversation key for the other party @$pb.TagNumber(2) $0.TypedKey get remoteConversationRecordKey => $_getN(1); @$pb.TagNumber(2) @@ -1719,26 +1311,8 @@ class ChatMember extends $pb.GeneratedMessage { $0.TypedKey ensureRemoteConversationRecordKey() => $_ensure(1); } -/// A 1-1 chat -/// Privately encrypted, this is the local user's copy of the chat class DirectChat extends $pb.GeneratedMessage { - factory DirectChat({ - ChatSettings? settings, - $0.TypedKey? localConversationRecordKey, - ChatMember? remoteMember, - }) { - final $result = create(); - if (settings != null) { - $result.settings = settings; - } - if (localConversationRecordKey != null) { - $result.localConversationRecordKey = localConversationRecordKey; - } - if (remoteMember != null) { - $result.remoteMember = remoteMember; - } - return $result; - } + factory DirectChat() => create(); DirectChat._() : super(); factory DirectChat.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory DirectChat.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -1771,7 +1345,6 @@ class DirectChat extends $pb.GeneratedMessage { static DirectChat getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static DirectChat? _defaultInstance; - /// Settings @$pb.TagNumber(1) ChatSettings get settings => $_getN(0); @$pb.TagNumber(1) @@ -1783,7 +1356,6 @@ class DirectChat extends $pb.GeneratedMessage { @$pb.TagNumber(1) ChatSettings ensureSettings() => $_ensure(0); - /// Conversation key for this user @$pb.TagNumber(2) $0.TypedKey get localConversationRecordKey => $_getN(1); @$pb.TagNumber(2) @@ -1795,7 +1367,6 @@ class DirectChat extends $pb.GeneratedMessage { @$pb.TagNumber(2) $0.TypedKey ensureLocalConversationRecordKey() => $_ensure(1); - /// Conversation key for the other party @$pb.TagNumber(3) ChatMember get remoteMember => $_getN(2); @$pb.TagNumber(3) @@ -1808,34 +1379,8 @@ class DirectChat extends $pb.GeneratedMessage { ChatMember ensureRemoteMember() => $_ensure(2); } -/// A group chat -/// Privately encrypted, this is the local user's copy of the chat class GroupChat extends $pb.GeneratedMessage { - factory GroupChat({ - ChatSettings? settings, - Membership? membership, - Permissions? permissions, - $0.TypedKey? localConversationRecordKey, - $core.Iterable? remoteMembers, - }) { - final $result = create(); - if (settings != null) { - $result.settings = settings; - } - if (membership != null) { - $result.membership = membership; - } - if (permissions != null) { - $result.permissions = permissions; - } - if (localConversationRecordKey != null) { - $result.localConversationRecordKey = localConversationRecordKey; - } - if (remoteMembers != null) { - $result.remoteMembers.addAll(remoteMembers); - } - return $result; - } + factory GroupChat() => create(); GroupChat._() : super(); factory GroupChat.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory GroupChat.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -1870,7 +1415,6 @@ class GroupChat extends $pb.GeneratedMessage { static GroupChat getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static GroupChat? _defaultInstance; - /// Settings @$pb.TagNumber(1) ChatSettings get settings => $_getN(0); @$pb.TagNumber(1) @@ -1882,7 +1426,6 @@ class GroupChat extends $pb.GeneratedMessage { @$pb.TagNumber(1) ChatSettings ensureSettings() => $_ensure(0); - /// Membership @$pb.TagNumber(2) Membership get membership => $_getN(1); @$pb.TagNumber(2) @@ -1894,7 +1437,6 @@ class GroupChat extends $pb.GeneratedMessage { @$pb.TagNumber(2) Membership ensureMembership() => $_ensure(1); - /// Permissions @$pb.TagNumber(3) Permissions get permissions => $_getN(2); @$pb.TagNumber(3) @@ -1906,7 +1448,6 @@ class GroupChat extends $pb.GeneratedMessage { @$pb.TagNumber(3) Permissions ensurePermissions() => $_ensure(2); - /// Conversation key for this user @$pb.TagNumber(4) $0.TypedKey get localConversationRecordKey => $_getN(3); @$pb.TagNumber(4) @@ -1918,7 +1459,6 @@ class GroupChat extends $pb.GeneratedMessage { @$pb.TagNumber(4) $0.TypedKey ensureLocalConversationRecordKey() => $_ensure(3); - /// Conversation keys for the other parties @$pb.TagNumber(5) $core.List get remoteMembers => $_getList(4); } @@ -1929,21 +1469,8 @@ enum Chat_Kind { notSet } -/// Some kind of chat class Chat extends $pb.GeneratedMessage { - factory Chat({ - DirectChat? direct, - GroupChat? group, - }) { - final $result = create(); - if (direct != null) { - $result.direct = direct; - } - if (group != null) { - $result.group = group; - } - return $result; - } + factory Chat() => create(); Chat._() : super(); factory Chat.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory Chat.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -2007,45 +1534,8 @@ class Chat extends $pb.GeneratedMessage { GroupChat ensureGroup() => $_ensure(1); } -/// Publicly shared profile information for both contacts and accounts -/// Contains: -/// Name - Friendly name -/// Pronouns - Pronouns of user -/// Icon - Little picture to represent user in contact list class Profile extends $pb.GeneratedMessage { - factory Profile({ - $core.String? name, - $core.String? pronouns, - $core.String? about, - $core.String? status, - Availability? availability, - DataReference? avatar, - $fixnum.Int64? timestamp, - }) { - final $result = create(); - if (name != null) { - $result.name = name; - } - if (pronouns != null) { - $result.pronouns = pronouns; - } - if (about != null) { - $result.about = about; - } - if (status != null) { - $result.status = status; - } - if (availability != null) { - $result.availability = availability; - } - if (avatar != null) { - $result.avatar = avatar; - } - if (timestamp != null) { - $result.timestamp = timestamp; - } - return $result; - } + factory Profile() => create(); Profile._() : super(); factory Profile.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory Profile.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -2082,7 +1572,6 @@ class Profile extends $pb.GeneratedMessage { static Profile getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static Profile? _defaultInstance; - /// Friendy name (max length 64) @$pb.TagNumber(1) $core.String get name => $_getSZ(0); @$pb.TagNumber(1) @@ -2092,7 +1581,6 @@ class Profile extends $pb.GeneratedMessage { @$pb.TagNumber(1) void clearName() => clearField(1); - /// Pronouns of user (max length 64) @$pb.TagNumber(2) $core.String get pronouns => $_getSZ(1); @$pb.TagNumber(2) @@ -2102,7 +1590,6 @@ class Profile extends $pb.GeneratedMessage { @$pb.TagNumber(2) void clearPronouns() => clearField(2); - /// Description of the user (max length 1024) @$pb.TagNumber(3) $core.String get about => $_getSZ(2); @$pb.TagNumber(3) @@ -2112,7 +1599,6 @@ class Profile extends $pb.GeneratedMessage { @$pb.TagNumber(3) void clearAbout() => clearField(3); - /// Status/away message (max length 128) @$pb.TagNumber(4) $core.String get status => $_getSZ(3); @$pb.TagNumber(4) @@ -2122,7 +1608,6 @@ class Profile extends $pb.GeneratedMessage { @$pb.TagNumber(4) void clearStatus() => clearField(4); - /// Availability @$pb.TagNumber(5) Availability get availability => $_getN(4); @$pb.TagNumber(5) @@ -2132,7 +1617,6 @@ class Profile extends $pb.GeneratedMessage { @$pb.TagNumber(5) void clearAvailability() => clearField(5); - /// Avatar @$pb.TagNumber(6) DataReference get avatar => $_getN(5); @$pb.TagNumber(6) @@ -2144,7 +1628,6 @@ class Profile extends $pb.GeneratedMessage { @$pb.TagNumber(6) DataReference ensureAvatar() => $_ensure(5); - /// Timestamp of last change @$pb.TagNumber(7) $fixnum.Int64 get timestamp => $_getI64(6); @$pb.TagNumber(7) @@ -2155,61 +1638,8 @@ class Profile extends $pb.GeneratedMessage { void clearTimestamp() => clearField(7); } -/// A record of an individual account -/// Pointed to by the identity account map in the identity key -/// -/// DHT Schema: DFLT(1) -/// DHT Private: accountSecretKey class Account extends $pb.GeneratedMessage { - factory Account({ - Profile? profile, - $core.bool? invisible, - $core.int? autoAwayTimeoutMin, - $1.OwnedDHTRecordPointer? contactList, - $1.OwnedDHTRecordPointer? contactInvitationRecords, - $1.OwnedDHTRecordPointer? chatList, - $1.OwnedDHTRecordPointer? groupChatList, - $core.String? freeMessage, - $core.String? busyMessage, - $core.String? awayMessage, - $core.bool? autodetectAway, - }) { - final $result = create(); - if (profile != null) { - $result.profile = profile; - } - if (invisible != null) { - $result.invisible = invisible; - } - if (autoAwayTimeoutMin != null) { - $result.autoAwayTimeoutMin = autoAwayTimeoutMin; - } - if (contactList != null) { - $result.contactList = contactList; - } - if (contactInvitationRecords != null) { - $result.contactInvitationRecords = contactInvitationRecords; - } - if (chatList != null) { - $result.chatList = chatList; - } - if (groupChatList != null) { - $result.groupChatList = groupChatList; - } - if (freeMessage != null) { - $result.freeMessage = freeMessage; - } - if (busyMessage != null) { - $result.busyMessage = busyMessage; - } - if (awayMessage != null) { - $result.awayMessage = awayMessage; - } - if (autodetectAway != null) { - $result.autodetectAway = autodetectAway; - } - return $result; - } + factory Account() => create(); Account._() : super(); factory Account.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory Account.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -2250,7 +1680,6 @@ class Account extends $pb.GeneratedMessage { static Account getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static Account? _defaultInstance; - /// The user's profile that gets shared with contacts @$pb.TagNumber(1) Profile get profile => $_getN(0); @$pb.TagNumber(1) @@ -2262,7 +1691,6 @@ class Account extends $pb.GeneratedMessage { @$pb.TagNumber(1) Profile ensureProfile() => $_ensure(0); - /// Invisibility makes you always look 'Offline' @$pb.TagNumber(2) $core.bool get invisible => $_getBF(1); @$pb.TagNumber(2) @@ -2272,7 +1700,6 @@ class Account extends $pb.GeneratedMessage { @$pb.TagNumber(2) void clearInvisible() => clearField(2); - /// Auto-away sets 'away' mode after an inactivity time (only if autodetect_away is set) @$pb.TagNumber(3) $core.int get autoAwayTimeoutMin => $_getIZ(2); @$pb.TagNumber(3) @@ -2282,8 +1709,6 @@ class Account extends $pb.GeneratedMessage { @$pb.TagNumber(3) void clearAutoAwayTimeoutMin() => clearField(3); - /// The contacts DHTList for this account - /// DHT Private @$pb.TagNumber(4) $1.OwnedDHTRecordPointer get contactList => $_getN(3); @$pb.TagNumber(4) @@ -2295,8 +1720,6 @@ class Account extends $pb.GeneratedMessage { @$pb.TagNumber(4) $1.OwnedDHTRecordPointer ensureContactList() => $_ensure(3); - /// The ContactInvitationRecord DHTShortArray for this account - /// DHT Private @$pb.TagNumber(5) $1.OwnedDHTRecordPointer get contactInvitationRecords => $_getN(4); @$pb.TagNumber(5) @@ -2308,8 +1731,6 @@ class Account extends $pb.GeneratedMessage { @$pb.TagNumber(5) $1.OwnedDHTRecordPointer ensureContactInvitationRecords() => $_ensure(4); - /// The Chats DHTList for this account - /// DHT Private @$pb.TagNumber(6) $1.OwnedDHTRecordPointer get chatList => $_getN(5); @$pb.TagNumber(6) @@ -2321,8 +1742,6 @@ class Account extends $pb.GeneratedMessage { @$pb.TagNumber(6) $1.OwnedDHTRecordPointer ensureChatList() => $_ensure(5); - /// The GroupChats DHTList for this account - /// DHT Private @$pb.TagNumber(7) $1.OwnedDHTRecordPointer get groupChatList => $_getN(6); @$pb.TagNumber(7) @@ -2334,7 +1753,6 @@ class Account extends $pb.GeneratedMessage { @$pb.TagNumber(7) $1.OwnedDHTRecordPointer ensureGroupChatList() => $_ensure(6); - /// Free message (max length 128) @$pb.TagNumber(8) $core.String get freeMessage => $_getSZ(7); @$pb.TagNumber(8) @@ -2344,7 +1762,6 @@ class Account extends $pb.GeneratedMessage { @$pb.TagNumber(8) void clearFreeMessage() => clearField(8); - /// Busy message (max length 128) @$pb.TagNumber(9) $core.String get busyMessage => $_getSZ(8); @$pb.TagNumber(9) @@ -2354,7 +1771,6 @@ class Account extends $pb.GeneratedMessage { @$pb.TagNumber(9) void clearBusyMessage() => clearField(9); - /// Away message (max length 128) @$pb.TagNumber(10) $core.String get awayMessage => $_getSZ(9); @$pb.TagNumber(10) @@ -2364,7 +1780,6 @@ class Account extends $pb.GeneratedMessage { @$pb.TagNumber(10) void clearAwayMessage() => clearField(10); - /// Auto-detect away @$pb.TagNumber(11) $core.bool get autodetectAway => $_getBF(10); @$pb.TagNumber(11) @@ -2375,51 +1790,8 @@ class Account extends $pb.GeneratedMessage { void clearAutodetectAway() => clearField(11); } -/// A record of a contact that has accepted a contact invitation -/// Contains a copy of the most recent remote profile as well as -/// a locally edited profile. -/// Contains a copy of the most recent identity from the contact's -/// Master identity dht key -/// -/// Stored in ContactList DHTList class Contact extends $pb.GeneratedMessage { - factory Contact({ - $core.String? nickname, - Profile? profile, - $core.String? superIdentityJson, - $0.TypedKey? identityPublicKey, - $0.TypedKey? remoteConversationRecordKey, - $0.TypedKey? localConversationRecordKey, - $core.bool? showAvailability, - $core.String? notes, - }) { - final $result = create(); - if (nickname != null) { - $result.nickname = nickname; - } - if (profile != null) { - $result.profile = profile; - } - if (superIdentityJson != null) { - $result.superIdentityJson = superIdentityJson; - } - if (identityPublicKey != null) { - $result.identityPublicKey = identityPublicKey; - } - if (remoteConversationRecordKey != null) { - $result.remoteConversationRecordKey = remoteConversationRecordKey; - } - if (localConversationRecordKey != null) { - $result.localConversationRecordKey = localConversationRecordKey; - } - if (showAvailability != null) { - $result.showAvailability = showAvailability; - } - if (notes != null) { - $result.notes = notes; - } - return $result; - } + factory Contact() => create(); Contact._() : super(); factory Contact.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory Contact.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -2457,7 +1829,6 @@ class Contact extends $pb.GeneratedMessage { static Contact getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static Contact? _defaultInstance; - /// Friend's nickname @$pb.TagNumber(1) $core.String get nickname => $_getSZ(0); @$pb.TagNumber(1) @@ -2467,7 +1838,6 @@ class Contact extends $pb.GeneratedMessage { @$pb.TagNumber(1) void clearNickname() => clearField(1); - /// Copy of friend's profile from remote conversation @$pb.TagNumber(2) Profile get profile => $_getN(1); @$pb.TagNumber(2) @@ -2479,7 +1849,6 @@ class Contact extends $pb.GeneratedMessage { @$pb.TagNumber(2) Profile ensureProfile() => $_ensure(1); - /// Copy of friend's SuperIdentity in JSON from remote conversation @$pb.TagNumber(3) $core.String get superIdentityJson => $_getSZ(2); @$pb.TagNumber(3) @@ -2489,7 +1858,6 @@ class Contact extends $pb.GeneratedMessage { @$pb.TagNumber(3) void clearSuperIdentityJson() => clearField(3); - /// Copy of friend's most recent identity public key from their identityMaster @$pb.TagNumber(4) $0.TypedKey get identityPublicKey => $_getN(3); @$pb.TagNumber(4) @@ -2501,7 +1869,6 @@ class Contact extends $pb.GeneratedMessage { @$pb.TagNumber(4) $0.TypedKey ensureIdentityPublicKey() => $_ensure(3); - /// Remote conversation key to sync from friend @$pb.TagNumber(5) $0.TypedKey get remoteConversationRecordKey => $_getN(4); @$pb.TagNumber(5) @@ -2513,7 +1880,6 @@ class Contact extends $pb.GeneratedMessage { @$pb.TagNumber(5) $0.TypedKey ensureRemoteConversationRecordKey() => $_ensure(4); - /// Our conversation key for friend to sync @$pb.TagNumber(6) $0.TypedKey get localConversationRecordKey => $_getN(5); @$pb.TagNumber(6) @@ -2525,7 +1891,6 @@ class Contact extends $pb.GeneratedMessage { @$pb.TagNumber(6) $0.TypedKey ensureLocalConversationRecordKey() => $_ensure(5); - /// Show availability to this contact @$pb.TagNumber(7) $core.bool get showAvailability => $_getBF(6); @$pb.TagNumber(7) @@ -2535,7 +1900,6 @@ class Contact extends $pb.GeneratedMessage { @$pb.TagNumber(7) void clearShowAvailability() => clearField(7); - /// Notes about this friend @$pb.TagNumber(8) $core.String get notes => $_getSZ(7); @$pb.TagNumber(8) @@ -2546,24 +1910,8 @@ class Contact extends $pb.GeneratedMessage { void clearNotes() => clearField(8); } -/// Invitation that is shared for VeilidChat contact connections -/// serialized to QR code or data blob, not send over DHT, out of band. -/// Writer secret is unique to this invitation. Writer public key is in the ContactRequestPrivate -/// in the ContactRequestInbox subkey 0 DHT key class ContactInvitation extends $pb.GeneratedMessage { - factory ContactInvitation({ - $0.TypedKey? contactRequestInboxKey, - $core.List<$core.int>? writerSecret, - }) { - final $result = create(); - if (contactRequestInboxKey != null) { - $result.contactRequestInboxKey = contactRequestInboxKey; - } - if (writerSecret != null) { - $result.writerSecret = writerSecret; - } - return $result; - } + factory ContactInvitation() => create(); ContactInvitation._() : super(); factory ContactInvitation.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ContactInvitation.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -2595,7 +1943,6 @@ class ContactInvitation extends $pb.GeneratedMessage { static ContactInvitation getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static ContactInvitation? _defaultInstance; - /// Contact request DHT record key @$pb.TagNumber(1) $0.TypedKey get contactRequestInboxKey => $_getN(0); @$pb.TagNumber(1) @@ -2607,7 +1954,6 @@ class ContactInvitation extends $pb.GeneratedMessage { @$pb.TagNumber(1) $0.TypedKey ensureContactRequestInboxKey() => $_ensure(0); - /// Writer secret key bytes possibly encrypted with nonce appended @$pb.TagNumber(2) $core.List<$core.int> get writerSecret => $_getN(1); @$pb.TagNumber(2) @@ -2618,21 +1964,8 @@ class ContactInvitation extends $pb.GeneratedMessage { void clearWriterSecret() => clearField(2); } -/// Signature of invitation with identity class SignedContactInvitation extends $pb.GeneratedMessage { - factory SignedContactInvitation({ - $core.List<$core.int>? contactInvitation, - $0.Signature? identitySignature, - }) { - final $result = create(); - if (contactInvitation != null) { - $result.contactInvitation = contactInvitation; - } - if (identitySignature != null) { - $result.identitySignature = identitySignature; - } - return $result; - } + factory SignedContactInvitation() => create(); SignedContactInvitation._() : super(); factory SignedContactInvitation.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory SignedContactInvitation.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -2664,7 +1997,6 @@ class SignedContactInvitation extends $pb.GeneratedMessage { static SignedContactInvitation getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static SignedContactInvitation? _defaultInstance; - /// The serialized bytes for the contact invitation @$pb.TagNumber(1) $core.List<$core.int> get contactInvitation => $_getN(0); @$pb.TagNumber(1) @@ -2674,7 +2006,6 @@ class SignedContactInvitation extends $pb.GeneratedMessage { @$pb.TagNumber(1) void clearContactInvitation() => clearField(1); - /// The signature of the contact_invitation bytes with the identity @$pb.TagNumber(2) $0.Signature get identitySignature => $_getN(1); @$pb.TagNumber(2) @@ -2687,22 +2018,8 @@ class SignedContactInvitation extends $pb.GeneratedMessage { $0.Signature ensureIdentitySignature() => $_ensure(1); } -/// Contact request unicastinbox on the DHT -/// DHTSchema: SMPL 1 owner key, 1 writer key symmetrically encrypted with writer secret class ContactRequest extends $pb.GeneratedMessage { - factory ContactRequest({ - EncryptionKeyType? encryptionKeyType, - $core.List<$core.int>? private, - }) { - final $result = create(); - if (encryptionKeyType != null) { - $result.encryptionKeyType = encryptionKeyType; - } - if (private != null) { - $result.private = private; - } - return $result; - } + factory ContactRequest() => create(); ContactRequest._() : super(); factory ContactRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ContactRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -2734,7 +2051,6 @@ class ContactRequest extends $pb.GeneratedMessage { static ContactRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static ContactRequest? _defaultInstance; - /// The kind of encryption used on the unicastinbox writer key @$pb.TagNumber(1) EncryptionKeyType get encryptionKeyType => $_getN(0); @$pb.TagNumber(1) @@ -2744,7 +2060,6 @@ class ContactRequest extends $pb.GeneratedMessage { @$pb.TagNumber(1) void clearEncryptionKeyType() => clearField(1); - /// The private part encoded and symmetrically encrypted with the unicastinbox writer secret @$pb.TagNumber(2) $core.List<$core.int> get private => $_getN(1); @$pb.TagNumber(2) @@ -2755,34 +2070,8 @@ class ContactRequest extends $pb.GeneratedMessage { void clearPrivate() => clearField(2); } -/// The private part of a possibly encrypted contact request -/// Symmetrically encrypted with writer secret class ContactRequestPrivate extends $pb.GeneratedMessage { - factory ContactRequestPrivate({ - $0.CryptoKey? writerKey, - Profile? profile, - $0.TypedKey? superIdentityRecordKey, - $0.TypedKey? chatRecordKey, - $fixnum.Int64? expiration, - }) { - final $result = create(); - if (writerKey != null) { - $result.writerKey = writerKey; - } - if (profile != null) { - $result.profile = profile; - } - if (superIdentityRecordKey != null) { - $result.superIdentityRecordKey = superIdentityRecordKey; - } - if (chatRecordKey != null) { - $result.chatRecordKey = chatRecordKey; - } - if (expiration != null) { - $result.expiration = expiration; - } - return $result; - } + factory ContactRequestPrivate() => create(); ContactRequestPrivate._() : super(); factory ContactRequestPrivate.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ContactRequestPrivate.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -2817,7 +2106,6 @@ class ContactRequestPrivate extends $pb.GeneratedMessage { static ContactRequestPrivate getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static ContactRequestPrivate? _defaultInstance; - /// Writer public key for signing writes to contact request unicastinbox @$pb.TagNumber(1) $0.CryptoKey get writerKey => $_getN(0); @$pb.TagNumber(1) @@ -2829,7 +2117,6 @@ class ContactRequestPrivate extends $pb.GeneratedMessage { @$pb.TagNumber(1) $0.CryptoKey ensureWriterKey() => $_ensure(0); - /// Snapshot of profile @$pb.TagNumber(2) Profile get profile => $_getN(1); @$pb.TagNumber(2) @@ -2841,7 +2128,6 @@ class ContactRequestPrivate extends $pb.GeneratedMessage { @$pb.TagNumber(2) Profile ensureProfile() => $_ensure(1); - /// SuperIdentity DHT record key @$pb.TagNumber(3) $0.TypedKey get superIdentityRecordKey => $_getN(2); @$pb.TagNumber(3) @@ -2853,7 +2139,6 @@ class ContactRequestPrivate extends $pb.GeneratedMessage { @$pb.TagNumber(3) $0.TypedKey ensureSuperIdentityRecordKey() => $_ensure(2); - /// Local chat DHT record key @$pb.TagNumber(4) $0.TypedKey get chatRecordKey => $_getN(3); @$pb.TagNumber(4) @@ -2865,7 +2150,6 @@ class ContactRequestPrivate extends $pb.GeneratedMessage { @$pb.TagNumber(4) $0.TypedKey ensureChatRecordKey() => $_ensure(3); - /// Expiration timestamp @$pb.TagNumber(5) $fixnum.Int64 get expiration => $_getI64(4); @$pb.TagNumber(5) @@ -2876,25 +2160,8 @@ class ContactRequestPrivate extends $pb.GeneratedMessage { void clearExpiration() => clearField(5); } -/// To accept or reject a contact request, fill this out and send to the ContactRequest unicastinbox class ContactResponse extends $pb.GeneratedMessage { - factory ContactResponse({ - $core.bool? accept, - $0.TypedKey? superIdentityRecordKey, - $0.TypedKey? remoteConversationRecordKey, - }) { - final $result = create(); - if (accept != null) { - $result.accept = accept; - } - if (superIdentityRecordKey != null) { - $result.superIdentityRecordKey = superIdentityRecordKey; - } - if (remoteConversationRecordKey != null) { - $result.remoteConversationRecordKey = remoteConversationRecordKey; - } - return $result; - } + factory ContactResponse() => create(); ContactResponse._() : super(); factory ContactResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ContactResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -2927,7 +2194,6 @@ class ContactResponse extends $pb.GeneratedMessage { static ContactResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static ContactResponse? _defaultInstance; - /// Accept or reject @$pb.TagNumber(1) $core.bool get accept => $_getBF(0); @$pb.TagNumber(1) @@ -2937,7 +2203,6 @@ class ContactResponse extends $pb.GeneratedMessage { @$pb.TagNumber(1) void clearAccept() => clearField(1); - /// Remote SuperIdentity DHT record key @$pb.TagNumber(2) $0.TypedKey get superIdentityRecordKey => $_getN(1); @$pb.TagNumber(2) @@ -2949,7 +2214,6 @@ class ContactResponse extends $pb.GeneratedMessage { @$pb.TagNumber(2) $0.TypedKey ensureSuperIdentityRecordKey() => $_ensure(1); - /// Remote chat DHT record key if accepted @$pb.TagNumber(3) $0.TypedKey get remoteConversationRecordKey => $_getN(2); @$pb.TagNumber(3) @@ -2962,22 +2226,8 @@ class ContactResponse extends $pb.GeneratedMessage { $0.TypedKey ensureRemoteConversationRecordKey() => $_ensure(2); } -/// Signature of response with identity -/// Symmetrically encrypted with writer secret class SignedContactResponse extends $pb.GeneratedMessage { - factory SignedContactResponse({ - $core.List<$core.int>? contactResponse, - $0.Signature? identitySignature, - }) { - final $result = create(); - if (contactResponse != null) { - $result.contactResponse = contactResponse; - } - if (identitySignature != null) { - $result.identitySignature = identitySignature; - } - return $result; - } + factory SignedContactResponse() => create(); SignedContactResponse._() : super(); factory SignedContactResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory SignedContactResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -3009,7 +2259,6 @@ class SignedContactResponse extends $pb.GeneratedMessage { static SignedContactResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static SignedContactResponse? _defaultInstance; - /// Serialized bytes for ContactResponse @$pb.TagNumber(1) $core.List<$core.int> get contactResponse => $_getN(0); @$pb.TagNumber(1) @@ -3019,7 +2268,6 @@ class SignedContactResponse extends $pb.GeneratedMessage { @$pb.TagNumber(1) void clearContactResponse() => clearField(1); - /// Signature of the contact_accept bytes with the identity @$pb.TagNumber(2) $0.Signature get identitySignature => $_getN(1); @$pb.TagNumber(2) @@ -3032,45 +2280,8 @@ class SignedContactResponse extends $pb.GeneratedMessage { $0.Signature ensureIdentitySignature() => $_ensure(1); } -/// Contact request record kept in Account DHTList to keep track of extant contact invitations class ContactInvitationRecord extends $pb.GeneratedMessage { - factory ContactInvitationRecord({ - $1.OwnedDHTRecordPointer? contactRequestInbox, - $0.CryptoKey? writerKey, - $0.CryptoKey? writerSecret, - $0.TypedKey? localConversationRecordKey, - $fixnum.Int64? expiration, - $core.List<$core.int>? invitation, - $core.String? message, - $core.String? recipient, - }) { - final $result = create(); - if (contactRequestInbox != null) { - $result.contactRequestInbox = contactRequestInbox; - } - if (writerKey != null) { - $result.writerKey = writerKey; - } - if (writerSecret != null) { - $result.writerSecret = writerSecret; - } - if (localConversationRecordKey != null) { - $result.localConversationRecordKey = localConversationRecordKey; - } - if (expiration != null) { - $result.expiration = expiration; - } - if (invitation != null) { - $result.invitation = invitation; - } - if (message != null) { - $result.message = message; - } - if (recipient != null) { - $result.recipient = recipient; - } - return $result; - } + factory ContactInvitationRecord() => create(); ContactInvitationRecord._() : super(); factory ContactInvitationRecord.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory ContactInvitationRecord.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -3083,7 +2294,6 @@ class ContactInvitationRecord extends $pb.GeneratedMessage { ..a<$fixnum.Int64>(5, _omitFieldNames ? '' : 'expiration', $pb.PbFieldType.OU6, defaultOrMaker: $fixnum.Int64.ZERO) ..a<$core.List<$core.int>>(6, _omitFieldNames ? '' : 'invitation', $pb.PbFieldType.OY) ..aOS(7, _omitFieldNames ? '' : 'message') - ..aOS(8, _omitFieldNames ? '' : 'recipient') ..hasRequiredFields = false ; @@ -3108,7 +2318,6 @@ class ContactInvitationRecord extends $pb.GeneratedMessage { static ContactInvitationRecord getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static ContactInvitationRecord? _defaultInstance; - /// Contact request unicastinbox DHT record key (parent is accountkey) @$pb.TagNumber(1) $1.OwnedDHTRecordPointer get contactRequestInbox => $_getN(0); @$pb.TagNumber(1) @@ -3120,7 +2329,6 @@ class ContactInvitationRecord extends $pb.GeneratedMessage { @$pb.TagNumber(1) $1.OwnedDHTRecordPointer ensureContactRequestInbox() => $_ensure(0); - /// Writer key sent to contact for the contact_request_inbox smpl inbox subkey @$pb.TagNumber(2) $0.CryptoKey get writerKey => $_getN(1); @$pb.TagNumber(2) @@ -3132,7 +2340,6 @@ class ContactInvitationRecord extends $pb.GeneratedMessage { @$pb.TagNumber(2) $0.CryptoKey ensureWriterKey() => $_ensure(1); - /// Writer secret sent encrypted in the invitation @$pb.TagNumber(3) $0.CryptoKey get writerSecret => $_getN(2); @$pb.TagNumber(3) @@ -3144,7 +2351,6 @@ class ContactInvitationRecord extends $pb.GeneratedMessage { @$pb.TagNumber(3) $0.CryptoKey ensureWriterSecret() => $_ensure(2); - /// Local chat DHT record key (parent is accountkey, will be moved to Contact if accepted) @$pb.TagNumber(4) $0.TypedKey get localConversationRecordKey => $_getN(3); @$pb.TagNumber(4) @@ -3156,7 +2362,6 @@ class ContactInvitationRecord extends $pb.GeneratedMessage { @$pb.TagNumber(4) $0.TypedKey ensureLocalConversationRecordKey() => $_ensure(3); - /// Expiration timestamp @$pb.TagNumber(5) $fixnum.Int64 get expiration => $_getI64(4); @$pb.TagNumber(5) @@ -3166,7 +2371,6 @@ class ContactInvitationRecord extends $pb.GeneratedMessage { @$pb.TagNumber(5) void clearExpiration() => clearField(5); - /// A copy of the raw SignedContactInvitation invitation bytes post-encryption and signing @$pb.TagNumber(6) $core.List<$core.int> get invitation => $_getN(5); @$pb.TagNumber(6) @@ -3176,7 +2380,6 @@ class ContactInvitationRecord extends $pb.GeneratedMessage { @$pb.TagNumber(6) void clearInvitation() => clearField(6); - /// The message sent along with the invitation @$pb.TagNumber(7) $core.String get message => $_getSZ(6); @$pb.TagNumber(7) @@ -3185,16 +2388,6 @@ class ContactInvitationRecord extends $pb.GeneratedMessage { $core.bool hasMessage() => $_has(6); @$pb.TagNumber(7) void clearMessage() => clearField(7); - - /// The recipient sent along with the invitation - @$pb.TagNumber(8) - $core.String get recipient => $_getSZ(7); - @$pb.TagNumber(8) - set recipient($core.String v) { $_setString(7, v); } - @$pb.TagNumber(8) - $core.bool hasRecipient() => $_has(7); - @$pb.TagNumber(8) - void clearRecipient() => clearField(8); } diff --git a/lib/proto/veilidchat.pbenum.dart b/lib/proto/veilidchat.pbenum.dart index 42009e8..9133788 100644 --- a/lib/proto/veilidchat.pbenum.dart +++ b/lib/proto/veilidchat.pbenum.dart @@ -4,7 +4,7 @@ // // @dart = 2.12 -// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: annotate_overrides, camel_case_types // ignore_for_file: constant_identifier_names, library_prefixes // ignore_for_file: non_constant_identifier_names, prefer_final_fields // ignore_for_file: unnecessary_import, unnecessary_this, unused_import @@ -13,7 +13,6 @@ import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; -/// Contact availability class Availability extends $pb.ProtobufEnum { static const Availability AVAILABILITY_UNSPECIFIED = Availability._(0, _omitEnumNames ? '' : 'AVAILABILITY_UNSPECIFIED'); static const Availability AVAILABILITY_OFFLINE = Availability._(1, _omitEnumNames ? '' : 'AVAILABILITY_OFFLINE'); @@ -35,7 +34,6 @@ class Availability extends $pb.ProtobufEnum { const Availability._($core.int v, $core.String n) : super(v, n); } -/// Encryption used on secret keys class EncryptionKeyType extends $pb.ProtobufEnum { static const EncryptionKeyType ENCRYPTION_KEY_TYPE_UNSPECIFIED = EncryptionKeyType._(0, _omitEnumNames ? '' : 'ENCRYPTION_KEY_TYPE_UNSPECIFIED'); static const EncryptionKeyType ENCRYPTION_KEY_TYPE_NONE = EncryptionKeyType._(1, _omitEnumNames ? '' : 'ENCRYPTION_KEY_TYPE_NONE'); @@ -55,7 +53,6 @@ class EncryptionKeyType extends $pb.ProtobufEnum { const EncryptionKeyType._($core.int v, $core.String n) : super(v, n); } -/// Scope of a chat class Scope extends $pb.ProtobufEnum { static const Scope WATCHERS = Scope._(0, _omitEnumNames ? '' : 'WATCHERS'); static const Scope MODERATED = Scope._(1, _omitEnumNames ? '' : 'MODERATED'); diff --git a/lib/proto/veilidchat.pbjson.dart b/lib/proto/veilidchat.pbjson.dart index 0958343..ec327f4 100644 --- a/lib/proto/veilidchat.pbjson.dart +++ b/lib/proto/veilidchat.pbjson.dart @@ -4,7 +4,7 @@ // // @dart = 2.12 -// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: annotate_overrides, camel_case_types // ignore_for_file: constant_identifier_names, library_prefixes // ignore_for_file: non_constant_identifier_names, prefer_final_fields // ignore_for_file: unnecessary_import, unnecessary_this, unused_import @@ -215,7 +215,6 @@ const Message$json = { {'1': 'permissions', '3': 9, '4': 1, '5': 11, '6': '.veilidchat.Message.ControlPermissions', '9': 0, '10': 'permissions'}, {'1': 'membership', '3': 10, '4': 1, '5': 11, '6': '.veilidchat.Message.ControlMembership', '9': 0, '10': 'membership'}, {'1': 'moderation', '3': 11, '4': 1, '5': 11, '6': '.veilidchat.Message.ControlModeration', '9': 0, '10': 'moderation'}, - {'1': 'readReceipt', '3': 13, '4': 1, '5': 11, '6': '.veilidchat.Message.ControlReadReceipt', '9': 0, '10': 'readReceipt'}, {'1': 'signature', '3': 12, '4': 1, '5': 11, '6': '.veilid.Signature', '10': 'signature'}, ], '3': [Message_Text$json, Message_Secret$json, Message_ControlDelete$json, Message_ControlErase$json, Message_ControlSettings$json, Message_ControlPermissions$json, Message_ControlMembership$json, Message_ControlModeration$json, Message_ControlReadReceipt$json], @@ -319,24 +318,22 @@ final $typed_data.Uint8List messageDescriptor = $convert.base64Decode( 'twZXJtaXNzaW9ucxgJIAEoCzImLnZlaWxpZGNoYXQuTWVzc2FnZS5Db250cm9sUGVybWlzc2lv' 'bnNIAFILcGVybWlzc2lvbnMSRwoKbWVtYmVyc2hpcBgKIAEoCzIlLnZlaWxpZGNoYXQuTWVzc2' 'FnZS5Db250cm9sTWVtYmVyc2hpcEgAUgptZW1iZXJzaGlwEkcKCm1vZGVyYXRpb24YCyABKAsy' - 'JS52ZWlsaWRjaGF0Lk1lc3NhZ2UuQ29udHJvbE1vZGVyYXRpb25IAFIKbW9kZXJhdGlvbhJKCg' - 'tyZWFkUmVjZWlwdBgNIAEoCzImLnZlaWxpZGNoYXQuTWVzc2FnZS5Db250cm9sUmVhZFJlY2Vp' - 'cHRIAFILcmVhZFJlY2VpcHQSLwoJc2lnbmF0dXJlGAwgASgLMhEudmVpbGlkLlNpZ25hdHVyZV' - 'IJc2lnbmF0dXJlGuUBCgRUZXh0EhIKBHRleHQYASABKAlSBHRleHQSGQoFdG9waWMYAiABKAlI' - 'AFIFdG9waWOIAQESHgoIcmVwbHlfaWQYAyABKAxIAVIHcmVwbHlJZIgBARIeCgpleHBpcmF0aW' - '9uGAQgASgEUgpleHBpcmF0aW9uEh0KCnZpZXdfbGltaXQYBSABKA1SCXZpZXdMaW1pdBI4Cgth' - 'dHRhY2htZW50cxgGIAMoCzIWLnZlaWxpZGNoYXQuQXR0YWNobWVudFILYXR0YWNobWVudHNCCA' - 'oGX3RvcGljQgsKCV9yZXBseV9pZBpICgZTZWNyZXQSHgoKY2lwaGVydGV4dBgBIAEoDFIKY2lw' - 'aGVydGV4dBIeCgpleHBpcmF0aW9uGAIgASgEUgpleHBpcmF0aW9uGiEKDUNvbnRyb2xEZWxldG' - 'USEAoDaWRzGAEgAygMUgNpZHMaLAoMQ29udHJvbEVyYXNlEhwKCXRpbWVzdGFtcBgBIAEoBFIJ' - 'dGltZXN0YW1wGkcKD0NvbnRyb2xTZXR0aW5ncxI0CghzZXR0aW5ncxgBIAEoCzIYLnZlaWxpZG' - 'NoYXQuQ2hhdFNldHRpbmdzUghzZXR0aW5ncxpPChJDb250cm9sUGVybWlzc2lvbnMSOQoLcGVy' - 'bWlzc2lvbnMYASABKAsyFy52ZWlsaWRjaGF0LlBlcm1pc3Npb25zUgtwZXJtaXNzaW9ucxpLCh' - 'FDb250cm9sTWVtYmVyc2hpcBI2CgptZW1iZXJzaGlwGAEgASgLMhYudmVpbGlkY2hhdC5NZW1i' - 'ZXJzaGlwUgptZW1iZXJzaGlwGlkKEUNvbnRyb2xNb2RlcmF0aW9uEiEKDGFjY2VwdGVkX2lkcx' - 'gBIAMoDFILYWNjZXB0ZWRJZHMSIQoMcmVqZWN0ZWRfaWRzGAIgAygMUgtyZWplY3RlZElkcxov' - 'ChJDb250cm9sUmVhZFJlY2VpcHQSGQoIcmVhZF9pZHMYASADKAxSB3JlYWRJZHNCBgoEa2luZA' - '=='); + 'JS52ZWlsaWRjaGF0Lk1lc3NhZ2UuQ29udHJvbE1vZGVyYXRpb25IAFIKbW9kZXJhdGlvbhIvCg' + 'lzaWduYXR1cmUYDCABKAsyES52ZWlsaWQuU2lnbmF0dXJlUglzaWduYXR1cmUa5QEKBFRleHQS' + 'EgoEdGV4dBgBIAEoCVIEdGV4dBIZCgV0b3BpYxgCIAEoCUgAUgV0b3BpY4gBARIeCghyZXBseV' + '9pZBgDIAEoDEgBUgdyZXBseUlkiAEBEh4KCmV4cGlyYXRpb24YBCABKARSCmV4cGlyYXRpb24S' + 'HQoKdmlld19saW1pdBgFIAEoDVIJdmlld0xpbWl0EjgKC2F0dGFjaG1lbnRzGAYgAygLMhYudm' + 'VpbGlkY2hhdC5BdHRhY2htZW50UgthdHRhY2htZW50c0IICgZfdG9waWNCCwoJX3JlcGx5X2lk' + 'GkgKBlNlY3JldBIeCgpjaXBoZXJ0ZXh0GAEgASgMUgpjaXBoZXJ0ZXh0Eh4KCmV4cGlyYXRpb2' + '4YAiABKARSCmV4cGlyYXRpb24aIQoNQ29udHJvbERlbGV0ZRIQCgNpZHMYASADKAxSA2lkcxos' + 'CgxDb250cm9sRXJhc2USHAoJdGltZXN0YW1wGAEgASgEUgl0aW1lc3RhbXAaRwoPQ29udHJvbF' + 'NldHRpbmdzEjQKCHNldHRpbmdzGAEgASgLMhgudmVpbGlkY2hhdC5DaGF0U2V0dGluZ3NSCHNl' + 'dHRpbmdzGk8KEkNvbnRyb2xQZXJtaXNzaW9ucxI5CgtwZXJtaXNzaW9ucxgBIAEoCzIXLnZlaW' + 'xpZGNoYXQuUGVybWlzc2lvbnNSC3Blcm1pc3Npb25zGksKEUNvbnRyb2xNZW1iZXJzaGlwEjYK' + 'Cm1lbWJlcnNoaXAYASABKAsyFi52ZWlsaWRjaGF0Lk1lbWJlcnNoaXBSCm1lbWJlcnNoaXAaWQ' + 'oRQ29udHJvbE1vZGVyYXRpb24SIQoMYWNjZXB0ZWRfaWRzGAEgAygMUgthY2NlcHRlZElkcxIh' + 'CgxyZWplY3RlZF9pZHMYAiADKAxSC3JlamVjdGVkSWRzGi8KEkNvbnRyb2xSZWFkUmVjZWlwdB' + 'IZCghyZWFkX2lkcxgBIAMoDFIHcmVhZElkc0IGCgRraW5k'); @$core.Deprecated('Use reconciledMessageDescriptor instead') const ReconciledMessage$json = { @@ -631,7 +628,6 @@ const ContactInvitationRecord$json = { {'1': 'expiration', '3': 5, '4': 1, '5': 4, '10': 'expiration'}, {'1': 'invitation', '3': 6, '4': 1, '5': 12, '10': 'invitation'}, {'1': 'message', '3': 7, '4': 1, '5': 9, '10': 'message'}, - {'1': 'recipient', '3': 8, '4': 1, '5': 9, '10': 'recipient'}, ], }; @@ -643,6 +639,5 @@ final $typed_data.Uint8List contactInvitationRecordDescriptor = $convert.base64D 'NlY3JldBgDIAEoCzIRLnZlaWxpZC5DcnlwdG9LZXlSDHdyaXRlclNlY3JldBJTCh1sb2NhbF9j' 'b252ZXJzYXRpb25fcmVjb3JkX2tleRgEIAEoCzIQLnZlaWxpZC5UeXBlZEtleVIabG9jYWxDb2' '52ZXJzYXRpb25SZWNvcmRLZXkSHgoKZXhwaXJhdGlvbhgFIAEoBFIKZXhwaXJhdGlvbhIeCgpp' - 'bnZpdGF0aW9uGAYgASgMUgppbnZpdGF0aW9uEhgKB21lc3NhZ2UYByABKAlSB21lc3NhZ2USHA' - 'oJcmVjaXBpZW50GAggASgJUglyZWNpcGllbnQ='); + 'bnZpdGF0aW9uGAYgASgMUgppbnZpdGF0aW9uEhgKB21lc3NhZ2UYByABKAlSB21lc3NhZ2U='); diff --git a/lib/proto/veilidchat.pbserver.dart b/lib/proto/veilidchat.pbserver.dart index 047feed..02a9ae4 100644 --- a/lib/proto/veilidchat.pbserver.dart +++ b/lib/proto/veilidchat.pbserver.dart @@ -4,7 +4,7 @@ // // @dart = 2.12 -// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: annotate_overrides, camel_case_types // ignore_for_file: constant_identifier_names // ignore_for_file: deprecated_member_use_from_same_package, library_prefixes // ignore_for_file: non_constant_identifier_names, prefer_final_fields diff --git a/lib/proto/veilidchat.proto b/lib/proto/veilidchat.proto index 5bff89c..0d4ca0a 100644 --- a/lib/proto/veilidchat.proto +++ b/lib/proto/veilidchat.proto @@ -228,7 +228,6 @@ message Message { ControlPermissions permissions = 9; ControlMembership membership = 10; ControlModeration moderation = 11; - ControlReadReceipt readReceipt = 13; } // Author signature over all of the fields and attachment signatures @@ -479,6 +478,4 @@ message ContactInvitationRecord { bytes invitation = 6; // The message sent along with the invitation string message = 7; - // The recipient sent along with the invitation - string recipient = 8; } \ No newline at end of file diff --git a/lib/router/cubits/router_cubit.dart b/lib/router/cubits/router_cubit.dart index d651611..d442485 100644 --- a/lib/router/cubits/router_cubit.dart +++ b/lib/router/cubits/router_cubit.dart @@ -11,21 +11,19 @@ import 'package:veilid_support/veilid_support.dart'; import '../../../account_manager/account_manager.dart'; import '../../layout/layout.dart'; +import '../../proto/proto.dart' as proto; import '../../settings/settings.dart'; -import '../../theme/theme.dart'; import '../../tools/tools.dart'; import '../../veilid_processor/views/developer.dart'; import '../views/router_shell.dart'; -export 'package:go_router/go_router.dart'; - part 'router_cubit.freezed.dart'; part 'router_cubit.g.dart'; final _rootNavKey = GlobalKey(debugLabel: 'rootNavKey'); @freezed -sealed class RouterState with _$RouterState { +class RouterState with _$RouterState { const factory RouterState({ required bool hasAnyAccount, }) = _RouterState; @@ -45,8 +43,10 @@ class RouterCubit extends Cubit { case AccountRepositoryChange.localAccounts: emit(state.copyWith( hasAnyAccount: accountRepository.getLocalAccounts().isNotEmpty)); + break; case AccountRepositoryChange.userLogins: case AccountRepositoryChange.activeLocalAccount: + break; } }); } @@ -68,53 +68,29 @@ class RouterCubit extends Cubit { ), GoRoute( path: '/edit_account', - redirect: (_, state) { - final extra = state.extra; - if (extra == null || - extra is! List || - extra[0] is! TypedKey) { - return '/'; - } - return null; - }, builder: (context, state) { - final extra = state.extra! as List; + final extra = state.extra! as List; return EditAccountPage( - superIdentityRecordKey: extra[0] as TypedKey, - initialValue: extra[1] as AccountSpec, - accountRecord: extra[2] as OwnedDHTRecordPointer, + superIdentityRecordKey: extra[0]! as TypedKey, + existingAccount: extra[1]! as proto.Account, + accountRecord: extra[2]! as OwnedDHTRecordPointer, ); }, ), GoRoute( - path: '/new_account', - builder: (context, state) => const NewAccountPage(), - routes: [ - GoRoute( - path: 'recovery_key', - redirect: (_, state) { - final extra = state.extra; - if (extra == null || - extra is! List || - extra[0] is! WritableSuperIdentity || - extra[1] is! String || - extra[2] is! bool) { - return '/'; - } - return null; - }, - builder: (context, state) { - final extra = state.extra! as List; + path: '/new_account', + builder: (context, state) => const NewAccountPage(), + ), + GoRoute( + path: '/new_account/recovery_key', + builder: (context, state) { + final extra = state.extra! as List; - return PopControl( - dismissible: false, - child: ShowRecoveryKeyPage( - writableSuperIdentity: - extra[0] as WritableSuperIdentity, - name: extra[1] as String, - isFirstAccount: extra[2] as bool)); - }), - ]), + return ShowRecoveryKeyPage( + writableSuperIdentity: + extra[0]! as WritableSuperIdentity, + name: extra[1]! as String); + }), GoRoute( path: '/settings', builder: (context, state) => const SettingsPage(), @@ -128,8 +104,14 @@ class RouterCubit extends Cubit { /// Redirects when our state changes String? redirect(BuildContext context, GoRouterState goRouterState) { + // No matter where we are, if there's not + switch (goRouterState.matchedLocation) { - // We can go to any of these routes without an account. + case '/': + if (!state.hasAnyAccount) { + return '/new_account'; + } + return null; case '/new_account': return null; case '/new_account/recovery_key': @@ -138,8 +120,6 @@ class RouterCubit extends Cubit { return null; case '/developer': return null; - // Otherwise, if there's no account, - // we need to go to the new account page. default: return state.hasAnyAccount ? null : '/new_account'; } @@ -154,7 +134,7 @@ class RouterCubit extends Cubit { return _router = GoRouter( navigatorKey: _rootNavKey, refreshListenable: StreamListenable(stream.startWith(state).distinct()), - debugLogDiagnostics: kIsDebugMode, + debugLogDiagnostics: kDebugMode, initialLocation: '/', routes: routes, redirect: redirect, @@ -167,14 +147,3 @@ class RouterCubit extends Cubit { _accountRepositorySubscription; GoRouter? _router; } - -extension GoRouterExtension on GoRouter { - String location() { - final lastMatch = routerDelegate.currentConfiguration.last; - final matchList = lastMatch is ImperativeRouteMatch - ? lastMatch.matches - : routerDelegate.currentConfiguration; - final location = matchList.uri.toString(); - return location; - } -} diff --git a/lib/router/cubits/router_cubit.freezed.dart b/lib/router/cubits/router_cubit.freezed.dart index 0f5b285..e44cd91 100644 --- a/lib/router/cubits/router_cubit.freezed.dart +++ b/lib/router/cubits/router_cubit.freezed.dart @@ -1,4 +1,3 @@ -// dart format width=80 // coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND // ignore_for_file: type=lint @@ -10,75 +9,85 @@ part of 'router_cubit.dart'; // FreezedGenerator // ************************************************************************** -// dart format off T _$identity(T value) => value; -/// @nodoc -mixin _$RouterState implements DiagnosticableTreeMixin { - bool get hasAnyAccount; +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - /// Create a copy of RouterState - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') - $RouterStateCopyWith get copyWith => - _$RouterStateCopyWithImpl(this as RouterState, _$identity); - - /// Serializes this RouterState to a JSON map. - Map toJson(); - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - properties - ..add(DiagnosticsProperty('type', 'RouterState')) - ..add(DiagnosticsProperty('hasAnyAccount', hasAnyAccount)); - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is RouterState && - (identical(other.hasAnyAccount, hasAnyAccount) || - other.hasAnyAccount == hasAnyAccount)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash(runtimeType, hasAnyAccount); - - @override - String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { - return 'RouterState(hasAnyAccount: $hasAnyAccount)'; - } +RouterState _$RouterStateFromJson(Map json) { + return _RouterState.fromJson(json); } /// @nodoc -abstract mixin class $RouterStateCopyWith<$Res> { +mixin _$RouterState { + bool get hasAnyAccount => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $RouterStateCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $RouterStateCopyWith<$Res> { factory $RouterStateCopyWith( - RouterState value, $Res Function(RouterState) _then) = - _$RouterStateCopyWithImpl; + RouterState value, $Res Function(RouterState) then) = + _$RouterStateCopyWithImpl<$Res, RouterState>; @useResult $Res call({bool hasAnyAccount}); } /// @nodoc -class _$RouterStateCopyWithImpl<$Res> implements $RouterStateCopyWith<$Res> { - _$RouterStateCopyWithImpl(this._self, this._then); +class _$RouterStateCopyWithImpl<$Res, $Val extends RouterState> + implements $RouterStateCopyWith<$Res> { + _$RouterStateCopyWithImpl(this._value, this._then); - final RouterState _self; - final $Res Function(RouterState) _then; + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; - /// Create a copy of RouterState - /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ Object? hasAnyAccount = null, }) { - return _then(_self.copyWith( + return _then(_value.copyWith( hasAnyAccount: null == hasAnyAccount - ? _self.hasAnyAccount + ? _value.hasAnyAccount + : hasAnyAccount // ignore: cast_nullable_to_non_nullable + as bool, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$RouterStateImplCopyWith<$Res> + implements $RouterStateCopyWith<$Res> { + factory _$$RouterStateImplCopyWith( + _$RouterStateImpl value, $Res Function(_$RouterStateImpl) then) = + __$$RouterStateImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({bool hasAnyAccount}); +} + +/// @nodoc +class __$$RouterStateImplCopyWithImpl<$Res> + extends _$RouterStateCopyWithImpl<$Res, _$RouterStateImpl> + implements _$$RouterStateImplCopyWith<$Res> { + __$$RouterStateImplCopyWithImpl( + _$RouterStateImpl _value, $Res Function(_$RouterStateImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? hasAnyAccount = null, + }) { + return _then(_$RouterStateImpl( + hasAnyAccount: null == hasAnyAccount + ? _value.hasAnyAccount : hasAnyAccount // ignore: cast_nullable_to_non_nullable as bool, )); @@ -87,31 +96,23 @@ class _$RouterStateCopyWithImpl<$Res> implements $RouterStateCopyWith<$Res> { /// @nodoc @JsonSerializable() -class _RouterState with DiagnosticableTreeMixin implements RouterState { - const _RouterState({required this.hasAnyAccount}); - factory _RouterState.fromJson(Map json) => - _$RouterStateFromJson(json); +class _$RouterStateImpl with DiagnosticableTreeMixin implements _RouterState { + const _$RouterStateImpl({required this.hasAnyAccount}); + + factory _$RouterStateImpl.fromJson(Map json) => + _$$RouterStateImplFromJson(json); @override final bool hasAnyAccount; - /// Create a copy of RouterState - /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') - _$RouterStateCopyWith<_RouterState> get copyWith => - __$RouterStateCopyWithImpl<_RouterState>(this, _$identity); - - @override - Map toJson() { - return _$RouterStateToJson( - this, - ); + String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { + return 'RouterState(hasAnyAccount: $hasAnyAccount)'; } @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); properties ..add(DiagnosticsProperty('type', 'RouterState')) ..add(DiagnosticsProperty('hasAnyAccount', hasAnyAccount)); @@ -121,53 +122,40 @@ class _RouterState with DiagnosticableTreeMixin implements RouterState { bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _RouterState && + other is _$RouterStateImpl && (identical(other.hasAnyAccount, hasAnyAccount) || other.hasAnyAccount == hasAnyAccount)); } - @JsonKey(includeFromJson: false, includeToJson: false) + @JsonKey(ignore: true) @override int get hashCode => Object.hash(runtimeType, hasAnyAccount); - @override - String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { - return 'RouterState(hasAnyAccount: $hasAnyAccount)'; - } -} - -/// @nodoc -abstract mixin class _$RouterStateCopyWith<$Res> - implements $RouterStateCopyWith<$Res> { - factory _$RouterStateCopyWith( - _RouterState value, $Res Function(_RouterState) _then) = - __$RouterStateCopyWithImpl; - @override - @useResult - $Res call({bool hasAnyAccount}); -} - -/// @nodoc -class __$RouterStateCopyWithImpl<$Res> implements _$RouterStateCopyWith<$Res> { - __$RouterStateCopyWithImpl(this._self, this._then); - - final _RouterState _self; - final $Res Function(_RouterState) _then; - - /// Create a copy of RouterState - /// with the given fields replaced by the non-null parameter values. + @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - $Res call({ - Object? hasAnyAccount = null, - }) { - return _then(_RouterState( - hasAnyAccount: null == hasAnyAccount - ? _self.hasAnyAccount - : hasAnyAccount // ignore: cast_nullable_to_non_nullable - as bool, - )); + _$$RouterStateImplCopyWith<_$RouterStateImpl> get copyWith => + __$$RouterStateImplCopyWithImpl<_$RouterStateImpl>(this, _$identity); + + @override + Map toJson() { + return _$$RouterStateImplToJson( + this, + ); } } -// dart format on +abstract class _RouterState implements RouterState { + const factory _RouterState({required final bool hasAnyAccount}) = + _$RouterStateImpl; + + factory _RouterState.fromJson(Map json) = + _$RouterStateImpl.fromJson; + + @override + bool get hasAnyAccount; + @override + @JsonKey(ignore: true) + _$$RouterStateImplCopyWith<_$RouterStateImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/router/cubits/router_cubit.g.dart b/lib/router/cubits/router_cubit.g.dart index 3623d0e..4d9241c 100644 --- a/lib/router/cubits/router_cubit.g.dart +++ b/lib/router/cubits/router_cubit.g.dart @@ -6,11 +6,12 @@ part of 'router_cubit.dart'; // JsonSerializableGenerator // ************************************************************************** -_RouterState _$RouterStateFromJson(Map json) => _RouterState( +_$RouterStateImpl _$$RouterStateImplFromJson(Map json) => + _$RouterStateImpl( hasAnyAccount: json['has_any_account'] as bool, ); -Map _$RouterStateToJson(_RouterState instance) => +Map _$$RouterStateImplToJson(_$RouterStateImpl instance) => { 'has_any_account': instance.hasAnyAccount, }; diff --git a/lib/router/views/router_shell.dart b/lib/router/views/router_shell.dart index 164c452..f2f035b 100644 --- a/lib/router/views/router_shell.dart +++ b/lib/router/views/router_shell.dart @@ -1,16 +1,12 @@ import 'package:flutter/widgets.dart'; -import '../../keyboard_shortcuts.dart'; import '../../notifications/notifications.dart'; -import '../../theme/theme.dart'; class RouterShell extends StatelessWidget { const RouterShell({required Widget child, super.key}) : _child = child; @override - Widget build(BuildContext context) => PopControl( - dismissible: false, - child: NotificationsWidget(child: KeyboardShortcuts(child: _child))); + Widget build(BuildContext context) => NotificationsWidget(child: _child); final Widget _child; } diff --git a/lib/settings/models/preferences.dart b/lib/settings/models/preferences.dart index 3ef683e..e646c61 100644 --- a/lib/settings/models/preferences.dart +++ b/lib/settings/models/preferences.dart @@ -10,7 +10,7 @@ part 'preferences.g.dart'; // Lock preference changes how frequently the messenger locks its // interface and requires the identitySecretKey to be entered (pin/password/etc) @freezed -sealed class LockPreference with _$LockPreference { +class LockPreference with _$LockPreference { const factory LockPreference({ @Default(0) int inactivityLockSecs, @Default(false) bool lockWhenSwitching, @@ -37,7 +37,7 @@ enum LanguagePreference { // Preferences are stored in a table locally and globally affect all // accounts imported/added and the app in general @freezed -sealed class Preferences with _$Preferences { +class Preferences with _$Preferences { const factory Preferences({ @Default(ThemePreferences.defaults) ThemePreferences themePreference, @Default(LanguagePreference.defaults) LanguagePreference languagePreference, diff --git a/lib/settings/models/preferences.freezed.dart b/lib/settings/models/preferences.freezed.dart index 9e090f5..1735d45 100644 --- a/lib/settings/models/preferences.freezed.dart +++ b/lib/settings/models/preferences.freezed.dart @@ -1,4 +1,3 @@ -// dart format width=80 // coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND // ignore_for_file: type=lint @@ -10,55 +9,32 @@ part of 'preferences.dart'; // FreezedGenerator // ************************************************************************** -// dart format off T _$identity(T value) => value; -/// @nodoc -mixin _$LockPreference { - int get inactivityLockSecs; - bool get lockWhenSwitching; - bool get lockWithSystemLock; +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - /// Create a copy of LockPreference - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') - $LockPreferenceCopyWith get copyWith => - _$LockPreferenceCopyWithImpl( - this as LockPreference, _$identity); - - /// Serializes this LockPreference to a JSON map. - Map toJson(); - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is LockPreference && - (identical(other.inactivityLockSecs, inactivityLockSecs) || - other.inactivityLockSecs == inactivityLockSecs) && - (identical(other.lockWhenSwitching, lockWhenSwitching) || - other.lockWhenSwitching == lockWhenSwitching) && - (identical(other.lockWithSystemLock, lockWithSystemLock) || - other.lockWithSystemLock == lockWithSystemLock)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash( - runtimeType, inactivityLockSecs, lockWhenSwitching, lockWithSystemLock); - - @override - String toString() { - return 'LockPreference(inactivityLockSecs: $inactivityLockSecs, lockWhenSwitching: $lockWhenSwitching, lockWithSystemLock: $lockWithSystemLock)'; - } +LockPreference _$LockPreferenceFromJson(Map json) { + return _LockPreference.fromJson(json); } /// @nodoc -abstract mixin class $LockPreferenceCopyWith<$Res> { +mixin _$LockPreference { + int get inactivityLockSecs => throw _privateConstructorUsedError; + bool get lockWhenSwitching => throw _privateConstructorUsedError; + bool get lockWithSystemLock => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $LockPreferenceCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $LockPreferenceCopyWith<$Res> { factory $LockPreferenceCopyWith( - LockPreference value, $Res Function(LockPreference) _then) = - _$LockPreferenceCopyWithImpl; + LockPreference value, $Res Function(LockPreference) then) = + _$LockPreferenceCopyWithImpl<$Res, LockPreference>; @useResult $Res call( {int inactivityLockSecs, @@ -67,15 +43,15 @@ abstract mixin class $LockPreferenceCopyWith<$Res> { } /// @nodoc -class _$LockPreferenceCopyWithImpl<$Res> +class _$LockPreferenceCopyWithImpl<$Res, $Val extends LockPreference> implements $LockPreferenceCopyWith<$Res> { - _$LockPreferenceCopyWithImpl(this._self, this._then); + _$LockPreferenceCopyWithImpl(this._value, this._then); - final LockPreference _self; - final $Res Function(LockPreference) _then; + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; - /// Create a copy of LockPreference - /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -83,17 +59,63 @@ class _$LockPreferenceCopyWithImpl<$Res> Object? lockWhenSwitching = null, Object? lockWithSystemLock = null, }) { - return _then(_self.copyWith( + return _then(_value.copyWith( inactivityLockSecs: null == inactivityLockSecs - ? _self.inactivityLockSecs + ? _value.inactivityLockSecs : inactivityLockSecs // ignore: cast_nullable_to_non_nullable as int, lockWhenSwitching: null == lockWhenSwitching - ? _self.lockWhenSwitching + ? _value.lockWhenSwitching : lockWhenSwitching // ignore: cast_nullable_to_non_nullable as bool, lockWithSystemLock: null == lockWithSystemLock - ? _self.lockWithSystemLock + ? _value.lockWithSystemLock + : lockWithSystemLock // ignore: cast_nullable_to_non_nullable + as bool, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$LockPreferenceImplCopyWith<$Res> + implements $LockPreferenceCopyWith<$Res> { + factory _$$LockPreferenceImplCopyWith(_$LockPreferenceImpl value, + $Res Function(_$LockPreferenceImpl) then) = + __$$LockPreferenceImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {int inactivityLockSecs, + bool lockWhenSwitching, + bool lockWithSystemLock}); +} + +/// @nodoc +class __$$LockPreferenceImplCopyWithImpl<$Res> + extends _$LockPreferenceCopyWithImpl<$Res, _$LockPreferenceImpl> + implements _$$LockPreferenceImplCopyWith<$Res> { + __$$LockPreferenceImplCopyWithImpl( + _$LockPreferenceImpl _value, $Res Function(_$LockPreferenceImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? inactivityLockSecs = null, + Object? lockWhenSwitching = null, + Object? lockWithSystemLock = null, + }) { + return _then(_$LockPreferenceImpl( + inactivityLockSecs: null == inactivityLockSecs + ? _value.inactivityLockSecs + : inactivityLockSecs // ignore: cast_nullable_to_non_nullable + as int, + lockWhenSwitching: null == lockWhenSwitching + ? _value.lockWhenSwitching + : lockWhenSwitching // ignore: cast_nullable_to_non_nullable + as bool, + lockWithSystemLock: null == lockWithSystemLock + ? _value.lockWithSystemLock : lockWithSystemLock // ignore: cast_nullable_to_non_nullable as bool, )); @@ -102,13 +124,14 @@ class _$LockPreferenceCopyWithImpl<$Res> /// @nodoc @JsonSerializable() -class _LockPreference implements LockPreference { - const _LockPreference( +class _$LockPreferenceImpl implements _LockPreference { + const _$LockPreferenceImpl( {this.inactivityLockSecs = 0, this.lockWhenSwitching = false, this.lockWithSystemLock = false}); - factory _LockPreference.fromJson(Map json) => - _$LockPreferenceFromJson(json); + + factory _$LockPreferenceImpl.fromJson(Map json) => + _$$LockPreferenceImplFromJson(json); @override @JsonKey() @@ -120,26 +143,16 @@ class _LockPreference implements LockPreference { @JsonKey() final bool lockWithSystemLock; - /// Create a copy of LockPreference - /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') - _$LockPreferenceCopyWith<_LockPreference> get copyWith => - __$LockPreferenceCopyWithImpl<_LockPreference>(this, _$identity); - - @override - Map toJson() { - return _$LockPreferenceToJson( - this, - ); + String toString() { + return 'LockPreference(inactivityLockSecs: $inactivityLockSecs, lockWhenSwitching: $lockWhenSwitching, lockWithSystemLock: $lockWithSystemLock)'; } @override bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _LockPreference && + other is _$LockPreferenceImpl && (identical(other.inactivityLockSecs, inactivityLockSecs) || other.inactivityLockSecs == inactivityLockSecs) && (identical(other.lockWhenSwitching, lockWhenSwitching) || @@ -148,114 +161,71 @@ class _LockPreference implements LockPreference { other.lockWithSystemLock == lockWithSystemLock)); } - @JsonKey(includeFromJson: false, includeToJson: false) + @JsonKey(ignore: true) @override int get hashCode => Object.hash( runtimeType, inactivityLockSecs, lockWhenSwitching, lockWithSystemLock); - @override - String toString() { - return 'LockPreference(inactivityLockSecs: $inactivityLockSecs, lockWhenSwitching: $lockWhenSwitching, lockWithSystemLock: $lockWithSystemLock)'; - } -} - -/// @nodoc -abstract mixin class _$LockPreferenceCopyWith<$Res> - implements $LockPreferenceCopyWith<$Res> { - factory _$LockPreferenceCopyWith( - _LockPreference value, $Res Function(_LockPreference) _then) = - __$LockPreferenceCopyWithImpl; - @override - @useResult - $Res call( - {int inactivityLockSecs, - bool lockWhenSwitching, - bool lockWithSystemLock}); -} - -/// @nodoc -class __$LockPreferenceCopyWithImpl<$Res> - implements _$LockPreferenceCopyWith<$Res> { - __$LockPreferenceCopyWithImpl(this._self, this._then); - - final _LockPreference _self; - final $Res Function(_LockPreference) _then; - - /// Create a copy of LockPreference - /// with the given fields replaced by the non-null parameter values. + @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - $Res call({ - Object? inactivityLockSecs = null, - Object? lockWhenSwitching = null, - Object? lockWithSystemLock = null, - }) { - return _then(_LockPreference( - inactivityLockSecs: null == inactivityLockSecs - ? _self.inactivityLockSecs - : inactivityLockSecs // ignore: cast_nullable_to_non_nullable - as int, - lockWhenSwitching: null == lockWhenSwitching - ? _self.lockWhenSwitching - : lockWhenSwitching // ignore: cast_nullable_to_non_nullable - as bool, - lockWithSystemLock: null == lockWithSystemLock - ? _self.lockWithSystemLock - : lockWithSystemLock // ignore: cast_nullable_to_non_nullable - as bool, - )); + _$$LockPreferenceImplCopyWith<_$LockPreferenceImpl> get copyWith => + __$$LockPreferenceImplCopyWithImpl<_$LockPreferenceImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$LockPreferenceImplToJson( + this, + ); } } +abstract class _LockPreference implements LockPreference { + const factory _LockPreference( + {final int inactivityLockSecs, + final bool lockWhenSwitching, + final bool lockWithSystemLock}) = _$LockPreferenceImpl; + + factory _LockPreference.fromJson(Map json) = + _$LockPreferenceImpl.fromJson; + + @override + int get inactivityLockSecs; + @override + bool get lockWhenSwitching; + @override + bool get lockWithSystemLock; + @override + @JsonKey(ignore: true) + _$$LockPreferenceImplCopyWith<_$LockPreferenceImpl> get copyWith => + throw _privateConstructorUsedError; +} + +Preferences _$PreferencesFromJson(Map json) { + return _Preferences.fromJson(json); +} + /// @nodoc mixin _$Preferences { - ThemePreferences get themePreference; - LanguagePreference get languagePreference; - LockPreference get lockPreference; - NotificationsPreference get notificationsPreference; + ThemePreferences get themePreference => throw _privateConstructorUsedError; + LanguagePreference get languagePreference => + throw _privateConstructorUsedError; + LockPreference get lockPreference => throw _privateConstructorUsedError; + NotificationsPreference get notificationsPreference => + throw _privateConstructorUsedError; - /// Create a copy of Preferences - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) $PreferencesCopyWith get copyWith => - _$PreferencesCopyWithImpl(this as Preferences, _$identity); - - /// Serializes this Preferences to a JSON map. - Map toJson(); - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is Preferences && - (identical(other.themePreference, themePreference) || - other.themePreference == themePreference) && - (identical(other.languagePreference, languagePreference) || - other.languagePreference == languagePreference) && - (identical(other.lockPreference, lockPreference) || - other.lockPreference == lockPreference) && - (identical( - other.notificationsPreference, notificationsPreference) || - other.notificationsPreference == notificationsPreference)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash(runtimeType, themePreference, - languagePreference, lockPreference, notificationsPreference); - - @override - String toString() { - return 'Preferences(themePreference: $themePreference, languagePreference: $languagePreference, lockPreference: $lockPreference, notificationsPreference: $notificationsPreference)'; - } + throw _privateConstructorUsedError; } /// @nodoc -abstract mixin class $PreferencesCopyWith<$Res> { +abstract class $PreferencesCopyWith<$Res> { factory $PreferencesCopyWith( - Preferences value, $Res Function(Preferences) _then) = - _$PreferencesCopyWithImpl; + Preferences value, $Res Function(Preferences) then) = + _$PreferencesCopyWithImpl<$Res, Preferences>; @useResult $Res call( {ThemePreferences themePreference, @@ -269,14 +239,15 @@ abstract mixin class $PreferencesCopyWith<$Res> { } /// @nodoc -class _$PreferencesCopyWithImpl<$Res> implements $PreferencesCopyWith<$Res> { - _$PreferencesCopyWithImpl(this._self, this._then); +class _$PreferencesCopyWithImpl<$Res, $Val extends Preferences> + implements $PreferencesCopyWith<$Res> { + _$PreferencesCopyWithImpl(this._value, this._then); - final Preferences _self; - final $Res Function(Preferences) _then; + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; - /// Create a copy of Preferences - /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -285,68 +256,122 @@ class _$PreferencesCopyWithImpl<$Res> implements $PreferencesCopyWith<$Res> { Object? lockPreference = null, Object? notificationsPreference = null, }) { - return _then(_self.copyWith( + return _then(_value.copyWith( themePreference: null == themePreference - ? _self.themePreference + ? _value.themePreference : themePreference // ignore: cast_nullable_to_non_nullable as ThemePreferences, languagePreference: null == languagePreference - ? _self.languagePreference + ? _value.languagePreference : languagePreference // ignore: cast_nullable_to_non_nullable as LanguagePreference, lockPreference: null == lockPreference - ? _self.lockPreference + ? _value.lockPreference : lockPreference // ignore: cast_nullable_to_non_nullable as LockPreference, notificationsPreference: null == notificationsPreference - ? _self.notificationsPreference + ? _value.notificationsPreference : notificationsPreference // ignore: cast_nullable_to_non_nullable as NotificationsPreference, - )); + ) as $Val); } - /// Create a copy of Preferences - /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $ThemePreferencesCopyWith<$Res> get themePreference { - return $ThemePreferencesCopyWith<$Res>(_self.themePreference, (value) { - return _then(_self.copyWith(themePreference: value)); + return $ThemePreferencesCopyWith<$Res>(_value.themePreference, (value) { + return _then(_value.copyWith(themePreference: value) as $Val); }); } - /// Create a copy of Preferences - /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $LockPreferenceCopyWith<$Res> get lockPreference { - return $LockPreferenceCopyWith<$Res>(_self.lockPreference, (value) { - return _then(_self.copyWith(lockPreference: value)); + return $LockPreferenceCopyWith<$Res>(_value.lockPreference, (value) { + return _then(_value.copyWith(lockPreference: value) as $Val); }); } - /// Create a copy of Preferences - /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $NotificationsPreferenceCopyWith<$Res> get notificationsPreference { - return $NotificationsPreferenceCopyWith<$Res>(_self.notificationsPreference, - (value) { - return _then(_self.copyWith(notificationsPreference: value)); + return $NotificationsPreferenceCopyWith<$Res>( + _value.notificationsPreference, (value) { + return _then(_value.copyWith(notificationsPreference: value) as $Val); }); } } +/// @nodoc +abstract class _$$PreferencesImplCopyWith<$Res> + implements $PreferencesCopyWith<$Res> { + factory _$$PreferencesImplCopyWith( + _$PreferencesImpl value, $Res Function(_$PreferencesImpl) then) = + __$$PreferencesImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {ThemePreferences themePreference, + LanguagePreference languagePreference, + LockPreference lockPreference, + NotificationsPreference notificationsPreference}); + + @override + $ThemePreferencesCopyWith<$Res> get themePreference; + @override + $LockPreferenceCopyWith<$Res> get lockPreference; + @override + $NotificationsPreferenceCopyWith<$Res> get notificationsPreference; +} + +/// @nodoc +class __$$PreferencesImplCopyWithImpl<$Res> + extends _$PreferencesCopyWithImpl<$Res, _$PreferencesImpl> + implements _$$PreferencesImplCopyWith<$Res> { + __$$PreferencesImplCopyWithImpl( + _$PreferencesImpl _value, $Res Function(_$PreferencesImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? themePreference = null, + Object? languagePreference = null, + Object? lockPreference = null, + Object? notificationsPreference = null, + }) { + return _then(_$PreferencesImpl( + themePreference: null == themePreference + ? _value.themePreference + : themePreference // ignore: cast_nullable_to_non_nullable + as ThemePreferences, + languagePreference: null == languagePreference + ? _value.languagePreference + : languagePreference // ignore: cast_nullable_to_non_nullable + as LanguagePreference, + lockPreference: null == lockPreference + ? _value.lockPreference + : lockPreference // ignore: cast_nullable_to_non_nullable + as LockPreference, + notificationsPreference: null == notificationsPreference + ? _value.notificationsPreference + : notificationsPreference // ignore: cast_nullable_to_non_nullable + as NotificationsPreference, + )); + } +} + /// @nodoc @JsonSerializable() -class _Preferences implements Preferences { - const _Preferences( +class _$PreferencesImpl implements _Preferences { + const _$PreferencesImpl( {this.themePreference = ThemePreferences.defaults, this.languagePreference = LanguagePreference.defaults, this.lockPreference = LockPreference.defaults, this.notificationsPreference = NotificationsPreference.defaults}); - factory _Preferences.fromJson(Map json) => - _$PreferencesFromJson(json); + + factory _$PreferencesImpl.fromJson(Map json) => + _$$PreferencesImplFromJson(json); @override @JsonKey() @@ -361,26 +386,16 @@ class _Preferences implements Preferences { @JsonKey() final NotificationsPreference notificationsPreference; - /// Create a copy of Preferences - /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') - _$PreferencesCopyWith<_Preferences> get copyWith => - __$PreferencesCopyWithImpl<_Preferences>(this, _$identity); - - @override - Map toJson() { - return _$PreferencesToJson( - this, - ); + String toString() { + return 'Preferences(themePreference: $themePreference, languagePreference: $languagePreference, lockPreference: $lockPreference, notificationsPreference: $notificationsPreference)'; } @override bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _Preferences && + other is _$PreferencesImpl && (identical(other.themePreference, themePreference) || other.themePreference == themePreference) && (identical(other.languagePreference, languagePreference) || @@ -392,106 +407,46 @@ class _Preferences implements Preferences { other.notificationsPreference == notificationsPreference)); } - @JsonKey(includeFromJson: false, includeToJson: false) + @JsonKey(ignore: true) @override int get hashCode => Object.hash(runtimeType, themePreference, languagePreference, lockPreference, notificationsPreference); + @JsonKey(ignore: true) @override - String toString() { - return 'Preferences(themePreference: $themePreference, languagePreference: $languagePreference, lockPreference: $lockPreference, notificationsPreference: $notificationsPreference)'; + @pragma('vm:prefer-inline') + _$$PreferencesImplCopyWith<_$PreferencesImpl> get copyWith => + __$$PreferencesImplCopyWithImpl<_$PreferencesImpl>(this, _$identity); + + @override + Map toJson() { + return _$$PreferencesImplToJson( + this, + ); } } -/// @nodoc -abstract mixin class _$PreferencesCopyWith<$Res> - implements $PreferencesCopyWith<$Res> { - factory _$PreferencesCopyWith( - _Preferences value, $Res Function(_Preferences) _then) = - __$PreferencesCopyWithImpl; - @override - @useResult - $Res call( - {ThemePreferences themePreference, - LanguagePreference languagePreference, - LockPreference lockPreference, - NotificationsPreference notificationsPreference}); +abstract class _Preferences implements Preferences { + const factory _Preferences( + {final ThemePreferences themePreference, + final LanguagePreference languagePreference, + final LockPreference lockPreference, + final NotificationsPreference notificationsPreference}) = + _$PreferencesImpl; + + factory _Preferences.fromJson(Map json) = + _$PreferencesImpl.fromJson; @override - $ThemePreferencesCopyWith<$Res> get themePreference; + ThemePreferences get themePreference; @override - $LockPreferenceCopyWith<$Res> get lockPreference; + LanguagePreference get languagePreference; @override - $NotificationsPreferenceCopyWith<$Res> get notificationsPreference; + LockPreference get lockPreference; + @override + NotificationsPreference get notificationsPreference; + @override + @JsonKey(ignore: true) + _$$PreferencesImplCopyWith<_$PreferencesImpl> get copyWith => + throw _privateConstructorUsedError; } - -/// @nodoc -class __$PreferencesCopyWithImpl<$Res> implements _$PreferencesCopyWith<$Res> { - __$PreferencesCopyWithImpl(this._self, this._then); - - final _Preferences _self; - final $Res Function(_Preferences) _then; - - /// Create a copy of Preferences - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $Res call({ - Object? themePreference = null, - Object? languagePreference = null, - Object? lockPreference = null, - Object? notificationsPreference = null, - }) { - return _then(_Preferences( - themePreference: null == themePreference - ? _self.themePreference - : themePreference // ignore: cast_nullable_to_non_nullable - as ThemePreferences, - languagePreference: null == languagePreference - ? _self.languagePreference - : languagePreference // ignore: cast_nullable_to_non_nullable - as LanguagePreference, - lockPreference: null == lockPreference - ? _self.lockPreference - : lockPreference // ignore: cast_nullable_to_non_nullable - as LockPreference, - notificationsPreference: null == notificationsPreference - ? _self.notificationsPreference - : notificationsPreference // ignore: cast_nullable_to_non_nullable - as NotificationsPreference, - )); - } - - /// Create a copy of Preferences - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $ThemePreferencesCopyWith<$Res> get themePreference { - return $ThemePreferencesCopyWith<$Res>(_self.themePreference, (value) { - return _then(_self.copyWith(themePreference: value)); - }); - } - - /// Create a copy of Preferences - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $LockPreferenceCopyWith<$Res> get lockPreference { - return $LockPreferenceCopyWith<$Res>(_self.lockPreference, (value) { - return _then(_self.copyWith(lockPreference: value)); - }); - } - - /// Create a copy of Preferences - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $NotificationsPreferenceCopyWith<$Res> get notificationsPreference { - return $NotificationsPreferenceCopyWith<$Res>(_self.notificationsPreference, - (value) { - return _then(_self.copyWith(notificationsPreference: value)); - }); - } -} - -// dart format on diff --git a/lib/settings/models/preferences.g.dart b/lib/settings/models/preferences.g.dart index 55f21a7..5813f67 100644 --- a/lib/settings/models/preferences.g.dart +++ b/lib/settings/models/preferences.g.dart @@ -6,21 +6,23 @@ part of 'preferences.dart'; // JsonSerializableGenerator // ************************************************************************** -_LockPreference _$LockPreferenceFromJson(Map json) => - _LockPreference( +_$LockPreferenceImpl _$$LockPreferenceImplFromJson(Map json) => + _$LockPreferenceImpl( inactivityLockSecs: (json['inactivity_lock_secs'] as num?)?.toInt() ?? 0, lockWhenSwitching: json['lock_when_switching'] as bool? ?? false, lockWithSystemLock: json['lock_with_system_lock'] as bool? ?? false, ); -Map _$LockPreferenceToJson(_LockPreference instance) => +Map _$$LockPreferenceImplToJson( + _$LockPreferenceImpl instance) => { 'inactivity_lock_secs': instance.inactivityLockSecs, 'lock_when_switching': instance.lockWhenSwitching, 'lock_with_system_lock': instance.lockWithSystemLock, }; -_Preferences _$PreferencesFromJson(Map json) => _Preferences( +_$PreferencesImpl _$$PreferencesImplFromJson(Map json) => + _$PreferencesImpl( themePreference: json['theme_preference'] == null ? ThemePreferences.defaults : ThemePreferences.fromJson(json['theme_preference']), @@ -35,7 +37,7 @@ _Preferences _$PreferencesFromJson(Map json) => _Preferences( : NotificationsPreference.fromJson(json['notifications_preference']), ); -Map _$PreferencesToJson(_Preferences instance) => +Map _$$PreferencesImplToJson(_$PreferencesImpl instance) => { 'theme_preference': instance.themePreference.toJson(), 'language_preference': instance.languagePreference.toJson(), diff --git a/lib/settings/settings_page.dart b/lib/settings/settings_page.dart index 2a05f08..94606aa 100644 --- a/lib/settings/settings_page.dart +++ b/lib/settings/settings_page.dart @@ -31,42 +31,31 @@ class SettingsPageState extends State { @override Widget build(BuildContext context) => AsyncBlocBuilder( - builder: (context, state) => ThemeSwitcher.withTheme( - builder: (_, switcher, theme) => StyledScaffold( - appBar: DefaultAppBar( - title: Text(translate('settings_page.titlebar')), - leading: IconButton( - icon: const Icon(Icons.arrow_back), - onPressed: () => GoRouterHelper(context).pop(), - ), - actions: [ - const SignalStrengthMeterWidget() - .paddingLTRB(16, 0, 16, 0), - ]), - body: ThemeSwitchingArea( - child: FormBuilder( - key: _formKey, - child: ListView( - padding: const EdgeInsets.all(8), - children: [ - buildSettingsPageColorPreferences( - context: context, - switcher: switcher, - onChanged: () => setState(() {})) - .paddingLTRB(0, 8, 0, 0), - buildSettingsPageBrightnessPreferences( + builder: (context, state) => StyledScaffold( + appBar: DefaultAppBar( + title: Text(translate('settings_page.titlebar')), + leading: IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: () => GoRouterHelper(context).pop(), + ), + actions: [ + const SignalStrengthMeterWidget().paddingLTRB(16, 0, 16, 0), + ]), + body: ThemeSwitchingArea( + child: FormBuilder( + key: _formKey, + child: ListView( + children: [ + buildSettingsPageColorPreferences( context: context, - switcher: switcher, - onChanged: () => setState(() {})), - buildSettingsPageWallpaperPreferences( - context: context, - switcher: switcher, - onChanged: () => setState(() {})), - buildSettingsPageNotificationPreferences( - context: context, - onChanged: () => setState(() {})), - ].map((x) => x.paddingLTRB(0, 0, 0, 8)).toList(), - ), - ).paddingSymmetric(horizontal: 8, vertical: 8), - )))); + onChanged: () => setState(() {})) + .paddingLTRB(0, 8, 0, 0), + buildSettingsPageBrightnessPreferences( + context: context, onChanged: () => setState(() {})), + buildSettingsPageNotificationPreferences( + context: context, onChanged: () => setState(() {})), + ].map((x) => x.paddingLTRB(0, 0, 0, 8)).toList(), + ), + ).paddingSymmetric(horizontal: 24, vertical: 16), + ))); } diff --git a/lib/theme/models/chat_theme.dart b/lib/theme/models/chat_theme.dart new file mode 100644 index 0000000..8623427 --- /dev/null +++ b/lib/theme/models/chat_theme.dart @@ -0,0 +1,484 @@ +// ignore_for_file: always_put_required_named_parameters_first + +import 'package:flutter/material.dart'; +import 'package:flutter_chat_ui/flutter_chat_ui.dart'; + +import 'scale_scheme.dart'; + +ChatTheme makeChatTheme( + ScaleScheme scale, ScaleConfig scaleConfig, TextTheme textTheme) => + DefaultChatTheme( + primaryColor: scaleConfig.preferBorders + ? scale.primaryScale.calloutText + : scale.primaryScale.calloutBackground, + secondaryColor: scaleConfig.preferBorders + ? scale.secondaryScale.calloutText + : scale.secondaryScale.calloutBackground, + backgroundColor: scale.grayScale.appBackground, + messageBorderRadius: scaleConfig.borderRadiusScale * 16, + bubbleBorderSide: scaleConfig.preferBorders + ? BorderSide( + color: scale.primaryScale.calloutBackground, + width: 2, + ) + : null, + sendButtonIcon: Image.asset( + 'assets/icon-send.png', + color: scaleConfig.preferBorders + ? scale.primaryScale.border + : scale.primaryScale.borderText, + package: 'flutter_chat_ui', + ), + inputBackgroundColor: Colors.blue, + inputBorderRadius: BorderRadius.zero, + inputTextStyle: textTheme.bodyLarge!, + inputTextDecoration: InputDecoration( + filled: !scaleConfig.preferBorders, + fillColor: scale.primaryScale.subtleBackground, + isDense: true, + contentPadding: const EdgeInsets.fromLTRB(8, 12, 8, 12), + disabledBorder: OutlineInputBorder( + borderSide: scaleConfig.preferBorders + ? BorderSide(color: scale.grayScale.border, width: 2) + : BorderSide.none, + borderRadius: BorderRadius.all( + Radius.circular(8 * scaleConfig.borderRadiusScale))), + enabledBorder: OutlineInputBorder( + borderSide: scaleConfig.preferBorders + ? BorderSide(color: scale.primaryScale.border, width: 2) + : BorderSide.none, + borderRadius: BorderRadius.all( + Radius.circular(8 * scaleConfig.borderRadiusScale))), + focusedBorder: OutlineInputBorder( + borderSide: scaleConfig.preferBorders + ? BorderSide(color: scale.primaryScale.border, width: 2) + : BorderSide.none, + borderRadius: BorderRadius.all( + Radius.circular(8 * scaleConfig.borderRadiusScale))), + ), + inputContainerDecoration: BoxDecoration( + border: scaleConfig.preferBorders + ? Border( + top: BorderSide(color: scale.primaryScale.border, width: 2)) + : null, + color: scaleConfig.preferBorders + ? scale.primaryScale.elementBackground + : scale.primaryScale.border), + inputPadding: const EdgeInsets.all(12), + inputTextColor: !scaleConfig.preferBorders + ? scale.primaryScale.appText + : scale.primaryScale.border, + attachmentButtonIcon: const Icon(Icons.attach_file), + sentMessageBodyTextStyle: textTheme.bodyLarge!.copyWith( + color: scaleConfig.preferBorders + ? scale.primaryScale.calloutBackground + : scale.primaryScale.calloutText, + ), + sentEmojiMessageTextStyle: const TextStyle( + color: Colors.white, + fontSize: 64, + ), + receivedMessageBodyTextStyle: textTheme.bodyLarge!.copyWith( + color: scaleConfig.preferBorders + ? scale.secondaryScale.calloutBackground + : scale.secondaryScale.calloutText, + ), + receivedEmojiMessageTextStyle: const TextStyle( + color: Colors.white, + fontSize: 64, + )); + +class EditedChatTheme extends ChatTheme { + const EditedChatTheme({ + required super.attachmentButtonIcon, + required super.attachmentButtonMargin, + required super.backgroundColor, + super.bubbleMargin, + required super.dateDividerMargin, + required super.dateDividerTextStyle, + required super.deliveredIcon, + required super.documentIcon, + required super.emptyChatPlaceholderTextStyle, + required super.errorColor, + required super.errorIcon, + required super.inputBackgroundColor, + required super.inputSurfaceTintColor, + required super.inputElevation, + required super.inputBorderRadius, + super.inputContainerDecoration, + required super.inputMargin, + required super.inputPadding, + required super.inputTextColor, + super.inputTextCursorColor, + required super.inputTextDecoration, + required super.inputTextStyle, + required super.messageBorderRadius, + required super.messageInsetsHorizontal, + required super.messageInsetsVertical, + required super.messageMaxWidth, + required super.primaryColor, + required super.receivedEmojiMessageTextStyle, + super.receivedMessageBodyBoldTextStyle, + super.receivedMessageBodyCodeTextStyle, + super.receivedMessageBodyLinkTextStyle, + required super.receivedMessageBodyTextStyle, + required super.receivedMessageCaptionTextStyle, + required super.receivedMessageDocumentIconColor, + required super.receivedMessageLinkDescriptionTextStyle, + required super.receivedMessageLinkTitleTextStyle, + required super.secondaryColor, + required super.seenIcon, + required super.sendButtonIcon, + required super.sendButtonMargin, + required super.sendingIcon, + required super.sentEmojiMessageTextStyle, + super.sentMessageBodyBoldTextStyle, + super.sentMessageBodyCodeTextStyle, + super.sentMessageBodyLinkTextStyle, + required super.sentMessageBodyTextStyle, + required super.sentMessageCaptionTextStyle, + required super.sentMessageDocumentIconColor, + required super.sentMessageLinkDescriptionTextStyle, + required super.sentMessageLinkTitleTextStyle, + required super.statusIconPadding, + required super.systemMessageTheme, + required super.typingIndicatorTheme, + required super.unreadHeaderTheme, + required super.userAvatarImageBackgroundColor, + required super.userAvatarNameColors, + required super.userAvatarTextStyle, + required super.userNameTextStyle, + super.highlightMessageColor, + }); +} + +class ChatThemeEditor { + ChatThemeEditor(ChatTheme base) + : attachmentButtonIcon = base.attachmentButtonIcon, + attachmentButtonMargin = base.attachmentButtonMargin, + backgroundColor = base.backgroundColor, + bubbleMargin = base.bubbleMargin, + dateDividerMargin = base.dateDividerMargin, + dateDividerTextStyle = base.dateDividerTextStyle, + deliveredIcon = base.deliveredIcon, + documentIcon = base.documentIcon, + emptyChatPlaceholderTextStyle = base.emptyChatPlaceholderTextStyle, + errorColor = base.errorColor, + errorIcon = base.errorIcon, + inputBackgroundColor = base.inputBackgroundColor, + inputSurfaceTintColor = base.inputSurfaceTintColor, + inputElevation = base.inputElevation, + inputBorderRadius = base.inputBorderRadius, + inputContainerDecoration = base.inputContainerDecoration, + inputMargin = base.inputMargin, + inputPadding = base.inputPadding, + inputTextColor = base.inputTextColor, + inputTextCursorColor = base.inputTextCursorColor, + inputTextDecoration = base.inputTextDecoration, + inputTextStyle = base.inputTextStyle, + messageBorderRadius = base.messageBorderRadius, + messageInsetsHorizontal = base.messageInsetsHorizontal, + messageInsetsVertical = base.messageInsetsVertical, + messageMaxWidth = base.messageMaxWidth, + primaryColor = base.primaryColor, + receivedEmojiMessageTextStyle = base.receivedEmojiMessageTextStyle, + receivedMessageBodyBoldTextStyle = + base.receivedMessageBodyBoldTextStyle, + receivedMessageBodyCodeTextStyle = + base.receivedMessageBodyCodeTextStyle, + receivedMessageBodyLinkTextStyle = + base.receivedMessageBodyLinkTextStyle, + receivedMessageBodyTextStyle = base.receivedMessageBodyTextStyle, + receivedMessageCaptionTextStyle = base.receivedMessageCaptionTextStyle, + receivedMessageDocumentIconColor = + base.receivedMessageDocumentIconColor, + receivedMessageLinkDescriptionTextStyle = + base.receivedMessageLinkDescriptionTextStyle, + receivedMessageLinkTitleTextStyle = + base.receivedMessageLinkTitleTextStyle, + secondaryColor = base.secondaryColor, + seenIcon = base.seenIcon, + sendButtonIcon = base.sendButtonIcon, + sendButtonMargin = base.sendButtonMargin, + sendingIcon = base.sendingIcon, + sentEmojiMessageTextStyle = base.sentEmojiMessageTextStyle, + sentMessageBodyBoldTextStyle = base.sentMessageBodyBoldTextStyle, + sentMessageBodyCodeTextStyle = base.sentMessageBodyCodeTextStyle, + sentMessageBodyLinkTextStyle = base.sentMessageBodyLinkTextStyle, + sentMessageBodyTextStyle = base.sentMessageBodyTextStyle, + sentMessageCaptionTextStyle = base.sentMessageCaptionTextStyle, + sentMessageDocumentIconColor = base.sentMessageDocumentIconColor, + sentMessageLinkDescriptionTextStyle = + base.sentMessageLinkDescriptionTextStyle, + sentMessageLinkTitleTextStyle = base.sentMessageLinkTitleTextStyle, + statusIconPadding = base.statusIconPadding, + systemMessageTheme = base.systemMessageTheme, + typingIndicatorTheme = base.typingIndicatorTheme, + unreadHeaderTheme = base.unreadHeaderTheme, + userAvatarImageBackgroundColor = base.userAvatarImageBackgroundColor, + userAvatarNameColors = base.userAvatarNameColors, + userAvatarTextStyle = base.userAvatarTextStyle, + userNameTextStyle = base.userNameTextStyle, + highlightMessageColor = base.highlightMessageColor; + + EditedChatTheme commit() => EditedChatTheme( + attachmentButtonIcon: attachmentButtonIcon, + attachmentButtonMargin: attachmentButtonMargin, + backgroundColor: backgroundColor, + bubbleMargin: bubbleMargin, + dateDividerMargin: dateDividerMargin, + dateDividerTextStyle: dateDividerTextStyle, + deliveredIcon: deliveredIcon, + documentIcon: documentIcon, + emptyChatPlaceholderTextStyle: emptyChatPlaceholderTextStyle, + errorColor: errorColor, + errorIcon: errorIcon, + inputBackgroundColor: inputBackgroundColor, + inputSurfaceTintColor: inputSurfaceTintColor, + inputElevation: inputElevation, + inputBorderRadius: inputBorderRadius, + inputContainerDecoration: inputContainerDecoration, + inputMargin: inputMargin, + inputPadding: inputPadding, + inputTextColor: inputTextColor, + inputTextCursorColor: inputTextCursorColor, + inputTextDecoration: inputTextDecoration, + inputTextStyle: inputTextStyle, + messageBorderRadius: messageBorderRadius, + messageInsetsHorizontal: messageInsetsHorizontal, + messageInsetsVertical: messageInsetsVertical, + messageMaxWidth: messageMaxWidth, + primaryColor: primaryColor, + receivedEmojiMessageTextStyle: receivedEmojiMessageTextStyle, + receivedMessageBodyBoldTextStyle: receivedMessageBodyBoldTextStyle, + receivedMessageBodyCodeTextStyle: receivedMessageBodyCodeTextStyle, + receivedMessageBodyLinkTextStyle: receivedMessageBodyLinkTextStyle, + receivedMessageBodyTextStyle: receivedMessageBodyTextStyle, + receivedMessageCaptionTextStyle: receivedMessageCaptionTextStyle, + receivedMessageDocumentIconColor: receivedMessageDocumentIconColor, + receivedMessageLinkDescriptionTextStyle: + receivedMessageLinkDescriptionTextStyle, + receivedMessageLinkTitleTextStyle: receivedMessageLinkTitleTextStyle, + secondaryColor: secondaryColor, + seenIcon: seenIcon, + sendButtonIcon: sendButtonIcon, + sendButtonMargin: sendButtonMargin, + sendingIcon: sendingIcon, + sentEmojiMessageTextStyle: sentEmojiMessageTextStyle, + sentMessageBodyBoldTextStyle: sentMessageBodyBoldTextStyle, + sentMessageBodyCodeTextStyle: sentMessageBodyCodeTextStyle, + sentMessageBodyLinkTextStyle: sentMessageBodyLinkTextStyle, + sentMessageBodyTextStyle: sentMessageBodyTextStyle, + sentMessageCaptionTextStyle: sentMessageCaptionTextStyle, + sentMessageDocumentIconColor: sentMessageDocumentIconColor, + sentMessageLinkDescriptionTextStyle: + sentMessageLinkDescriptionTextStyle, + sentMessageLinkTitleTextStyle: sentMessageLinkTitleTextStyle, + statusIconPadding: statusIconPadding, + systemMessageTheme: systemMessageTheme, + typingIndicatorTheme: typingIndicatorTheme, + unreadHeaderTheme: unreadHeaderTheme, + userAvatarImageBackgroundColor: userAvatarImageBackgroundColor, + userAvatarNameColors: userAvatarNameColors, + userAvatarTextStyle: userAvatarTextStyle, + userNameTextStyle: userNameTextStyle, + highlightMessageColor: highlightMessageColor, + ); + + ///////////////////////////////////////////////////////////////////////////// + + /// Icon for select attachment button. + Widget? attachmentButtonIcon; + + /// Margin of attachment button. + EdgeInsets? attachmentButtonMargin; + + /// Used as a background color of a chat widget. + Color backgroundColor; + + // Margin around the message bubble. + EdgeInsetsGeometry? bubbleMargin; + + /// Margin around date dividers. + EdgeInsets dateDividerMargin; + + /// Text style of the date dividers. + TextStyle dateDividerTextStyle; + + /// Icon for message's `delivered` status. For the best look use size of 16. + Widget? deliveredIcon; + + /// Icon inside file message. + Widget? documentIcon; + + /// Text style of the empty chat placeholder. + TextStyle emptyChatPlaceholderTextStyle; + + /// Color to indicate something bad happened (usually - shades of red). + Color errorColor; + + /// Icon for message's `error` status. For the best look use size of 16. + Widget? errorIcon; + + /// Color of the bottom bar where text field is. + Color inputBackgroundColor; + + /// Surface Tint Color of the bottom bar where text field is. + Color inputSurfaceTintColor; + + double inputElevation; + + /// Top border radius of the bottom bar where text field is. + BorderRadius inputBorderRadius; + + /// Decoration of the container wrapping the text field. + Decoration? inputContainerDecoration; + + /// Outer insets of the bottom bar where text field is. + EdgeInsets inputMargin; + + /// Inner insets of the bottom bar where text field is. + EdgeInsets inputPadding; + + /// Color of the text field's text and attachment/send buttons. + Color inputTextColor; + + /// Color of the text field's cursor. + Color? inputTextCursorColor; + + /// Decoration of the input text field. + InputDecoration inputTextDecoration; + + /// Text style of the message input. To change the color use [inputTextColor]. + TextStyle inputTextStyle; + + /// Border radius of message container. + double messageBorderRadius; + + /// Horizontal message bubble insets. + double messageInsetsHorizontal; + + /// Vertical message bubble insets. + double messageInsetsVertical; + + /// Message bubble max width. set to [double.infinity] adaptive screen. + double messageMaxWidth; + + /// Primary color of the chat used as a background of sent messages + /// and statuses. + Color primaryColor; + + /// Text style used for displaying emojis on text messages. + TextStyle receivedEmojiMessageTextStyle; + + /// Body text style used for displaying bold text on received text messages. + /// Default to a bold version of [receivedMessageBodyTextStyle]. + TextStyle? receivedMessageBodyBoldTextStyle; + + /// Body text style used for displaying code text on received text messages. + /// Defaults to a mono version of [receivedMessageBodyTextStyle]. + TextStyle? receivedMessageBodyCodeTextStyle; + + /// Text style used for displaying link text on received text messages. + /// Defaults to [receivedMessageBodyTextStyle]. + TextStyle? receivedMessageBodyLinkTextStyle; + + /// Body text style used for displaying text on different types + /// of received messages. + TextStyle receivedMessageBodyTextStyle; + + /// Caption text style used for displaying secondary info (e.g. file size) on + /// different types of received messages. + TextStyle receivedMessageCaptionTextStyle; + + /// Color of the document icon on received messages. Has no effect when + /// [documentIcon] is used. + Color receivedMessageDocumentIconColor; + + /// Text style used for displaying link description on received messages. + TextStyle receivedMessageLinkDescriptionTextStyle; + + /// Text style used for displaying link title on received messages. + TextStyle receivedMessageLinkTitleTextStyle; + + /// Secondary color, used as a background of received messages. + Color secondaryColor; + + /// Icon for message's `seen` status. For the best look use size of 16. + Widget? seenIcon; + + /// Icon for send button. + Widget? sendButtonIcon; + + /// Margin of send button. + EdgeInsets? sendButtonMargin; + + /// Icon for message's `sending` status. For the best look use size of 10. + Widget? sendingIcon; + + /// Text style used for displaying emojis on text messages. + TextStyle sentEmojiMessageTextStyle; + + /// Body text style used for displaying bold text on sent text messages. + /// Defaults to a bold version of [sentMessageBodyTextStyle]. + TextStyle? sentMessageBodyBoldTextStyle; + + /// Body text style used for displaying code text on sent text messages. + /// Defaults to a mono version of [sentMessageBodyTextStyle]. + TextStyle? sentMessageBodyCodeTextStyle; + + /// Text style used for displaying link text on sent text messages. + /// Defaults to [sentMessageBodyTextStyle]. + TextStyle? sentMessageBodyLinkTextStyle; + + /// Body text style used for displaying text on different types + /// of sent messages. + TextStyle sentMessageBodyTextStyle; + + /// Caption text style used for displaying secondary info (e.g. file size) on + /// different types of sent messages. + TextStyle sentMessageCaptionTextStyle; + + /// Color of the document icon on sent messages. Has no effect when + /// [documentIcon] is used. + Color sentMessageDocumentIconColor; + + /// Text style used for displaying link description on sent messages. + TextStyle sentMessageLinkDescriptionTextStyle; + + /// Text style used for displaying link title on sent messages. + TextStyle sentMessageLinkTitleTextStyle; + + /// Padding around status icons. + EdgeInsets statusIconPadding; + + /// Theme for the system message. Will not have an effect if a custom builder + /// is provided. + SystemMessageTheme systemMessageTheme; + + /// Theme for typing indicator. See [TypingIndicator]. + TypingIndicatorTheme typingIndicatorTheme; + + /// Theme for the unread header. + UnreadHeaderTheme unreadHeaderTheme; + + /// Color used as a background for user avatar if an image is provided. + /// Visible if the image has some transparent parts. + Color userAvatarImageBackgroundColor; + + /// Colors used as backgrounds for user avatars with no image and so, + /// corresponding user names. + /// Calculated based on a user ID, so unique across the whole app. + List userAvatarNameColors; + + /// Text style used for displaying initials on user avatar if no + /// image is provided. + TextStyle userAvatarTextStyle; + + /// User names text style. Color will be overwritten + /// with [userAvatarNameColors]. + TextStyle userNameTextStyle; + + /// Color used as background of message row on highligth. + Color? highlightMessageColor; +} diff --git a/lib/theme/models/contrast_generator.dart b/lib/theme/models/contrast_generator.dart index 314e28a..09cef2b 100644 --- a/lib/theme/models/contrast_generator.dart +++ b/lib/theme/models/contrast_generator.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; import 'radix_generator.dart'; -import 'scale_theme/scale_theme.dart'; +import 'scale_color.dart'; +import 'scale_input_decorator_theme.dart'; +import 'scale_scheme.dart'; ScaleColor _contrastScaleColor( {required Brightness brightness, @@ -26,7 +28,6 @@ ScaleColor _contrastScaleColor( primaryText: front, borderText: back, dialogBorder: front, - dialogBorderText: back, calloutBackground: front, calloutText: back, ); @@ -244,7 +245,7 @@ ThemeData contrastGenerator({ TextTheme? customTextTheme, }) { final textTheme = customTextTheme ?? makeRadixTextTheme(brightness); - final scheme = _contrastScaleScheme( + final scaleScheme = _contrastScaleScheme( brightness: brightness, primaryFront: primaryFront, primaryBack: primaryBack, @@ -257,70 +258,47 @@ ThemeData contrastGenerator({ errorFront: errorFront, errorBack: errorBack, ); + final colorScheme = scaleScheme.toColorScheme( + brightness, + ); - final scaleTheme = - ScaleTheme(textTheme: textTheme, scheme: scheme, config: scaleConfig); - - final baseThemeData = scaleTheme.toThemeData(brightness); - - WidgetStateProperty elementBorderWidgetStateProperty() => - WidgetStateProperty.resolveWith((states) { - if (states.contains(WidgetState.disabled)) { - return BorderSide(color: scheme.grayScale.border.withAlpha(0x7F)); - } else if (states.contains(WidgetState.pressed)) { - return BorderSide( - color: scheme.primaryScale.border, - strokeAlign: BorderSide.strokeAlignOutside); - } else if (states.contains(WidgetState.hovered)) { - return BorderSide(color: scheme.primaryScale.hoverBorder, width: 2); - } else if (states.contains(WidgetState.focused)) { - return BorderSide(color: scheme.primaryScale.hoverBorder, width: 2); - } - return BorderSide(color: scheme.primaryScale.border); - }); - - WidgetStateProperty elementBackgroundWidgetStateProperty() => - WidgetStateProperty.resolveWith((states) { - if (states.contains(WidgetState.disabled)) { - return scheme.grayScale.elementBackground; - } else if (states.contains(WidgetState.pressed)) { - return scheme.primaryScale.activeElementBackground; - } else if (states.contains(WidgetState.hovered)) { - return scheme.primaryScale.hoverElementBackground; - } else if (states.contains(WidgetState.focused)) { - return scheme.primaryScale.activeElementBackground; - } - return scheme.primaryScale.elementBackground; - }); - - final elevatedButtonTheme = ElevatedButtonThemeData( - style: ElevatedButton.styleFrom( - backgroundColor: scheme.primaryScale.elementBackground, - foregroundColor: scheme.primaryScale.appText, - disabledBackgroundColor: - scheme.grayScale.elementBackground.withAlpha(0x7F), - disabledForegroundColor: scheme.grayScale.appText.withAlpha(0x7F), - shape: RoundedRectangleBorder( - side: BorderSide(color: scheme.primaryScale.border), - borderRadius: - BorderRadius.circular(8 * scaleConfig.borderRadiusScale))) - .copyWith( - side: elementBorderWidgetStateProperty(), - backgroundColor: elementBackgroundWidgetStateProperty())); - - final themeData = baseThemeData.copyWith( - // chipTheme: baseThemeData.chipTheme.copyWith( - // backgroundColor: scaleScheme.primaryScale.elementBackground, - // selectedColor: scaleScheme.primaryScale.activeElementBackground, - // surfaceTintColor: scaleScheme.primaryScale.hoverElementBackground, - // checkmarkColor: scaleScheme.primaryScale.border, - // side: BorderSide(color: scaleScheme.primaryScale.border)), - elevatedButtonTheme: elevatedButtonTheme, + final themeData = ThemeData.from( + colorScheme: colorScheme, textTheme: textTheme, useMaterial3: true); + return themeData.copyWith( + bottomSheetTheme: themeData.bottomSheetTheme.copyWith( + elevation: 0, + modalElevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(16 * scaleConfig.borderRadiusScale), + topRight: + Radius.circular(16 * scaleConfig.borderRadiusScale)))), + canvasColor: scaleScheme.primaryScale.subtleBackground, + chipTheme: themeData.chipTheme.copyWith( + backgroundColor: scaleScheme.primaryScale.elementBackground, + selectedColor: scaleScheme.primaryScale.activeElementBackground, + surfaceTintColor: scaleScheme.primaryScale.hoverElementBackground, + checkmarkColor: scaleScheme.primaryScale.border, + side: BorderSide(color: scaleScheme.primaryScale.border)), + elevatedButtonTheme: ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( + backgroundColor: scaleScheme.primaryScale.elementBackground, + foregroundColor: scaleScheme.primaryScale.appText, + disabledBackgroundColor: scaleScheme.grayScale.elementBackground, + disabledForegroundColor: scaleScheme.grayScale.appText, + shape: RoundedRectangleBorder( + side: BorderSide(color: scaleScheme.primaryScale.border), + borderRadius: + BorderRadius.circular(8 * scaleConfig.borderRadiusScale))), + ), textSelectionTheme: TextSelectionThemeData( - cursorColor: scheme.primaryScale.appText, - selectionColor: scheme.primaryScale.appText.withAlpha(0x7F), - selectionHandleColor: scheme.primaryScale.appText), - extensions: >[scheme, scaleConfig, scaleTheme]); - - return themeData; + cursorColor: scaleScheme.primaryScale.appText, + selectionColor: scaleScheme.primaryScale.appText.withAlpha(0x7F), + selectionHandleColor: scaleScheme.primaryScale.appText), + inputDecorationTheme: + ScaleInputDecoratorTheme(scaleScheme, scaleConfig, textTheme), + extensions: >[ + scaleScheme, + scaleConfig, + ]); } diff --git a/lib/theme/models/models.dart b/lib/theme/models/models.dart index 7806ede..e0ba490 100644 --- a/lib/theme/models/models.dart +++ b/lib/theme/models/models.dart @@ -1,3 +1,6 @@ +export 'chat_theme.dart'; export 'radix_generator.dart'; -export 'scale_theme/scale_theme.dart'; +export 'scale_color.dart'; +export 'scale_scheme.dart'; +export 'slider_tile.dart'; export 'theme_preference.dart'; diff --git a/lib/theme/models/radix_generator.dart b/lib/theme/models/radix_generator.dart index ce05769..92d52c8 100644 --- a/lib/theme/models/radix_generator.dart +++ b/lib/theme/models/radix_generator.dart @@ -1,11 +1,12 @@ import 'dart:io'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:radix_colors/radix_colors.dart'; import '../../tools/tools.dart'; -import 'scale_theme/scale_theme.dart'; +import 'scale_color.dart'; +import 'scale_input_decorator_theme.dart'; +import 'scale_scheme.dart'; enum RadixThemeColor { scarlet, // red + violet + tomato @@ -288,7 +289,6 @@ extension ToScaleColor on RadixColor { primaryText: scaleExtra.foregroundText, borderText: step12, dialogBorder: step9, - dialogBorderText: scaleExtra.foregroundText, calloutBackground: step9, calloutText: scaleExtra.foregroundText, ); @@ -570,11 +570,7 @@ RadixScheme _radixScheme(Brightness brightness, RadixThemeColor themeColor) { TextTheme makeRadixTextTheme(Brightness brightness) { late final TextTheme textTheme; - if (kIsWeb) { - textTheme = (brightness == Brightness.light) - ? Typography.blackHelsinki - : Typography.whiteHelsinki; - } else if (Platform.isIOS) { + if (Platform.isIOS) { textTheme = (brightness == Brightness.light) ? Typography.blackCupertino : Typography.whiteCupertino; @@ -603,48 +599,69 @@ TextTheme makeRadixTextTheme(Brightness brightness) { return textTheme; } -double wallpaperAlpha(Brightness brightness, RadixThemeColor themeColor) { - switch (themeColor) { - case RadixThemeColor.scarlet: - return 64; - case RadixThemeColor.babydoll: - return 192; - case RadixThemeColor.vapor: - return 192; - case RadixThemeColor.gold: - return 192; - case RadixThemeColor.garden: - return brightness == Brightness.dark ? 192 : 128; - case RadixThemeColor.forest: - return 192; - case RadixThemeColor.arctic: - return brightness == Brightness.dark ? 208 : 180; - case RadixThemeColor.lapis: - return brightness == Brightness.dark ? 128 : 192; - case RadixThemeColor.eggplant: - return brightness == Brightness.dark ? 192 : 192; - case RadixThemeColor.lime: - return brightness == Brightness.dark ? 192 : 128; - case RadixThemeColor.grim: - return brightness == Brightness.dark ? 240 : 224; - } -} - ThemeData radixGenerator(Brightness brightness, RadixThemeColor themeColor) { final textTheme = makeRadixTextTheme(brightness); final radix = _radixScheme(brightness, themeColor); final scaleScheme = radix.toScale(); + final colorScheme = scaleScheme.toColorScheme(brightness); final scaleConfig = ScaleConfig( useVisualIndicators: false, preferBorders: false, borderRadiusScale: 1, - wallpaperOpacity: wallpaperAlpha(brightness, themeColor), ); - final scaleTheme = ScaleTheme( - textTheme: textTheme, scheme: scaleScheme, config: scaleConfig); - - final themeData = scaleTheme.toThemeData(brightness); - - return themeData; + final themeData = ThemeData.from( + colorScheme: colorScheme, textTheme: textTheme, useMaterial3: true); + return themeData.copyWith( + scrollbarTheme: themeData.scrollbarTheme.copyWith( + thumbColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.pressed)) { + return scaleScheme.primaryScale.border; + } else if (states.contains(WidgetState.hovered)) { + return scaleScheme.primaryScale.hoverBorder; + } + return scaleScheme.primaryScale.subtleBorder; + }), trackColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.pressed)) { + return scaleScheme.primaryScale.activeElementBackground; + } else if (states.contains(WidgetState.hovered)) { + return scaleScheme.primaryScale.hoverElementBackground; + } + return scaleScheme.primaryScale.elementBackground; + }), trackBorderColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.pressed)) { + return scaleScheme.primaryScale.subtleBorder; + } else if (states.contains(WidgetState.hovered)) { + return scaleScheme.primaryScale.subtleBorder; + } + return scaleScheme.primaryScale.subtleBorder; + })), + bottomSheetTheme: themeData.bottomSheetTheme.copyWith( + elevation: 0, + modalElevation: 0, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(16), + topRight: Radius.circular(16)))), + canvasColor: scaleScheme.primaryScale.subtleBackground, + chipTheme: themeData.chipTheme.copyWith( + backgroundColor: scaleScheme.primaryScale.elementBackground, + selectedColor: scaleScheme.primaryScale.activeElementBackground, + surfaceTintColor: scaleScheme.primaryScale.hoverElementBackground, + checkmarkColor: scaleScheme.primaryScale.primary, + side: BorderSide(color: scaleScheme.primaryScale.border)), + elevatedButtonTheme: ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( + backgroundColor: scaleScheme.primaryScale.elementBackground, + foregroundColor: scaleScheme.primaryScale.primary, + disabledBackgroundColor: scaleScheme.grayScale.elementBackground, + disabledForegroundColor: scaleScheme.grayScale.primary, + shape: RoundedRectangleBorder( + side: BorderSide(color: scaleScheme.primaryScale.border), + borderRadius: + BorderRadius.circular(8 * scaleConfig.borderRadiusScale))), + ), + inputDecorationTheme: + ScaleInputDecoratorTheme(scaleScheme, scaleConfig, textTheme), + extensions: >[scaleScheme, scaleConfig]); } diff --git a/lib/theme/models/scale_theme/scale_color.dart b/lib/theme/models/scale_color.dart similarity index 88% rename from lib/theme/models/scale_theme/scale_color.dart rename to lib/theme/models/scale_color.dart index f3c884f..244f6a3 100644 --- a/lib/theme/models/scale_theme/scale_color.dart +++ b/lib/theme/models/scale_color.dart @@ -17,7 +17,6 @@ class ScaleColor { required this.primaryText, required this.borderText, required this.dialogBorder, - required this.dialogBorderText, required this.calloutBackground, required this.calloutText, }); @@ -37,7 +36,6 @@ class ScaleColor { Color primaryText; Color borderText; Color dialogBorder; - Color dialogBorderText; Color calloutBackground; Color calloutText; @@ -50,14 +48,13 @@ class ScaleColor { Color? subtleBorder, Color? border, Color? hoverBorder, - Color? primary, - Color? hoverPrimary, + Color? background, + Color? hoverBackground, Color? subtleText, Color? appText, - Color? primaryText, + Color? foregroundText, Color? borderText, Color? dialogBorder, - Color? dialogBorderText, Color? calloutBackground, Color? calloutText, }) => @@ -72,18 +69,16 @@ class ScaleColor { subtleBorder: subtleBorder ?? this.subtleBorder, border: border ?? this.border, hoverBorder: hoverBorder ?? this.hoverBorder, - primary: primary ?? this.primary, - hoverPrimary: hoverPrimary ?? this.hoverPrimary, + primary: background ?? this.primary, + hoverPrimary: hoverBackground ?? this.hoverPrimary, subtleText: subtleText ?? this.subtleText, appText: appText ?? this.appText, - primaryText: primaryText ?? this.primaryText, + primaryText: foregroundText ?? this.primaryText, borderText: borderText ?? this.borderText, dialogBorder: dialogBorder ?? this.dialogBorder, - dialogBorderText: dialogBorderText ?? this.dialogBorderText, calloutBackground: calloutBackground ?? this.calloutBackground, calloutText: calloutText ?? this.calloutText); - // Use static method // ignore: prefer_constructors_over_static_methods static ScaleColor lerp(ScaleColor a, ScaleColor b, double t) => ScaleColor( appBackground: Color.lerp(a.appBackground, b.appBackground, t) ?? @@ -117,9 +112,6 @@ class ScaleColor { const Color(0x00000000), dialogBorder: Color.lerp(a.dialogBorder, b.dialogBorder, t) ?? const Color(0x00000000), - dialogBorderText: - Color.lerp(a.dialogBorderText, b.dialogBorderText, t) ?? - const Color(0x00000000), calloutBackground: Color.lerp(a.calloutBackground, b.calloutBackground, t) ?? const Color(0x00000000), diff --git a/lib/theme/models/scale_input_decorator_theme.dart b/lib/theme/models/scale_input_decorator_theme.dart new file mode 100644 index 0000000..265670e --- /dev/null +++ b/lib/theme/models/scale_input_decorator_theme.dart @@ -0,0 +1,168 @@ +import 'package:flutter/material.dart'; + +import 'scale_scheme.dart'; + +class ScaleInputDecoratorTheme extends InputDecorationTheme { + ScaleInputDecoratorTheme( + this._scaleScheme, ScaleConfig scaleConfig, this._textTheme) + : super( + border: OutlineInputBorder( + borderSide: BorderSide(color: _scaleScheme.primaryScale.border), + borderRadius: + BorderRadius.circular(8 * scaleConfig.borderRadiusScale)), + contentPadding: const EdgeInsets.all(8), + labelStyle: TextStyle( + color: _scaleScheme.primaryScale.subtleText.withAlpha(127)), + floatingLabelStyle: + TextStyle(color: _scaleScheme.primaryScale.subtleText), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: _scaleScheme.primaryScale.hoverBorder, width: 2), + borderRadius: + BorderRadius.circular(8 * scaleConfig.borderRadiusScale))); + + final ScaleScheme _scaleScheme; + final TextTheme _textTheme; + + @override + TextStyle? get hintStyle => MaterialStateTextStyle.resolveWith((states) { + if (states.contains(MaterialState.disabled)) { + return TextStyle(color: _scaleScheme.grayScale.border); + } + return TextStyle(color: _scaleScheme.primaryScale.border); + }); + + @override + Color? get fillColor => MaterialStateColor.resolveWith((states) { + if (states.contains(MaterialState.disabled)) { + return _scaleScheme.grayScale.primary.withOpacity(0.04); + } + return _scaleScheme.primaryScale.primary.withOpacity(0.04); + }); + + @override + BorderSide? get activeIndicatorBorder => + MaterialStateBorderSide.resolveWith((states) { + if (states.contains(MaterialState.disabled)) { + return BorderSide( + color: _scaleScheme.grayScale.border.withAlpha(0x7F)); + } + if (states.contains(MaterialState.error)) { + if (states.contains(MaterialState.hovered)) { + return BorderSide(color: _scaleScheme.errorScale.hoverBorder); + } + if (states.contains(MaterialState.focused)) { + return BorderSide(color: _scaleScheme.errorScale.border, width: 2); + } + return BorderSide(color: _scaleScheme.errorScale.subtleBorder); + } + if (states.contains(MaterialState.hovered)) { + return BorderSide(color: _scaleScheme.secondaryScale.hoverBorder); + } + if (states.contains(MaterialState.focused)) { + return BorderSide( + color: _scaleScheme.secondaryScale.border, width: 2); + } + return BorderSide(color: _scaleScheme.secondaryScale.subtleBorder); + }); + + @override + BorderSide? get outlineBorder => + MaterialStateBorderSide.resolveWith((states) { + if (states.contains(MaterialState.disabled)) { + return BorderSide( + color: _scaleScheme.grayScale.border.withAlpha(0x7F)); + } + if (states.contains(MaterialState.error)) { + if (states.contains(MaterialState.hovered)) { + return BorderSide(color: _scaleScheme.errorScale.hoverBorder); + } + if (states.contains(MaterialState.focused)) { + return BorderSide(color: _scaleScheme.errorScale.border, width: 2); + } + return BorderSide(color: _scaleScheme.errorScale.subtleBorder); + } + if (states.contains(MaterialState.hovered)) { + return BorderSide(color: _scaleScheme.primaryScale.hoverBorder); + } + if (states.contains(MaterialState.focused)) { + return BorderSide(color: _scaleScheme.primaryScale.border, width: 2); + } + return BorderSide(color: _scaleScheme.primaryScale.subtleBorder); + }); + + @override + Color? get iconColor => _scaleScheme.primaryScale.primary; + + @override + Color? get prefixIconColor => MaterialStateColor.resolveWith((states) { + if (states.contains(MaterialState.disabled)) { + return _scaleScheme.primaryScale.primary.withAlpha(0x3F); + } + if (states.contains(MaterialState.error)) { + return _scaleScheme.errorScale.primary; + } + return _scaleScheme.primaryScale.primary; + }); + + @override + Color? get suffixIconColor => MaterialStateColor.resolveWith((states) { + if (states.contains(MaterialState.disabled)) { + return _scaleScheme.primaryScale.primary.withAlpha(0x3F); + } + if (states.contains(MaterialState.error)) { + return _scaleScheme.errorScale.primary; + } + return _scaleScheme.primaryScale.primary; + }); + + @override + TextStyle? get labelStyle => MaterialStateTextStyle.resolveWith((states) { + final textStyle = _textTheme.bodyLarge ?? const TextStyle(); + if (states.contains(MaterialState.disabled)) { + return textStyle.copyWith( + color: _scaleScheme.grayScale.border.withAlpha(0x7F)); + } + if (states.contains(MaterialState.error)) { + if (states.contains(MaterialState.hovered)) { + return textStyle.copyWith( + color: _scaleScheme.errorScale.hoverBorder); + } + if (states.contains(MaterialState.focused)) { + return textStyle.copyWith( + color: _scaleScheme.errorScale.hoverBorder); + } + return textStyle.copyWith( + color: _scaleScheme.errorScale.subtleBorder); + } + if (states.contains(MaterialState.hovered)) { + return textStyle.copyWith( + color: _scaleScheme.primaryScale.hoverBorder); + } + if (states.contains(MaterialState.focused)) { + return textStyle.copyWith( + color: _scaleScheme.primaryScale.hoverBorder); + } + return textStyle.copyWith(color: _scaleScheme.primaryScale.border); + }); + + @override + TextStyle? get floatingLabelStyle => labelStyle; + + @override + TextStyle? get helperStyle => MaterialStateTextStyle.resolveWith((states) { + final textStyle = _textTheme.bodySmall ?? const TextStyle(); + if (states.contains(MaterialState.disabled)) { + return textStyle.copyWith( + color: _scaleScheme.grayScale.border.withAlpha(0x7F)); + } + return textStyle.copyWith( + color: _scaleScheme.secondaryScale.border.withAlpha(0x7F)); + }); + + @override + TextStyle? get errorStyle => MaterialStateTextStyle.resolveWith((states) { + final textStyle = _textTheme.bodySmall ?? const TextStyle(); + return textStyle.copyWith(color: _scaleScheme.errorScale.primary); + }); +} diff --git a/lib/theme/models/scale_theme/scale_scheme.dart b/lib/theme/models/scale_scheme.dart similarity index 85% rename from lib/theme/models/scale_theme/scale_scheme.dart rename to lib/theme/models/scale_scheme.dart index 8363476..512fda6 100644 --- a/lib/theme/models/scale_theme/scale_scheme.dart +++ b/lib/theme/models/scale_scheme.dart @@ -76,8 +76,8 @@ class ScaleScheme extends ThemeExtension { ColorScheme toColorScheme(Brightness brightness) => ColorScheme( brightness: brightness, - primary: primaryScale.primary, - onPrimary: primaryScale.primaryText, + primary: primaryScale.primary, // reviewed + onPrimary: primaryScale.primaryText, // reviewed // primaryContainer: primaryScale.hoverElementBackground, // onPrimaryContainer: primaryScale.subtleText, secondary: secondaryScale.primary, @@ -92,12 +92,15 @@ class ScaleScheme extends ThemeExtension { onError: errorScale.primaryText, // errorContainer: errorScale.hoverElementBackground, // onErrorContainer: errorScale.subtleText, - surface: primaryScale.appBackground, - onSurface: primaryScale.appText, - onSurfaceVariant: secondaryScale.appText, + background: grayScale.appBackground, // reviewed + onBackground: grayScale.appText, // reviewed + surface: primaryScale.primary, // reviewed + onSurface: primaryScale.primaryText, // reviewed + surfaceVariant: secondaryScale.primary, + onSurfaceVariant: secondaryScale.primaryText, // ?? reviewed a little outline: primaryScale.border, outlineVariant: secondaryScale.border, - shadow: primaryScale.primary.darken(60), + shadow: primaryScale.primary.darken(80), //scrim: primaryScale.background, // inverseSurface: primaryScale.subtleText, // onInverseSurface: primaryScale.subtleBackground, @@ -111,27 +114,22 @@ class ScaleConfig extends ThemeExtension { required this.useVisualIndicators, required this.preferBorders, required this.borderRadiusScale, - required this.wallpaperOpacity, }); final bool useVisualIndicators; final bool preferBorders; final double borderRadiusScale; - final double wallpaperOpacity; - - int get wallpaperAlpha => wallpaperOpacity.toInt(); @override - ScaleConfig copyWith( - {bool? useVisualIndicators, - bool? preferBorders, - double? borderRadiusScale, - double? wallpaperOpacity}) => + ScaleConfig copyWith({ + bool? useVisualIndicators, + bool? preferBorders, + double? borderRadiusScale, + }) => ScaleConfig( useVisualIndicators: useVisualIndicators ?? this.useVisualIndicators, preferBorders: preferBorders ?? this.preferBorders, borderRadiusScale: borderRadiusScale ?? this.borderRadiusScale, - wallpaperOpacity: wallpaperOpacity ?? this.wallpaperOpacity, ); @override @@ -144,8 +142,6 @@ class ScaleConfig extends ThemeExtension { t < .5 ? useVisualIndicators : other.useVisualIndicators, preferBorders: t < .5 ? preferBorders : other.preferBorders, borderRadiusScale: - lerpDouble(borderRadiusScale, other.borderRadiusScale, t) ?? 1, - wallpaperOpacity: - lerpDouble(wallpaperOpacity, other.wallpaperOpacity, t) ?? 1); + lerpDouble(borderRadiusScale, other.borderRadiusScale, t) ?? 1); } } diff --git a/lib/theme/models/scale_theme/scale_app_bar_theme.dart b/lib/theme/models/scale_theme/scale_app_bar_theme.dart deleted file mode 100644 index ea8e83e..0000000 --- a/lib/theme/models/scale_theme/scale_app_bar_theme.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'scale_theme.dart'; - -class ScaleAppBarTheme { - ScaleAppBarTheme({ - required this.textStyle, - required this.iconColor, - required this.backgroundColor, - }); - - final TextStyle textStyle; - final Color iconColor; - final Color backgroundColor; -} - -extension ScaleAppBarThemeExt on ScaleTheme { - ScaleAppBarTheme appBarTheme({ScaleKind scaleKind = ScaleKind.primary}) { - final scale = scheme.scale(scaleKind); - - final textStyle = textTheme.titleLarge!.copyWith(color: scale.borderText); - final iconColor = scale.borderText; - final backgroundColor = scale.border; - - return ScaleAppBarTheme( - textStyle: textStyle, - iconColor: iconColor, - backgroundColor: backgroundColor, - ); - } -} diff --git a/lib/theme/models/scale_theme/scale_chat_theme.dart b/lib/theme/models/scale_theme/scale_chat_theme.dart deleted file mode 100644 index 5da0fe2..0000000 --- a/lib/theme/models/scale_theme/scale_chat_theme.dart +++ /dev/null @@ -1,369 +0,0 @@ -import 'package:awesome_extensions/awesome_extensions.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_chat_core/flutter_chat_core.dart' as core; - -import 'scale_theme.dart'; - -class ScaleChatTheme { - ScaleChatTheme({ - // Default chat theme - required this.chatTheme, - - // Customization fields (from v1 of flutter chat ui) - required this.attachmentButtonIcon, - // required this.attachmentButtonMargin, - required this.backgroundColor, - required this.bubbleBorderSide, - // required this.dateDividerMargin, - // required this.chatContentMargin, - required this.dateDividerTextStyle, - // required this.deliveredIcon, - // required this.documentIcon, - // required this.emptyChatPlaceholderTextStyle, - // required this.errorColor, - // required this.errorIcon, - required this.inputBackgroundColor, - // required this.inputSurfaceTintColor, - // required this.inputElevation, - required this.inputBorderRadius, - // required this.inputMargin, - required this.inputPadding, - required this.inputTextColor, - required this.inputTextStyle, - required this.messageBorderRadius, - required this.messageInsetsHorizontal, - required this.messageInsetsVertical, - // required this.messageMaxWidth, - required this.primaryColor, - required this.receivedEmojiMessageTextStyle, - required this.receivedMessageBodyTextStyle, - // required this.receivedMessageCaptionTextStyle, - // required this.receivedMessageDocumentIconColor, - // required this.receivedMessageLinkDescriptionTextStyle, - // required this.receivedMessageLinkTitleTextStyle, - required this.secondaryColor, - // required this.seenIcon, - required this.sendButtonIcon, - // required this.sendButtonMargin, - // required this.sendingIcon, - required this.onlyEmojiFontSize, - required this.timeStyle, - required this.sentMessageBodyTextStyle, - // required this.sentMessageCaptionTextStyle, - // required this.sentMessageDocumentIconColor, - // required this.sentMessageLinkDescriptionTextStyle, - // required this.sentMessageLinkTitleTextStyle, - // required this.statusIconPadding, - // required this.userAvatarImageBackgroundColor, - // required this.userAvatarNameColors, - // required this.userAvatarTextStyle, - // required this.userNameTextStyle, - // required this.bubbleMargin, - required this.inputContainerDecoration, - // required this.inputTextCursorColor, - // required this.receivedMessageBodyBoldTextStyle, - // required this.receivedMessageBodyCodeTextStyle, - // required this.receivedMessageBodyLinkTextStyle, - // required this.sentMessageBodyBoldTextStyle, - // required this.sentMessageBodyCodeTextStyle, - // required this.sentMessageBodyLinkTextStyle, - // required this.highlightMessageColor, - }); - - final core.ChatTheme chatTheme; - - /// Icon for select attachment button. - final Widget? attachmentButtonIcon; - - /// Margin of attachment button. - // final EdgeInsets? attachmentButtonMargin; - - /// Used as a background color of a chat widget. - final Color backgroundColor; - - // Margin around the message bubble. - // final EdgeInsetsGeometry? bubbleMargin; - - /// Border for chat bubbles - final BorderSide bubbleBorderSide; - - /// Margin around date dividers. - // final EdgeInsets dateDividerMargin; - - /// Margin inside chat area. - // final EdgeInsets chatContentMargin; - - /// Text style of the date dividers. - final TextStyle dateDividerTextStyle; - - /// Icon for message's `delivered` status. For the best look use size of 16. - // final Widget? deliveredIcon; - - /// Icon inside file message. - // final Widget? documentIcon; - - /// Text style of the empty chat placeholder. - // final TextStyle emptyChatPlaceholderTextStyle; - - /// Color to indicate something bad happened (usually - shades of red). - // final Color errorColor; - - /// Icon for message's `error` status. For the best look use size of 16. - // final Widget? errorIcon; - - /// Color of the bottom bar where text field is. - final Color inputBackgroundColor; - - /// Surface Tint Color of the bottom bar where text field is. - // final Color inputSurfaceTintColor; - - /// Elevation to use for input material - // final double inputElevation; - - /// Top border radius of the bottom bar where text field is. - final BorderRadius inputBorderRadius; - - /// Decoration of the container wrapping the text field. - final Decoration? inputContainerDecoration; - - /// Outer insets of the bottom bar where text field is. - // final EdgeInsets inputMargin; - - /// Inner insets of the bottom bar where text field is. - final EdgeInsets inputPadding; - - /// Color of the text field's text and attachment/send buttons. - final Color inputTextColor; - - /// Color of the text field's cursor. - // final Color? inputTextCursorColor; - - /// Text style of the message input. To change the color use [inputTextColor]. - final TextStyle inputTextStyle; - - /// Border radius of message container. - final double messageBorderRadius; - - /// Horizontal message bubble insets. - final double messageInsetsHorizontal; - - /// Vertical message bubble insets. - final double messageInsetsVertical; - - /// Message bubble max width. set to [double.infinity] adaptive screen. - // final double messageMaxWidth; - - /// Primary color of the chat used as a background of sent messages - /// and statuses. - final Color primaryColor; - - /// Text style used for displaying emojis on text messages. - final TextStyle receivedEmojiMessageTextStyle; - - /// Body text style used for displaying bold text on received text messages. - // Default to a bold version of [receivedMessageBodyTextStyle]. - // final TextStyle? receivedMessageBodyBoldTextStyle; - - /// Body text style used for displaying code text on received text messages. - // Defaults to a mono version of [receivedMessageBodyTextStyle]. - // final TextStyle? receivedMessageBodyCodeTextStyle; - - /// Text style used for displaying link text on received text messages. - // Defaults to [receivedMessageBodyTextStyle]. - // final TextStyle? receivedMessageBodyLinkTextStyle; - - /// Body text style used for displaying text on different types - /// of received messages. - final TextStyle receivedMessageBodyTextStyle; - - /// Caption text style used for displaying secondary info (e.g. file size) on - /// different types of received messages. - // final TextStyle receivedMessageCaptionTextStyle; - - /// Color of the document icon on received messages. Has no effect when - // [documentIcon] is used. - // final Color receivedMessageDocumentIconColor; - - /// Text style used for displaying link description on received messages. - // final TextStyle receivedMessageLinkDescriptionTextStyle; - - /// Text style used for displaying link title on received messages. - // final TextStyle receivedMessageLinkTitleTextStyle; - - /// Secondary color, used as a background of received messages. - final Color secondaryColor; - - /// Icon for message's `seen` status. For the best look use size of 16. - // final Widget? seenIcon; - - /// Icon for send button. - final Widget? sendButtonIcon; - - /// Margin of send button. - // final EdgeInsets? sendButtonMargin; - - /// Icon for message's `sending` status. For the best look use size of 10. - // final Widget? sendingIcon; - - /// Text size for displaying emojis on text messages. - final double onlyEmojiFontSize; - - /// Text style used for time and status - final TextStyle timeStyle; - - /// Body text style used for displaying bold text on sent text messages. - /// Defaults to a bold version of [sentMessageBodyTextStyle]. - // final TextStyle? sentMessageBodyBoldTextStyle; - - /// Body text style used for displaying code text on sent text messages. - /// Defaults to a mono version of [sentMessageBodyTextStyle]. - // final TextStyle? sentMessageBodyCodeTextStyle; - - /// Text style used for displaying link text on sent text messages. - /// Defaults to [sentMessageBodyTextStyle]. - // final TextStyle? sentMessageBodyLinkTextStyle; - - /// Body text style used for displaying text on different types - /// of sent messages. - final TextStyle sentMessageBodyTextStyle; - - /// Caption text style used for displaying secondary info (e.g. file size) on - /// different types of sent messages. - // final TextStyle sentMessageCaptionTextStyle; - - /// Color of the document icon on sent messages. Has no effect when - // [documentIcon] is used. - // final Color sentMessageDocumentIconColor; - - /// Text style used for displaying link description on sent messages. - // final TextStyle sentMessageLinkDescriptionTextStyle; - - /// Text style used for displaying link title on sent messages. - // final TextStyle sentMessageLinkTitleTextStyle; - - /// Padding around status icons. - // final EdgeInsets statusIconPadding; - - /// Color used as a background for user avatar if an image is provided. - /// Visible if the image has some transparent parts. - // final Color userAvatarImageBackgroundColor; - - /// Colors used as backgrounds for user avatars with no image and so, - /// corresponding user names. - /// Calculated based on a user ID, so unique across the whole app. - // final List userAvatarNameColors; - - /// Text style used for displaying initials on user avatar if no - /// image is provided. - // final TextStyle userAvatarTextStyle; - - /// User names text style. Color will be overwritten with - // [userAvatarNameColors]. - // final TextStyle userNameTextStyle; - - /// Color used as background of message row on highlight. - // final Color? highlightMessageColor; -} - -extension ScaleChatThemeExt on ScaleTheme { - ScaleChatTheme chatTheme() { - // 'brightness' is not actually used by ChatColors.fromThemeData, - // or ChatTypography.fromThemeData so just say 'light' here - final themeData = toThemeData(Brightness.light); - final typography = core.ChatTypography.fromThemeData(themeData); - - final surfaceContainer = config.preferBorders - ? scheme.secondaryScale.calloutText - : scheme.secondaryScale.calloutBackground; - - final colors = core.ChatColors( - // Primary color, often used for sent messages and accents. - primary: config.preferBorders - ? scheme.primaryScale.calloutText - : scheme.primaryScale.calloutBackground, - // Color for text and icons displayed on top of [primary]. - onPrimary: scheme.primaryScale.primaryText, - // The main background color of the chat screen. - surface: - scheme.grayScale.appBackground.withAlpha(config.wallpaperAlpha), - - // Color for text and icons displayed on top of [surface]. - onSurface: scheme.primaryScale.appText, - - // Background color for elements like received messages. - surfaceContainer: surfaceContainer, - - // A slightly lighter/darker variant of [surfaceContainer]. - surfaceContainerLow: surfaceContainer.darken(25), - - // A slightly lighter/darker variant of [surfaceContainer]. - surfaceContainerHigh: surfaceContainer.lighten(25)); - - final chatTheme = core.ChatTheme( - colors: colors, - typography: typography, - shape: - BorderRadius.all(Radius.circular(config.borderRadiusScale * 12))); - - return ScaleChatTheme( - chatTheme: chatTheme, - primaryColor: config.preferBorders - ? scheme.primaryScale.calloutText - : scheme.primaryScale.calloutBackground, - secondaryColor: config.preferBorders - ? scheme.secondaryScale.calloutText - : scheme.secondaryScale.calloutBackground, - backgroundColor: - scheme.grayScale.appBackground.withAlpha(config.wallpaperAlpha), - messageBorderRadius: config.borderRadiusScale * 12, - bubbleBorderSide: config.preferBorders - ? BorderSide( - color: scheme.primaryScale.calloutBackground, - width: 2, - ) - : BorderSide(width: 2, color: Colors.black.withAlpha(96)), - sendButtonIcon: Image.asset( - 'assets/icon-send.png', - color: config.preferBorders - ? scheme.primaryScale.border - : scheme.primaryScale.borderText, - package: 'flutter_chat_ui', - ), - inputBackgroundColor: Colors.blue, - inputBorderRadius: BorderRadius.zero, - inputTextStyle: textTheme.bodyLarge!, - inputContainerDecoration: BoxDecoration( - border: config.preferBorders - ? Border( - top: - BorderSide(color: scheme.primaryScale.border, width: 2)) - : null, - color: config.preferBorders - ? scheme.primaryScale.elementBackground - : scheme.primaryScale.border), - inputPadding: const EdgeInsets.all(6), - inputTextColor: !config.preferBorders - ? scheme.primaryScale.appText - : scheme.primaryScale.border, - messageInsetsHorizontal: 12, - messageInsetsVertical: 8, - attachmentButtonIcon: const Icon(Icons.attach_file), - sentMessageBodyTextStyle: textTheme.bodyLarge!.copyWith( - color: config.preferBorders - ? scheme.primaryScale.calloutBackground - : scheme.primaryScale.calloutText, - ), - onlyEmojiFontSize: 64, - timeStyle: textTheme.bodySmall!.copyWith(fontSize: 9), - receivedMessageBodyTextStyle: textTheme.bodyLarge!.copyWith( - color: config.preferBorders - ? scheme.secondaryScale.calloutBackground - : scheme.secondaryScale.calloutText, - ), - receivedEmojiMessageTextStyle: const TextStyle( - color: Colors.white, - fontSize: 64, - ), - dateDividerTextStyle: textTheme.labelSmall!); - } -} diff --git a/lib/theme/models/scale_theme/scale_custom_dropdown_theme.dart b/lib/theme/models/scale_theme/scale_custom_dropdown_theme.dart deleted file mode 100644 index 2c5eb1c..0000000 --- a/lib/theme/models/scale_theme/scale_custom_dropdown_theme.dart +++ /dev/null @@ -1,92 +0,0 @@ -import 'package:animated_custom_dropdown/custom_dropdown.dart'; -import 'package:flutter/material.dart'; - -import 'scale_theme.dart'; - -class ScaleCustomDropdownTheme { - ScaleCustomDropdownTheme({ - required this.decoration, - required this.closedHeaderPadding, - required this.expandedHeaderPadding, - required this.itemsListPadding, - required this.listItemPadding, - required this.disabledDecoration, - required this.textStyle, - }); - - final CustomDropdownDecoration decoration; - final EdgeInsets closedHeaderPadding; - final EdgeInsets expandedHeaderPadding; - final EdgeInsets itemsListPadding; - final EdgeInsets listItemPadding; - final CustomDropdownDisabledDecoration disabledDecoration; - final TextStyle textStyle; -} - -extension ScaleCustomDropdownThemeExt on ScaleTheme { - ScaleCustomDropdownTheme customDropdownTheme() { - final scale = scheme.primaryScale; - final borderColor = scale.borderText; - final fillColor = scale.subtleBorder; - - // final backgroundColor = config.useVisualIndicators && !selected - // ? tileColor.borderText - // : borderColor; - // final textColor = config.useVisualIndicators && !selected - // ? borderColor - // : tileColor.borderText; - - // final largeTextStyle = textTheme.labelMedium!.copyWith(color: textColor); - // final smallTextStyle = textTheme.labelSmall!.copyWith(color: textColor); - - final border = Border.fromBorderSide(config.useVisualIndicators - ? BorderSide(width: 2, color: borderColor, strokeAlign: 0) - : BorderSide.none); - final borderRadius = BorderRadius.circular(8 * config.borderRadiusScale); - - final decoration = CustomDropdownDecoration( - closedFillColor: fillColor, - expandedFillColor: fillColor, - closedShadow: [], - expandedShadow: [], - closedSuffixIcon: Icon(Icons.arrow_drop_down, color: borderColor), - expandedSuffixIcon: Icon(Icons.arrow_drop_up, color: borderColor), - prefixIcon: null, - closedBorder: border, - closedBorderRadius: borderRadius, - closedErrorBorder: null, - closedErrorBorderRadius: null, - expandedBorder: border, - expandedBorderRadius: borderRadius, - hintStyle: null, - headerStyle: null, - noResultFoundStyle: null, - errorStyle: null, - listItemStyle: null, - overlayScrollbarDecoration: null, - searchFieldDecoration: null, - listItemDecoration: null, - ); - - const disabledDecoration = CustomDropdownDisabledDecoration( - fillColor: null, - shadow: null, - suffixIcon: null, - prefixIcon: null, - border: null, - borderRadius: null, - headerStyle: null, - hintStyle: null, - ); - - return ScaleCustomDropdownTheme( - textStyle: textTheme.labelSmall!.copyWith(color: borderColor), - decoration: decoration, - closedHeaderPadding: const EdgeInsets.all(4), - expandedHeaderPadding: const EdgeInsets.all(4), - itemsListPadding: const EdgeInsets.all(4), - listItemPadding: const EdgeInsets.all(4), - disabledDecoration: disabledDecoration, - ); - } -} diff --git a/lib/theme/models/scale_theme/scale_input_decorator_theme.dart b/lib/theme/models/scale_theme/scale_input_decorator_theme.dart deleted file mode 100644 index 1fb26a4..0000000 --- a/lib/theme/models/scale_theme/scale_input_decorator_theme.dart +++ /dev/null @@ -1,234 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - -import 'scale_theme.dart'; - -class ScaleInputDecoratorTheme extends InputDecorationTheme { - ScaleInputDecoratorTheme( - this._scaleScheme, ScaleConfig scaleConfig, this._textTheme) - : hintAlpha = scaleConfig.preferBorders ? 127 : 255, - super( - contentPadding: const EdgeInsets.all(8), - labelStyle: TextStyle(color: _scaleScheme.primaryScale.subtleText), - floatingLabelStyle: - TextStyle(color: _scaleScheme.primaryScale.subtleText), - border: OutlineInputBorder( - borderSide: BorderSide(color: _scaleScheme.primaryScale.border), - borderRadius: - BorderRadius.circular(8 * scaleConfig.borderRadiusScale)), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: _scaleScheme.primaryScale.border), - borderRadius: - BorderRadius.circular(8 * scaleConfig.borderRadiusScale)), - disabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: _scaleScheme.grayScale.border.withAlpha(0x7F)), - borderRadius: - BorderRadius.circular(8 * scaleConfig.borderRadiusScale)), - errorBorder: OutlineInputBorder( - borderSide: BorderSide(color: _scaleScheme.errorScale.border), - borderRadius: - BorderRadius.circular(8 * scaleConfig.borderRadiusScale)), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: _scaleScheme.primaryScale.hoverBorder, width: 2), - borderRadius: - BorderRadius.circular(8 * scaleConfig.borderRadiusScale)), - hoverColor: - _scaleScheme.primaryScale.hoverElementBackground.withAlpha(0x7F), - filled: true, - focusedErrorBorder: OutlineInputBorder( - borderSide: - BorderSide(color: _scaleScheme.errorScale.border, width: 2), - borderRadius: - BorderRadius.circular(8 * scaleConfig.borderRadiusScale)), - ); - - final ScaleScheme _scaleScheme; - final TextTheme _textTheme; - final int hintAlpha; - final int disabledAlpha = 127; - - @override - TextStyle? get hintStyle => WidgetStateTextStyle.resolveWith((states) { - if (states.contains(WidgetState.disabled)) { - return TextStyle(color: _scaleScheme.grayScale.border); - } - return TextStyle( - color: _scaleScheme.primaryScale.border.withAlpha(hintAlpha)); - }); - - @override - Color? get fillColor => WidgetStateColor.resolveWith((states) { - if (states.contains(WidgetState.disabled)) { - return _scaleScheme.grayScale.primary.withAlpha(10); - } - return _scaleScheme.primaryScale.primary.withAlpha(10); - }); - - @override - BorderSide? get activeIndicatorBorder => - WidgetStateBorderSide.resolveWith((states) { - if (states.contains(WidgetState.disabled)) { - return BorderSide( - color: _scaleScheme.grayScale.border.withAlpha(disabledAlpha)); - } - if (states.contains(WidgetState.error)) { - if (states.contains(WidgetState.hovered)) { - return BorderSide(color: _scaleScheme.errorScale.hoverBorder); - } - if (states.contains(WidgetState.focused)) { - return BorderSide(color: _scaleScheme.errorScale.border, width: 2); - } - return BorderSide(color: _scaleScheme.errorScale.subtleBorder); - } - if (states.contains(WidgetState.hovered)) { - return BorderSide(color: _scaleScheme.secondaryScale.hoverBorder); - } - if (states.contains(WidgetState.focused)) { - return BorderSide( - color: _scaleScheme.secondaryScale.border, width: 2); - } - return BorderSide(color: _scaleScheme.secondaryScale.subtleBorder); - }); - - @override - BorderSide? get outlineBorder => WidgetStateBorderSide.resolveWith((states) { - if (states.contains(WidgetState.disabled)) { - return BorderSide( - color: _scaleScheme.grayScale.border.withAlpha(disabledAlpha)); - } - if (states.contains(WidgetState.error)) { - if (states.contains(WidgetState.hovered)) { - return BorderSide(color: _scaleScheme.errorScale.hoverBorder); - } - if (states.contains(WidgetState.focused)) { - return BorderSide(color: _scaleScheme.errorScale.border, width: 2); - } - return BorderSide(color: _scaleScheme.errorScale.subtleBorder); - } - if (states.contains(WidgetState.hovered)) { - return BorderSide(color: _scaleScheme.primaryScale.hoverBorder); - } - if (states.contains(WidgetState.focused)) { - return BorderSide(color: _scaleScheme.primaryScale.border, width: 2); - } - return BorderSide(color: _scaleScheme.primaryScale.subtleBorder); - }); - - @override - Color? get iconColor => _scaleScheme.primaryScale.primary; - - @override - Color? get prefixIconColor => WidgetStateColor.resolveWith((states) { - if (states.contains(WidgetState.disabled)) { - return _scaleScheme.primaryScale.primary.withAlpha(disabledAlpha); - } - if (states.contains(WidgetState.error)) { - return _scaleScheme.errorScale.primary; - } - return _scaleScheme.primaryScale.primary; - }); - - @override - Color? get suffixIconColor => WidgetStateColor.resolveWith((states) { - if (states.contains(WidgetState.disabled)) { - return _scaleScheme.primaryScale.primary.withAlpha(disabledAlpha); - } - if (states.contains(WidgetState.error)) { - return _scaleScheme.errorScale.primary; - } - return _scaleScheme.primaryScale.primary; - }); - - @override - TextStyle? get labelStyle => WidgetStateTextStyle.resolveWith((states) { - final textStyle = _textTheme.bodyLarge ?? const TextStyle(); - if (states.contains(WidgetState.disabled)) { - return textStyle.copyWith( - color: _scaleScheme.grayScale.border.withAlpha(disabledAlpha)); - } - if (states.contains(WidgetState.error)) { - if (states.contains(WidgetState.hovered)) { - return textStyle.copyWith( - color: _scaleScheme.errorScale.hoverBorder); - } - if (states.contains(WidgetState.focused)) { - return textStyle.copyWith( - color: _scaleScheme.errorScale.hoverBorder); - } - return textStyle.copyWith( - color: _scaleScheme.errorScale.subtleBorder); - } - if (states.contains(WidgetState.hovered)) { - return textStyle.copyWith( - color: _scaleScheme.primaryScale.hoverBorder); - } - if (states.contains(WidgetState.focused)) { - return textStyle.copyWith( - color: _scaleScheme.primaryScale.hoverBorder); - } - return textStyle.copyWith( - color: _scaleScheme.primaryScale.border.withAlpha(hintAlpha)); - }); - - @override - TextStyle? get floatingLabelStyle => - WidgetStateTextStyle.resolveWith((states) { - final textStyle = _textTheme.bodyLarge ?? const TextStyle(); - if (states.contains(WidgetState.disabled)) { - return textStyle.copyWith( - color: _scaleScheme.grayScale.border.withAlpha(disabledAlpha)); - } - if (states.contains(WidgetState.error)) { - if (states.contains(WidgetState.hovered)) { - return textStyle.copyWith( - color: _scaleScheme.errorScale.hoverBorder); - } - if (states.contains(WidgetState.focused)) { - return textStyle.copyWith( - color: _scaleScheme.errorScale.hoverBorder); - } - return textStyle.copyWith( - color: _scaleScheme.errorScale.subtleBorder); - } - if (states.contains(WidgetState.hovered)) { - return textStyle.copyWith( - color: _scaleScheme.primaryScale.hoverBorder); - } - if (states.contains(WidgetState.focused)) { - return textStyle.copyWith( - color: _scaleScheme.primaryScale.hoverBorder); - } - return textStyle.copyWith(color: _scaleScheme.primaryScale.border); - }); - - @override - TextStyle? get helperStyle => WidgetStateTextStyle.resolveWith((states) { - final textStyle = _textTheme.bodySmall ?? const TextStyle(); - if (states.contains(WidgetState.disabled)) { - return textStyle.copyWith(color: _scaleScheme.grayScale.border); - } - return textStyle.copyWith( - color: _scaleScheme.primaryScale.border.withAlpha(hintAlpha)); - }); - - @override - TextStyle? get errorStyle => WidgetStateTextStyle.resolveWith((states) { - final textStyle = _textTheme.bodySmall ?? const TextStyle(); - return textStyle.copyWith(color: _scaleScheme.errorScale.primary); - }); - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(IntProperty('disabledAlpha', disabledAlpha)) - ..add(IntProperty('hintAlpha', hintAlpha)); - } -} - -extension ScaleInputDecoratorThemeExt on ScaleTheme { - ScaleInputDecoratorTheme inputDecoratorTheme() => - ScaleInputDecoratorTheme(scheme, config, textTheme); -} diff --git a/lib/theme/models/scale_theme/scale_theme.dart b/lib/theme/models/scale_theme/scale_theme.dart deleted file mode 100644 index c3217ea..0000000 --- a/lib/theme/models/scale_theme/scale_theme.dart +++ /dev/null @@ -1,190 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'scale_input_decorator_theme.dart'; -import 'scale_scheme.dart'; - -export 'scale_app_bar_theme.dart'; -export 'scale_chat_theme.dart'; -export 'scale_color.dart'; -export 'scale_input_decorator_theme.dart'; -export 'scale_scheme.dart'; -export 'scale_tile_theme.dart'; -export 'scale_toast_theme.dart'; - -class ScaleTheme extends ThemeExtension { - ScaleTheme({ - required this.textTheme, - required this.scheme, - required this.config, - }); - - final TextTheme textTheme; - final ScaleScheme scheme; - final ScaleConfig config; - - @override - ScaleTheme copyWith({ - TextTheme? textTheme, - ScaleScheme? scheme, - ScaleConfig? config, - }) => - ScaleTheme( - textTheme: textTheme ?? this.textTheme, - scheme: scheme ?? this.scheme, - config: config ?? this.config, - ); - - @override - ScaleTheme lerp(ScaleTheme? other, double t) { - if (other is! ScaleTheme) { - return this; - } - return ScaleTheme( - textTheme: TextTheme.lerp(textTheme, other.textTheme, t), - scheme: scheme.lerp(other.scheme, t), - config: config.lerp(other.config, t)); - } - - WidgetStateProperty elementBorderWidgetStateProperty() => - WidgetStateProperty.resolveWith((states) { - if (states.contains(WidgetState.disabled)) { - return BorderSide( - color: scheme.grayScale.border.withAlpha(0x7F), - strokeAlign: BorderSide.strokeAlignOutside); - } else if (states.contains(WidgetState.pressed)) { - return BorderSide( - color: scheme.primaryScale.border, - ); - } else if (states.contains(WidgetState.hovered)) { - return BorderSide( - color: scheme.primaryScale.hoverBorder, - strokeAlign: BorderSide.strokeAlignOutside); - } else if (states.contains(WidgetState.focused)) { - return BorderSide( - color: scheme.primaryScale.hoverBorder, - width: 2, - strokeAlign: BorderSide.strokeAlignOutside); - } - return BorderSide( - color: scheme.primaryScale.border, - strokeAlign: BorderSide.strokeAlignOutside); - }); - - WidgetStateProperty elementColorWidgetStateProperty() => - WidgetStateProperty.resolveWith((states) { - if (states.contains(WidgetState.disabled)) { - return scheme.grayScale.primary.withAlpha(0x7F); - } else if (states.contains(WidgetState.pressed)) { - return scheme.primaryScale.borderText; - } else if (states.contains(WidgetState.hovered)) { - return scheme.primaryScale.borderText; - } else if (states.contains(WidgetState.focused)) { - return scheme.primaryScale.borderText; - } - return Color.lerp( - scheme.primaryScale.borderText, scheme.primaryScale.primary, 0.25); - }); - - WidgetStateProperty checkboxFillColorWidgetStateProperty() => - WidgetStateProperty.resolveWith((states) { - if (states.contains(WidgetState.selected)) { - if (states.contains(WidgetState.disabled)) { - return scheme.grayScale.primary.withAlpha(0x7F); - } else if (states.contains(WidgetState.pressed)) { - return scheme.primaryScale.hoverBorder; - } else if (states.contains(WidgetState.hovered)) { - return scheme.primaryScale.hoverBorder; - } else if (states.contains(WidgetState.focused)) { - return scheme.primaryScale.border; - } - return scheme.primaryScale.border; - } else { - return Colors.transparent; - } - }); - - // WidgetStateProperty elementBackgroundWidgetStateProperty() { - // return null; - // } - - ThemeData toThemeData(Brightness brightness) { - final colorScheme = scheme.toColorScheme(brightness); - - final baseThemeData = ThemeData.from( - colorScheme: colorScheme, textTheme: textTheme, useMaterial3: true); - - final elevatedButtonTheme = ElevatedButtonThemeData( - style: ElevatedButton.styleFrom( - elevation: 0, - textStyle: textTheme.labelSmall, - backgroundColor: scheme.primaryScale.elementBackground, - disabledBackgroundColor: - scheme.grayScale.elementBackground.withAlpha(0x7F), - disabledForegroundColor: - scheme.grayScale.primary.withAlpha(0x7F), - shape: RoundedRectangleBorder( - side: BorderSide(color: scheme.primaryScale.border), - borderRadius: - BorderRadius.circular(8 * config.borderRadiusScale))) - .copyWith( - foregroundColor: elementColorWidgetStateProperty(), - side: elementBorderWidgetStateProperty(), - iconColor: elementColorWidgetStateProperty(), - )); - - final themeData = baseThemeData.copyWith( - scrollbarTheme: baseThemeData.scrollbarTheme.copyWith( - thumbColor: WidgetStateProperty.resolveWith((states) { - if (states.contains(WidgetState.pressed)) { - return scheme.primaryScale.border; - } else if (states.contains(WidgetState.hovered)) { - return scheme.primaryScale.hoverBorder; - } - return scheme.primaryScale.subtleBorder; - }), trackColor: WidgetStateProperty.resolveWith((states) { - if (states.contains(WidgetState.pressed)) { - return scheme.primaryScale.activeElementBackground; - } else if (states.contains(WidgetState.hovered)) { - return scheme.primaryScale.hoverElementBackground; - } - return scheme.primaryScale.elementBackground; - }), trackBorderColor: WidgetStateProperty.resolveWith((states) { - if (states.contains(WidgetState.pressed)) { - return scheme.primaryScale.subtleBorder; - } else if (states.contains(WidgetState.hovered)) { - return scheme.primaryScale.subtleBorder; - } - return scheme.primaryScale.subtleBorder; - })), - appBarTheme: baseThemeData.appBarTheme.copyWith( - backgroundColor: scheme.primaryScale.border, - foregroundColor: scheme.primaryScale.borderText, - toolbarHeight: 48, - ), - bottomSheetTheme: baseThemeData.bottomSheetTheme.copyWith( - elevation: 0, - modalElevation: 0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(16 * config.borderRadiusScale), - topRight: Radius.circular(16 * config.borderRadiusScale)))), - canvasColor: scheme.primaryScale.subtleBackground, - checkboxTheme: baseThemeData.checkboxTheme.copyWith( - side: BorderSide(color: scheme.primaryScale.border, width: 2), - checkColor: elementColorWidgetStateProperty(), - fillColor: checkboxFillColorWidgetStateProperty(), - ), - chipTheme: baseThemeData.chipTheme.copyWith( - backgroundColor: scheme.primaryScale.elementBackground, - selectedColor: scheme.primaryScale.activeElementBackground, - surfaceTintColor: scheme.primaryScale.hoverElementBackground, - checkmarkColor: scheme.primaryScale.primary, - side: BorderSide(color: scheme.primaryScale.border)), - elevatedButtonTheme: elevatedButtonTheme, - inputDecorationTheme: - ScaleInputDecoratorTheme(scheme, config, textTheme), - extensions: >[scheme, config, this]); - - return themeData; - } -} diff --git a/lib/theme/models/scale_theme/scale_tile_theme.dart b/lib/theme/models/scale_theme/scale_tile_theme.dart deleted file mode 100644 index d549157..0000000 --- a/lib/theme/models/scale_theme/scale_tile_theme.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'scale_theme.dart'; - -class ScaleTileTheme { - ScaleTileTheme( - {required this.textColor, - required this.backgroundColor, - required this.borderColor, - required this.shapeBorder, - required this.largeTextStyle, - required this.smallTextStyle}); - - final Color textColor; - final Color backgroundColor; - final Color borderColor; - final ShapeBorder shapeBorder; - final TextStyle largeTextStyle; - final TextStyle smallTextStyle; -} - -extension ScaleTileThemeExt on ScaleTheme { - ScaleTileTheme tileTheme( - {bool disabled = false, - bool selected = false, - ScaleKind scaleKind = ScaleKind.primary}) { - final tileColor = scheme.scale(!disabled ? scaleKind : ScaleKind.gray); - - final borderColor = selected ? tileColor.hoverBorder : tileColor.border; - final backgroundColor = config.useVisualIndicators && !selected - ? tileColor.borderText - : borderColor; - final textColor = config.useVisualIndicators && !selected - ? borderColor - : tileColor.borderText; - - final largeTextStyle = textTheme.labelMedium!.copyWith(color: textColor); - final smallTextStyle = textTheme.labelSmall!.copyWith(color: textColor); - - final shapeBorder = RoundedRectangleBorder( - side: config.useVisualIndicators - ? BorderSide( - width: 2, - color: borderColor, - ) - : BorderSide.none, - borderRadius: BorderRadius.circular(8 * config.borderRadiusScale)); - - return ScaleTileTheme( - textColor: textColor, - backgroundColor: backgroundColor, - borderColor: borderColor, - shapeBorder: shapeBorder, - largeTextStyle: largeTextStyle, - smallTextStyle: smallTextStyle, - ); - } -} diff --git a/lib/theme/models/scale_theme/scale_toast_theme.dart b/lib/theme/models/scale_theme/scale_toast_theme.dart deleted file mode 100644 index 61f119d..0000000 --- a/lib/theme/models/scale_theme/scale_toast_theme.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'scale_theme.dart'; - -enum ScaleToastKind { - info, - error, -} - -class ScaleToastTheme { - ScaleToastTheme( - {required this.primaryColor, - required this.backgroundColor, - required this.foregroundColor, - required this.borderSide, - required this.borderRadius, - required this.padding, - required this.icon, - required this.titleTextStyle, - required this.descriptionTextStyle}); - - final Color primaryColor; - final Color backgroundColor; - final Color foregroundColor; - final BorderSide? borderSide; - final BorderRadiusGeometry borderRadius; - final EdgeInsetsGeometry padding; - final Icon icon; - final TextStyle titleTextStyle; - final TextStyle descriptionTextStyle; -} - -extension ScaleToastThemeExt on ScaleTheme { - ScaleToastTheme toastTheme(ScaleToastKind kind) { - final toastScaleColor = scheme.scale(ScaleKind.tertiary); - - final primaryColor = toastScaleColor.calloutText; - final borderColor = toastScaleColor.border; - final backgroundColor = config.useVisualIndicators - ? toastScaleColor.calloutText - : toastScaleColor.calloutBackground; - final textColor = config.useVisualIndicators - ? toastScaleColor.calloutBackground - : toastScaleColor.calloutText; - final titleColor = config.useVisualIndicators - ? toastScaleColor.calloutBackground - : toastScaleColor.calloutText; - Icon icon; - switch (kind) { - case ScaleToastKind.info: - icon = Icon(Icons.info, size: 32, color: primaryColor); - case ScaleToastKind.error: - icon = Icon(Icons.dangerous, size: 32, color: primaryColor); - } - - return ScaleToastTheme( - primaryColor: primaryColor, - backgroundColor: backgroundColor, - foregroundColor: textColor, - borderSide: (config.useVisualIndicators || config.preferBorders) - ? BorderSide(color: borderColor, width: 2) - : const BorderSide(color: Colors.transparent, width: 0), - borderRadius: BorderRadius.circular(12 * config.borderRadiusScale), - padding: const EdgeInsets.all(8), - icon: icon, - titleTextStyle: textTheme.labelMedium!.copyWith(color: titleColor), - descriptionTextStyle: - textTheme.labelMedium!.copyWith(color: textColor)); - } -} diff --git a/lib/theme/models/slider_tile.dart b/lib/theme/models/slider_tile.dart new file mode 100644 index 0000000..e70c6bd --- /dev/null +++ b/lib/theme/models/slider_tile.dart @@ -0,0 +1,165 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_slidable/flutter_slidable.dart'; + +import '../theme.dart'; + +class SliderTileAction { + const SliderTileAction({ + required this.actionScale, + required this.onPressed, + this.key, + this.icon, + this.label, + }); + + final Key? key; + final ScaleKind actionScale; + final String? label; + final IconData? icon; + final SlidableActionCallback? onPressed; +} + +class SliderTile extends StatelessWidget { + const SliderTile( + {required this.disabled, + required this.selected, + required this.tileScale, + required this.title, + this.subtitle = '', + this.endActions = const [], + this.startActions = const [], + this.onTap, + this.onDoubleTap, + this.leading, + this.trailing, + super.key}); + + final bool disabled; + final bool selected; + final ScaleKind tileScale; + final List endActions; + final List startActions; + final GestureTapCallback? onTap; + final GestureTapCallback? onDoubleTap; + final Widget? leading; + final Widget? trailing; + final String title; + final String subtitle; + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('disabled', disabled)) + ..add(DiagnosticsProperty('selected', selected)) + ..add(DiagnosticsProperty('tileScale', tileScale)) + ..add(IterableProperty('endActions', endActions)) + ..add(IterableProperty('startActions', startActions)) + ..add(ObjectFlagProperty.has('onTap', onTap)) + ..add(DiagnosticsProperty('leading', leading)) + ..add(StringProperty('title', title)) + ..add(StringProperty('subtitle', subtitle)) + ..add(ObjectFlagProperty.has( + 'onDoubleTap', onDoubleTap)) + ..add(DiagnosticsProperty('trailing', trailing)); + } + + @override + // ignore: prefer_expression_function_bodies + Widget build(BuildContext context) { + final theme = Theme.of(context); + final scale = theme.extension()!; + final tileColor = scale.scale(!disabled ? tileScale : ScaleKind.gray); + final scaleConfig = theme.extension()!; + + final borderColor = selected ? tileColor.hoverBorder : tileColor.border; + final backgroundColor = scaleConfig.useVisualIndicators && !selected + ? tileColor.borderText + : borderColor; + final textColor = scaleConfig.useVisualIndicators && !selected + ? borderColor + : tileColor.borderText; + + return Container( + clipBehavior: Clip.antiAlias, + decoration: ShapeDecoration( + color: backgroundColor, + shape: RoundedRectangleBorder( + side: scaleConfig.useVisualIndicators + ? BorderSide(width: 2, color: borderColor, strokeAlign: 0) + : BorderSide.none, + borderRadius: + BorderRadius.circular(8 * scaleConfig.borderRadiusScale), + )), + child: Slidable( + // Specify a key if the Slidable is dismissible. + key: key, + endActionPane: endActions.isEmpty + ? null + : ActionPane( + motion: const DrawerMotion(), + children: endActions + .map( + (a) => SlidableAction( + onPressed: disabled ? null : a.onPressed, + backgroundColor: scaleConfig.useVisualIndicators + ? (selected + ? tileColor.borderText + : tileColor.border) + : scale.scale(a.actionScale).primary, + foregroundColor: scaleConfig.useVisualIndicators + ? (selected + ? tileColor.border + : tileColor.borderText) + : scale.scale(a.actionScale).primaryText, + icon: subtitle.isNotEmpty ? a.icon : null, + label: a.label, + padding: const EdgeInsets.all(2)), + ) + .toList()), + startActionPane: startActions.isEmpty + ? null + : ActionPane( + motion: const DrawerMotion(), + children: startActions + .map( + (a) => SlidableAction( + onPressed: disabled ? null : a.onPressed, + backgroundColor: scaleConfig.useVisualIndicators + ? (selected + ? tileColor.borderText + : tileColor.border) + : scale.scale(a.actionScale).primary, + foregroundColor: scaleConfig.useVisualIndicators + ? (selected + ? tileColor.border + : tileColor.borderText) + : scale.scale(a.actionScale).primaryText, + icon: subtitle.isNotEmpty ? a.icon : null, + label: a.label, + padding: const EdgeInsets.all(2)), + ) + .toList()), + child: Padding( + padding: scaleConfig.useVisualIndicators + ? EdgeInsets.zero + : const EdgeInsets.fromLTRB(0, 2, 0, 2), + child: GestureDetector( + onDoubleTap: onDoubleTap, + child: ListTile( + onTap: onTap, + dense: true, + visualDensity: const VisualDensity(vertical: -4), + title: Text( + title, + overflow: TextOverflow.fade, + softWrap: false, + ), + subtitle: subtitle.isNotEmpty ? Text(subtitle) : null, + iconColor: textColor, + textColor: textColor, + leading: FittedBox(child: leading), + trailing: FittedBox(child: trailing)))))); + } +} diff --git a/lib/theme/models/theme_preference.dart b/lib/theme/models/theme_preference.dart index aaad52d..cfa05c9 100644 --- a/lib/theme/models/theme_preference.dart +++ b/lib/theme/models/theme_preference.dart @@ -1,13 +1,11 @@ import 'package:change_case/change_case.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/svg.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; -import '../../init.dart'; import '../views/widget_helpers.dart'; import 'contrast_generator.dart'; import 'radix_generator.dart'; -import 'scale_theme/scale_scheme.dart'; +import 'scale_scheme.dart'; part 'theme_preference.freezed.dart'; part 'theme_preference.g.dart'; @@ -49,13 +47,12 @@ enum ColorPreference { } @freezed -sealed class ThemePreferences with _$ThemePreferences { +class ThemePreferences with _$ThemePreferences { const factory ThemePreferences({ @Default(BrightnessPreference.system) BrightnessPreference brightnessPreference, @Default(ColorPreference.vapor) ColorPreference colorPreference, @Default(1) double displayScale, - @Default(true) bool enableWallpaper, }) = _ThemePreferences; factory ThemePreferences.fromJson(dynamic json) => @@ -65,17 +62,6 @@ sealed class ThemePreferences with _$ThemePreferences { } extension ThemePreferencesExt on ThemePreferences { - /// Get wallpaper for existing theme - Widget? wallpaper() { - if (enableWallpaper) { - final assetName = 'assets/images/wallpaper/${colorPreference.name}.svg'; - if (rootAssets.contains(assetName)) { - return SvgPicture.asset(assetName, fit: BoxFit.cover); - } - } - return null; - } - /// Get material 'ThemeData' for existing theme ThemeData themeData() { late final Brightness brightness; @@ -101,8 +87,7 @@ extension ThemePreferencesExt on ThemePreferences { scaleConfig: ScaleConfig( useVisualIndicators: true, preferBorders: false, - borderRadiusScale: 1, - wallpaperOpacity: 255), + borderRadiusScale: 1), primaryFront: Colors.black, primaryBack: Colors.white, secondaryFront: Colors.black, @@ -121,8 +106,7 @@ extension ThemePreferencesExt on ThemePreferences { scaleConfig: ScaleConfig( useVisualIndicators: true, preferBorders: true, - borderRadiusScale: 0.2, - wallpaperOpacity: 208), + borderRadiusScale: 0.2), primaryFront: const Color(0xFF000000), primaryBack: const Color(0xFF00FF00), secondaryFront: const Color(0xFF000000), @@ -139,8 +123,7 @@ extension ThemePreferencesExt on ThemePreferences { scaleConfig: ScaleConfig( useVisualIndicators: true, preferBorders: true, - borderRadiusScale: 0.2, - wallpaperOpacity: 192), + borderRadiusScale: 0.5), primaryFront: const Color(0xFF000000), primaryBack: const Color(0xFF00FF00), secondaryFront: const Color(0xFF000000), diff --git a/lib/theme/models/theme_preference.freezed.dart b/lib/theme/models/theme_preference.freezed.dart index c915bca..97e3f81 100644 --- a/lib/theme/models/theme_preference.freezed.dart +++ b/lib/theme/models/theme_preference.freezed.dart @@ -1,4 +1,3 @@ -// dart format width=80 // coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND // ignore_for_file: type=lint @@ -10,115 +9,130 @@ part of 'theme_preference.dart'; // FreezedGenerator // ************************************************************************** -// dart format off T _$identity(T value) => value; -/// @nodoc -mixin _$ThemePreferences { - BrightnessPreference get brightnessPreference; - ColorPreference get colorPreference; - double get displayScale; - bool get enableWallpaper; +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - /// Create a copy of ThemePreferences - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') - $ThemePreferencesCopyWith get copyWith => - _$ThemePreferencesCopyWithImpl( - this as ThemePreferences, _$identity); - - /// Serializes this ThemePreferences to a JSON map. - Map toJson(); - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is ThemePreferences && - (identical(other.brightnessPreference, brightnessPreference) || - other.brightnessPreference == brightnessPreference) && - (identical(other.colorPreference, colorPreference) || - other.colorPreference == colorPreference) && - (identical(other.displayScale, displayScale) || - other.displayScale == displayScale) && - (identical(other.enableWallpaper, enableWallpaper) || - other.enableWallpaper == enableWallpaper)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash(runtimeType, brightnessPreference, - colorPreference, displayScale, enableWallpaper); - - @override - String toString() { - return 'ThemePreferences(brightnessPreference: $brightnessPreference, colorPreference: $colorPreference, displayScale: $displayScale, enableWallpaper: $enableWallpaper)'; - } +ThemePreferences _$ThemePreferencesFromJson(Map json) { + return _ThemePreferences.fromJson(json); } /// @nodoc -abstract mixin class $ThemePreferencesCopyWith<$Res> { +mixin _$ThemePreferences { + BrightnessPreference get brightnessPreference => + throw _privateConstructorUsedError; + ColorPreference get colorPreference => throw _privateConstructorUsedError; + double get displayScale => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $ThemePreferencesCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $ThemePreferencesCopyWith<$Res> { factory $ThemePreferencesCopyWith( - ThemePreferences value, $Res Function(ThemePreferences) _then) = - _$ThemePreferencesCopyWithImpl; + ThemePreferences value, $Res Function(ThemePreferences) then) = + _$ThemePreferencesCopyWithImpl<$Res, ThemePreferences>; @useResult $Res call( {BrightnessPreference brightnessPreference, ColorPreference colorPreference, - double displayScale, - bool enableWallpaper}); + double displayScale}); } /// @nodoc -class _$ThemePreferencesCopyWithImpl<$Res> +class _$ThemePreferencesCopyWithImpl<$Res, $Val extends ThemePreferences> implements $ThemePreferencesCopyWith<$Res> { - _$ThemePreferencesCopyWithImpl(this._self, this._then); + _$ThemePreferencesCopyWithImpl(this._value, this._then); - final ThemePreferences _self; - final $Res Function(ThemePreferences) _then; + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; - /// Create a copy of ThemePreferences - /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ Object? brightnessPreference = null, Object? colorPreference = null, Object? displayScale = null, - Object? enableWallpaper = null, }) { - return _then(_self.copyWith( + return _then(_value.copyWith( brightnessPreference: null == brightnessPreference - ? _self.brightnessPreference + ? _value.brightnessPreference : brightnessPreference // ignore: cast_nullable_to_non_nullable as BrightnessPreference, colorPreference: null == colorPreference - ? _self.colorPreference + ? _value.colorPreference : colorPreference // ignore: cast_nullable_to_non_nullable as ColorPreference, displayScale: null == displayScale - ? _self.displayScale + ? _value.displayScale + : displayScale // ignore: cast_nullable_to_non_nullable + as double, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$ThemePreferencesImplCopyWith<$Res> + implements $ThemePreferencesCopyWith<$Res> { + factory _$$ThemePreferencesImplCopyWith(_$ThemePreferencesImpl value, + $Res Function(_$ThemePreferencesImpl) then) = + __$$ThemePreferencesImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {BrightnessPreference brightnessPreference, + ColorPreference colorPreference, + double displayScale}); +} + +/// @nodoc +class __$$ThemePreferencesImplCopyWithImpl<$Res> + extends _$ThemePreferencesCopyWithImpl<$Res, _$ThemePreferencesImpl> + implements _$$ThemePreferencesImplCopyWith<$Res> { + __$$ThemePreferencesImplCopyWithImpl(_$ThemePreferencesImpl _value, + $Res Function(_$ThemePreferencesImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? brightnessPreference = null, + Object? colorPreference = null, + Object? displayScale = null, + }) { + return _then(_$ThemePreferencesImpl( + brightnessPreference: null == brightnessPreference + ? _value.brightnessPreference + : brightnessPreference // ignore: cast_nullable_to_non_nullable + as BrightnessPreference, + colorPreference: null == colorPreference + ? _value.colorPreference + : colorPreference // ignore: cast_nullable_to_non_nullable + as ColorPreference, + displayScale: null == displayScale + ? _value.displayScale : displayScale // ignore: cast_nullable_to_non_nullable as double, - enableWallpaper: null == enableWallpaper - ? _self.enableWallpaper - : enableWallpaper // ignore: cast_nullable_to_non_nullable - as bool, )); } } /// @nodoc @JsonSerializable() -class _ThemePreferences implements ThemePreferences { - const _ThemePreferences( +class _$ThemePreferencesImpl implements _ThemePreferences { + const _$ThemePreferencesImpl( {this.brightnessPreference = BrightnessPreference.system, this.colorPreference = ColorPreference.vapor, - this.displayScale = 1, - this.enableWallpaper = true}); - factory _ThemePreferences.fromJson(Map json) => - _$ThemePreferencesFromJson(json); + this.displayScale = 1}); + + factory _$ThemePreferencesImpl.fromJson(Map json) => + _$$ThemePreferencesImplFromJson(json); @override @JsonKey() @@ -129,103 +143,62 @@ class _ThemePreferences implements ThemePreferences { @override @JsonKey() final double displayScale; - @override - @JsonKey() - final bool enableWallpaper; - - /// Create a copy of ThemePreferences - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') - _$ThemePreferencesCopyWith<_ThemePreferences> get copyWith => - __$ThemePreferencesCopyWithImpl<_ThemePreferences>(this, _$identity); @override - Map toJson() { - return _$ThemePreferencesToJson( - this, - ); + String toString() { + return 'ThemePreferences(brightnessPreference: $brightnessPreference, colorPreference: $colorPreference, displayScale: $displayScale)'; } @override bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _ThemePreferences && + other is _$ThemePreferencesImpl && (identical(other.brightnessPreference, brightnessPreference) || other.brightnessPreference == brightnessPreference) && (identical(other.colorPreference, colorPreference) || other.colorPreference == colorPreference) && (identical(other.displayScale, displayScale) || - other.displayScale == displayScale) && - (identical(other.enableWallpaper, enableWallpaper) || - other.enableWallpaper == enableWallpaper)); + other.displayScale == displayScale)); } - @JsonKey(includeFromJson: false, includeToJson: false) + @JsonKey(ignore: true) @override - int get hashCode => Object.hash(runtimeType, brightnessPreference, - colorPreference, displayScale, enableWallpaper); + int get hashCode => Object.hash( + runtimeType, brightnessPreference, colorPreference, displayScale); - @override - String toString() { - return 'ThemePreferences(brightnessPreference: $brightnessPreference, colorPreference: $colorPreference, displayScale: $displayScale, enableWallpaper: $enableWallpaper)'; - } -} - -/// @nodoc -abstract mixin class _$ThemePreferencesCopyWith<$Res> - implements $ThemePreferencesCopyWith<$Res> { - factory _$ThemePreferencesCopyWith( - _ThemePreferences value, $Res Function(_ThemePreferences) _then) = - __$ThemePreferencesCopyWithImpl; - @override - @useResult - $Res call( - {BrightnessPreference brightnessPreference, - ColorPreference colorPreference, - double displayScale, - bool enableWallpaper}); -} - -/// @nodoc -class __$ThemePreferencesCopyWithImpl<$Res> - implements _$ThemePreferencesCopyWith<$Res> { - __$ThemePreferencesCopyWithImpl(this._self, this._then); - - final _ThemePreferences _self; - final $Res Function(_ThemePreferences) _then; - - /// Create a copy of ThemePreferences - /// with the given fields replaced by the non-null parameter values. + @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - $Res call({ - Object? brightnessPreference = null, - Object? colorPreference = null, - Object? displayScale = null, - Object? enableWallpaper = null, - }) { - return _then(_ThemePreferences( - brightnessPreference: null == brightnessPreference - ? _self.brightnessPreference - : brightnessPreference // ignore: cast_nullable_to_non_nullable - as BrightnessPreference, - colorPreference: null == colorPreference - ? _self.colorPreference - : colorPreference // ignore: cast_nullable_to_non_nullable - as ColorPreference, - displayScale: null == displayScale - ? _self.displayScale - : displayScale // ignore: cast_nullable_to_non_nullable - as double, - enableWallpaper: null == enableWallpaper - ? _self.enableWallpaper - : enableWallpaper // ignore: cast_nullable_to_non_nullable - as bool, - )); + _$$ThemePreferencesImplCopyWith<_$ThemePreferencesImpl> get copyWith => + __$$ThemePreferencesImplCopyWithImpl<_$ThemePreferencesImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$ThemePreferencesImplToJson( + this, + ); } } -// dart format on +abstract class _ThemePreferences implements ThemePreferences { + const factory _ThemePreferences( + {final BrightnessPreference brightnessPreference, + final ColorPreference colorPreference, + final double displayScale}) = _$ThemePreferencesImpl; + + factory _ThemePreferences.fromJson(Map json) = + _$ThemePreferencesImpl.fromJson; + + @override + BrightnessPreference get brightnessPreference; + @override + ColorPreference get colorPreference; + @override + double get displayScale; + @override + @JsonKey(ignore: true) + _$$ThemePreferencesImplCopyWith<_$ThemePreferencesImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/theme/models/theme_preference.g.dart b/lib/theme/models/theme_preference.g.dart index f052e2c..4cb2d71 100644 --- a/lib/theme/models/theme_preference.g.dart +++ b/lib/theme/models/theme_preference.g.dart @@ -6,8 +6,9 @@ part of 'theme_preference.dart'; // JsonSerializableGenerator // ************************************************************************** -_ThemePreferences _$ThemePreferencesFromJson(Map json) => - _ThemePreferences( +_$ThemePreferencesImpl _$$ThemePreferencesImplFromJson( + Map json) => + _$ThemePreferencesImpl( brightnessPreference: json['brightness_preference'] == null ? BrightnessPreference.system : BrightnessPreference.fromJson(json['brightness_preference']), @@ -15,13 +16,12 @@ _ThemePreferences _$ThemePreferencesFromJson(Map json) => ? ColorPreference.vapor : ColorPreference.fromJson(json['color_preference']), displayScale: (json['display_scale'] as num?)?.toDouble() ?? 1, - enableWallpaper: json['enable_wallpaper'] as bool? ?? true, ); -Map _$ThemePreferencesToJson(_ThemePreferences instance) => +Map _$$ThemePreferencesImplToJson( + _$ThemePreferencesImpl instance) => { 'brightness_preference': instance.brightnessPreference.toJson(), 'color_preference': instance.colorPreference.toJson(), 'display_scale': instance.displayScale, - 'enable_wallpaper': instance.enableWallpaper, }; diff --git a/lib/theme/views/avatar_widget.dart b/lib/theme/views/avatar_widget.dart index 42bea11..43d351b 100644 --- a/lib/theme/views/avatar_widget.dart +++ b/lib/theme/views/avatar_widget.dart @@ -5,7 +5,7 @@ import 'package:flutter/widgets.dart'; import '../theme.dart'; class AvatarWidget extends StatelessWidget { - const AvatarWidget({ + AvatarWidget({ required String name, required double size, required Color borderColor, @@ -38,11 +38,15 @@ class AvatarWidget extends StatelessWidget { height: _size, width: _size, decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all( - color: _borderColor, - width: 1 * (_size ~/ 32 + 1), - strokeAlign: BorderSide.strokeAlignOutside)), + shape: BoxShape.circle, + border: _scaleConfig.preferBorders + ? Border.all( + color: _borderColor, + width: 1 * (_size ~/ 32 + 1), + strokeAlign: BorderSide.strokeAlignOutside) + : null, + color: _borderColor, + ), child: AvatarImage( //size: 32, backgroundImage: _imageProvider, @@ -51,15 +55,14 @@ class AvatarWidget extends StatelessWidget { ? _foregroundColor : _backgroundColor, child: Text( - shortname.isNotEmpty ? shortname : '?', - softWrap: false, + shortname, style: _textStyle.copyWith( color: _scaleConfig.useVisualIndicators && !_scaleConfig.preferBorders ? _backgroundColor : _foregroundColor, ), - ).fit().paddingAll(_size / 16))); + ))); } //////////////////////////////////////////////////////////////////////////// diff --git a/lib/theme/views/brightness_preferences.dart b/lib/theme/views/brightness_preferences.dart index 7a1bb1d..0c39976 100644 --- a/lib/theme/views/brightness_preferences.dart +++ b/lib/theme/views/brightness_preferences.dart @@ -22,25 +22,24 @@ List> _getBrightnessDropdownItems() { } Widget buildSettingsPageBrightnessPreferences( - {required BuildContext context, - required void Function() onChanged, - required ThemeSwitcherState switcher}) { + {required BuildContext context, required void Function() onChanged}) { final preferencesRepository = PreferencesRepository.instance; final themePreferences = preferencesRepository.value.themePreference; - return FormBuilderDropdown( - name: formFieldBrightness, - decoration: InputDecoration( - label: Text(translate('settings_page.brightness_mode'))), - items: _getBrightnessDropdownItems(), - initialValue: themePreferences.brightnessPreference, - onChanged: (value) async { - final newThemePrefs = themePreferences.copyWith( - brightnessPreference: value as BrightnessPreference); - final newPrefs = preferencesRepository.value - .copyWith(themePreference: newThemePrefs); + return ThemeSwitcher.withTheme( + builder: (_, switcher, theme) => FormBuilderDropdown( + name: formFieldBrightness, + decoration: InputDecoration( + label: Text(translate('settings_page.brightness_mode'))), + items: _getBrightnessDropdownItems(), + initialValue: themePreferences.brightnessPreference, + onChanged: (value) async { + final newThemePrefs = themePreferences.copyWith( + brightnessPreference: value as BrightnessPreference); + final newPrefs = preferencesRepository.value + .copyWith(themePreference: newThemePrefs); - await preferencesRepository.set(newPrefs); - switcher.changeTheme(theme: newThemePrefs.themeData()); - onChanged(); - }); + await preferencesRepository.set(newPrefs); + switcher.changeTheme(theme: newThemePrefs.themeData()); + onChanged(); + })); } diff --git a/lib/theme/views/color_preferences.dart b/lib/theme/views/color_preferences.dart index a9a8841..ce03c0a 100644 --- a/lib/theme/views/color_preferences.dart +++ b/lib/theme/views/color_preferences.dart @@ -1,5 +1,4 @@ import 'package:animated_theme_switcher/animated_theme_switcher.dart'; -import 'package:async_tools/async_tools.dart'; import 'package:flutter/material.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_translate/flutter_translate.dart'; @@ -8,7 +7,6 @@ import '../../settings/settings.dart'; import '../models/models.dart'; const String formFieldTheme = 'theme'; -const String _kSwitchTheme = 'switchTheme'; List> _getThemeDropdownItems() { const colorPrefs = ColorPreference.values; @@ -34,27 +32,24 @@ List> _getThemeDropdownItems() { } Widget buildSettingsPageColorPreferences( - {required BuildContext context, - required void Function() onChanged, - required ThemeSwitcherState switcher}) { + {required BuildContext context, required void Function() onChanged}) { final preferencesRepository = PreferencesRepository.instance; final themePreferences = preferencesRepository.value.themePreference; - return FormBuilderDropdown( - name: formFieldTheme, - decoration: - InputDecoration(label: Text(translate('settings_page.color_theme'))), - items: _getThemeDropdownItems(), - initialValue: themePreferences.colorPreference, - onChanged: (value) { - singleFuture(_kSwitchTheme, () async { - final newThemePrefs = themePreferences.copyWith( - colorPreference: value as ColorPreference); - final newPrefs = preferencesRepository.value - .copyWith(themePreference: newThemePrefs); + return ThemeSwitcher.withTheme( + builder: (_, switcher, theme) => FormBuilderDropdown( + name: formFieldTheme, + decoration: InputDecoration( + label: Text(translate('settings_page.color_theme'))), + items: _getThemeDropdownItems(), + initialValue: themePreferences.colorPreference, + onChanged: (value) async { + final newThemePrefs = themePreferences.copyWith( + colorPreference: value as ColorPreference); + final newPrefs = preferencesRepository.value + .copyWith(themePreference: newThemePrefs); - await preferencesRepository.set(newPrefs); - switcher.changeTheme(theme: newThemePrefs.themeData()); - onChanged(); - }); - }); + await preferencesRepository.set(newPrefs); + switcher.changeTheme(theme: newThemePrefs.themeData()); + onChanged(); + })); } diff --git a/lib/theme/views/option_box.dart b/lib/theme/views/option_box.dart index 06a3293..508c7ba 100644 --- a/lib/theme/views/option_box.dart +++ b/lib/theme/views/option_box.dart @@ -40,9 +40,7 @@ class OptionBox extends StatelessWidget { ElevatedButton( onPressed: _onClick, child: Row(mainAxisSize: MainAxisSize.min, children: [ - Icon(_buttonIcon, - size: 24, color: scale.primaryScale.appText) - .paddingLTRB(0, 8, 8, 8), + Icon(_buttonIcon, size: 24).paddingLTRB(0, 8, 8, 8), Text(textAlign: TextAlign.center, _buttonText) ])).paddingLTRB(0, 12, 0, 0).toCenter() ]).paddingAll(12)) diff --git a/lib/theme/views/pop_control.dart b/lib/theme/views/pop_control.dart index d2e98f9..29dc562 100644 --- a/lib/theme/views/pop_control.dart +++ b/lib/theme/views/pop_control.dart @@ -8,15 +8,18 @@ class PopControl extends StatelessWidget { super.key, }); - void _doDismiss(BuildContext context) { + void _doDismiss(NavigatorState navigator) { if (!dismissible) { return; } - Navigator.of(context).pop(); + navigator.pop(); } @override + // ignore: prefer_expression_function_bodies Widget build(BuildContext context) { + final navigator = Navigator.of(context); + final route = ModalRoute.of(context); if (route != null && route is PopControlDialogRoute) { WidgetsBinding.instance.addPostFrameCallback((_) { @@ -26,12 +29,11 @@ class PopControl extends StatelessWidget { return PopScope( canPop: false, - onPopInvokedWithResult: (didPop, _) { + onPopInvoked: (didPop) { if (didPop) { return; } - _doDismiss(context); - return; + _doDismiss(navigator); }, child: child); } diff --git a/lib/theme/views/responsive.dart b/lib/theme/views/responsive.dart index 0182c9f..91af81d 100644 --- a/lib/theme/views/responsive.dart +++ b/lib/theme/views/responsive.dart @@ -1,24 +1,13 @@ +import 'dart:io'; + import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -final isAndroid = !kIsWeb && defaultTargetPlatform == TargetPlatform.android; -final isiOS = !kIsWeb && defaultTargetPlatform == TargetPlatform.iOS; -final isMobile = !kIsWeb && - (defaultTargetPlatform == TargetPlatform.iOS || - defaultTargetPlatform == TargetPlatform.android); -final isDesktop = !kIsWeb && - !(defaultTargetPlatform == TargetPlatform.iOS || - defaultTargetPlatform == TargetPlatform.android); - -const isWeb = kIsWeb; -final isWebMobile = kIsWeb && - (defaultTargetPlatform == TargetPlatform.iOS || - defaultTargetPlatform == TargetPlatform.android); -final isWebDesktop = kIsWeb && - !(defaultTargetPlatform == TargetPlatform.iOS || - defaultTargetPlatform == TargetPlatform.android); - -final isAnyMobile = isMobile || isWebMobile; +bool get isAndroid => !kIsWeb && Platform.isAndroid; +bool get isiOS => !kIsWeb && Platform.isIOS; +bool get isWeb => kIsWeb; +bool get isDesktop => + !isWeb && (Platform.isWindows || Platform.isLinux || Platform.isMacOS); const kMobileWidthCutoff = 500.0; diff --git a/lib/theme/views/scanner_error_widget.dart b/lib/theme/views/scanner_error_widget.dart index d5463f4..0926128 100644 --- a/lib/theme/views/scanner_error_widget.dart +++ b/lib/theme/views/scanner_error_widget.dart @@ -14,12 +14,16 @@ class ScannerErrorWidget extends StatelessWidget { switch (error.errorCode) { case MobileScannerErrorCode.controllerUninitialized: errorMessage = 'Controller not ready.'; + break; case MobileScannerErrorCode.permissionDenied: errorMessage = 'Permission denied'; + break; case MobileScannerErrorCode.unsupported: errorMessage = 'Scanning is unsupported on this device'; + break; default: errorMessage = 'Generic Error'; + break; } return ColoredBox( diff --git a/lib/theme/views/slider_tile.dart b/lib/theme/views/slider_tile.dart deleted file mode 100644 index 8e5f178..0000000 --- a/lib/theme/views/slider_tile.dart +++ /dev/null @@ -1,143 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_slidable/flutter_slidable.dart'; - -import '../theme.dart'; - -class SliderTileAction { - const SliderTileAction({ - required this.actionScale, - required this.onPressed, - this.key, - this.icon, - this.label, - }); - - final Key? key; - final ScaleKind actionScale; - final String? label; - final IconData? icon; - final SlidableActionCallback? onPressed; -} - -class SliderTile extends StatelessWidget { - const SliderTile( - {required this.disabled, - required this.selected, - required this.tileScale, - required this.title, - this.subtitle = '', - this.endActions = const [], - this.startActions = const [], - this.onTap, - this.onDoubleTap, - this.leading, - this.trailing, - super.key}); - - final bool disabled; - final bool selected; - final ScaleKind tileScale; - final List endActions; - final List startActions; - final GestureTapCallback? onTap; - final GestureTapCallback? onDoubleTap; - final Widget? leading; - final Widget? trailing; - final String title; - final String subtitle; - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(DiagnosticsProperty('disabled', disabled)) - ..add(DiagnosticsProperty('selected', selected)) - ..add(DiagnosticsProperty('tileScale', tileScale)) - ..add(IterableProperty('endActions', endActions)) - ..add(IterableProperty('startActions', startActions)) - ..add(ObjectFlagProperty.has('onTap', onTap)) - ..add(DiagnosticsProperty('leading', leading)) - ..add(StringProperty('title', title)) - ..add(StringProperty('subtitle', subtitle)) - ..add(ObjectFlagProperty.has( - 'onDoubleTap', onDoubleTap)) - ..add(DiagnosticsProperty('trailing', trailing)); - } - - @override - // ignore: prefer_expression_function_bodies - Widget build(BuildContext context) { - final theme = Theme.of(context); - final scaleTheme = theme.extension()!; - final scaleTileTheme = scaleTheme.tileTheme( - disabled: disabled, selected: selected, scaleKind: tileScale); - - return Container( - clipBehavior: Clip.antiAlias, - decoration: ShapeDecoration( - color: scaleTileTheme.backgroundColor, - shape: scaleTileTheme.shapeBorder), - child: Slidable( - // Specify a key if the Slidable is dismissible. - key: key, - endActionPane: endActions.isEmpty - ? null - : ActionPane( - motion: const DrawerMotion(), - children: endActions.map((a) { - final scaleActionTheme = scaleTheme.tileTheme( - disabled: disabled, - selected: true, - scaleKind: a.actionScale); - return SlidableAction( - onPressed: disabled ? null : a.onPressed, - backgroundColor: scaleActionTheme.backgroundColor, - foregroundColor: scaleActionTheme.textColor, - icon: subtitle.isEmpty ? a.icon : null, - label: a.label, - padding: const EdgeInsets.all(2)); - }).toList()), - startActionPane: startActions.isEmpty - ? null - : ActionPane( - motion: const DrawerMotion(), - children: startActions.map((a) { - final scaleActionTheme = scaleTheme.tileTheme( - disabled: disabled, - selected: true, - scaleKind: a.actionScale); - - return SlidableAction( - onPressed: disabled ? null : a.onPressed, - backgroundColor: scaleActionTheme.backgroundColor, - foregroundColor: scaleActionTheme.textColor, - icon: subtitle.isEmpty ? a.icon : null, - label: a.label, - padding: const EdgeInsets.all(2)); - }).toList()), - child: Padding( - padding: scaleTheme.config.useVisualIndicators - ? EdgeInsets.zero - : const EdgeInsets.fromLTRB(0, 2, 0, 2), - child: GestureDetector( - onDoubleTap: onDoubleTap, - child: ListTile( - onTap: onTap, - dense: true, - title: Text( - title, - overflow: TextOverflow.fade, - softWrap: false, - ), - subtitle: subtitle.isNotEmpty ? Text(subtitle) : null, - minTileHeight: 52, - iconColor: scaleTileTheme.textColor, - textColor: scaleTileTheme.textColor, - leading: - leading != null ? FittedBox(child: leading) : null, - trailing: trailing != null - ? FittedBox(child: trailing) - : null))))); - } -} diff --git a/lib/theme/views/styled_alert.dart b/lib/theme/views/styled_alert.dart deleted file mode 100644 index 1215c84..0000000 --- a/lib/theme/views/styled_alert.dart +++ /dev/null @@ -1,242 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_translate/flutter_translate.dart'; -import 'package:rflutter_alert/rflutter_alert.dart'; - -import '../theme.dart'; - -AlertStyle _alertStyle(BuildContext context) { - final theme = Theme.of(context); - final scale = theme.extension()!; - final scaleConfig = theme.extension()!; - - return AlertStyle( - animationType: AnimationType.grow, - isCloseButton: false, - //animationDuration: const Duration(milliseconds: 200), - alertBorder: RoundedRectangleBorder( - side: !scaleConfig.useVisualIndicators - ? BorderSide.none - : BorderSide( - strokeAlign: BorderSide.strokeAlignCenter, - color: scale.primaryScale.border, - width: 2), - borderRadius: BorderRadius.all( - Radius.circular(12 * scaleConfig.borderRadiusScale))), - // isButtonVisible: true, - // isCloseButton: true, - // isOverlayTapDismiss: true, - backgroundColor: scale.primaryScale.subtleBackground, - // overlayColor: Colors.black87, - titleStyle: theme.textTheme.titleMedium! - .copyWith(color: scale.primaryScale.appText), - // titleTextAlign: TextAlign.center, - descStyle: - theme.textTheme.bodyMedium!.copyWith(color: scale.primaryScale.appText), - // descTextAlign: TextAlign.center, - // buttonAreaPadding: const EdgeInsets.all(20.0), - // constraints: null, - // buttonsDirection: ButtonsDirection.row, - // alertElevation: null, - // alertPadding: defaultAlertPadding, - // alertAlignment: Alignment.center, - // isTitleSelectable: false, - // isDescSelectable: false, - // titlePadding: null, - //descPadding: const EdgeInsets.all(0.0), - ); -} - -Color _buttonColor(BuildContext context, bool highlight) { - final theme = Theme.of(context); - final scale = theme.extension()!; - final scaleConfig = theme.extension()!; - - if (scaleConfig.useVisualIndicators && !scaleConfig.preferBorders) { - return scale.secondaryScale.border; - } - - return highlight - ? scale.secondaryScale.elementBackground - : scale.secondaryScale.hoverElementBackground; -} - -TextStyle _buttonTextStyle(BuildContext context) { - final theme = Theme.of(context); - final scale = theme.extension()!; - final scaleConfig = theme.extension()!; - - if (scaleConfig.useVisualIndicators && !scaleConfig.preferBorders) { - return theme.textTheme.bodyMedium! - .copyWith(color: scale.secondaryScale.borderText); - } - - return theme.textTheme.bodyMedium! - .copyWith(color: scale.secondaryScale.appText); -} - -BoxBorder _buttonBorder(BuildContext context) { - final theme = Theme.of(context); - final scale = theme.extension()!; - final scaleConfig = theme.extension()!; - - return Border.fromBorderSide(BorderSide( - color: scale.secondaryScale.border, - width: scaleConfig.preferBorders ? 2 : 0)); -} - -BorderRadius _buttonRadius(BuildContext context) { - final theme = Theme.of(context); - final scaleConfig = theme.extension()!; - - return BorderRadius.circular(8 * scaleConfig.borderRadiusScale); -} - -Future showErrorModal( - {required BuildContext context, - required String title, - required String text}) async { - await Alert( - context: context, - style: _alertStyle(context), - useRootNavigator: false, - type: AlertType.error, - title: title, - desc: text, - buttons: [ - DialogButton( - color: _buttonColor(context, false), - highlightColor: _buttonColor(context, true), - border: _buttonBorder(context), - radius: _buttonRadius(context), - width: 120, - onPressed: () { - Navigator.pop(context); - }, - child: Text( - translate('button.ok'), - style: _buttonTextStyle(context), - ), - ) - ], - ).show(); -} - -Future showErrorStacktraceModal( - {required BuildContext context, - required Object error, - StackTrace? stackTrace}) async { - await showErrorModal( - context: context, - title: translate('toast.error'), - text: 'Error: $error\n StackTrace: $stackTrace', - ); -} - -Future showAlertModal( - {required BuildContext context, - required String title, - required String text}) async { - await Alert( - context: context, - style: _alertStyle(context), - useRootNavigator: false, - type: AlertType.none, - title: title, - desc: text, - buttons: [ - DialogButton( - color: _buttonColor(context, false), - highlightColor: _buttonColor(context, true), - border: _buttonBorder(context), - radius: _buttonRadius(context), - width: 120, - onPressed: () { - Navigator.pop(context); - }, - child: Text( - translate('button.ok'), - style: _buttonTextStyle(context), - ), - ) - ], - ).show(); -} - -Future showAlertWidgetModal( - {required BuildContext context, - required String title, - required Widget child}) async { - await Alert( - context: context, - style: _alertStyle(context), - useRootNavigator: false, - type: AlertType.none, - title: title, - content: child, - buttons: [ - DialogButton( - color: _buttonColor(context, false), - highlightColor: _buttonColor(context, true), - border: _buttonBorder(context), - radius: _buttonRadius(context), - width: 120, - onPressed: () { - Navigator.pop(context); - }, - child: Text( - translate('button.ok'), - style: _buttonTextStyle(context), - ), - ) - ], - ).show(); -} - -Future showConfirmModal( - {required BuildContext context, - required String title, - required String text}) async { - var confirm = false; - - await Alert( - context: context, - style: _alertStyle(context), - useRootNavigator: false, - type: AlertType.none, - title: title, - desc: text, - buttons: [ - DialogButton( - color: _buttonColor(context, false), - highlightColor: _buttonColor(context, true), - border: _buttonBorder(context), - radius: _buttonRadius(context), - width: 120, - onPressed: () { - Navigator.pop(context); - }, - child: Text( - translate('button.no'), - style: _buttonTextStyle(context), - ), - ), - DialogButton( - color: _buttonColor(context, false), - highlightColor: _buttonColor(context, true), - border: _buttonBorder(context), - radius: _buttonRadius(context), - width: 120, - onPressed: () { - confirm = true; - Navigator.pop(context); - }, - child: Text( - translate('button.yes'), - style: _buttonTextStyle(context), - ), - ) - ], - ).show(); - - return confirm; -} diff --git a/lib/theme/views/styled_dialog.dart b/lib/theme/views/styled_dialog.dart index 75a0f6b..4e4bd50 100644 --- a/lib/theme/views/styled_dialog.dart +++ b/lib/theme/views/styled_dialog.dart @@ -21,7 +21,7 @@ class StyledDialog extends StatelessWidget { Radius.circular(16 * scaleConfig.borderRadiusScale)), ), contentPadding: const EdgeInsets.all(4), - backgroundColor: scale.primaryScale.border, + backgroundColor: scale.primaryScale.dialogBorder, title: Text( title, style: textTheme.titleMedium! diff --git a/lib/theme/views/styled_scaffold.dart b/lib/theme/views/styled_scaffold.dart index 4fc803f..9560ecc 100644 --- a/lib/theme/views/styled_scaffold.dart +++ b/lib/theme/views/styled_scaffold.dart @@ -1,3 +1,4 @@ +import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:flutter/material.dart'; import '../theme.dart'; @@ -8,26 +9,22 @@ class StyledScaffold extends StatelessWidget { @override Widget build(BuildContext context) { final theme = Theme.of(context); - final scaleScheme = theme.extension()!; + final scale = theme.extension()!; final scaleConfig = theme.extension()!; - final scale = scaleScheme.scale(ScaleKind.primary); - const enableBorder = false; //!isMobileSize(context); + final enableBorder = !isMobileSize(context); - var scaffold = clipBorder( - clipEnabled: enableBorder, - borderEnabled: scaleConfig.useVisualIndicators, - borderRadius: 16 * scaleConfig.borderRadiusScale, - borderColor: scale.border, - child: Scaffold(appBar: appBar, body: body, key: key)); - - if (!scaleConfig.useVisualIndicators) { - scaffold = scaffold.withThemedShadow(scaleConfig, scale); - } + final scaffold = clipBorder( + clipEnabled: enableBorder, + borderEnabled: scaleConfig.useVisualIndicators, + borderRadius: 16 * scaleConfig.borderRadiusScale, + borderColor: scale.primaryScale.border, + child: Scaffold(appBar: appBar, body: body, key: key)) + .paddingAll(enableBorder ? 32 : 0); return GestureDetector( onTap: () => FocusManager.instance.primaryFocus?.unfocus(), - child: scaffold /*.paddingAll(enableBorder ? 32 : 0) */); + child: scaffold); } //////////////////////////////////////////////////////////////////////////// diff --git a/lib/theme/views/views.dart b/lib/theme/views/views.dart index 88f4a4a..b81f184 100644 --- a/lib/theme/views/views.dart +++ b/lib/theme/views/views.dart @@ -8,9 +8,6 @@ export 'pop_control.dart'; export 'recovery_key_widget.dart'; export 'responsive.dart'; export 'scanner_error_widget.dart'; -export 'slider_tile.dart'; -export 'styled_alert.dart'; export 'styled_dialog.dart'; export 'styled_scaffold.dart'; -export 'wallpaper_preferences.dart'; export 'widget_helpers.dart'; diff --git a/lib/theme/views/wallpaper_preferences.dart b/lib/theme/views/wallpaper_preferences.dart deleted file mode 100644 index f9ae94c..0000000 --- a/lib/theme/views/wallpaper_preferences.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:animated_theme_switcher/animated_theme_switcher.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_form_builder/flutter_form_builder.dart'; -import 'package:flutter_translate/flutter_translate.dart'; - -import '../../settings/settings.dart'; -import '../models/models.dart'; - -const String formFieldEnableWallpaper = 'enable_wallpaper'; - -Widget buildSettingsPageWallpaperPreferences( - {required BuildContext context, - required void Function() onChanged, - required ThemeSwitcherState switcher}) { - final preferencesRepository = PreferencesRepository.instance; - final themePreferences = preferencesRepository.value.themePreference; - final theme = Theme.of(context); - final textTheme = theme.textTheme; - - return FormBuilderCheckbox( - name: formFieldEnableWallpaper, - title: Text(translate('settings_page.enable_wallpaper'), - style: textTheme.labelMedium), - initialValue: themePreferences.enableWallpaper, - onChanged: (value) async { - if (value != null) { - final newThemePrefs = - themePreferences.copyWith(enableWallpaper: value); - final newPrefs = preferencesRepository.value - .copyWith(themePreference: newThemePrefs); - - await preferencesRepository.set(newPrefs); - switcher.changeTheme(theme: newThemePrefs.themeData()); - onChanged(); - } - }); -} diff --git a/lib/theme/views/widget_helpers.dart b/lib/theme/views/widget_helpers.dart index 910074e..b079d2b 100644 --- a/lib/theme/views/widget_helpers.dart +++ b/lib/theme/views/widget_helpers.dart @@ -8,7 +8,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_sticky_header/flutter_sticky_header.dart'; -import 'package:flutter_translate/flutter_translate.dart'; +import 'package:quickalert/quickalert.dart'; import 'package:sliver_expandable/sliver_expandable.dart'; import '../theme.dart'; @@ -19,40 +19,6 @@ extension BorderExt on Widget { child: this); } -extension ShadowExt on Widget { - Container withThemedShadow(ScaleConfig scaleConfig, ScaleColor scale) => - // ignore: use_decorated_box - Container( - decoration: BoxDecoration( - boxShadow: themedShadow(scaleConfig, scale), - ), - child: this, - ); -} - -List themedShadow(ScaleConfig scaleConfig, ScaleColor scale) => [ - if (scaleConfig.useVisualIndicators && !scaleConfig.preferBorders) - BoxShadow( - color: scale.primary.darken(60), - spreadRadius: 2, - ) - else if (scaleConfig.useVisualIndicators && scaleConfig.preferBorders) - BoxShadow( - color: scale.border, - spreadRadius: 2, - ) - else - BoxShadow( - color: scale.primary.darken(60).withAlpha(0x7F), - blurRadius: 16, - spreadRadius: 2, - offset: const Offset( - 0, - 2, - ), - ), - ]; - extension SizeToFixExt on Widget { FittedBox fit({BoxFit? fit, Key? key}) => FittedBox( key: key, @@ -143,21 +109,6 @@ extension LabelExt on Widget { ), child: this); } - - Widget noEditDecoratorLabel(BuildContext context, String label, - {ScaleColor? scale}) { - final theme = Theme.of(context); - final scaleScheme = theme.extension()!; - scale = scale ?? scaleScheme.primaryScale; - - return Wrap(crossAxisAlignment: WrapCrossAlignment.end, children: [ - Text( - '$label:', - style: theme.textTheme.bodyLarge!.copyWith(color: scale.hoverBorder), - ).paddingLTRB(0, 0, 8, 0), - this - ]); - } } Widget buildProgressIndicator() => Builder(builder: (context) { @@ -166,13 +117,12 @@ Widget buildProgressIndicator() => Builder(builder: (context) { return FittedBox( fit: BoxFit.scaleDown, child: SpinKitFoldingCube( - color: scale.tertiaryScale.border, + color: scale.tertiaryScale.primary, size: 80, )); }); -Widget waitingPage({String? text, void Function()? onCancel}) => - Builder(builder: (context) { +Widget waitingPage({String? text}) => Builder(builder: (context) { final theme = Theme.of(context); final scale = theme.extension()!; return ColoredBox( @@ -181,20 +131,12 @@ Widget waitingPage({String? text, void Function()? onCancel}) => crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisAlignment: MainAxisAlignment.center, children: [ - buildProgressIndicator().paddingAll(24), + buildProgressIndicator(), if (text != null) Text(text, textAlign: TextAlign.center, style: theme.textTheme.bodySmall! - .copyWith(color: scale.tertiaryScale.appText)), - if (onCancel != null) - ElevatedButton( - onPressed: onCancel, - child: Text(translate('button.cancel'), - textAlign: TextAlign.center, - style: theme.textTheme.bodySmall!.copyWith( - color: scale.tertiaryScale.appText))) - .alignAtCenter(), + .copyWith(color: scale.tertiaryScale.appText)) ])); }); @@ -254,6 +196,19 @@ class AsyncBlocBuilder>, S> data: (d) => builder(context, d))); } +Future showErrorModal( + BuildContext context, String title, String text) async { + await QuickAlert.show( + context: context, + type: QuickAlertType.error, + title: title, + text: text, + //backgroundColor: Colors.black, + //titleColor: Colors.white, + //textColor: Colors.white, + ); +} + SliverAppBar styledSliverAppBar( {required BuildContext context, required String title, Color? titleColor}) { final theme = Theme.of(context); @@ -442,7 +397,7 @@ Widget styledTitleContainer({ color: borderColor ?? scale.primaryScale.border, shape: RoundedRectangleBorder( borderRadius: - BorderRadius.circular(8 * scaleConfig.borderRadiusScale), + BorderRadius.circular(12 * scaleConfig.borderRadiusScale), )), child: Column(children: [ Text( @@ -456,7 +411,7 @@ Widget styledTitleContainer({ backgroundColor ?? scale.primaryScale.subtleBackground, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular( - 8 * scaleConfig.borderRadiusScale), + 12 * scaleConfig.borderRadiusScale), )), child: child) .paddingAll(4) @@ -464,31 +419,6 @@ Widget styledTitleContainer({ ])); } -Widget styledCard({ - required BuildContext context, - required Widget child, - Color? borderColor, - Color? backgroundColor, - Color? titleColor, -}) { - final theme = Theme.of(context); - final scale = theme.extension()!; - final scaleConfig = theme.extension()!; - - return DecoratedBox( - decoration: ShapeDecoration( - color: backgroundColor ?? scale.primaryScale.elementBackground, - shape: RoundedRectangleBorder( - side: (scaleConfig.useVisualIndicators || scaleConfig.preferBorders) - ? BorderSide( - color: borderColor ?? scale.primaryScale.border, width: 2) - : BorderSide.none, - borderRadius: - BorderRadius.circular(12 * scaleConfig.borderRadiusScale), - )), - child: child.paddingAll(4)); -} - Widget styledBottomSheet({ required BuildContext context, required String title, @@ -558,33 +488,24 @@ const grayColorFilter = ColorFilter.matrix([ 0, ]); -const src96StencilFilter = - ColorFilter.mode(Color.fromARGB(96, 255, 255, 255), BlendMode.srcIn); - -const dst127StencilFilter = - ColorFilter.mode(Color.fromARGB(127, 255, 255, 255), BlendMode.dstIn); - -Container clipBorder({ +Widget clipBorder({ required bool clipEnabled, required bool borderEnabled, required double borderRadius, required Color borderColor, required Widget child, }) => - // ignore: avoid_unnecessary_containers, use_decorated_box - Container( - decoration: ShapeDecoration( - shape: RoundedRectangleBorder( - side: borderEnabled && clipEnabled - ? BorderSide(color: borderColor, width: 2) - : BorderSide.none, - borderRadius: clipEnabled - ? BorderRadius.circular(borderRadius) - : BorderRadius.zero, - )), - child: ClipRRect( - clipBehavior: Clip.antiAliasWithSaveLayer, - borderRadius: clipEnabled - ? BorderRadius.circular(borderRadius - 4) - : BorderRadius.zero, - child: child)); + ClipRRect( + borderRadius: clipEnabled + ? BorderRadius.circular(borderRadius) + : BorderRadius.zero, + child: DecoratedBox( + decoration: BoxDecoration(boxShadow: [ + if (borderEnabled) BoxShadow(color: borderColor, spreadRadius: 2) + ]), + child: ClipRRect( + borderRadius: clipEnabled + ? BorderRadius.circular(borderRadius) + : BorderRadius.zero, + child: child, + )).paddingAll(clipEnabled && borderEnabled ? 2 : 0)); diff --git a/lib/tick.dart b/lib/tick.dart index 21c18a3..8ec3db7 100644 --- a/lib/tick.dart +++ b/lib/tick.dart @@ -28,7 +28,11 @@ class BackgroundTickerState extends State { @override void dispose() { - _tickTimer?.cancel(); + final tickTimer = _tickTimer; + if (tickTimer != null) { + tickTimer.cancel(); + } + super.dispose(); } diff --git a/lib/tools/loggy.dart b/lib/tools/loggy.dart index adc62d1..69faeb7 100644 --- a/lib/tools/loggy.dart +++ b/lib/tools/loggy.dart @@ -8,8 +8,8 @@ import 'package:intl/intl.dart'; import 'package:loggy/loggy.dart'; import 'package:veilid_support/veilid_support.dart'; -import '../proto/proto.dart'; import '../veilid_processor/views/developer.dart'; +import '../theme/views/responsive.dart'; import 'state_logger.dart'; String wrapWithLogColor(LogLevel? level, String text) { @@ -111,19 +111,14 @@ class CallbackPrinter extends LoggyPrinter { @override void onLog(LogRecord record) { - final out = record.pretty().replaceAll('\uFFFD', ''); - - if (!kIsWeb && Platform.isAndroid) { - debugPrint(out); - } else { + final out = record.pretty(); + if (isDesktop) { debugPrintSynchronously(out); } globalDebugTerminal.write('$out\n'.replaceAll('\n', '\r\n')); callback?.call(record); } - // Change callback function - // ignore: use_setters_to_change_properties void setCallback(void Function(LogRecord)? cb) { callback = cb; } @@ -149,24 +144,17 @@ void initLoggy() { logOptions: getLogOptions(null), ); - // Allow trace logging from the command line // ignore: do_not_use_environment const isTrace = String.fromEnvironment('LOG_TRACE') != ''; LogLevel logLevel; if (isTrace) { logLevel = traceLevel; } else { - logLevel = kIsDebugMode ? LogLevel.debug : LogLevel.info; + logLevel = kDebugMode ? LogLevel.debug : LogLevel.info; } Loggy('').level = getLogOptions(logLevel); // Create state logger - registerVeilidProtoToDebug(); - registerVeilidDHTProtoToDebug(); - registerVeilidchatProtoToDebug(); - - if (kIsDebugMode) { - Bloc.observer = const StateLogger(); - } + Bloc.observer = const StateLogger(); } diff --git a/lib/tools/native_safe_area.dart b/lib/tools/native_safe_area.dart new file mode 100644 index 0000000..ed3746e --- /dev/null +++ b/lib/tools/native_safe_area.dart @@ -0,0 +1,111 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:native_device_orientation/native_device_orientation.dart'; + +class NativeSafeArea extends StatelessWidget { + const NativeSafeArea({ + required this.child, + this.left = true, + this.top = true, + this.right = true, + this.bottom = true, + this.minimum = EdgeInsets.zero, + this.maintainBottomViewPadding = false, + super.key, + }); + + /// Whether to avoid system intrusions on the left. + final bool left; + + /// Whether to avoid system intrusions at the top of the screen, typically the + /// system status bar. + final bool top; + + /// Whether to avoid system intrusions on the right. + final bool right; + + /// Whether to avoid system intrusions on the bottom side of the screen. + final bool bottom; + + /// This minimum padding to apply. + /// + /// The greater of the minimum insets and the media padding will be applied. + final EdgeInsets minimum; + + /// Specifies whether the [SafeArea] should maintain the bottom + /// [MediaQueryData.viewPadding] instead of the bottom + /// [MediaQueryData.padding], defaults to false. + /// + /// For example, if there is an onscreen keyboard displayed above the + /// SafeArea, the padding can be maintained below the obstruction rather than + /// being consumed. This can be helpful in cases where your layout contains + /// flexible widgets, which could visibly move when opening a software + /// keyboard due to the change in the padding value. Setting this to true will + /// avoid the UI shift. + final bool maintainBottomViewPadding; + + /// The widget below this widget in the tree. + /// + /// The padding on the [MediaQuery] for the [child] will be suitably adjusted + /// to zero out any sides that were avoided by this widget. + /// + /// {@macro flutter.widgets.ProxyWidget.child} + final Widget child; + + @override + Widget build(BuildContext context) { + final nativeOrientation = + NativeDeviceOrientationReader.orientation(context); + + late final bool realLeft; + late final bool realRight; + late final bool realTop; + late final bool realBottom; + + switch (nativeOrientation) { + case NativeDeviceOrientation.unknown: + case NativeDeviceOrientation.portraitUp: + realLeft = left; + realRight = right; + realTop = top; + realBottom = bottom; + case NativeDeviceOrientation.portraitDown: + realLeft = right; + realRight = left; + realTop = bottom; + realBottom = top; + case NativeDeviceOrientation.landscapeRight: + realLeft = bottom; + realRight = top; + realTop = left; + realBottom = right; + case NativeDeviceOrientation.landscapeLeft: + realLeft = top; + realRight = bottom; + realTop = right; + realBottom = left; + } + + return SafeArea( + left: realLeft, + right: realRight, + top: realTop, + bottom: realBottom, + minimum: minimum, + maintainBottomViewPadding: maintainBottomViewPadding, + child: child); + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('left', left)) + ..add(DiagnosticsProperty('top', top)) + ..add(DiagnosticsProperty('right', right)) + ..add(DiagnosticsProperty('bottom', bottom)) + ..add(DiagnosticsProperty('minimum', minimum)) + ..add(DiagnosticsProperty( + 'maintainBottomViewPadding', maintainBottomViewPadding)); + } +} diff --git a/lib/tools/state_logger.dart b/lib/tools/state_logger.dart index 8782662..08e32b3 100644 --- a/lib/tools/state_logger.dart +++ b/lib/tools/state_logger.dart @@ -1,23 +1,16 @@ -import 'dart:convert'; - import 'package:bloc/bloc.dart'; import 'package:loggy/loggy.dart'; -import 'package:veilid_support/veilid_support.dart'; import 'loggy.dart'; const Map _blocChangeLogLevels = { - 'RouterCubit': LogLevel.debug, - 'PerAccountCollectionBlocMapCubit': LogLevel.debug, - 'PerAccountCollectionCubit': LogLevel.debug, - 'ActiveChatCubit': LogLevel.debug, - 'AccountRecordCubit': LogLevel.debug, - 'ContactListCubit': LogLevel.debug, - 'ContactInvitationListCubit': LogLevel.debug, - 'ChatListCubit': LogLevel.debug, - 'PreferencesCubit': LogLevel.debug, - 'ConversationCubit': LogLevel.debug, - 'DefaultDHTRecordCubit': LogLevel.debug, - 'WaitingInvitationCubit': LogLevel.debug, + 'ConnectionStateCubit': LogLevel.off, + 'ActiveSingleContactChatBlocMapCubit': LogLevel.off, + 'ActiveConversationsBlocMapCubit': LogLevel.off, + 'PersistentQueueCubit': LogLevel.off, + 'TableDBArrayProtobufCubit': LogLevel.off, + 'DHTLogCubit': LogLevel.off, + 'SingleContactMessagesCubit': LogLevel.off, + 'ChatComponentCubit': LogLevel.off, }; const Map _blocCreateCloseLogLevels = {}; @@ -44,13 +37,8 @@ class StateLogger extends BlocObserver { @override void onChange(BlocBase bloc, Change change) { super.onChange(bloc, change); - _checkLogLevel(_blocChangeLogLevels, LogLevel.off, bloc, (logLevel) { - const encoder = JsonEncoder.withIndent(' ', DynamicDebug.toDebug); - log.log( - logLevel, - 'Change: ${bloc.runtimeType}\n' - 'currentState: ${encoder.convert(change.currentState)}\n' - 'nextState: ${encoder.convert(change.nextState)}\n'); + _checkLogLevel(_blocChangeLogLevels, LogLevel.debug, bloc, (logLevel) { + log.log(logLevel, 'Change: ${bloc.runtimeType} $change'); }); } diff --git a/lib/tools/window_control.dart b/lib/tools/window_control.dart index 32c05ff..2e9a21f 100644 --- a/lib/tools/window_control.dart +++ b/lib/tools/window_control.dart @@ -7,6 +7,7 @@ import 'package:flutter/services.dart'; import 'package:window_manager/window_manager.dart'; import '../theme/views/responsive.dart'; +import 'tools.dart'; export 'package:window_manager/window_manager.dart' show TitleBarStyle; diff --git a/lib/veilid_processor/models/processor_connection_state.dart b/lib/veilid_processor/models/processor_connection_state.dart index 6b68a8e..e92ebdc 100644 --- a/lib/veilid_processor/models/processor_connection_state.dart +++ b/lib/veilid_processor/models/processor_connection_state.dart @@ -4,7 +4,7 @@ import 'package:veilid_support/veilid_support.dart'; part 'processor_connection_state.freezed.dart'; @freezed -sealed class ProcessorConnectionState with _$ProcessorConnectionState { +class ProcessorConnectionState with _$ProcessorConnectionState { const factory ProcessorConnectionState({ required VeilidStateAttachment attachment, required VeilidStateNetwork network, diff --git a/lib/veilid_processor/models/processor_connection_state.freezed.dart b/lib/veilid_processor/models/processor_connection_state.freezed.dart index c7c5288..d857318 100644 --- a/lib/veilid_processor/models/processor_connection_state.freezed.dart +++ b/lib/veilid_processor/models/processor_connection_state.freezed.dart @@ -1,4 +1,3 @@ -// dart format width=80 // coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND // ignore_for_file: type=lint @@ -10,46 +9,26 @@ part of 'processor_connection_state.dart'; // FreezedGenerator // ************************************************************************** -// dart format off T _$identity(T value) => value; +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + /// @nodoc mixin _$ProcessorConnectionState { - VeilidStateAttachment get attachment; - VeilidStateNetwork get network; + VeilidStateAttachment get attachment => throw _privateConstructorUsedError; + VeilidStateNetwork get network => throw _privateConstructorUsedError; - /// Create a copy of ProcessorConnectionState - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') + @JsonKey(ignore: true) $ProcessorConnectionStateCopyWith get copyWith => - _$ProcessorConnectionStateCopyWithImpl( - this as ProcessorConnectionState, _$identity); - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is ProcessorConnectionState && - (identical(other.attachment, attachment) || - other.attachment == attachment) && - (identical(other.network, network) || other.network == network)); - } - - @override - int get hashCode => Object.hash(runtimeType, attachment, network); - - @override - String toString() { - return 'ProcessorConnectionState(attachment: $attachment, network: $network)'; - } + throw _privateConstructorUsedError; } /// @nodoc -abstract mixin class $ProcessorConnectionStateCopyWith<$Res> { +abstract class $ProcessorConnectionStateCopyWith<$Res> { factory $ProcessorConnectionStateCopyWith(ProcessorConnectionState value, - $Res Function(ProcessorConnectionState) _then) = - _$ProcessorConnectionStateCopyWithImpl; + $Res Function(ProcessorConnectionState) then) = + _$ProcessorConnectionStateCopyWithImpl<$Res, ProcessorConnectionState>; @useResult $Res call({VeilidStateAttachment attachment, VeilidStateNetwork network}); @@ -58,58 +37,101 @@ abstract mixin class $ProcessorConnectionStateCopyWith<$Res> { } /// @nodoc -class _$ProcessorConnectionStateCopyWithImpl<$Res> +class _$ProcessorConnectionStateCopyWithImpl<$Res, + $Val extends ProcessorConnectionState> implements $ProcessorConnectionStateCopyWith<$Res> { - _$ProcessorConnectionStateCopyWithImpl(this._self, this._then); + _$ProcessorConnectionStateCopyWithImpl(this._value, this._then); - final ProcessorConnectionState _self; - final $Res Function(ProcessorConnectionState) _then; + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; - /// Create a copy of ProcessorConnectionState - /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ Object? attachment = null, Object? network = null, }) { - return _then(_self.copyWith( + return _then(_value.copyWith( attachment: null == attachment - ? _self.attachment + ? _value.attachment : attachment // ignore: cast_nullable_to_non_nullable as VeilidStateAttachment, network: null == network - ? _self.network + ? _value.network : network // ignore: cast_nullable_to_non_nullable as VeilidStateNetwork, - )); + ) as $Val); } - /// Create a copy of ProcessorConnectionState - /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $VeilidStateAttachmentCopyWith<$Res> get attachment { - return $VeilidStateAttachmentCopyWith<$Res>(_self.attachment, (value) { - return _then(_self.copyWith(attachment: value)); + return $VeilidStateAttachmentCopyWith<$Res>(_value.attachment, (value) { + return _then(_value.copyWith(attachment: value) as $Val); }); } - /// Create a copy of ProcessorConnectionState - /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $VeilidStateNetworkCopyWith<$Res> get network { - return $VeilidStateNetworkCopyWith<$Res>(_self.network, (value) { - return _then(_self.copyWith(network: value)); + return $VeilidStateNetworkCopyWith<$Res>(_value.network, (value) { + return _then(_value.copyWith(network: value) as $Val); }); } } /// @nodoc +abstract class _$$ProcessorConnectionStateImplCopyWith<$Res> + implements $ProcessorConnectionStateCopyWith<$Res> { + factory _$$ProcessorConnectionStateImplCopyWith( + _$ProcessorConnectionStateImpl value, + $Res Function(_$ProcessorConnectionStateImpl) then) = + __$$ProcessorConnectionStateImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({VeilidStateAttachment attachment, VeilidStateNetwork network}); -class _ProcessorConnectionState extends ProcessorConnectionState { - const _ProcessorConnectionState( + @override + $VeilidStateAttachmentCopyWith<$Res> get attachment; + @override + $VeilidStateNetworkCopyWith<$Res> get network; +} + +/// @nodoc +class __$$ProcessorConnectionStateImplCopyWithImpl<$Res> + extends _$ProcessorConnectionStateCopyWithImpl<$Res, + _$ProcessorConnectionStateImpl> + implements _$$ProcessorConnectionStateImplCopyWith<$Res> { + __$$ProcessorConnectionStateImplCopyWithImpl( + _$ProcessorConnectionStateImpl _value, + $Res Function(_$ProcessorConnectionStateImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? attachment = null, + Object? network = null, + }) { + return _then(_$ProcessorConnectionStateImpl( + attachment: null == attachment + ? _value.attachment + : attachment // ignore: cast_nullable_to_non_nullable + as VeilidStateAttachment, + network: null == network + ? _value.network + : network // ignore: cast_nullable_to_non_nullable + as VeilidStateNetwork, + )); + } +} + +/// @nodoc + +class _$ProcessorConnectionStateImpl extends _ProcessorConnectionState { + const _$ProcessorConnectionStateImpl( {required this.attachment, required this.network}) : super._(); @@ -118,20 +140,16 @@ class _ProcessorConnectionState extends ProcessorConnectionState { @override final VeilidStateNetwork network; - /// Create a copy of ProcessorConnectionState - /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') - _$ProcessorConnectionStateCopyWith<_ProcessorConnectionState> get copyWith => - __$ProcessorConnectionStateCopyWithImpl<_ProcessorConnectionState>( - this, _$identity); + String toString() { + return 'ProcessorConnectionState(attachment: $attachment, network: $network)'; + } @override bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _ProcessorConnectionState && + other is _$ProcessorConnectionStateImpl && (identical(other.attachment, attachment) || other.attachment == attachment) && (identical(other.network, network) || other.network == network)); @@ -140,75 +158,27 @@ class _ProcessorConnectionState extends ProcessorConnectionState { @override int get hashCode => Object.hash(runtimeType, attachment, network); - @override - String toString() { - return 'ProcessorConnectionState(attachment: $attachment, network: $network)'; - } -} - -/// @nodoc -abstract mixin class _$ProcessorConnectionStateCopyWith<$Res> - implements $ProcessorConnectionStateCopyWith<$Res> { - factory _$ProcessorConnectionStateCopyWith(_ProcessorConnectionState value, - $Res Function(_ProcessorConnectionState) _then) = - __$ProcessorConnectionStateCopyWithImpl; - @override - @useResult - $Res call({VeilidStateAttachment attachment, VeilidStateNetwork network}); - - @override - $VeilidStateAttachmentCopyWith<$Res> get attachment; - @override - $VeilidStateNetworkCopyWith<$Res> get network; -} - -/// @nodoc -class __$ProcessorConnectionStateCopyWithImpl<$Res> - implements _$ProcessorConnectionStateCopyWith<$Res> { - __$ProcessorConnectionStateCopyWithImpl(this._self, this._then); - - final _ProcessorConnectionState _self; - final $Res Function(_ProcessorConnectionState) _then; - - /// Create a copy of ProcessorConnectionState - /// with the given fields replaced by the non-null parameter values. + @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - $Res call({ - Object? attachment = null, - Object? network = null, - }) { - return _then(_ProcessorConnectionState( - attachment: null == attachment - ? _self.attachment - : attachment // ignore: cast_nullable_to_non_nullable - as VeilidStateAttachment, - network: null == network - ? _self.network - : network // ignore: cast_nullable_to_non_nullable - as VeilidStateNetwork, - )); - } - - /// Create a copy of ProcessorConnectionState - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $VeilidStateAttachmentCopyWith<$Res> get attachment { - return $VeilidStateAttachmentCopyWith<$Res>(_self.attachment, (value) { - return _then(_self.copyWith(attachment: value)); - }); - } - - /// Create a copy of ProcessorConnectionState - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $VeilidStateNetworkCopyWith<$Res> get network { - return $VeilidStateNetworkCopyWith<$Res>(_self.network, (value) { - return _then(_self.copyWith(network: value)); - }); - } + _$$ProcessorConnectionStateImplCopyWith<_$ProcessorConnectionStateImpl> + get copyWith => __$$ProcessorConnectionStateImplCopyWithImpl< + _$ProcessorConnectionStateImpl>(this, _$identity); } -// dart format on +abstract class _ProcessorConnectionState extends ProcessorConnectionState { + const factory _ProcessorConnectionState( + {required final VeilidStateAttachment attachment, + required final VeilidStateNetwork network}) = + _$ProcessorConnectionStateImpl; + const _ProcessorConnectionState._() : super._(); + + @override + VeilidStateAttachment get attachment; + @override + VeilidStateNetwork get network; + @override + @JsonKey(ignore: true) + _$$ProcessorConnectionStateImplCopyWith<_$ProcessorConnectionStateImpl> + get copyWith => throw _privateConstructorUsedError; +} diff --git a/lib/veilid_processor/repository/processor_repository.dart b/lib/veilid_processor/repository/processor_repository.dart index a92347c..e021648 100644 --- a/lib/veilid_processor/repository/processor_repository.dart +++ b/lib/veilid_processor/repository/processor_repository.dart @@ -12,12 +12,10 @@ class ProcessorRepository { : startedUp = false, _controllerConnectionState = StreamController.broadcast(sync: true), processorConnectionState = ProcessorConnectionState( - attachment: VeilidStateAttachment( + attachment: const VeilidStateAttachment( state: AttachmentState.detached, publicInternetReady: false, - localNetworkReady: false, - uptime: TimestampDuration(value: BigInt.zero), - attachedUptime: null), + localNetworkReady: false), network: VeilidStateNetwork( started: false, bpsDown: BigInt.zero, @@ -44,21 +42,15 @@ class ProcessorRepository { log.info('Veilid version: $veilidVersion'); - Stream updateStream; - + // HACK: In case of hot restart shut down first try { - log.debug('Starting VeilidCore'); - updateStream = await Veilid.instance - .startupVeilidCore(await getVeilidConfig(kIsWeb, VeilidChatApp.name)); - } on VeilidAPIExceptionAlreadyInitialized catch (_) { - log.debug( - 'VeilidCore is already started, shutting down and restarting...'); - startedUp = true; - await shutdown(); - updateStream = await Veilid.instance - .startupVeilidCore(await getVeilidConfig(kIsWeb, VeilidChatApp.name)); + await Veilid.instance.shutdownVeilidCore(); + } on Exception { + // Do nothing on failure here } + final updateStream = await Veilid.instance + .startupVeilidCore(await getVeilidConfig(kIsWeb, VeilidChatApp.name)); _updateSubscription = updateStream.listen((update) { if (update is VeilidLog) { processLog(update); @@ -104,9 +96,7 @@ class ProcessorRepository { attachment: VeilidStateAttachment( state: updateAttachment.state, publicInternetReady: updateAttachment.publicInternetReady, - localNetworkReady: updateAttachment.localNetworkReady, - uptime: updateAttachment.uptime, - attachedUptime: updateAttachment.attachedUptime)); + localNetworkReady: updateAttachment.localNetworkReady)); } void processUpdateConfig(VeilidUpdateConfig updateConfig) { diff --git a/lib/veilid_processor/views/developer.dart b/lib/veilid_processor/views/developer.dart index d749a9c..f4fe836 100644 --- a/lib/veilid_processor/views/developer.dart +++ b/lib/veilid_processor/views/developer.dart @@ -1,27 +1,29 @@ import 'dart:async'; -import 'package:animated_custom_dropdown/custom_dropdown.dart'; import 'package:ansicolor/ansicolor.dart'; import 'package:awesome_extensions/awesome_extensions.dart'; +import 'package:cool_dropdown/cool_dropdown.dart'; +import 'package:cool_dropdown/models/cool_dropdown_item.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_animate/flutter_animate.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_translate/flutter_translate.dart'; import 'package:go_router/go_router.dart'; import 'package:loggy/loggy.dart'; +import 'package:quickalert/quickalert.dart'; import 'package:veilid_support/veilid_support.dart'; import 'package:xterm/xterm.dart'; import '../../layout/layout.dart'; import '../../notifications/notifications.dart'; -import '../../theme/models/scale_theme/scale_custom_dropdown_theme.dart'; import '../../theme/theme.dart'; import '../../tools/tools.dart'; import 'history_text_editing_controller.dart'; final globalDebugTerminal = Terminal( - maxLines: 10000, + maxLines: 50000, ); const kDefaultTerminalStyle = TerminalStyle( @@ -29,15 +31,6 @@ const kDefaultTerminalStyle = TerminalStyle( // height: 1.2, fontFamily: 'Source Code Pro'); -class LogLevelDropdownItem { - const LogLevelDropdownItem( - {required this.label, required this.icon, required this.value}); - - final String label; - final Widget icon; - final LogLevel value; -} - class DeveloperPage extends StatefulWidget { const DeveloperPage({super.key}); @@ -57,7 +50,7 @@ class _DeveloperPageState extends State { }); for (var i = 0; i < logLevels.length; i++) { - _logLevelDropdownItems.add(LogLevelDropdownItem( + _logLevelDropdownItems.add(CoolDropdownItem( label: logLevelName(logLevels[i]), icon: Text(logLevelEmoji(logLevels[i])), value: logLevels[i])); @@ -65,9 +58,8 @@ class _DeveloperPageState extends State { } void _debugOut(String out) { - final sanitizedOut = out.replaceAll('\uFFFD', ''); final pen = AnsiPen()..cyan(bold: true); - final colorOut = pen(sanitizedOut); + final colorOut = pen(out); debugPrint(colorOut); globalDebugTerminal.write(colorOut.replaceAll('\n', '\r\n')); } @@ -98,16 +90,6 @@ class _DeveloperPageState extends State { return true; } - if (debugCommand == 'pool stats') { - try { - DHTRecordPool.instance.debugPrintStats(); - } on Exception catch (e, st) { - _debugOut('<<< ERROR\n$e\n<<< STACK\n$st'); - return false; - } - return true; - } - if (debugCommand.startsWith('change_log_ignore ')) { final args = debugCommand.split(' '); if (args.length < 3) { @@ -135,22 +117,7 @@ class _DeveloperPageState extends State { _debugOut('DEBUG >>>\n$debugCommand\n'); try { - var out = await Veilid.instance.debug(debugCommand); - - if (debugCommand == 'help') { - out = 'VeilidChat Commands:\n' - ' pool \n' - ' allocations - List DHTRecordPool allocations\n' - ' opened - List opened DHTRecord instances\n' - ' stats - Dump DHTRecordPool statistics\n' - ' change_log_ignore change the log' - ' target ignore list for a tracing layer\n' - ' targets to add to the ignore list can be separated by' - ' a comma.\n' - ' to remove a target from the ignore list, prepend it' - ' with a minus.\n\n$out'; - } - + final out = await Veilid.instance.debug(debugCommand); _debugOut('<<< DEBUG\n$out\n'); } on Exception catch (e, st) { _debugOut('<<< ERROR\n$e\n<<< STACK\n$st'); @@ -198,40 +165,32 @@ class _DeveloperPageState extends State { } } - Future _onSubmitCommand(String debugCommand) async { - final ok = await _sendDebugCommand(debugCommand); - if (ok) { - setState(() { - _historyController.submit(debugCommand); - }); - } - } - @override Widget build(BuildContext context) { final theme = Theme.of(context); + final textTheme = theme.textTheme; final scale = theme.extension()!; - final scaleTheme = theme.extension()!; - final dropdownTheme = scaleTheme.customDropdownTheme(); final scaleConfig = theme.extension()!; - final hintColor = scaleConfig.useVisualIndicators - ? scale.primaryScale.primaryText - : scale.primaryScale.primary; + // WidgetsBinding.instance.addPostFrameCallback((_) { + // if (!_isScrolling && _wantsBottom) { + // _scrollToBottom(); + // } + // }); return Scaffold( - backgroundColor: scale.primaryScale.border, + backgroundColor: scale.primaryScale.primary, appBar: DefaultAppBar( title: Text(translate('developer.title')), leading: IconButton( - icon: Icon(Icons.arrow_back, color: scale.primaryScale.borderText), + icon: Icon(Icons.arrow_back, color: scale.primaryScale.primaryText), onPressed: () => GoRouterHelper(context).pop(), ), actions: [ IconButton( icon: const Icon(Icons.copy), - color: scale.primaryScale.borderText, - disabledColor: scale.primaryScale.borderText.withAlpha(0x3F), + color: scale.primaryScale.primaryText, + disabledColor: scale.primaryScale.primaryText.withAlpha(0x3F), onPressed: _terminalController.selection == null ? null : () async { @@ -239,104 +198,149 @@ class _DeveloperPageState extends State { }), IconButton( icon: const Icon(Icons.copy_all), - color: scale.primaryScale.borderText, - disabledColor: scale.primaryScale.borderText.withAlpha(0x3F), + color: scale.primaryScale.primaryText, + disabledColor: scale.primaryScale.primaryText.withAlpha(0x3F), onPressed: () async { await copyAll(context); }), IconButton( icon: const Icon(Icons.clear_all), - color: scale.primaryScale.borderText, - disabledColor: scale.primaryScale.borderText.withAlpha(0x3F), + color: scale.primaryScale.primaryText, + disabledColor: scale.primaryScale.primaryText.withAlpha(0x3F), onPressed: () async { - final confirm = await showConfirmModal( - context: context, - title: translate('confirmation.confirm'), - text: translate('developer.are_you_sure_clear'), - ); - if (confirm && context.mounted) { - await clear(context); - } - }), - SizedBox.fromSize( - size: const Size(140, 48), - child: CustomDropdown( - items: _logLevelDropdownItems, - initialItem: _logLevelDropdownItems - .singleWhere((x) => x.value == _logLevelDropDown), - onChanged: (item) { - if (item != null) { - setState(() { - _logLevelDropDown = item.value; - Loggy('').level = getLogOptions(item.value); - setVeilidLogLevel(item.value); + await QuickAlert.show( + context: context, + type: QuickAlertType.confirm, + title: translate('developer.are_you_sure_clear'), + titleColor: scale.primaryScale.appText, + textColor: scale.primaryScale.subtleText, + confirmBtnColor: scale.primaryScale.primary, + cancelBtnTextStyle: TextStyle( + fontWeight: FontWeight.w600, + fontSize: 18, + color: scale.primaryScale.appText), + backgroundColor: scale.primaryScale.appBackground, + headerBackgroundColor: scale.primaryScale.primary, + confirmBtnText: translate('button.ok'), + cancelBtnText: translate('button.cancel'), + onConfirmBtnTap: () async { + Navigator.pop(context); + if (context.mounted) { + await clear(context); + } }); - } - }, - headerBuilder: (context, item, enabled) => Row(children: [ - item.icon, - const Spacer(), - Text(item.label).copyWith(style: dropdownTheme.textStyle) - ]), - listItemBuilder: (context, item, enabled, onItemSelect) => - Row(children: [ - item.icon, - const Spacer(), - Text(item.label).copyWith(style: dropdownTheme.textStyle) - ]), - decoration: dropdownTheme.decoration, - disabledDecoration: dropdownTheme.disabledDecoration, - listItemPadding: dropdownTheme.listItemPadding, - itemsListPadding: dropdownTheme.itemsListPadding, - expandedHeaderPadding: dropdownTheme.expandedHeaderPadding, - closedHeaderPadding: dropdownTheme.closedHeaderPadding, - )).paddingLTRB(0, 4, 8, 4), + }), + CoolDropdown( + controller: _logLevelController, + defaultItem: _logLevelDropdownItems + .singleWhere((x) => x.value == _logLevelDropDown), + onChange: (value) { + setState(() { + _logLevelDropDown = value; + Loggy('').level = getLogOptions(value); + setVeilidLogLevel(value); + _logLevelController.close(); + }); + }, + resultOptions: ResultOptions( + width: 64, + height: 40, + render: ResultRender.icon, + icon: SizedBox( + width: 10, + height: 10, + child: CustomPaint( + painter: DropdownArrowPainter( + color: scale.primaryScale.primaryText))), + textStyle: textTheme.labelMedium! + .copyWith(color: scale.primaryScale.primaryText), + padding: const EdgeInsets.fromLTRB(8, 4, 8, 4), + openBoxDecoration: BoxDecoration( + //color: scale.primaryScale.border, + border: Border.all( + color: scaleConfig.useVisualIndicators + ? scale.primaryScale.hoverBorder + : scale.primaryScale.borderText), + borderRadius: + BorderRadius.circular(8 * scaleConfig.borderRadiusScale), + ), + boxDecoration: BoxDecoration( + //color: scale.primaryScale.hoverBorder, + border: Border.all( + color: scaleConfig.useVisualIndicators + ? scale.primaryScale.hoverBorder + : scale.primaryScale.borderText), + borderRadius: + BorderRadius.circular(8 * scaleConfig.borderRadiusScale), + ), + ), + dropdownOptions: DropdownOptions( + width: 160, + align: DropdownAlign.right, + duration: 150.ms, + color: scale.primaryScale.elementBackground, + borderSide: BorderSide(color: scale.primaryScale.border), + borderRadius: + BorderRadius.circular(8 * scaleConfig.borderRadiusScale), + padding: const EdgeInsets.fromLTRB(8, 4, 8, 4), + ), + dropdownTriangleOptions: const DropdownTriangleOptions( + align: DropdownTriangleAlign.right), + dropdownItemOptions: DropdownItemOptions( + selectedTextStyle: textTheme.labelMedium! + .copyWith(color: scale.primaryScale.appText), + textStyle: textTheme.labelMedium! + .copyWith(color: scale.primaryScale.appText), + selectedBoxDecoration: BoxDecoration( + color: scale.primaryScale.activeElementBackground), + mainAxisAlignment: MainAxisAlignment.spaceBetween, + padding: const EdgeInsets.fromLTRB(8, 4, 8, 4), + selectedPadding: const EdgeInsets.fromLTRB(8, 4, 8, 4)), + dropdownList: _logLevelDropdownItems, + ).paddingLTRB(0, 0, 8, 0) ], ), body: GestureDetector( onTap: () => FocusScope.of(context).unfocus(), - child: Column(children: [ + child: SafeArea( + child: Column(children: [ Stack(alignment: AlignmentDirectional.center, children: [ Image.asset('assets/images/ellet.png'), TerminalView(globalDebugTerminal, textStyle: kDefaultTerminalStyle, controller: _terminalController, keyboardType: TextInputType.none, + //autofocus: true, backgroundOpacity: _showEllet ? 0.75 : 1.0, onSecondaryTapDown: (details, offset) async { await copySelection(context); }) ]).expanded(), - TextFormField( + TextField( enabled: !_busy, - autofocus: true, controller: _historyController.controller, focusNode: _historyController.focusNode, - textInputAction: TextInputAction.send, onTapOutside: (event) { FocusManager.instance.primaryFocus?.unfocus(); }, decoration: InputDecoration( filled: true, contentPadding: const EdgeInsets.fromLTRB(8, 2, 8, 2), - enabledBorder: - const OutlineInputBorder(borderSide: BorderSide.none), - border: - const OutlineInputBorder(borderSide: BorderSide.none), - focusedBorder: - const OutlineInputBorder(borderSide: BorderSide.none), - fillColor: scale.primaryScale.elementBackground, - hoverColor: scale.primaryScale.elementBackground, - hintStyle: scaleTheme.textTheme.labelMedium!.copyWith( - color: scaleConfig.useVisualIndicators - ? hintColor.withAlpha(0x7F) - : hintColor), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular( + 8 * scaleConfig.borderRadiusScale), + borderSide: BorderSide.none), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular( + 8 * scaleConfig.borderRadiusScale), + ), + fillColor: scale.primaryScale.subtleBackground, hintText: translate('developer.command'), suffixIcon: IconButton( icon: Icon(Icons.send, color: _historyController.controller.text.isEmpty - ? hintColor.withAlpha(0x7F) - : hintColor), + ? scale.primaryScale.primary.withAlpha(0x3F) + : scale.primaryScale.primary), onPressed: (_historyController.controller.text.isEmpty || _busy) ? null @@ -344,7 +348,7 @@ class _DeveloperPageState extends State { final debugCommand = _historyController.controller.text; _historyController.controller.clear(); - await _onSubmitCommand(debugCommand); + await _sendDebugCommand(debugCommand); }, )), onChanged: (_) { @@ -355,15 +359,20 @@ class _DeveloperPageState extends State { _historyController.controller.clearComposing(); // don't give up focus though }, - onFieldSubmitted: (debugCommand) async { + onSubmitted: (debugCommand) async { if (debugCommand.isEmpty) { return; } - await _onSubmitCommand(debugCommand); - _historyController.focusNode.requestFocus(); + + final ok = await _sendDebugCommand(debugCommand); + if (ok) { + setState(() { + _historyController.submit(debugCommand); + }); + } }, ).paddingAll(4) - ]))); + ])))); } //////////////////////////////////////////////////////////////////////////// @@ -371,9 +380,9 @@ class _DeveloperPageState extends State { final _terminalController = TerminalController(); late final HistoryTextEditingController _historyController; - final List _logLevelDropdownItems = []; + final _logLevelController = DropdownController(duration: 250.ms); + final List> _logLevelDropdownItems = []; var _logLevelDropDown = log.level.logLevel; - var _showEllet = false; var _busy = false; diff --git a/lib/veilid_processor/views/signal_strength_meter.dart b/lib/veilid_processor/views/signal_strength_meter.dart index 5385bb1..73842f1 100644 --- a/lib/veilid_processor/views/signal_strength_meter.dart +++ b/lib/veilid_processor/views/signal_strength_meter.dart @@ -4,6 +4,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; +import 'package:quickalert/quickalert.dart'; import 'package:signal_strength_indicator/signal_strength_indicator.dart'; import 'package:veilid_support/veilid_support.dart'; @@ -34,33 +35,34 @@ class SignalStrengthMeterWidget extends StatelessWidget { case AttachmentState.detached: iconWidget = Icon(Icons.signal_cellular_nodata, size: iconSize, - color: this.color ?? scale.primaryScale.borderText); + color: this.color ?? scale.primaryScale.primaryText); return; case AttachmentState.detaching: iconWidget = Icon(Icons.signal_cellular_off, size: iconSize, - color: this.color ?? scale.primaryScale.borderText); + color: this.color ?? scale.primaryScale.primaryText); return; case AttachmentState.attaching: value = 0; - color = this.color ?? scale.primaryScale.borderText; + color = this.color ?? scale.primaryScale.primaryText; case AttachmentState.attachedWeak: value = 1; - color = this.color ?? scale.primaryScale.borderText; + color = this.color ?? scale.primaryScale.primaryText; case AttachmentState.attachedStrong: value = 2; - color = this.color ?? scale.primaryScale.borderText; + color = this.color ?? scale.primaryScale.primaryText; case AttachmentState.attachedGood: value = 3; - color = this.color ?? scale.primaryScale.borderText; + color = this.color ?? scale.primaryScale.primaryText; case AttachmentState.fullyAttached: value = 4; - color = this.color ?? scale.primaryScale.borderText; + color = this.color ?? scale.primaryScale.primaryText; case AttachmentState.overAttached: value = 4; - color = this.color ?? scale.primaryScale.borderText; + color = this.color ?? scale.primaryScale.primaryText; } - inactiveColor = this.inactiveColor ?? scale.grayScale.borderText; + inactiveColor = + this.inactiveColor ?? scale.primaryScale.primaryText; iconWidget = SignalStrengthIndicator.bars( value: value, @@ -73,8 +75,11 @@ class SignalStrengthMeterWidget extends StatelessWidget { loading: () => {iconWidget = const Icon(Icons.warning)}, error: (e, st) => { iconWidget = const Icon(Icons.error).onTap( - () async => showErrorStacktraceModal( - context: context, error: e, stackTrace: st), + () async => QuickAlert.show( + type: QuickAlertType.error, + context: context, + title: 'Error', + text: 'Error: {e}\n StackTrace: {st}'), ) }); diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 0ac222b..3acb238 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -9,7 +9,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -24,9 +25,12 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) printing_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "PrintingPlugin"); printing_plugin_register_with_registrar(printing_registrar); - g_autoptr(FlPluginRegistrar) screen_retriever_linux_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverLinuxPlugin"); - screen_retriever_linux_plugin_register_with_registrar(screen_retriever_linux_registrar); + g_autoptr(FlPluginRegistrar) screen_retriever_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin"); + screen_retriever_plugin_register_with_registrar(screen_retriever_registrar); + g_autoptr(FlPluginRegistrar) smart_auth_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "SmartAuthPlugin"); + smart_auth_plugin_register_with_registrar(smart_auth_registrar); g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index a48f10f..d09262f 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -6,7 +6,8 @@ list(APPEND FLUTTER_PLUGIN_LIST file_saver pasteboard printing - screen_retriever_linux + screen_retriever + smart_auth url_launcher_linux veilid window_manager diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index a599497..c311845 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -11,10 +11,11 @@ import package_info_plus import pasteboard import path_provider_foundation import printing -import screen_retriever_macos +import screen_retriever import share_plus import shared_preferences_foundation -import sqflite_darwin +import smart_auth +import sqflite import url_launcher_macos import veilid import window_manager @@ -26,9 +27,10 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PrintingPlugin.register(with: registry.registrar(forPlugin: "PrintingPlugin")) - ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin")) + ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + SmartAuthPlugin.register(with: registry.registrar(forPlugin: "SmartAuthPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) VeilidPlugin.register(with: registry.registrar(forPlugin: "VeilidPlugin")) diff --git a/macos/Podfile.lock b/macos/Podfile.lock index beb7d0e..3a69524 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -2,8 +2,7 @@ PODS: - file_saver (0.0.1): - FlutterMacOS - FlutterMacOS (1.0.0) - - mobile_scanner (7.0.0): - - Flutter + - mobile_scanner (5.1.1): - FlutterMacOS - package_info_plus (0.0.1): - FlutterMacOS @@ -14,14 +13,16 @@ PODS: - FlutterMacOS - printing (1.0.0): - FlutterMacOS - - screen_retriever_macos (0.0.1): + - screen_retriever (0.0.1): - FlutterMacOS - share_plus (0.0.1): - FlutterMacOS - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS - - sqflite_darwin (0.0.4): + - smart_auth (0.0.1): + - FlutterMacOS + - sqflite (0.0.3): - Flutter - FlutterMacOS - url_launcher_macos (0.0.1): @@ -34,15 +35,16 @@ PODS: DEPENDENCIES: - file_saver (from `Flutter/ephemeral/.symlinks/plugins/file_saver/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - - mobile_scanner (from `Flutter/ephemeral/.symlinks/plugins/mobile_scanner/darwin`) + - mobile_scanner (from `Flutter/ephemeral/.symlinks/plugins/mobile_scanner/macos`) - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) - pasteboard (from `Flutter/ephemeral/.symlinks/plugins/pasteboard/macos`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - printing (from `Flutter/ephemeral/.symlinks/plugins/printing/macos`) - - screen_retriever_macos (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos`) + - screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`) - share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`) - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) - - sqflite_darwin (from `Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin`) + - smart_auth (from `Flutter/ephemeral/.symlinks/plugins/smart_auth/macos`) + - sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/darwin`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) - veilid (from `Flutter/ephemeral/.symlinks/plugins/veilid/macos`) - window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`) @@ -53,7 +55,7 @@ EXTERNAL SOURCES: FlutterMacOS: :path: Flutter/ephemeral mobile_scanner: - :path: Flutter/ephemeral/.symlinks/plugins/mobile_scanner/darwin + :path: Flutter/ephemeral/.symlinks/plugins/mobile_scanner/macos package_info_plus: :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos pasteboard: @@ -62,14 +64,16 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin printing: :path: Flutter/ephemeral/.symlinks/plugins/printing/macos - screen_retriever_macos: - :path: Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos + screen_retriever: + :path: Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos share_plus: :path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos shared_preferences_foundation: :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin - sqflite_darwin: - :path: Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin + smart_auth: + :path: Flutter/ephemeral/.symlinks/plugins/smart_auth/macos + sqflite: + :path: Flutter/ephemeral/.symlinks/plugins/sqflite/darwin url_launcher_macos: :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos veilid: @@ -78,21 +82,22 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos SPEC CHECKSUMS: - file_saver: e35bd97de451dde55ff8c38862ed7ad0f3418d0f + file_saver: 44e6fbf666677faf097302460e214e977fdd977b FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 - mobile_scanner: 9157936403f5a0644ca3779a38ff8404c5434a93 - package_info_plus: f0052d280d17aa382b932f399edf32507174e870 - pasteboard: 278d8100149f940fb795d6b3a74f0720c890ecb7 - path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 - printing: c4cf83c78fd684f9bc318e6aadc18972aa48f617 - screen_retriever_macos: 452e51764a9e1cdb74b3c541238795849f21557f - share_plus: 510bf0af1a42cd602274b4629920c9649c52f4cc - shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 - sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 - url_launcher_macos: 0fba8ddabfc33ce0a9afe7c5fef5aab3d8d2d673 - veilid: 319e2e78836d7b3d08203596d0b4a0e244b68d29 - window_manager: 1d01fa7ac65a6e6f83b965471b1a7fdd3f06166c + mobile_scanner: 1efac1e53c294b24e3bb55bcc7f4deee0233a86b + package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c + pasteboard: 9b69dba6fedbb04866be632205d532fe2f6b1d99 + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + printing: 1dd6a1fce2209ec240698e2439a4adbb9b427637 + screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38 + share_plus: 36537c04ce0c3e3f5bd297ce4318b6d5ee5fd6cf + shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 + smart_auth: b38e3ab4bfe089eacb1e233aca1a2340f96c28e9 + sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec + url_launcher_macos: 5f437abeda8c85500ceb03f5c1938a8c5a705399 + veilid: a54f57b7bcf0e4e072fe99272d76ca126b2026d0 + window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8 PODFILE CHECKSUM: ff0a9a3ce75ee73f200ca7e2f47745698c917ef9 -COCOAPODS: 1.16.2 +COCOAPODS: 1.15.2 diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 408e781..c94b139 100644 --- a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -48,7 +48,6 @@ ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" debugServiceExtension = "internal" - enableGPUValidationMode = "1" allowLocationSimulation = "YES"> diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift index b3c1761..d53ef64 100644 --- a/macos/Runner/AppDelegate.swift +++ b/macos/Runner/AppDelegate.swift @@ -1,13 +1,9 @@ import Cocoa import FlutterMacOS -@main +@NSApplicationMain class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } - - override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { - return true - } } diff --git a/packages/veilid_support/example/.gitignore b/packages/veilid_support/example/.gitignore index 79c113f..29a3a50 100644 --- a/packages/veilid_support/example/.gitignore +++ b/packages/veilid_support/example/.gitignore @@ -5,11 +5,9 @@ *.swp .DS_Store .atom/ -.build/ .buildlog/ .history .svn/ -.swiftpm/ migrate_working_dir/ # IntelliJ related diff --git a/packages/veilid_support/example/android/app/build.gradle b/packages/veilid_support/example/android/app/build.gradle index 2ba6503..033f4ac 100644 --- a/packages/veilid_support/example/android/app/build.gradle +++ b/packages/veilid_support/example/android/app/build.gradle @@ -23,19 +23,19 @@ if (flutterVersionName == null) { } android { - ndkVersion '27.0.12077973' - ndkVersion '27.0.12077973' + ndkVersion '26.3.11579264' + ndkVersion '26.3.11579264' namespace "com.example.example" compileSdk flutter.compileSdkVersion ndkVersion flutter.ndkVersion compileOptions { - sourceCompatibility JavaVersion.VERSION_17 - targetCompatibility JavaVersion.VERSION_17 + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { - jvmTarget = '17' + jvmTarget = '1.8' } sourceSets { diff --git a/packages/veilid_support/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/veilid_support/example/android/gradle/wrapper/gradle-wrapper.properties index 6f8524c..c3433f7 100644 --- a/packages/veilid_support/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/veilid_support/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https://services.gradle.org/distributions/gradle-8.10.2-all.zip +distributionUrl=https://services.gradle.org/distributions/gradle-7.3.3-all.zip diff --git a/packages/veilid_support/example/android/settings.gradle b/packages/veilid_support/example/android/settings.gradle index b1ae36a..1d6d19b 100644 --- a/packages/veilid_support/example/android/settings.gradle +++ b/packages/veilid_support/example/android/settings.gradle @@ -5,9 +5,10 @@ pluginManagement { def flutterSdkPath = properties.getProperty("flutter.sdk") assert flutterSdkPath != null, "flutter.sdk not set in local.properties" return flutterSdkPath - }() + } + settings.ext.flutterSdkPath = flutterSdkPath() - includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") repositories { google() @@ -18,8 +19,8 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "8.8.0" apply false - id "org.jetbrains.kotlin.android" version "1.9.25" apply false + id "com.android.application" version "7.3.0" apply false + id "org.jetbrains.kotlin.android" version "1.7.10" apply false } -include ":app" \ No newline at end of file +include ":app" diff --git a/packages/veilid_support/example/dev-setup/flutter_config.sh b/packages/veilid_support/example/dev-setup/flutter_config.sh index a1b8c8d..9cb53c7 100755 --- a/packages/veilid_support/example/dev-setup/flutter_config.sh +++ b/packages/veilid_support/example/dev-setup/flutter_config.sh @@ -14,13 +14,13 @@ sed -i '' 's/MACOSX_DEPLOYMENT_TARGET = [^;]*/MACOSX_DEPLOYMENT_TARGET = 10.14.6 sed -i '' "s/platform :osx, '[^']*'/platform :osx, '10.14.6'/g" $APPDIR/macos/Podfile # Android: Set NDK version -if [[ "$TMPDIR" != "" ]]; then +if [[ "$TMPDIR" != "" ]]; then ANDTMP=$TMPDIR/andtmp_$(date +%s) -else +else ANDTMP=/tmp/andtmp_$(date +%s) fi cat < $ANDTMP - ndkVersion '27.0.12077973' + ndkVersion '26.3.11579264' EOF sed -i '' -e "/android {/r $ANDTMP" $APPDIR/android/app/build.gradle rm -- $ANDTMP @@ -29,7 +29,7 @@ rm -- $ANDTMP sed -i '' 's/minSdkVersion .*/minSdkVersion Math.max(flutter.minSdkVersion, 24)/g' $APPDIR/android/app/build.gradle # Android: Set gradle plugin version -sed -i '' "s/classpath \'com.android.tools.build:gradle:[^\']*\'/classpath 'com.android.tools.build:gradle:8.8.0'/g" $APPDIR/android/build.gradle +sed -i '' "s/classpath \'com.android.tools.build:gradle:[^\']*\'/classpath 'com.android.tools.build:gradle:7.2.0'/g" $APPDIR/android/build.gradle # Android: Set gradle version -sed -i '' 's/distributionUrl=.*/distributionUrl=https:\/\/services.gradle.org\/distributions\/gradle-8.10.2-all.zip/g' $APPDIR/android/gradle/wrapper/gradle-wrapper.properties +sed -i '' 's/distributionUrl=.*/distributionUrl=https:\/\/services.gradle.org\/distributions\/gradle-7.3.3-all.zip/g' $APPDIR/android/gradle/wrapper/gradle-wrapper.properties diff --git a/packages/veilid_support/example/integration_test/app_test.dart b/packages/veilid_support/example/integration_test/app_test.dart index 5dc7acd..a0f3b7f 100644 --- a/packages/veilid_support/example/integration_test/app_test.dart +++ b/packages/veilid_support/example/integration_test/app_test.dart @@ -36,6 +36,117 @@ void main() { setUpAll(veilidFixture.attach); tearDownAll(veilidFixture.detach); + // group('TableDB Tests', () { + // group('TableDBArray Tests', () { + // // test('create/delete TableDBArray', testTableDBArrayCreateDelete); + + // group('TableDBArray Add/Get Tests', () { + // for (final params in [ + // // + // (99, 3, 15), + // (100, 4, 16), + // (101, 5, 17), + // // + // (511, 3, 127), + // (512, 4, 128), + // (513, 5, 129), + // // + // (4095, 3, 1023), + // (4096, 4, 1024), + // (4097, 5, 1025), + // // + // (65535, 3, 16383), + // (65536, 4, 16384), + // (65537, 5, 16385), + // ]) { + // final count = params.$1; + // final singles = params.$2; + // final batchSize = params.$3; + + // test( + // timeout: const Timeout(Duration(seconds: 480)), + // 'add/remove TableDBArray count = $count batchSize=$batchSize', + // makeTestTableDBArrayAddGetClear( + // count: count, + // singles: singles, + // batchSize: batchSize, + // crypto: const VeilidCryptoPublic()), + // ); + // } + // }); + + // group('TableDBArray Insert Tests', () { + // for (final params in [ + // // + // (99, 3, 15), + // (100, 4, 16), + // (101, 5, 17), + // // + // (511, 3, 127), + // (512, 4, 128), + // (513, 5, 129), + // // + // (4095, 3, 1023), + // (4096, 4, 1024), + // (4097, 5, 1025), + // // + // (65535, 3, 16383), + // (65536, 4, 16384), + // (65537, 5, 16385), + // ]) { + // final count = params.$1; + // final singles = params.$2; + // final batchSize = params.$3; + + // test( + // timeout: const Timeout(Duration(seconds: 480)), + // 'insert TableDBArray count=$count singles=$singles batchSize=$batchSize', + // makeTestTableDBArrayInsert( + // count: count, + // singles: singles, + // batchSize: batchSize, + // crypto: const VeilidCryptoPublic()), + // ); + // } + // }); + + // group('TableDBArray Remove Tests', () { + // for (final params in [ + // // + // (99, 3, 15), + // (100, 4, 16), + // (101, 5, 17), + // // + // (511, 3, 127), + // (512, 4, 128), + // (513, 5, 129), + // // + // (4095, 3, 1023), + // (4096, 4, 1024), + // (4097, 5, 1025), + // // + // (16383, 3, 4095), + // (16384, 4, 4096), + // (16385, 5, 4097), + // ]) { + // final count = params.$1; + // final singles = params.$2; + // final batchSize = params.$3; + + // test( + // timeout: const Timeout(Duration(seconds: 480)), + // 'remove TableDBArray count=$count singles=$singles batchSize=$batchSize', + // makeTestTableDBArrayRemove( + // count: count, + // singles: singles, + // batchSize: batchSize, + // crypto: const VeilidCryptoPublic()), + // ); + // } + // }); + // }); + // }); + group('DHT Support Tests', () { setUpAll(updateProcessorFixture.setUp); setUpAll(tickerFixture.setUp); @@ -80,117 +191,6 @@ void main() { } }); }); - - group('TableDB Tests', () { - group('TableDBArray Tests', () { - // test('create/delete TableDBArray', testTableDBArrayCreateDelete); - - group('TableDBArray Add/Get Tests', () { - for (final params in [ - // - (99, 3, 15), - (100, 4, 16), - (101, 5, 17), - // - (511, 3, 127), - (512, 4, 128), - (513, 5, 129), - // - (4095, 3, 1023), - (4096, 4, 1024), - (4097, 5, 1025), - // - (65535, 3, 16383), - (65536, 4, 16384), - (65537, 5, 16385), - ]) { - final count = params.$1; - final singles = params.$2; - final batchSize = params.$3; - - test( - timeout: const Timeout(Duration(seconds: 480)), - 'add/remove TableDBArray count = $count batchSize=$batchSize', - makeTestTableDBArrayAddGetClear( - count: count, - singles: singles, - batchSize: batchSize, - crypto: const VeilidCryptoPublic()), - ); - } - }); - - group('TableDBArray Insert Tests', () { - for (final params in [ - // - (99, 3, 15), - (100, 4, 16), - (101, 5, 17), - // - (511, 3, 127), - (512, 4, 128), - (513, 5, 129), - // - (4095, 3, 1023), - (4096, 4, 1024), - (4097, 5, 1025), - // - (65535, 3, 16383), - (65536, 4, 16384), - (65537, 5, 16385), - ]) { - final count = params.$1; - final singles = params.$2; - final batchSize = params.$3; - - test( - timeout: const Timeout(Duration(seconds: 480)), - 'insert TableDBArray count=$count singles=$singles batchSize=$batchSize', - makeTestTableDBArrayInsert( - count: count, - singles: singles, - batchSize: batchSize, - crypto: const VeilidCryptoPublic()), - ); - } - }); - - group('TableDBArray Remove Tests', () { - for (final params in [ - // - (99, 3, 15), - (100, 4, 16), - (101, 5, 17), - // - (511, 3, 127), - (512, 4, 128), - (513, 5, 129), - // - (4095, 3, 1023), - (4096, 4, 1024), - (4097, 5, 1025), - // - (16383, 3, 4095), - (16384, 4, 4096), - (16385, 5, 4097), - ]) { - final count = params.$1; - final singles = params.$2; - final batchSize = params.$3; - - test( - timeout: const Timeout(Duration(seconds: 480)), - 'remove TableDBArray count=$count singles=$singles batchSize=$batchSize', - makeTestTableDBArrayRemove( - count: count, - singles: singles, - batchSize: batchSize, - crypto: const VeilidCryptoPublic()), - ); - } - }); - }); - }); }); }); } diff --git a/packages/veilid_support/example/macos/Podfile.lock b/packages/veilid_support/example/macos/Podfile.lock index a2618bd..6a58494 100644 --- a/packages/veilid_support/example/macos/Podfile.lock +++ b/packages/veilid_support/example/macos/Podfile.lock @@ -21,9 +21,9 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 - path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 - veilid: 319e2e78836d7b3d08203596d0b4a0e244b68d29 + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + veilid: a54f57b7bcf0e4e072fe99272d76ca126b2026d0 PODFILE CHECKSUM: 16208599a12443d53889ba2270a4985981cfb204 -COCOAPODS: 1.16.2 +COCOAPODS: 1.15.2 diff --git a/packages/veilid_support/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/veilid_support/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index ac78810..15368ec 100644 --- a/packages/veilid_support/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/veilid_support/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -59,7 +59,6 @@ ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" debugServiceExtension = "internal" - enableGPUValidationMode = "1" allowLocationSimulation = "YES"> diff --git a/packages/veilid_support/example/macos/Runner/AppDelegate.swift b/packages/veilid_support/example/macos/Runner/AppDelegate.swift index b3c1761..d53ef64 100644 --- a/packages/veilid_support/example/macos/Runner/AppDelegate.swift +++ b/packages/veilid_support/example/macos/Runner/AppDelegate.swift @@ -1,13 +1,9 @@ import Cocoa import FlutterMacOS -@main +@NSApplicationMain class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } - - override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { - return true - } } diff --git a/packages/veilid_support/example/pubspec.lock b/packages/veilid_support/example/pubspec.lock index 9af9773..aa857d4 100644 --- a/packages/veilid_support/example/pubspec.lock +++ b/packages/veilid_support/example/pubspec.lock @@ -5,138 +5,130 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: e55636ed79578b9abca5fecf9437947798f5ef7456308b5cb85720b793eac92f + sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" url: "https://pub.dev" source: hosted - version: "82.0.0" + version: "67.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "904ae5bb474d32c38fb9482e2d925d5454cda04ddd0e55d2e6826bc72f6ba8c0" + sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" url: "https://pub.dev" source: hosted - version: "7.4.5" + version: "6.4.1" args: dependency: transitive description: name: args - sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" url: "https://pub.dev" source: hosted - version: "2.7.0" + version: "2.5.0" async: dependency: transitive description: name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.11.0" async_tools: dependency: "direct dev" description: name: async_tools - sha256: afd5426e76631172f8ce6a6359b264b092fa9d2a52cd2528100115be9525e067 + sha256: "9166e8fe65fc65eb79202a6d540f4de768553d78141b885f5bd3f8d7d30eef5e" url: "https://pub.dev" source: hosted - version: "0.1.9" + version: "0.1.5" bloc: dependency: transitive description: name: bloc - sha256: "52c10575f4445c61dd9e0cafcc6356fdd827c4c64dd7945ef3c4105f6b6ac189" + sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e" url: "https://pub.dev" source: hosted - version: "9.0.0" + version: "8.1.4" bloc_advanced_tools: dependency: transitive description: name: bloc_advanced_tools - sha256: dfb142569814952af8d93e7fe045972d847e29382471687db59913e253202f6e + sha256: "2b2dd492a350e7192a933d09f15ea04d5d00e7bd3fe2a906fe629cd461ddbf94" url: "https://pub.dev" source: hosted - version: "0.1.12" + version: "0.1.5" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" change_case: dependency: transitive description: name: change_case - sha256: e41ef3df58521194ef8d7649928954805aeb08061917cf658322305e61568003 + sha256: "47c48c36f95f20c6d0ba03efabceff261d05026cca322cc2c4c01c343371b5bb" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.0.1" characters: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.0" charcode: dependency: transitive description: name: charcode - sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a + sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 url: "https://pub.dev" source: hosted - version: "1.4.0" - cli_config: - dependency: transitive - description: - name: cli_config - sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec - url: "https://pub.dev" - source: hosted - version: "0.2.0" + version: "1.3.1" clock: dependency: transitive description: name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.1" collection: dependency: transitive description: name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.19.1" + version: "1.18.0" convert: dependency: transitive description: name: convert - sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.1.1" coverage: dependency: transitive description: name: coverage - sha256: "802bd084fb82e55df091ec8ad1553a7331b61c08251eef19a508b6f3f3a9858d" + sha256: "3945034e86ea203af7a056d98e98e42a5518fff200d6e8e6647e1886b07e936e" url: "https://pub.dev" source: hosted - version: "1.13.1" + version: "1.8.0" crypto: dependency: transitive description: name: crypto - sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.0.3" cupertino_icons: dependency: "direct main" description: @@ -149,50 +141,50 @@ packages: dependency: transitive description: name: equatable - sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 url: "https://pub.dev" source: hosted - version: "2.0.7" + version: "2.0.5" fake_async: dependency: transitive description: name: fake_async - sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" url: "https://pub.dev" source: hosted - version: "1.3.3" + version: "1.3.1" fast_immutable_collections: dependency: transitive description: name: fast_immutable_collections - sha256: d1aa3d7788fab06cce7f303f4969c7a16a10c865e1bd2478291a8ebcbee084e5 + sha256: "533806a7f0c624c2e479d05d3fdce4c87109a7cd0db39b8cc3830d3a2e8dedc7" url: "https://pub.dev" source: hosted - version: "11.0.4" + version: "10.2.3" ffi: dependency: transitive description: name: ffi - sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.2" file: dependency: transitive description: name: file - sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" url: "https://pub.dev" source: hosted - version: "7.0.1" + version: "7.0.0" fixnum: dependency: transitive description: name: fixnum - sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.0" flutter: dependency: "direct main" description: flutter @@ -217,10 +209,10 @@ packages: dependency: transitive description: name: freezed_annotation - sha256: c87ff004c8aa6af2d531668b46a4ea379f7191dc6dfa066acd53d506da6e044b + sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "2.4.1" frontend_server_client: dependency: transitive description: @@ -238,10 +230,10 @@ packages: dependency: transitive description: name: glob - sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.2" globbing: dependency: transitive description: @@ -254,26 +246,18 @@ packages: dependency: transitive description: name: http_multi_server - sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" url: "https://pub.dev" source: hosted - version: "3.2.2" + version: "3.2.1" http_parser: dependency: transitive description: name: http_parser - sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" url: "https://pub.dev" source: hosted - version: "4.1.2" - indent: - dependency: transitive - description: - name: indent - sha256: "819319a5c185f7fe412750c798953378b37a0d0d32564ce33e7c5acfd1372d2a" - url: "https://pub.dev" - source: hosted - version: "2.0.0" + version: "4.0.2" integration_test: dependency: "direct dev" description: flutter @@ -283,18 +267,18 @@ packages: dependency: transitive description: name: io - sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" url: "https://pub.dev" source: hosted - version: "1.0.5" + version: "1.0.4" js: dependency: transitive description: name: js - sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" + sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf url: "https://pub.dev" source: hosted - version: "0.7.2" + version: "0.7.1" json_annotation: dependency: transitive description: @@ -307,18 +291,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "10.0.4" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.3" leak_tracker_testing: dependency: transitive description: @@ -331,18 +315,18 @@ packages: dependency: "direct dev" description: name: lint_hard - sha256: "2073d4e83ac4e3f2b87cc615fff41abb5c2c5618e117edcd3d71f40f2186f4d5" + sha256: "44d15ec309b1a8e1aff99069df9dcb1597f49d5f588f32811ca28fb7b38c32fe" url: "https://pub.dev" source: hosted - version: "6.1.1" + version: "4.0.0" logging: dependency: transitive description: name: logging - sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.2.0" loggy: dependency: transitive description: @@ -355,34 +339,34 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.11.1" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.12.0" mime: dependency: transitive description: name: mime - sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "1.0.5" node_preamble: dependency: transitive description: @@ -395,42 +379,42 @@ packages: dependency: transitive description: name: package_config - sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.1.0" path: dependency: transitive description: name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.9.0" path_provider: dependency: transitive description: name: path_provider - sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" + sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.1.3" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 + sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d url: "https://pub.dev" source: hosted - version: "2.2.17" + version: "2.2.4" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.0" path_provider_linux: dependency: transitive description: @@ -451,18 +435,18 @@ packages: dependency: transitive description: name: path_provider_windows - sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.2.1" platform: dependency: transitive description: name: platform - sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" url: "https://pub.dev" source: hosted - version: "3.1.6" + version: "3.1.4" plugin_platform_interface: dependency: transitive description: @@ -483,34 +467,34 @@ packages: dependency: transitive description: name: process - sha256: "107d8be718f120bbba9dcd1e95e3bd325b1b4a4f07db64154635ba03f2567a0d" + sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32" url: "https://pub.dev" source: hosted - version: "5.0.3" + version: "5.0.2" protobuf: dependency: transitive description: name: protobuf - sha256: "579fe5557eae58e3adca2e999e38f02441d8aa908703854a9e0a0f47fa857731" + sha256: "68645b24e0716782e58948f8467fd42a880f255096a821f9e7d0ec625b00c84d" url: "https://pub.dev" source: hosted - version: "4.1.0" + version: "3.1.0" pub_semver: dependency: transitive description: name: pub_semver - sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.1.4" shelf: dependency: transitive description: name: shelf - sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 url: "https://pub.dev" source: hosted - version: "1.4.2" + version: "1.4.1" shelf_packages_handler: dependency: transitive description: @@ -523,71 +507,71 @@ packages: dependency: transitive description: name: shelf_static - sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 + sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e url: "https://pub.dev" source: hosted - version: "1.1.3" + version: "1.1.2" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "1.0.4" sky_engine: dependency: transitive description: flutter source: sdk - version: "0.0.0" + version: "0.0.99" source_map_stack_trace: dependency: transitive description: name: source_map_stack_trace - sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b + sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" source_maps: dependency: transitive description: name: source_maps - sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812" + sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" url: "https://pub.dev" source: hosted - version: "0.10.13" + version: "0.10.12" source_span: dependency: transitive description: name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.10.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.12.1" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.2" string_scanner: dependency: transitive description: name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.2.0" sync_http: dependency: transitive description: @@ -608,50 +592,50 @@ packages: dependency: transitive description: name: system_info_plus - sha256: df94187e95527f9cb459e6a9f6e0b1ea20c157d8029bc233de34b3c1e17e1c48 + sha256: b915c811c6605b802f3988859bc2bb79c95f735762a75b5451741f7a2b949d1b url: "https://pub.dev" source: hosted - version: "0.0.6" + version: "0.0.5" term_glyph: dependency: transitive description: name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.1" test: dependency: "direct dev" description: name: test - sha256: "301b213cd241ca982e9ba50266bd3f5bd1ea33f1455554c5abb85d1be0e2d87e" + sha256: "7ee446762c2c50b3bd4ea96fe13ffac69919352bd3b4b17bac3f3465edc58073" url: "https://pub.dev" source: hosted - version: "1.25.15" + version: "1.25.2" test_api: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.0" test_core: dependency: transitive description: name: test_core - sha256: "84d17c3486c8dfdbe5e12a50c8ae176d15e2a771b96909a9442b40173649ccaa" + sha256: "2bc4b4ecddd75309300d8096f781c0e3280ca1ef85beda558d33fcbedc2eead4" url: "https://pub.dev" source: hosted - version: "0.6.8" + version: "0.6.0" typed_data: dependency: transitive description: name: typed_data - sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.2" vector_math: dependency: transitive description: @@ -666,7 +650,7 @@ packages: path: "../../../../veilid/veilid-flutter" relative: true source: path - version: "0.4.6" + version: "0.3.3" veilid_support: dependency: "direct main" description: @@ -685,50 +669,42 @@ packages: dependency: transitive description: name: vm_service - sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "15.0.0" + version: "14.2.1" watcher: dependency: transitive description: name: watcher - sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104" + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.0" web: dependency: transitive description: name: web - sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" url: "https://pub.dev" source: hosted - version: "1.1.1" - web_socket: - dependency: transitive - description: - name: web_socket - sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" - url: "https://pub.dev" - source: hosted - version: "1.0.1" + version: "0.5.1" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 + sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "2.4.5" webdriver: dependency: transitive description: name: webdriver - sha256: "2f3a14ca026957870cfd9c635b83507e0e51d8091568e90129fbf805aba7cade" + sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.0.3" webkit_inspection_protocol: dependency: transitive description: @@ -737,22 +713,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.1" + win32: + dependency: transitive + description: + name: win32 + sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4 + url: "https://pub.dev" + source: hosted + version: "5.5.1" xdg_directories: dependency: transitive description: name: xdg_directories - sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.0.4" yaml: dependency: transitive description: name: yaml - sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" url: "https://pub.dev" source: hosted - version: "3.1.3" + version: "3.1.2" sdks: - dart: ">=3.7.0 <4.0.0" - flutter: ">=3.27.0" + dart: ">=3.4.0 <4.0.0" + flutter: ">=3.19.1" diff --git a/packages/veilid_support/example/pubspec.yaml b/packages/veilid_support/example/pubspec.yaml index b8333e6..eb1c45a 100644 --- a/packages/veilid_support/example/pubspec.yaml +++ b/packages/veilid_support/example/pubspec.yaml @@ -14,11 +14,11 @@ dependencies: path: ../ dev_dependencies: - async_tools: ^0.1.9 + async_tools: ^0.1.5 integration_test: sdk: flutter - lint_hard: ^6.0.0 - test: ^1.25.15 + lint_hard: ^4.0.0 + test: ^1.25.2 veilid_test: path: ../../../../veilid/veilid-flutter/packages/veilid_test diff --git a/packages/veilid_support/example/test/widget_test.dart b/packages/veilid_support/example/test/widget_test.dart index d5cfa06..092d222 100644 --- a/packages/veilid_support/example/test/widget_test.dart +++ b/packages/veilid_support/example/test/widget_test.dart @@ -5,12 +5,13 @@ // gestures. You can also use WidgetTester to find child widgets in the widget // tree, read text, and verify that the values of widget properties are correct. -import 'package:example/main.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:example/main.dart'; + void main() { - testWidgets('Counter increments smoke test', (tester) async { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. await tester.pumpWidget(const MyApp()); diff --git a/packages/veilid_support/lib/dht_support/proto/proto.dart b/packages/veilid_support/lib/dht_support/proto/proto.dart index ceac3d5..6b36970 100644 --- a/packages/veilid_support/lib/dht_support/proto/proto.dart +++ b/packages/veilid_support/lib/dht_support/proto/proto.dart @@ -1,6 +1,5 @@ import '../../proto/dht.pb.dart' as dhtproto; import '../../proto/proto.dart' as veilidproto; -import '../../src/dynamic_debug.dart'; import '../dht_support.dart'; export '../../proto/dht.pb.dart'; @@ -24,44 +23,3 @@ extension ProtoOwnedDHTRecordPointer on dhtproto.OwnedDHTRecordPointer { OwnedDHTRecordPointer toVeilid() => OwnedDHTRecordPointer( recordKey: recordKey.toVeilid(), owner: owner.toVeilid()); } - -void registerVeilidDHTProtoToDebug() { - dynamic toDebug(dynamic obj) { - if (obj is dhtproto.OwnedDHTRecordPointer) { - return { - r'$runtimeType': obj.runtimeType, - 'recordKey': obj.recordKey, - 'owner': obj.owner, - }; - } - if (obj is dhtproto.DHTData) { - return { - r'$runtimeType': obj.runtimeType, - 'keys': obj.keys, - 'hash': obj.hash, - 'chunk': obj.chunk, - 'size': obj.size - }; - } - if (obj is dhtproto.DHTLog) { - return { - r'$runtimeType': obj.runtimeType, - 'head': obj.head, - 'tail': obj.tail, - 'stride': obj.stride, - }; - } - if (obj is dhtproto.DHTShortArray) { - return { - r'$runtimeType': obj.runtimeType, - 'keys': obj.keys, - 'index': obj.index, - 'seqs': obj.seqs, - }; - } - - return obj; - } - - DynamicDebug.registerToDebug(toDebug); -} diff --git a/packages/veilid_support/lib/dht_support/src/dht_log/dht_log.dart b/packages/veilid_support/lib/dht_support/src/dht_log/dht_log.dart index 2687bdc..71bcfa2 100644 --- a/packages/veilid_support/lib/dht_support/src/dht_log/dht_log.dart +++ b/packages/veilid_support/lib/dht_support/src/dht_log/dht_log.dart @@ -7,7 +7,6 @@ import 'package:collection/collection.dart'; import 'package:equatable/equatable.dart'; import 'package:meta/meta.dart'; -import '../../../src/veilid_log.dart'; import '../../../veilid_support.dart'; import '../../proto/proto.dart' as proto; @@ -172,31 +171,32 @@ class DHTLog implements DHTDeleteable { /// Add a reference to this log @override - void ref() { - _openCount++; - } + Future ref() async => _mutex.protect(() async { + _openCount++; + }); /// Free all resources for the DHTLog @override - Future close() async { - if (_openCount == 0) { - throw StateError('already closed'); - } - _openCount--; - if (_openCount != 0) { - return false; - } - // - await _watchController?.close(); - _watchController = null; - await _spine.close(); - return true; - } + Future close() async => _mutex.protect(() async { + if (_openCount == 0) { + throw StateError('already closed'); + } + _openCount--; + if (_openCount != 0) { + return false; + } + await _watchController?.close(); + _watchController = null; + await _spine.close(); + return true; + }); /// Free all resources for the DHTLog and delete it from the DHT /// Will wait until the short array is closed to delete it @override - Future delete() => _spine.delete(); + Future delete() async { + await _spine.delete(); + } //////////////////////////////////////////////////////////////////////////// // Public API @@ -213,7 +213,7 @@ class DHTLog implements DHTDeleteable { /// Runs a closure allowing read-only access to the log Future operate(Future Function(DHTLogReadOperations) closure) async { if (!isOpen) { - throw StateError('log is not open'); + throw StateError('log is not open"'); } return _spine.operate((spine) async { @@ -230,7 +230,7 @@ class DHTLog implements DHTDeleteable { Future operateAppend( Future Function(DHTLogWriteOperations) closure) async { if (!isOpen) { - throw StateError('log is not open'); + throw StateError('log is not open"'); } return _spine.operateAppend((spine) async { @@ -249,7 +249,7 @@ class DHTLog implements DHTDeleteable { Future Function(DHTLogWriteOperations) closure, {Duration? timeout}) async { if (!isOpen) { - throw StateError('log is not open'); + throw StateError('log is not open"'); } return _spine.operateAppendEventual((spine) async { @@ -264,7 +264,7 @@ class DHTLog implements DHTDeleteable { void Function(DHTLogUpdate) onChanged, ) { if (!isOpen) { - throw StateError('log is not open'); + throw StateError('log is not open"'); } return _listenMutex.protect(() async { @@ -293,12 +293,11 @@ class DHTLog implements DHTDeleteable { //////////////////////////////////////////////////////////////// // Fields - // 55 subkeys * 512 segments * 36 bytes per typedkey = - // 1013760 bytes per record - // Leaves 34816 bytes for 0th subkey as head, 56 subkeys total + // 56 subkeys * 512 segments * 36 bytes per typedkey = + // 1032192 bytes per record // 512*36 = 18432 bytes per subkey - // 28160 shortarrays * 256 elements = 7208960 elements - static const spineSubkeys = 55; + // 28672 shortarrays * 256 elements = 7340032 elements + static const spineSubkeys = 56; static const segmentsPerSubkey = 512; // Internal representation refreshed from spine record @@ -306,9 +305,10 @@ class DHTLog implements DHTDeleteable { // Openable int _openCount; + final _mutex = Mutex(); // Watch mutex to ensure we keep the representation valid - final Mutex _listenMutex = Mutex(debugLockTimeout: kIsDebugMode ? 60 : null); + final Mutex _listenMutex = Mutex(); // Stream of external changes StreamController? _watchController; } diff --git a/packages/veilid_support/lib/dht_support/src/dht_log/dht_log_cubit.dart b/packages/veilid_support/lib/dht_support/src/dht_log/dht_log_cubit.dart index a7884f9..08501e6 100644 --- a/packages/veilid_support/lib/dht_support/src/dht_log/dht_log_cubit.dart +++ b/packages/veilid_support/lib/dht_support/src/dht_log/dht_log_cubit.dart @@ -31,14 +31,6 @@ class DHTLogStateData extends Equatable { @override List get props => [length, window, windowTail, windowSize, follow]; - - @override - String toString() => 'DHTLogStateData(' - 'length: $length, ' - 'windowTail: $windowTail, ' - 'windowSize: $windowSize, ' - 'follow: $follow, ' - 'window: ${DynamicDebug.toDebug(window)})'; } typedef DHTLogState = AsyncValue>; @@ -66,7 +58,6 @@ class DHTLogCubit extends Cubit> } } } on Exception catch (e, st) { - addError(e, st); emit(DHTLogBusyState(AsyncValue.error(e, st))); return; } @@ -112,34 +103,33 @@ class DHTLogCubit extends Cubit> Future _refreshInner(void Function(AsyncValue>) emit, {bool forceRefresh = false}) async { late final int length; - final windowElements = await _log.operate((reader) async { + final window = await _log.operate((reader) async { length = reader.length; - return _loadElementsFromReader(reader, _windowTail, _windowSize); + return loadElementsFromReader(reader, _windowTail, _windowSize); }); - if (windowElements == null) { + if (window == null) { setWantsRefresh(); return; } - emit(AsyncValue.data(DHTLogStateData( length: length, - window: windowElements.$2, - windowTail: windowElements.$1 + windowElements.$2.length, - windowSize: windowElements.$2.length, + window: window, + windowTail: _windowTail, + windowSize: _windowSize, follow: _follow))); setRefreshed(); } // Tail is one past the last element to load - Future<(int, IList>)?> _loadElementsFromReader( + Future>?> loadElementsFromReader( DHTLogReadOperations reader, int tail, int count, {bool forceRefresh = false}) async { final length = reader.length; + if (length == 0) { + return const IList.empty(); + } final end = ((tail - 1) % length) + 1; final start = (count < end) ? end - count : 0; - if (length == 0) { - return (start, IList>.empty()); - } // If this is writeable get the offline positions Set? offlinePositions; @@ -155,11 +145,8 @@ class DHTLogCubit extends Cubit> value: _decodeElement(x.$2), isOffline: offlinePositions?.contains(x.$1) ?? false)) .toIList(); - if (allItems == null) { - return null; - } - return (start, allItems); + return allItems; } void _update(DHTLogUpdate upd) { diff --git a/packages/veilid_support/lib/dht_support/src/dht_log/dht_log_read.dart b/packages/veilid_support/lib/dht_support/src/dht_log/dht_log_read.dart index 3ebb2b8..6281d6e 100644 --- a/packages/veilid_support/lib/dht_support/src/dht_log/dht_log_read.dart +++ b/packages/veilid_support/lib/dht_support/src/dht_log/dht_log_read.dart @@ -21,14 +21,8 @@ class _DHTLogRead implements DHTLogReadOperations { return null; } - return lookup.scope((sa) => sa.operate((read) async { - if (lookup.pos >= read.length) { - veilidLoggy.error('DHTLog shortarray read @ ${lookup.pos}' - ' >= length ${read.length}'); - return null; - } - return read.get(lookup.pos, forceRefresh: forceRefresh); - })); + return lookup.scope((sa) => + sa.operate((read) => read.get(lookup.pos, forceRefresh: forceRefresh))); } (int, int) _clampStartLen(int start, int? len) { @@ -53,54 +47,40 @@ class _DHTLogRead implements DHTLogReadOperations { final chunks = Iterable.generate(length) .slices(kMaxDHTConcurrency) - .map((chunk) => chunk.map((pos) async { - try { - return await get(pos + start, forceRefresh: forceRefresh); - // Need some way to debug ParallelWaitError - // ignore: avoid_catches_without_on_clauses - } catch (e, st) { - veilidLoggy.error('$e\n$st\n'); - rethrow; - } - })); + .map((chunk) => chunk + .map((pos) async => get(pos + start, forceRefresh: forceRefresh))); for (final chunk in chunks) { - var elems = await chunk.wait; + final elems = await chunk.wait; - // Return only the first contiguous range, anything else is garbage - // due to a representational error in the head or shortarray legnth - final nullPos = elems.indexOf(null); - if (nullPos != -1) { - elems = elems.sublist(0, nullPos); + // If any element was unavailable, return null + if (elems.contains(null)) { + return null; } - out.addAll(elems.cast()); - - if (nullPos != -1) { - break; - } } return out; } @override - Future> getOfflinePositions() async { + Future?> getOfflinePositions() async { final positionOffline = {}; // Iterate positions backward from most recent for (var pos = _spine.length - 1; pos >= 0; pos--) { - // Get position final lookup = await _spine.lookupPosition(pos); - // If position doesn't exist then it definitely wasn't written to offline if (lookup == null) { - continue; + return null; } // Check each segment for offline positions var foundOffline = false; - await lookup.scope((sa) => sa.operate((read) async { + final success = await lookup.scope((sa) => sa.operate((read) async { final segmentOffline = await read.getOfflinePositions(); + if (segmentOffline == null) { + return false; + } // For each shortarray segment go through their segment positions // in reverse order and see if they are offline @@ -114,7 +94,11 @@ class _DHTLogRead implements DHTLogReadOperations { foundOffline = true; } } + return true; })); + if (!success) { + return null; + } // If we found nothing offline in this segment then we can stop if (!foundOffline) { break; diff --git a/packages/veilid_support/lib/dht_support/src/dht_log/dht_log_spine.dart b/packages/veilid_support/lib/dht_support/src/dht_log/dht_log_spine.dart index 6323692..e9442f0 100644 --- a/packages/veilid_support/lib/dht_support/src/dht_log/dht_log_spine.dart +++ b/packages/veilid_support/lib/dht_support/src/dht_log/dht_log_spine.dart @@ -24,11 +24,13 @@ class _DHTLogPosition extends DHTCloseable { /// Add a reference to this log @override - void ref() => shortArray.ref(); + Future ref() async { + await shortArray.ref(); + } /// Free all resources for the DHTLogPosition @override - Future close() => _dhtLogSpine._segmentClosed(_segmentNumber); + Future close() async => _dhtLogSpine._segmentClosed(_segmentNumber); } class _DHTLogSegmentLookup extends Equatable { @@ -122,11 +124,18 @@ class _DHTLogSpine { }); } - // Will deep delete all segment records as they are children - Future delete() async => _spineMutex.protect(_spineRecord.delete); + Future delete() async { + await _spineMutex.protect(() async { + // Will deep delete all segment records as they are children + await _spineRecord.delete(); + }); + } Future operate(Future Function(_DHTLogSpine) closure) async => - _spineMutex.protect(() async => closure(this)); + // ignore: prefer_expression_function_bodies + _spineMutex.protect(() async { + return closure(this); + }); Future operateAppend(Future Function(_DHTLogSpine) closure) async => _spineMutex.protect(() async { @@ -138,7 +147,7 @@ class _DHTLogSpine { if (!await writeSpineHead(old: (oldHead, oldTail))) { // Failed to write head means head got overwritten so write should // be considered failed - throw const DHTExceptionOutdated(); + throw DHTExceptionOutdated(); } return out; } on Exception { @@ -248,12 +257,7 @@ class _DHTLogSpine { final headDelta = _ringDistance(newHead, oldHead); final tailDelta = _ringDistance(newTail, oldTail); if (headDelta > _positionLimit ~/ 2 || tailDelta > _positionLimit ~/ 2) { - throw DHTExceptionInvalidData('_DHTLogSpine::_updateHead ' - '_head=$_head _tail=$_tail ' - 'oldHead=$oldHead oldTail=$oldTail ' - 'newHead=$newHead newTail=$newTail ' - 'headDelta=$headDelta tailDelta=$tailDelta ' - '_positionLimit=$_positionLimit'); + throw DHTExceptionInvalidData(); } } @@ -427,7 +431,7 @@ class _DHTLogSpine { late DHTShortArray shortArray; if (openedSegment != null) { // If so, return a ref - openedSegment.ref(); + await openedSegment.ref(); shortArray = openedSegment; } else { // Otherwise open a segment @@ -449,7 +453,7 @@ class _DHTLogSpine { // LRU cache the segment number if (!_openCache.remove(segmentNumber)) { // If this is new to the cache ref it when it goes in - shortArray.ref(); + await shortArray.ref(); } _openCache.add(segmentNumber); if (_openCache.length > _openCacheSize) { @@ -608,13 +612,13 @@ class _DHTLogSpine { Future watch() async { // This will update any existing watches if necessary try { - // Update changes to the head record - // xxx: check if this localChanges can be false... - // xxx: Don't watch for local changes because this class already handles - // xxx: notifying listeners and knows when it makes local changes - _subscription ??= - await _spineRecord.listen(localChanges: false, _onSpineChanged); await _spineRecord.watch(subkeys: [ValueSubkeyRange.single(0)]); + + // Update changes to the head record + // Don't watch for local changes because this class already handles + // notifying listeners and knows when it makes local changes + _subscription ??= + await _spineRecord.listen(localChanges: true, _onSpineChanged); } on Exception { // If anything fails, try to cancel the watches await cancelWatch(); @@ -709,7 +713,7 @@ class _DHTLogSpine { DHTShortArray.maxElements; // Spine head mutex to ensure we keep the representation valid - final Mutex _spineMutex = Mutex(debugLockTimeout: kIsDebugMode ? 60 : null); + final Mutex _spineMutex = Mutex(); // Subscription to head record internal changes StreamSubscription? _subscription; // Notify closure for external spine head changes @@ -729,8 +733,7 @@ class _DHTLogSpine { // LRU cache of DHT spine elements accessed recently // Pair of position and associated shortarray segment - final Mutex _spineCacheMutex = - Mutex(debugLockTimeout: kIsDebugMode ? 60 : null); + final Mutex _spineCacheMutex = Mutex(); final List _openCache; final Map _openedSegments; static const int _openCacheSize = 3; diff --git a/packages/veilid_support/lib/dht_support/src/dht_log/dht_log_write.dart b/packages/veilid_support/lib/dht_support/src/dht_log/dht_log_write.dart index 397a1dc..1b5c09f 100644 --- a/packages/veilid_support/lib/dht_support/src/dht_log/dht_log_write.dart +++ b/packages/veilid_support/lib/dht_support/src/dht_log/dht_log_write.dart @@ -17,8 +17,7 @@ class _DHTLogWrite extends _DHTLogRead implements DHTLogWriteOperations { } final lookup = await _spine.lookupPosition(pos); if (lookup == null) { - throw DHTExceptionInvalidData( - '_DHTLogRead::tryWriteItem pos=$pos _spine.length=${_spine.length}'); + throw const DHTExceptionInvalidData(); } // Write item to the segment @@ -46,14 +45,12 @@ class _DHTLogWrite extends _DHTLogRead implements DHTLogWriteOperations { } final aLookup = await _spine.lookupPosition(aPos); if (aLookup == null) { - throw DHTExceptionInvalidData('_DHTLogWrite::swap aPos=$aPos bPos=$bPos ' - '_spine.length=${_spine.length}'); + throw const DHTExceptionInvalidData(); } final bLookup = await _spine.lookupPosition(bPos); if (bLookup == null) { await aLookup.close(); - throw DHTExceptionInvalidData('_DHTLogWrite::swap aPos=$aPos bPos=$bPos ' - '_spine.length=${_spine.length}'); + throw const DHTExceptionInvalidData(); } // Swap items in the segments @@ -68,10 +65,7 @@ class _DHTLogWrite extends _DHTLogRead implements DHTLogWriteOperations { if (bItem.value == null) { final aItem = await aWrite.get(aLookup.pos); if (aItem == null) { - throw DHTExceptionInvalidData( - '_DHTLogWrite::swap aPos=$aPos bPos=$bPos ' - 'aLookup.pos=${aLookup.pos} bLookup.pos=${bLookup.pos} ' - '_spine.length=${_spine.length}'); + throw const DHTExceptionInvalidData(); } await sb.operateWriteEventual((bWrite) async { final success = await bWrite @@ -107,7 +101,7 @@ class _DHTLogWrite extends _DHTLogRead implements DHTLogWriteOperations { await write.clear(); } else if (lookup.pos != write.length) { // We should always be appending at the length - await write.truncate(lookup.pos); + throw const DHTExceptionInvalidData(); } return write.add(value); })); @@ -123,16 +117,12 @@ class _DHTLogWrite extends _DHTLogRead implements DHTLogWriteOperations { final dws = DelayedWaitSet(); var success = true; - for (var valueIdxIter = 0; valueIdxIter < values.length;) { - final valueIdx = valueIdxIter; + for (var valueIdx = 0; valueIdx < values.length;) { final remaining = values.length - valueIdx; final lookup = await _spine.lookupPosition(insertPos + valueIdx); if (lookup == null) { - throw DHTExceptionInvalidData('_DHTLogWrite::addAll ' - '_spine.length=${_spine.length}' - 'insertPos=$insertPos valueIdx=$valueIdx ' - 'values.length=${values.length} '); + throw const DHTExceptionInvalidData(); } final sacount = min(remaining, DHTShortArray.maxElements - lookup.pos); @@ -147,21 +137,16 @@ class _DHTLogWrite extends _DHTLogRead implements DHTLogWriteOperations { await write.clear(); } else if (lookup.pos != write.length) { // We should always be appending at the length - await write.truncate(lookup.pos); + throw const DHTExceptionInvalidData(); } - await write.addAll(sublistValues); - success = true; + return write.addAll(sublistValues); })); } on DHTExceptionOutdated { success = false; - // Need some way to debug ParallelWaitError - // ignore: avoid_catches_without_on_clauses - } catch (e, st) { - veilidLoggy.error('$e\n$st\n'); } }); - valueIdxIter += sacount; + valueIdx += sacount; } await dws(); diff --git a/packages/veilid_support/lib/dht_support/src/dht_record/barrel.dart b/packages/veilid_support/lib/dht_support/src/dht_record/barrel.dart index 2d7e677..06933be 100644 --- a/packages/veilid_support/lib/dht_support/src/dht_record/barrel.dart +++ b/packages/veilid_support/lib/dht_support/src/dht_record/barrel.dart @@ -1,4 +1,3 @@ export 'default_dht_record_cubit.dart'; export 'dht_record_cubit.dart'; export 'dht_record_pool.dart'; -export 'stats.dart'; diff --git a/packages/veilid_support/lib/dht_support/src/dht_record/default_dht_record_cubit.dart b/packages/veilid_support/lib/dht_support/src/dht_record/default_dht_record_cubit.dart index 3d396d2..5ea6761 100644 --- a/packages/veilid_support/lib/dht_support/src/dht_record/default_dht_record_cubit.dart +++ b/packages/veilid_support/lib/dht_support/src/dht_record/default_dht_record_cubit.dart @@ -6,14 +6,14 @@ import '../../../veilid_support.dart'; class DefaultDHTRecordCubit extends DHTRecordCubit { DefaultDHTRecordCubit({ required super.open, - required T Function(Uint8List data) decodeState, + required T Function(List data) decodeState, }) : super( initialStateFunction: _makeInitialStateFunction(decodeState), stateFunction: _makeStateFunction(decodeState), watchFunction: _makeWatchFunction()); static InitialStateFunction _makeInitialStateFunction( - T Function(Uint8List data) decodeState) => + T Function(List data) decodeState) => (record) async { final initialData = await record.get(); if (initialData == null) { @@ -23,7 +23,7 @@ class DefaultDHTRecordCubit extends DHTRecordCubit { }; static StateFunction _makeStateFunction( - T Function(Uint8List data) decodeState) => + T Function(List data) decodeState) => (record, subkeys, updatedata) async { final defaultSubkey = record.subkeyOrDefault(-1); if (subkeys.containsSubkey(defaultSubkey)) { @@ -52,11 +52,8 @@ class DefaultDHTRecordCubit extends DHTRecordCubit { Future refreshDefault() async { await initWait(); - final rec = record; - if (rec != null) { - final defaultSubkey = rec.subkeyOrDefault(-1); - await refresh( - [ValueSubkeyRange(low: defaultSubkey, high: defaultSubkey)]); - } + + final defaultSubkey = record.subkeyOrDefault(-1); + await refresh([ValueSubkeyRange(low: defaultSubkey, high: defaultSubkey)]); } } diff --git a/packages/veilid_support/lib/dht_support/src/dht_record/dht_record.dart b/packages/veilid_support/lib/dht_support/src/dht_record/dht_record.dart index b9218e4..a0994bf 100644 --- a/packages/veilid_support/lib/dht_support/src/dht_record/dht_record.dart +++ b/packages/veilid_support/lib/dht_support/src/dht_record/dht_record.dart @@ -64,35 +64,34 @@ class DHTRecord implements DHTDeleteable { /// Add a reference to this DHTRecord @override - void ref() { - _openCount++; - } + Future ref() async => _mutex.protect(() async { + _openCount++; + }); /// Free all resources for the DHTRecord @override - Future close() async { - if (_openCount == 0) { - throw StateError('already closed'); - } - _openCount--; - if (_openCount != 0) { - return false; - } + Future close() async => _mutex.protect(() async { + if (_openCount == 0) { + throw StateError('already closed'); + } + _openCount--; + if (_openCount != 0) { + return false; + } - await _watchController?.close(); - _watchController = null; - await serialFutureClose((this, _sfListen)); - - await DHTRecordPool.instance._recordClosed(this); - - return true; - } + await serialFuturePause((this, _sfListen)); + await _watchController?.close(); + _watchController = null; + await DHTRecordPool.instance._recordClosed(this); + return true; + }); /// Free all resources for the DHTRecord and delete it from the DHT - /// Returns true if the deletion was processed immediately - /// Returns false if the deletion was marked for later + /// Will wait until the record is closed to delete it @override - Future delete() async => DHTRecordPool.instance.deleteRecord(key); + Future delete() async => _mutex.protect(() async { + await DHTRecordPool.instance.deleteRecord(key); + }); //////////////////////////////////////////////////////////////////////////// // Public API @@ -119,56 +118,55 @@ class DHTRecord implements DHTDeleteable { /// * 'outSeqNum' optionally returns the sequence number of the value being /// returned if one was returned. Future get( - {int subkey = -1, - VeilidCrypto? crypto, - DHTRecordRefreshMode refreshMode = DHTRecordRefreshMode.cached, - Output? outSeqNum}) async => - _wrapStats('get', () async { - subkey = subkeyOrDefault(subkey); + {int subkey = -1, + VeilidCrypto? crypto, + DHTRecordRefreshMode refreshMode = DHTRecordRefreshMode.cached, + Output? outSeqNum}) async { + subkey = subkeyOrDefault(subkey); - // Get the last sequence number if we need it - final lastSeq = - refreshMode._inspectLocal ? await _localSubkeySeq(subkey) : null; + // Get the last sequence number if we need it + final lastSeq = + refreshMode._inspectLocal ? await _localSubkeySeq(subkey) : null; - // See if we only ever want the locally stored value - if (refreshMode == DHTRecordRefreshMode.local && lastSeq == null) { - // If it's not available locally already just return null now - return null; - } + // See if we only ever want the locally stored value + if (refreshMode == DHTRecordRefreshMode.local && lastSeq == null) { + // If it's not available locally already just return null now + return null; + } - var retry = kDHTTryAgainTries; - ValueData? valueData; - while (true) { - try { - valueData = await _routingContext.getDHTValue(key, subkey, - forceRefresh: refreshMode._forceRefresh); - break; - } on VeilidAPIExceptionTryAgain { - retry--; - if (retry == 0) { - throw const DHTExceptionNotAvailable(); - } - await asyncSleep(); - } - } - if (valueData == null) { - return null; + var retry = kDHTTryAgainTries; + ValueData? valueData; + while (true) { + try { + valueData = await _routingContext.getDHTValue(key, subkey, + forceRefresh: refreshMode._forceRefresh); + break; + } on VeilidAPIExceptionTryAgain { + retry--; + if (retry == 0) { + throw const DHTExceptionNotAvailable(); } + await asyncSleep(); + } + } + if (valueData == null) { + return null; + } - // See if this get resulted in a newer sequence number - if (refreshMode == DHTRecordRefreshMode.update && - lastSeq != null && - valueData.seq <= lastSeq) { - // If we're only returning updates then punt now - return null; - } - // If we're returning a value, decrypt it - final out = (crypto ?? _crypto).decrypt(valueData.data); - if (outSeqNum != null) { - outSeqNum.save(valueData.seq); - } - return out; - }); + // See if this get resulted in a newer sequence number + if (refreshMode == DHTRecordRefreshMode.update && + lastSeq != null && + valueData.seq <= lastSeq) { + // If we're only returning updates then punt now + return null; + } + // If we're returning a value, decrypt it + final out = (crypto ?? _crypto).decrypt(valueData.data); + if (outSeqNum != null) { + outSeqNum.save(valueData.seq); + } + return out; + } /// Get a subkey value from this record. /// Process the record returned with a JSON unmarshal function 'fromJson'. @@ -224,102 +222,97 @@ class DHTRecord implements DHTDeleteable { /// If a newer value was found on the network, it is returned /// If the value was succesfully written, null is returned Future tryWriteBytes(Uint8List newValue, - {int subkey = -1, - VeilidCrypto? crypto, - KeyPair? writer, - Output? outSeqNum}) async => - _wrapStats('tryWriteBytes', () async { - subkey = subkeyOrDefault(subkey); - final lastSeq = await _localSubkeySeq(subkey); - final encryptedNewValue = await (crypto ?? _crypto).encrypt(newValue); + {int subkey = -1, + VeilidCrypto? crypto, + KeyPair? writer, + Output? outSeqNum}) async { + subkey = subkeyOrDefault(subkey); + final lastSeq = await _localSubkeySeq(subkey); + final encryptedNewValue = await (crypto ?? _crypto).encrypt(newValue); - // Set the new data if possible - var newValueData = await _routingContext.setDHTValue( - key, subkey, encryptedNewValue, - writer: writer ?? _writer); - if (newValueData == null) { - // A newer value wasn't found on the set, but - // we may get a newer value when getting the value for the sequence number - newValueData = await _routingContext.getDHTValue(key, subkey); - if (newValueData == null) { - assert(newValueData != null, "can't get value that was just set"); - return null; - } - } + // Set the new data if possible + var newValueData = await _routingContext + .setDHTValue(key, subkey, encryptedNewValue, writer: writer ?? _writer); + if (newValueData == null) { + // A newer value wasn't found on the set, but + // we may get a newer value when getting the value for the sequence number + newValueData = await _routingContext.getDHTValue(key, subkey); + if (newValueData == null) { + assert(newValueData != null, "can't get value that was just set"); + return null; + } + } - // Record new sequence number - final isUpdated = newValueData.seq != lastSeq; - if (isUpdated && outSeqNum != null) { - outSeqNum.save(newValueData.seq); - } + // Record new sequence number + final isUpdated = newValueData.seq != lastSeq; + if (isUpdated && outSeqNum != null) { + outSeqNum.save(newValueData.seq); + } - // See if the encrypted data returned is exactly the same - // if so, shortcut and don't bother decrypting it - if (newValueData.data.equals(encryptedNewValue)) { - if (isUpdated) { - DHTRecordPool.instance - ._processLocalValueChange(key, newValue, subkey); - } - return null; - } + // See if the encrypted data returned is exactly the same + // if so, shortcut and don't bother decrypting it + if (newValueData.data.equals(encryptedNewValue)) { + if (isUpdated) { + DHTRecordPool.instance._processLocalValueChange(key, newValue, subkey); + } + return null; + } - // Decrypt value to return it - final decryptedNewValue = - await (crypto ?? _crypto).decrypt(newValueData.data); - if (isUpdated) { - DHTRecordPool.instance - ._processLocalValueChange(key, decryptedNewValue, subkey); - } - return decryptedNewValue; - }); + // Decrypt value to return it + final decryptedNewValue = + await (crypto ?? _crypto).decrypt(newValueData.data); + if (isUpdated) { + DHTRecordPool.instance + ._processLocalValueChange(key, decryptedNewValue, subkey); + } + return decryptedNewValue; + } /// Attempt to write a byte buffer to a DHTRecord subkey /// If a newer value was found on the network, another attempt /// will be made to write the subkey until this succeeds Future eventualWriteBytes(Uint8List newValue, - {int subkey = -1, - VeilidCrypto? crypto, - KeyPair? writer, - Output? outSeqNum}) async => - _wrapStats('eventualWriteBytes', () async { - subkey = subkeyOrDefault(subkey); - final lastSeq = await _localSubkeySeq(subkey); - final encryptedNewValue = await (crypto ?? _crypto).encrypt(newValue); + {int subkey = -1, + VeilidCrypto? crypto, + KeyPair? writer, + Output? outSeqNum}) async { + subkey = subkeyOrDefault(subkey); + final lastSeq = await _localSubkeySeq(subkey); + final encryptedNewValue = await (crypto ?? _crypto).encrypt(newValue); - ValueData? newValueData; - do { - do { - // Set the new data - newValueData = await _routingContext.setDHTValue( - key, subkey, encryptedNewValue, - writer: writer ?? _writer); + ValueData? newValueData; + do { + do { + // Set the new data + newValueData = await _routingContext.setDHTValue( + key, subkey, encryptedNewValue, + writer: writer ?? _writer); - // Repeat if newer data on the network was found - } while (newValueData != null); + // Repeat if newer data on the network was found + } while (newValueData != null); - // Get the data to check its sequence number - newValueData = await _routingContext.getDHTValue(key, subkey); - if (newValueData == null) { - assert(newValueData != null, "can't get value that was just set"); - return; - } + // Get the data to check its sequence number + newValueData = await _routingContext.getDHTValue(key, subkey); + if (newValueData == null) { + assert(newValueData != null, "can't get value that was just set"); + return; + } - // Record new sequence number - if (outSeqNum != null) { - outSeqNum.save(newValueData.seq); - } + // Record new sequence number + if (outSeqNum != null) { + outSeqNum.save(newValueData.seq); + } - // The encrypted data returned should be exactly the same - // as what we are trying to set, - // otherwise we still need to keep trying to set the value - } while (!newValueData.data.equals(encryptedNewValue)); + // The encrypted data returned should be exactly the same + // as what we are trying to set, + // otherwise we still need to keep trying to set the value + } while (!newValueData.data.equals(encryptedNewValue)); - final isUpdated = newValueData.seq != lastSeq; - if (isUpdated) { - DHTRecordPool.instance - ._processLocalValueChange(key, newValue, subkey); - } - }); + final isUpdated = newValueData.seq != lastSeq; + if (isUpdated) { + DHTRecordPool.instance._processLocalValueChange(key, newValue, subkey); + } + } /// Attempt to write a byte buffer to a DHTRecord subkey /// If a newer value was found on the network, another attempt @@ -327,36 +320,32 @@ class DHTRecord implements DHTDeleteable { /// Each attempt to write the value calls an update function with the /// old value to determine what new value should be attempted for that write. Future eventualUpdateBytes( - Future Function(Uint8List? oldValue) update, - {int subkey = -1, - VeilidCrypto? crypto, - KeyPair? writer, - Output? outSeqNum}) async => - _wrapStats('eventualUpdateBytes', () async { - subkey = subkeyOrDefault(subkey); + Future Function(Uint8List? oldValue) update, + {int subkey = -1, + VeilidCrypto? crypto, + KeyPair? writer, + Output? outSeqNum}) async { + subkey = subkeyOrDefault(subkey); - // Get the existing data, do not allow force refresh here - // because if we need a refresh the setDHTValue will fail anyway - var oldValue = - await get(subkey: subkey, crypto: crypto, outSeqNum: outSeqNum); + // Get the existing data, do not allow force refresh here + // because if we need a refresh the setDHTValue will fail anyway + var oldValue = + await get(subkey: subkey, crypto: crypto, outSeqNum: outSeqNum); - do { - // Update the data - final updatedValue = await update(oldValue); - if (updatedValue == null) { - // If null is returned from the update, stop trying to do the update - break; - } - // Try to write it back to the network - oldValue = await tryWriteBytes(updatedValue, - subkey: subkey, - crypto: crypto, - writer: writer, - outSeqNum: outSeqNum); + do { + // Update the data + final updatedValue = await update(oldValue); + if (updatedValue == null) { + // If null is returned from the update, stop trying to do the update + break; + } + // Try to write it back to the network + oldValue = await tryWriteBytes(updatedValue, + subkey: subkey, crypto: crypto, writer: writer, outSeqNum: outSeqNum); - // Repeat update if newer data on the network was found - } while (oldValue != null); - }); + // Repeat update if newer data on the network was found + } while (oldValue != null); + } /// Like 'tryWriteBytes' but with JSON marshal/unmarshal of the value Future tryWriteJson(T Function(dynamic) fromJson, T newValue, @@ -521,7 +510,7 @@ class DHTRecord implements DHTDeleteable { key, subkeys: [ValueSubkeyRange.single(subkey)], ); - return rr.localSeqs.firstOrNull; + return rr.localSeqs.firstOrNull ?? 0xFFFFFFFF; } void _addValueChange( @@ -565,9 +554,6 @@ class DHTRecord implements DHTDeleteable { local: false, data: update.value?.data, subkeys: update.subkeys); } - Future _wrapStats(String func, Future Function() closure) => - DHTRecordPool.instance._stats.measure(key, debugName, func, closure); - ////////////////////////////////////////////////////////////// final _SharedDHTRecordData _sharedDHTRecordData; @@ -576,6 +562,7 @@ class DHTRecord implements DHTDeleteable { final KeyPair? _writer; final VeilidCrypto _crypto; final String debugName; + final _mutex = Mutex(); int _openCount; StreamController? _watchController; _WatchState? _watchState; diff --git a/packages/veilid_support/lib/dht_support/src/dht_record/dht_record_cubit.dart b/packages/veilid_support/lib/dht_support/src/dht_record/dht_record_cubit.dart index eb56f7c..ac33716 100644 --- a/packages/veilid_support/lib/dht_support/src/dht_record/dht_record_cubit.dart +++ b/packages/veilid_support/lib/dht_support/src/dht_record/dht_record_cubit.dart @@ -26,7 +26,7 @@ abstract class DHTRecordCubit extends Cubit> { // Do record open/create while (!cancel.isCompleted) { try { - record = await open(); + _record = await open(); _wantsCloseRecord = true; break; } on DHTExceptionNotAvailable { @@ -35,7 +35,6 @@ abstract class DHTRecordCubit extends Cubit> { } } } on Exception catch (e, st) { - addError(e, st); emit(AsyncValue.error(e, st)); return; } @@ -50,47 +49,41 @@ abstract class DHTRecordCubit extends Cubit> { ) async { // Make initial state update try { - final initialState = await initialStateFunction(record!); + final initialState = await initialStateFunction(_record); if (initialState != null) { emit(AsyncValue.data(initialState)); } - } on Exception catch (e, st) { - addError(e, st); - emit(AsyncValue.error(e, st)); + } on Exception catch (e) { + emit(AsyncValue.error(e)); } - _subscription = await record!.listen((record, data, subkeys) async { + _subscription = await _record.listen((record, data, subkeys) async { try { final newState = await stateFunction(record, subkeys, data); if (newState != null) { emit(AsyncValue.data(newState)); } - } on Exception catch (e, st) { - addError(e, st); - emit(AsyncValue.error(e, st)); + } on Exception catch (e) { + emit(AsyncValue.error(e)); } }); - await watchFunction(record!); + await watchFunction(_record); } @override Future close() async { await initWait(cancelValue: true); - await record?.cancelWatch(); + await _record.cancelWatch(); await _subscription?.cancel(); _subscription = null; if (_wantsCloseRecord) { - await record?.close(); + await _record.close(); _wantsCloseRecord = false; } await super.close(); } - Future ready() async { - await initWait(); - } - Future refresh(List subkeys) async { await initWait(); @@ -98,10 +91,10 @@ abstract class DHTRecordCubit extends Cubit> { for (final skr in subkeys) { for (var sk = skr.low; sk <= skr.high; sk++) { - final data = await record! - .get(subkey: sk, refreshMode: DHTRecordRefreshMode.update); + final data = await _record.get( + subkey: sk, refreshMode: DHTRecordRefreshMode.update); if (data != null) { - final newState = await _stateFunction(record!, updateSubkeys, data); + final newState = await _stateFunction(_record, updateSubkeys, data); if (newState != null) { // Emit the new state emit(AsyncValue.data(newState)); @@ -115,11 +108,13 @@ abstract class DHTRecordCubit extends Cubit> { } } + DHTRecord get record => _record; + @protected final WaitSet initWait = WaitSet(); StreamSubscription? _subscription; - DHTRecord? record; + late DHTRecord _record; bool _wantsCloseRecord; final StateFunction _stateFunction; } diff --git a/packages/veilid_support/lib/dht_support/src/dht_record/dht_record_pool.dart b/packages/veilid_support/lib/dht_support/src/dht_record/dht_record_pool.dart index 6dc0634..68ea53d 100644 --- a/packages/veilid_support/lib/dht_support/src/dht_record/dht_record_pool.dart +++ b/packages/veilid_support/lib/dht_support/src/dht_record/dht_record_pool.dart @@ -9,7 +9,6 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:protobuf/protobuf.dart'; import '../../../../veilid_support.dart'; -import 'extensions.dart'; export 'package:fast_immutable_collections/fast_immutable_collections.dart' show Output; @@ -33,7 +32,7 @@ typedef DHTRecordPoolLogger = void Function(String message); /// Record pool that managed DHTRecords and allows for tagged deletion /// String versions of keys due to IMap<> json unsupported in key @freezed -sealed class DHTRecordPoolAllocations with _$DHTRecordPoolAllocations { +class DHTRecordPoolAllocations with _$DHTRecordPoolAllocations { const factory DHTRecordPoolAllocations({ @Default(IMapConst>({})) IMap> childrenByParent, @@ -50,7 +49,7 @@ sealed class DHTRecordPoolAllocations with _$DHTRecordPoolAllocations { /// Pointer to an owned record, with key, owner key and owner secret /// Ensure that these are only serialized encrypted @freezed -sealed class OwnedDHTRecordPointer with _$OwnedDHTRecordPointer { +class OwnedDHTRecordPointer with _$OwnedDHTRecordPointer { const factory OwnedDHTRecordPointer({ required TypedKey recordKey, required KeyPair owner, @@ -66,7 +65,7 @@ sealed class OwnedDHTRecordPointer with _$OwnedDHTRecordPointer { class DHTRecordPool with TableDBBackedJson { DHTRecordPool._(Veilid veilid, VeilidRoutingContext routingContext) : _state = const DHTRecordPoolAllocations(), - _mutex = Mutex(debugLockTimeout: kIsDebugMode ? 60 : null), + _mutex = Mutex(), _recordTagLock = AsyncTagLock(), _opened = {}, _markedForDelete = {}, @@ -208,8 +207,10 @@ class DHTRecordPool with TableDBBackedJson { ); /// Get the parent of a DHTRecord key if it exists - Future getParentRecordKey(TypedKey child) => - _mutex.protect(() async => _getParentRecordKeyInner(child)); + TypedKey? getParentRecordKey(TypedKey child) { + final childJson = child.toJson(); + return _state.parentByChild[childJson]; + } /// Check if record is allocated Future isValidRecordKey(TypedKey key) => @@ -246,13 +247,11 @@ class DHTRecordPool with TableDBBackedJson { /// Print children String debugChildren(TypedKey recordKey, {List? allDeps}) { allDeps ??= _collectChildrenInner(recordKey); - // Debugging // ignore: avoid_print var out = 'Parent: $recordKey (${_state.debugNames[recordKey.toString()]})\n'; for (final dep in allDeps) { if (dep != recordKey) { - // Debugging // ignore: avoid_print out += ' Child: $dep (${_state.debugNames[dep.toString()]})\n'; } @@ -272,6 +271,31 @@ class DHTRecordPool with TableDBBackedJson { break; } } + } else { + final now = Veilid.instance.now().value; + // Expired, process renewal if desired + for (final entry in _opened.entries) { + final openedKey = entry.key; + final openedRecordInfo = entry.value; + + if (openedKey == updateValueChange.key) { + // Renew watch state for each opened record + for (final rec in openedRecordInfo.records) { + // See if the watch had an expiration and if it has expired + // otherwise the renewal will keep the same parameters + final watchState = rec._watchState; + if (watchState != null) { + final exp = watchState.expiration; + if (exp != null && exp.value < now) { + // Has expiration, and it has expired, clear watch state + rec._watchState = null; + } + } + } + openedRecordInfo.shared.needsWatchStateUpdate = true; + break; + } + } } } @@ -302,11 +326,6 @@ class DHTRecordPool with TableDBBackedJson { } } - /// Log the performance stats - void debugPrintStats() { - log('DHTRecordPool Stats:\n${_stats.debugString()}'); - } - /// Public interface to DHTRecordPool logger void log(String message) { _logger?.call(message); @@ -356,151 +375,140 @@ class DHTRecordPool with TableDBBackedJson { } Future _recordOpenCommon( - {required String debugName, - required VeilidRoutingContext dhtctx, - required TypedKey recordKey, - required VeilidCrypto crypto, - required KeyPair? writer, - required TypedKey? parent, - required int defaultSubkey}) async => - _stats.measure(recordKey, debugName, '_recordOpenCommon', () async { - log('openDHTRecord: debugName=$debugName key=$recordKey'); + {required String debugName, + required VeilidRoutingContext dhtctx, + required TypedKey recordKey, + required VeilidCrypto crypto, + required KeyPair? writer, + required TypedKey? parent, + required int defaultSubkey}) async { + log('openDHTRecord: debugName=$debugName key=$recordKey'); - // See if this has been opened yet - final openedRecordInfo = await _mutex.protect(() async { - // If we are opening a key that already exists - // make sure we are using the same parent if one was specified - _validateParentInner(parent, recordKey); + // See if this has been opened yet + final openedRecordInfo = await _mutex.protect(() async { + // If we are opening a key that already exists + // make sure we are using the same parent if one was specified + _validateParentInner(parent, recordKey); - return _opened[recordKey]; - }); + return _opened[recordKey]; + }); - if (openedRecordInfo == null) { - // Fresh open, just open the record - var retry = kDHTKeyNotFoundTries; - late final DHTRecordDescriptor recordDescriptor; - while (true) { - try { - recordDescriptor = - await dhtctx.openDHTRecord(recordKey, writer: writer); - break; - } on VeilidAPIExceptionTryAgain { - throw const DHTExceptionNotAvailable(); - } on VeilidAPIExceptionKeyNotFound { - await asyncSleep(); - retry--; - if (retry == 0) { - throw const DHTExceptionNotAvailable(); - } - } + if (openedRecordInfo == null) { + // Fresh open, just open the record + var retry = kDHTKeyNotFoundTries; + late final DHTRecordDescriptor recordDescriptor; + while (true) { + try { + recordDescriptor = + await dhtctx.openDHTRecord(recordKey, writer: writer); + break; + } on VeilidAPIExceptionKeyNotFound { + await asyncSleep(); + retry--; + if (retry == 0) { + throw DHTExceptionNotAvailable(); } - - final newOpenedRecordInfo = _OpenedRecordInfo( - recordDescriptor: recordDescriptor, - defaultWriter: writer, - defaultRoutingContext: dhtctx); - - final rec = DHTRecord._( - debugName: debugName, - routingContext: dhtctx, - defaultSubkey: defaultSubkey, - sharedDHTRecordData: newOpenedRecordInfo.shared, - writer: writer, - crypto: crypto); - - await _mutex.protect(() async { - // Register the opened record - _opened[recordDescriptor.key] = newOpenedRecordInfo; - - // Register the dependency - await _addDependencyInner( - parent, - recordKey, - debugName: debugName, - ); - - // Register the newly opened record - newOpenedRecordInfo.records.add(rec); - }); - - return rec; } + } - // Already opened + final newOpenedRecordInfo = _OpenedRecordInfo( + recordDescriptor: recordDescriptor, + defaultWriter: writer, + defaultRoutingContext: dhtctx); - // See if we need to reopen the record with a default writer and possibly - // a different routing context - if (writer != null && openedRecordInfo.shared.defaultWriter == null) { - await dhtctx.openDHTRecord(recordKey, writer: writer); - // New writer if we didn't specify one before - openedRecordInfo.shared.defaultWriter = writer; - // New default routing context if we opened it again - openedRecordInfo.shared.defaultRoutingContext = dhtctx; - } + final rec = DHTRecord._( + debugName: debugName, + routingContext: dhtctx, + defaultSubkey: defaultSubkey, + sharedDHTRecordData: newOpenedRecordInfo.shared, + writer: writer, + crypto: crypto); - final rec = DHTRecord._( - debugName: debugName, - routingContext: dhtctx, - defaultSubkey: defaultSubkey, - sharedDHTRecordData: openedRecordInfo.shared, - writer: writer, - crypto: crypto); + await _mutex.protect(() async { + // Register the opened record + _opened[recordDescriptor.key] = newOpenedRecordInfo; - await _mutex.protect(() async { - // Register the dependency - await _addDependencyInner( - parent, - recordKey, - debugName: debugName, - ); + // Register the dependency + await _addDependencyInner( + parent, + recordKey, + debugName: debugName, + ); - openedRecordInfo.records.add(rec); - }); - - return rec; + // Register the newly opened record + newOpenedRecordInfo.records.add(rec); }); + return rec; + } + + // Already opened + + // See if we need to reopen the record with a default writer and possibly + // a different routing context + if (writer != null && openedRecordInfo.shared.defaultWriter == null) { + await dhtctx.openDHTRecord(recordKey, writer: writer); + // New writer if we didn't specify one before + openedRecordInfo.shared.defaultWriter = writer; + // New default routing context if we opened it again + openedRecordInfo.shared.defaultRoutingContext = dhtctx; + } + + final rec = DHTRecord._( + debugName: debugName, + routingContext: dhtctx, + defaultSubkey: defaultSubkey, + sharedDHTRecordData: openedRecordInfo.shared, + writer: writer, + crypto: crypto); + + await _mutex.protect(() async { + // Register the dependency + await _addDependencyInner( + parent, + recordKey, + debugName: debugName, + ); + + openedRecordInfo.records.add(rec); + }); + + return rec; + } + // Called when a DHTRecord is closed // Cleans up the opened record housekeeping and processes any late deletions Future _recordClosed(DHTRecord record) async { - final key = record.key; - await _recordTagLock.protect(key, closure: () async { - await _mutex.protect(() async { - log('closeDHTRecord: debugName=${record.debugName} key=$key'); + await _recordTagLock.protect(record.key, + closure: () => _mutex.protect(() async { + final key = record.key; - final openedRecordInfo = _opened[key]; - if (openedRecordInfo == null || - !openedRecordInfo.records.remove(record)) { - throw StateError('record already closed'); - } - if (openedRecordInfo.records.isNotEmpty) { - return; - } - _opened.remove(key); - await _routingContext.closeDHTRecord(key); - await _checkForLateDeletesInner(key); - }); + log('closeDHTRecord: debugName=${record.debugName} key=$key'); - // This happens after the mutex is released - // because the record has already been removed from _opened - // which means that updates to the state processor won't happen - await _watchStateProcessors.remove(key); - }); + final openedRecordInfo = _opened[key]; + if (openedRecordInfo == null || + !openedRecordInfo.records.remove(record)) { + throw StateError('record already closed'); + } + if (openedRecordInfo.records.isEmpty) { + await _watchStateProcessors.remove(key); + await _routingContext.closeDHTRecord(key); + _opened.remove(key); + + await _checkForLateDeletesInner(key); + } + })); } // Check to see if this key can finally be deleted // If any parents are marked for deletion, try them first Future _checkForLateDeletesInner(TypedKey key) async { - if (!_mutex.isLocked) { - throw StateError('should be locked here'); - } - // Get parent list in bottom up order including our own key final parents = []; TypedKey? nextParent = key; while (nextParent != null) { parents.add(nextParent); - nextParent = _getParentRecordKeyInner(nextParent); + nextParent = getParentRecordKey(nextParent); } // If any parent is ready to delete all its children do it @@ -537,10 +545,6 @@ class DHTRecordPool with TableDBBackedJson { // Actual delete function Future _finalizeDeleteRecordInner(TypedKey recordKey) async { - if (!_mutex.isLocked) { - throw StateError('should be locked here'); - } - log('_finalizeDeleteRecordInner: key=$recordKey'); // Remove this child from parents @@ -551,10 +555,6 @@ class DHTRecordPool with TableDBBackedJson { // Deep delete mechanism inside mutex Future _deleteRecordInner(TypedKey recordKey) async { - if (!_mutex.isLocked) { - throw StateError('should be locked here'); - } - final toDelete = _readyForDeleteInner(recordKey); if (toDelete.isNotEmpty) { // delete now @@ -654,20 +654,7 @@ class DHTRecordPool with TableDBBackedJson { } } - TypedKey? _getParentRecordKeyInner(TypedKey child) { - if (!_mutex.isLocked) { - throw StateError('should be locked here'); - } - - final childJson = child.toJson(); - return _state.parentByChild[childJson]; - } - bool _isValidRecordKeyInner(TypedKey key) { - if (!_mutex.isLocked) { - throw StateError('should be locked here'); - } - if (_state.rootRecords.contains(key)) { return true; } @@ -678,10 +665,6 @@ class DHTRecordPool with TableDBBackedJson { } bool _isDeletedRecordKeyInner(TypedKey key) { - if (!_mutex.isLocked) { - throw StateError('should be locked here'); - } - // Is this key gone? if (!_isValidRecordKeyInner(key)) { return true; @@ -694,7 +677,7 @@ class DHTRecordPool with TableDBBackedJson { if (_markedForDelete.contains(nextParent)) { return true; } - nextParent = _getParentRecordKeyInner(nextParent); + nextParent = getParentRecordKey(nextParent); } return false; @@ -718,6 +701,7 @@ class DHTRecordPool with TableDBBackedJson { int? totalCount; Timestamp? maxExpiration; List? allSubkeys; + Timestamp? earliestRenewalTime; var noExpiration = false; var everySubkey = false; @@ -750,6 +734,15 @@ class DHTRecordPool with TableDBBackedJson { } else { everySubkey = true; } + final wsRenewalTime = ws.renewalTime; + if (wsRenewalTime != null) { + earliestRenewalTime = earliestRenewalTime == null + ? wsRenewalTime + : Timestamp( + value: (wsRenewalTime.value < earliestRenewalTime.value + ? wsRenewalTime.value + : earliestRenewalTime.value)); + } } } if (noExpiration) { @@ -763,10 +756,25 @@ class DHTRecordPool with TableDBBackedJson { } return _WatchState( - subkeys: allSubkeys, - expiration: maxExpiration, - count: totalCount, - ); + subkeys: allSubkeys, + expiration: maxExpiration, + count: totalCount, + renewalTime: earliestRenewalTime); + } + + static void _updateWatchRealExpirations(Iterable records, + Timestamp realExpiration, Timestamp renewalTime) { + for (final rec in records) { + final ws = rec._watchState; + if (ws != null) { + rec._watchState = _WatchState( + subkeys: ws.subkeys, + expiration: ws.expiration, + count: ws.count, + realExpiration: realExpiration, + renewalTime: renewalTime); + } + } } Future _watchStateChange( @@ -791,23 +799,17 @@ class DHTRecordPool with TableDBBackedJson { // Only try this once, if it doesn't succeed then it can just expire // on its own. try { - final stillActive = await dhtctx.cancelDHTWatch(openedRecordKey); + final cancelled = await dhtctx.cancelDHTWatch(openedRecordKey); - log('cancelDHTWatch: key=$openedRecordKey, stillActive=$stillActive, ' + log('cancelDHTWatch: key=$openedRecordKey, cancelled=$cancelled, ' 'debugNames=${openedRecordInfo.debugNames}'); openedRecordInfo.shared.unionWatchState = null; openedRecordInfo.shared.needsWatchStateUpdate = false; - } on VeilidAPIExceptionTimeout { - log('Timeout in watch cancel for key=$openedRecordKey'); } on VeilidAPIException catch (e) { // Failed to cancel DHT watch, try again next tick - log('VeilidAPIException in watch cancel for key=$openedRecordKey: $e'); - } catch (e) { - log('Unhandled exception in watch cancel for key=$openedRecordKey: $e'); - rethrow; + log('Exception in watch cancel: $e'); } - return; } @@ -816,104 +818,117 @@ class DHTRecordPool with TableDBBackedJson { final subkeys = unionWatchState.subkeys?.toList(); final count = unionWatchState.count; final expiration = unionWatchState.expiration; + final now = veilid.now(); - final active = await dhtctx.watchDHTValues(openedRecordKey, + final realExpiration = await dhtctx.watchDHTValues(openedRecordKey, subkeys: unionWatchState.subkeys?.toList(), count: unionWatchState.count, - expiration: unionWatchState.expiration); + expiration: unionWatchState.expiration ?? + (_defaultWatchDurationSecs == null + ? null + : veilid.now().offset(TimestampDuration.fromMillis( + _defaultWatchDurationSecs! * 1000)))); - log('watchDHTValues(active=$active): ' - 'key=$openedRecordKey, subkeys=$subkeys, ' + final expirationDuration = realExpiration.diff(now); + final renewalTime = now.offset(TimestampDuration( + value: expirationDuration.value * + BigInt.from(_watchRenewalNumerator) ~/ + BigInt.from(_watchRenewalDenominator))); + + log('watchDHTValues: key=$openedRecordKey, subkeys=$subkeys, ' 'count=$count, expiration=$expiration, ' + 'realExpiration=$realExpiration, ' + 'renewalTime=$renewalTime, ' 'debugNames=${openedRecordInfo.debugNames}'); // Update watch states with real expiration - if (active) { + if (realExpiration.value != BigInt.zero) { openedRecordInfo.shared.unionWatchState = unionWatchState; + _updateWatchRealExpirations( + openedRecordInfo.records, realExpiration, renewalTime); openedRecordInfo.shared.needsWatchStateUpdate = false; } - } on VeilidAPIExceptionTimeout { - log('Timeout in watch update for key=$openedRecordKey'); } on VeilidAPIException catch (e) { - // Failed to update DHT watch, try again next tick - log('VeilidAPIException in watch update for key=$openedRecordKey: $e'); - } catch (e) { - log('Unhandled exception in watch update for key=$openedRecordKey: $e'); - rethrow; - } - - // If we still need a state update after this then do a poll instead - if (openedRecordInfo.shared.needsWatchStateUpdate) { - _pollWatch(openedRecordKey, openedRecordInfo, unionWatchState); + // Failed to cancel DHT watch, try again next tick + log('Exception in watch update: $e'); } } - // In lieu of a completed watch, set off a polling operation - // on the first value of the watched range, which, due to current - // veilid limitations can only be one subkey at a time right now void _pollWatch(TypedKey openedRecordKey, _OpenedRecordInfo openedRecordInfo, _WatchState unionWatchState) { singleFuture((this, _sfPollWatch, openedRecordKey), () async { - await _stats.measure( - openedRecordKey, openedRecordInfo.debugNames, '_pollWatch', () async { - final dhtctx = openedRecordInfo.shared.defaultRoutingContext; + final dhtctx = openedRecordInfo.shared.defaultRoutingContext; - final currentReport = await dhtctx.inspectDHTRecord(openedRecordKey, - subkeys: unionWatchState.subkeys, scope: DHTReportScope.syncGet); + // Get single subkey to poll + // XXX: veilid api limits this for now until everyone supports + // inspectDHTRecord + final pollSubkey = unionWatchState.subkeys?.firstSubkey; + if (pollSubkey == null) { + return; + } + final pollSubkeys = [ValueSubkeyRange.single(pollSubkey)]; - final fsc = currentReport.firstSeqChange; - if (fsc == null) { - return null; - } - final newerSubkeys = currentReport.newerOnlineSubkeys; + final currentReport = + await dhtctx.inspectDHTRecord(openedRecordKey, subkeys: pollSubkeys); + final currentSeq = currentReport.localSeqs.firstOrNull ?? -1; - final valueData = await dhtctx.getDHTValue(openedRecordKey, fsc.subkey, - forceRefresh: true); - if (valueData == null) { - return; - } - - if (valueData.seq < fsc.newSeq) { - log('inspect returned a newer seq than get: ${valueData.seq} < $fsc'); - } - - if (fsc.oldSeq == null || valueData.seq > fsc.oldSeq!) { - processRemoteValueChange(VeilidUpdateValueChange( - key: openedRecordKey, - subkeys: newerSubkeys, - count: 0xFFFFFFFF, - value: valueData)); - } - }); + final valueData = await dhtctx.getDHTValue(openedRecordKey, pollSubkey, + forceRefresh: true); + if (valueData == null) { + return; + } + if (valueData.seq > currentSeq) { + processRemoteValueChange(VeilidUpdateValueChange( + key: openedRecordKey, + subkeys: pollSubkeys, + count: 0xFFFFFFFF, + value: valueData)); + } }); } /// Ticker to check watch state change requests - Future tick() async => _mutex.protect(() async { - // See if any opened records need watch state changes - for (final kv in _opened.entries) { - final openedRecordKey = kv.key; - final openedRecordInfo = kv.value; + Future tick() async { + final now = veilid.now(); - final wantsWatchStateUpdate = - openedRecordInfo.shared.needsWatchStateUpdate; + await _mutex.protect(() async { + // See if any opened records need watch state changes + for (final kv in _opened.entries) { + final openedRecordKey = kv.key; + final openedRecordInfo = kv.value; - if (wantsWatchStateUpdate) { - // Update union watch state - final unionWatchState = - _collectUnionWatchState(openedRecordInfo.records); + var wantsWatchStateUpdate = + openedRecordInfo.shared.needsWatchStateUpdate; - _watchStateProcessors.updateState( - openedRecordKey, - unionWatchState, - (newState) => _stats.measure( - openedRecordKey, - openedRecordInfo.debugNames, - '_watchStateChange', - () => _watchStateChange(openedRecordKey, unionWatchState))); + // Check if we have reached renewal time for the watch + if (openedRecordInfo.shared.unionWatchState != null && + openedRecordInfo.shared.unionWatchState!.renewalTime != null && + now.value > + openedRecordInfo.shared.unionWatchState!.renewalTime!.value) { + wantsWatchStateUpdate = true; + } + + if (wantsWatchStateUpdate) { + // Update union watch state + final unionWatchState = + _collectUnionWatchState(openedRecordInfo.records); + + final processed = _watchStateProcessors.updateState( + openedRecordKey, + unionWatchState, + (newState) => + _watchStateChange(openedRecordKey, unionWatchState)); + + // In lieu of a completed watch, set off a polling operation + // on the first value of the watched range, which, due to current + // veilid limitations can only be one subkey at a time right now + if (!processed && unionWatchState != null) { + _pollWatch(openedRecordKey, openedRecordInfo, unionWatchState); } } - }); + } + }); + } ////////////////////////////////////////////////////////////// // AsyncTableDBBacked @@ -952,8 +967,6 @@ class DHTRecordPool with TableDBBackedJson { // Watch state processors final _watchStateProcessors = SingleStateProcessorMap(); - // Statistics - final _stats = DHTStats(); static DHTRecordPool? _singleton; } diff --git a/packages/veilid_support/lib/dht_support/src/dht_record/dht_record_pool.freezed.dart b/packages/veilid_support/lib/dht_support/src/dht_record/dht_record_pool.freezed.dart index 48372bb..e09fc0c 100644 --- a/packages/veilid_support/lib/dht_support/src/dht_record/dht_record_pool.freezed.dart +++ b/packages/veilid_support/lib/dht_support/src/dht_record/dht_record_pool.freezed.dart @@ -1,4 +1,3 @@ -// dart format width=80 // coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND // ignore_for_file: type=lint @@ -10,58 +9,37 @@ part of 'dht_record_pool.dart'; // FreezedGenerator // ************************************************************************** -// dart format off T _$identity(T value) => value; -/// @nodoc -mixin _$DHTRecordPoolAllocations { - IMap> get childrenByParent; - IMap get parentByChild; - ISet get rootRecords; - IMap get debugNames; +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - /// Create a copy of DHTRecordPoolAllocations - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') - $DHTRecordPoolAllocationsCopyWith get copyWith => - _$DHTRecordPoolAllocationsCopyWithImpl( - this as DHTRecordPoolAllocations, _$identity); - - /// Serializes this DHTRecordPoolAllocations to a JSON map. - Map toJson(); - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is DHTRecordPoolAllocations && - (identical(other.childrenByParent, childrenByParent) || - other.childrenByParent == childrenByParent) && - (identical(other.parentByChild, parentByChild) || - other.parentByChild == parentByChild) && - const DeepCollectionEquality() - .equals(other.rootRecords, rootRecords) && - (identical(other.debugNames, debugNames) || - other.debugNames == debugNames)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash(runtimeType, childrenByParent, parentByChild, - const DeepCollectionEquality().hash(rootRecords), debugNames); - - @override - String toString() { - return 'DHTRecordPoolAllocations(childrenByParent: $childrenByParent, parentByChild: $parentByChild, rootRecords: $rootRecords, debugNames: $debugNames)'; - } +DHTRecordPoolAllocations _$DHTRecordPoolAllocationsFromJson( + Map json) { + return _DHTRecordPoolAllocations.fromJson(json); } /// @nodoc -abstract mixin class $DHTRecordPoolAllocationsCopyWith<$Res> { +mixin _$DHTRecordPoolAllocations { + IMap>> get childrenByParent => + throw _privateConstructorUsedError; + IMap> get parentByChild => + throw _privateConstructorUsedError; + ISet> get rootRecords => + throw _privateConstructorUsedError; + IMap get debugNames => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $DHTRecordPoolAllocationsCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $DHTRecordPoolAllocationsCopyWith<$Res> { factory $DHTRecordPoolAllocationsCopyWith(DHTRecordPoolAllocations value, - $Res Function(DHTRecordPoolAllocations) _then) = - _$DHTRecordPoolAllocationsCopyWithImpl; + $Res Function(DHTRecordPoolAllocations) then) = + _$DHTRecordPoolAllocationsCopyWithImpl<$Res, DHTRecordPoolAllocations>; @useResult $Res call( {IMap>> childrenByParent, @@ -71,15 +49,16 @@ abstract mixin class $DHTRecordPoolAllocationsCopyWith<$Res> { } /// @nodoc -class _$DHTRecordPoolAllocationsCopyWithImpl<$Res> +class _$DHTRecordPoolAllocationsCopyWithImpl<$Res, + $Val extends DHTRecordPoolAllocations> implements $DHTRecordPoolAllocationsCopyWith<$Res> { - _$DHTRecordPoolAllocationsCopyWithImpl(this._self, this._then); + _$DHTRecordPoolAllocationsCopyWithImpl(this._value, this._then); - final DHTRecordPoolAllocations _self; - final $Res Function(DHTRecordPoolAllocations) _then; + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; - /// Create a copy of DHTRecordPoolAllocations - /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -88,21 +67,76 @@ class _$DHTRecordPoolAllocationsCopyWithImpl<$Res> Object? rootRecords = null, Object? debugNames = null, }) { - return _then(_self.copyWith( + return _then(_value.copyWith( childrenByParent: null == childrenByParent - ? _self.childrenByParent! + ? _value.childrenByParent : childrenByParent // ignore: cast_nullable_to_non_nullable as IMap>>, parentByChild: null == parentByChild - ? _self.parentByChild! + ? _value.parentByChild : parentByChild // ignore: cast_nullable_to_non_nullable as IMap>, rootRecords: null == rootRecords - ? _self.rootRecords! + ? _value.rootRecords : rootRecords // ignore: cast_nullable_to_non_nullable as ISet>, debugNames: null == debugNames - ? _self.debugNames + ? _value.debugNames + : debugNames // ignore: cast_nullable_to_non_nullable + as IMap, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$DHTRecordPoolAllocationsImplCopyWith<$Res> + implements $DHTRecordPoolAllocationsCopyWith<$Res> { + factory _$$DHTRecordPoolAllocationsImplCopyWith( + _$DHTRecordPoolAllocationsImpl value, + $Res Function(_$DHTRecordPoolAllocationsImpl) then) = + __$$DHTRecordPoolAllocationsImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {IMap>> childrenByParent, + IMap> parentByChild, + ISet> rootRecords, + IMap debugNames}); +} + +/// @nodoc +class __$$DHTRecordPoolAllocationsImplCopyWithImpl<$Res> + extends _$DHTRecordPoolAllocationsCopyWithImpl<$Res, + _$DHTRecordPoolAllocationsImpl> + implements _$$DHTRecordPoolAllocationsImplCopyWith<$Res> { + __$$DHTRecordPoolAllocationsImplCopyWithImpl( + _$DHTRecordPoolAllocationsImpl _value, + $Res Function(_$DHTRecordPoolAllocationsImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? childrenByParent = null, + Object? parentByChild = null, + Object? rootRecords = null, + Object? debugNames = null, + }) { + return _then(_$DHTRecordPoolAllocationsImpl( + childrenByParent: null == childrenByParent + ? _value.childrenByParent + : childrenByParent // ignore: cast_nullable_to_non_nullable + as IMap>>, + parentByChild: null == parentByChild + ? _value.parentByChild + : parentByChild // ignore: cast_nullable_to_non_nullable + as IMap>, + rootRecords: null == rootRecords + ? _value.rootRecords + : rootRecords // ignore: cast_nullable_to_non_nullable + as ISet>, + debugNames: null == debugNames + ? _value.debugNames : debugNames // ignore: cast_nullable_to_non_nullable as IMap, )); @@ -111,14 +145,15 @@ class _$DHTRecordPoolAllocationsCopyWithImpl<$Res> /// @nodoc @JsonSerializable() -class _DHTRecordPoolAllocations implements DHTRecordPoolAllocations { - const _DHTRecordPoolAllocations( +class _$DHTRecordPoolAllocationsImpl implements _DHTRecordPoolAllocations { + const _$DHTRecordPoolAllocationsImpl( {this.childrenByParent = const IMapConst>({}), this.parentByChild = const IMapConst({}), this.rootRecords = const ISetConst({}), this.debugNames = const IMapConst({})}); - factory _DHTRecordPoolAllocations.fromJson(Map json) => - _$DHTRecordPoolAllocationsFromJson(json); + + factory _$DHTRecordPoolAllocationsImpl.fromJson(Map json) => + _$$DHTRecordPoolAllocationsImplFromJson(json); @override @JsonKey() @@ -133,27 +168,16 @@ class _DHTRecordPoolAllocations implements DHTRecordPoolAllocations { @JsonKey() final IMap debugNames; - /// Create a copy of DHTRecordPoolAllocations - /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') - _$DHTRecordPoolAllocationsCopyWith<_DHTRecordPoolAllocations> get copyWith => - __$DHTRecordPoolAllocationsCopyWithImpl<_DHTRecordPoolAllocations>( - this, _$identity); - - @override - Map toJson() { - return _$DHTRecordPoolAllocationsToJson( - this, - ); + String toString() { + return 'DHTRecordPoolAllocations(childrenByParent: $childrenByParent, parentByChild: $parentByChild, rootRecords: $rootRecords, debugNames: $debugNames)'; } @override bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _DHTRecordPoolAllocations && + other is _$DHTRecordPoolAllocationsImpl && (identical(other.childrenByParent, childrenByParent) || other.childrenByParent == childrenByParent) && (identical(other.parentByChild, parentByChild) || @@ -164,139 +188,140 @@ class _DHTRecordPoolAllocations implements DHTRecordPoolAllocations { other.debugNames == debugNames)); } - @JsonKey(includeFromJson: false, includeToJson: false) + @JsonKey(ignore: true) @override int get hashCode => Object.hash(runtimeType, childrenByParent, parentByChild, const DeepCollectionEquality().hash(rootRecords), debugNames); - @override - String toString() { - return 'DHTRecordPoolAllocations(childrenByParent: $childrenByParent, parentByChild: $parentByChild, rootRecords: $rootRecords, debugNames: $debugNames)'; - } -} - -/// @nodoc -abstract mixin class _$DHTRecordPoolAllocationsCopyWith<$Res> - implements $DHTRecordPoolAllocationsCopyWith<$Res> { - factory _$DHTRecordPoolAllocationsCopyWith(_DHTRecordPoolAllocations value, - $Res Function(_DHTRecordPoolAllocations) _then) = - __$DHTRecordPoolAllocationsCopyWithImpl; - @override - @useResult - $Res call( - {IMap>> childrenByParent, - IMap> parentByChild, - ISet> rootRecords, - IMap debugNames}); -} - -/// @nodoc -class __$DHTRecordPoolAllocationsCopyWithImpl<$Res> - implements _$DHTRecordPoolAllocationsCopyWith<$Res> { - __$DHTRecordPoolAllocationsCopyWithImpl(this._self, this._then); - - final _DHTRecordPoolAllocations _self; - final $Res Function(_DHTRecordPoolAllocations) _then; - - /// Create a copy of DHTRecordPoolAllocations - /// with the given fields replaced by the non-null parameter values. + @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - $Res call({ - Object? childrenByParent = null, - Object? parentByChild = null, - Object? rootRecords = null, - Object? debugNames = null, - }) { - return _then(_DHTRecordPoolAllocations( - childrenByParent: null == childrenByParent - ? _self.childrenByParent - : childrenByParent // ignore: cast_nullable_to_non_nullable - as IMap>>, - parentByChild: null == parentByChild - ? _self.parentByChild - : parentByChild // ignore: cast_nullable_to_non_nullable - as IMap>, - rootRecords: null == rootRecords - ? _self.rootRecords - : rootRecords // ignore: cast_nullable_to_non_nullable - as ISet>, - debugNames: null == debugNames - ? _self.debugNames - : debugNames // ignore: cast_nullable_to_non_nullable - as IMap, - )); + _$$DHTRecordPoolAllocationsImplCopyWith<_$DHTRecordPoolAllocationsImpl> + get copyWith => __$$DHTRecordPoolAllocationsImplCopyWithImpl< + _$DHTRecordPoolAllocationsImpl>(this, _$identity); + + @override + Map toJson() { + return _$$DHTRecordPoolAllocationsImplToJson( + this, + ); } } +abstract class _DHTRecordPoolAllocations implements DHTRecordPoolAllocations { + const factory _DHTRecordPoolAllocations( + {final IMap>> childrenByParent, + final IMap> parentByChild, + final ISet> rootRecords, + final IMap debugNames}) = _$DHTRecordPoolAllocationsImpl; + + factory _DHTRecordPoolAllocations.fromJson(Map json) = + _$DHTRecordPoolAllocationsImpl.fromJson; + + @override + IMap>> get childrenByParent; + @override + IMap> get parentByChild; + @override + ISet> get rootRecords; + @override + IMap get debugNames; + @override + @JsonKey(ignore: true) + _$$DHTRecordPoolAllocationsImplCopyWith<_$DHTRecordPoolAllocationsImpl> + get copyWith => throw _privateConstructorUsedError; +} + +OwnedDHTRecordPointer _$OwnedDHTRecordPointerFromJson( + Map json) { + return _OwnedDHTRecordPointer.fromJson(json); +} + /// @nodoc mixin _$OwnedDHTRecordPointer { - TypedKey get recordKey; - KeyPair get owner; + Typed get recordKey => + throw _privateConstructorUsedError; + KeyPair get owner => throw _privateConstructorUsedError; - /// Create a copy of OwnedDHTRecordPointer - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) $OwnedDHTRecordPointerCopyWith get copyWith => - _$OwnedDHTRecordPointerCopyWithImpl( - this as OwnedDHTRecordPointer, _$identity); - - /// Serializes this OwnedDHTRecordPointer to a JSON map. - Map toJson(); - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is OwnedDHTRecordPointer && - (identical(other.recordKey, recordKey) || - other.recordKey == recordKey) && - (identical(other.owner, owner) || other.owner == owner)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash(runtimeType, recordKey, owner); - - @override - String toString() { - return 'OwnedDHTRecordPointer(recordKey: $recordKey, owner: $owner)'; - } + throw _privateConstructorUsedError; } /// @nodoc -abstract mixin class $OwnedDHTRecordPointerCopyWith<$Res> { +abstract class $OwnedDHTRecordPointerCopyWith<$Res> { factory $OwnedDHTRecordPointerCopyWith(OwnedDHTRecordPointer value, - $Res Function(OwnedDHTRecordPointer) _then) = - _$OwnedDHTRecordPointerCopyWithImpl; + $Res Function(OwnedDHTRecordPointer) then) = + _$OwnedDHTRecordPointerCopyWithImpl<$Res, OwnedDHTRecordPointer>; @useResult $Res call({Typed recordKey, KeyPair owner}); } /// @nodoc -class _$OwnedDHTRecordPointerCopyWithImpl<$Res> +class _$OwnedDHTRecordPointerCopyWithImpl<$Res, + $Val extends OwnedDHTRecordPointer> implements $OwnedDHTRecordPointerCopyWith<$Res> { - _$OwnedDHTRecordPointerCopyWithImpl(this._self, this._then); + _$OwnedDHTRecordPointerCopyWithImpl(this._value, this._then); - final OwnedDHTRecordPointer _self; - final $Res Function(OwnedDHTRecordPointer) _then; + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; - /// Create a copy of OwnedDHTRecordPointer - /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ Object? recordKey = null, Object? owner = null, }) { - return _then(_self.copyWith( + return _then(_value.copyWith( recordKey: null == recordKey - ? _self.recordKey! + ? _value.recordKey : recordKey // ignore: cast_nullable_to_non_nullable as Typed, owner: null == owner - ? _self.owner + ? _value.owner + : owner // ignore: cast_nullable_to_non_nullable + as KeyPair, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$OwnedDHTRecordPointerImplCopyWith<$Res> + implements $OwnedDHTRecordPointerCopyWith<$Res> { + factory _$$OwnedDHTRecordPointerImplCopyWith( + _$OwnedDHTRecordPointerImpl value, + $Res Function(_$OwnedDHTRecordPointerImpl) then) = + __$$OwnedDHTRecordPointerImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({Typed recordKey, KeyPair owner}); +} + +/// @nodoc +class __$$OwnedDHTRecordPointerImplCopyWithImpl<$Res> + extends _$OwnedDHTRecordPointerCopyWithImpl<$Res, + _$OwnedDHTRecordPointerImpl> + implements _$$OwnedDHTRecordPointerImplCopyWith<$Res> { + __$$OwnedDHTRecordPointerImplCopyWithImpl(_$OwnedDHTRecordPointerImpl _value, + $Res Function(_$OwnedDHTRecordPointerImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? recordKey = null, + Object? owner = null, + }) { + return _then(_$OwnedDHTRecordPointerImpl( + recordKey: null == recordKey + ? _value.recordKey + : recordKey // ignore: cast_nullable_to_non_nullable + as Typed, + owner: null == owner + ? _value.owner : owner // ignore: cast_nullable_to_non_nullable as KeyPair, )); @@ -305,90 +330,66 @@ class _$OwnedDHTRecordPointerCopyWithImpl<$Res> /// @nodoc @JsonSerializable() -class _OwnedDHTRecordPointer implements OwnedDHTRecordPointer { - const _OwnedDHTRecordPointer({required this.recordKey, required this.owner}); - factory _OwnedDHTRecordPointer.fromJson(Map json) => - _$OwnedDHTRecordPointerFromJson(json); +class _$OwnedDHTRecordPointerImpl implements _OwnedDHTRecordPointer { + const _$OwnedDHTRecordPointerImpl( + {required this.recordKey, required this.owner}); + + factory _$OwnedDHTRecordPointerImpl.fromJson(Map json) => + _$$OwnedDHTRecordPointerImplFromJson(json); @override final Typed recordKey; @override final KeyPair owner; - /// Create a copy of OwnedDHTRecordPointer - /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') - _$OwnedDHTRecordPointerCopyWith<_OwnedDHTRecordPointer> get copyWith => - __$OwnedDHTRecordPointerCopyWithImpl<_OwnedDHTRecordPointer>( - this, _$identity); - - @override - Map toJson() { - return _$OwnedDHTRecordPointerToJson( - this, - ); + String toString() { + return 'OwnedDHTRecordPointer(recordKey: $recordKey, owner: $owner)'; } @override bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _OwnedDHTRecordPointer && + other is _$OwnedDHTRecordPointerImpl && (identical(other.recordKey, recordKey) || other.recordKey == recordKey) && (identical(other.owner, owner) || other.owner == owner)); } - @JsonKey(includeFromJson: false, includeToJson: false) + @JsonKey(ignore: true) @override int get hashCode => Object.hash(runtimeType, recordKey, owner); - @override - String toString() { - return 'OwnedDHTRecordPointer(recordKey: $recordKey, owner: $owner)'; - } -} - -/// @nodoc -abstract mixin class _$OwnedDHTRecordPointerCopyWith<$Res> - implements $OwnedDHTRecordPointerCopyWith<$Res> { - factory _$OwnedDHTRecordPointerCopyWith(_OwnedDHTRecordPointer value, - $Res Function(_OwnedDHTRecordPointer) _then) = - __$OwnedDHTRecordPointerCopyWithImpl; - @override - @useResult - $Res call({Typed recordKey, KeyPair owner}); -} - -/// @nodoc -class __$OwnedDHTRecordPointerCopyWithImpl<$Res> - implements _$OwnedDHTRecordPointerCopyWith<$Res> { - __$OwnedDHTRecordPointerCopyWithImpl(this._self, this._then); - - final _OwnedDHTRecordPointer _self; - final $Res Function(_OwnedDHTRecordPointer) _then; - - /// Create a copy of OwnedDHTRecordPointer - /// with the given fields replaced by the non-null parameter values. + @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - $Res call({ - Object? recordKey = null, - Object? owner = null, - }) { - return _then(_OwnedDHTRecordPointer( - recordKey: null == recordKey - ? _self.recordKey - : recordKey // ignore: cast_nullable_to_non_nullable - as Typed, - owner: null == owner - ? _self.owner - : owner // ignore: cast_nullable_to_non_nullable - as KeyPair, - )); + _$$OwnedDHTRecordPointerImplCopyWith<_$OwnedDHTRecordPointerImpl> + get copyWith => __$$OwnedDHTRecordPointerImplCopyWithImpl< + _$OwnedDHTRecordPointerImpl>(this, _$identity); + + @override + Map toJson() { + return _$$OwnedDHTRecordPointerImplToJson( + this, + ); } } -// dart format on +abstract class _OwnedDHTRecordPointer implements OwnedDHTRecordPointer { + const factory _OwnedDHTRecordPointer( + {required final Typed recordKey, + required final KeyPair owner}) = _$OwnedDHTRecordPointerImpl; + + factory _OwnedDHTRecordPointer.fromJson(Map json) = + _$OwnedDHTRecordPointerImpl.fromJson; + + @override + Typed get recordKey; + @override + KeyPair get owner; + @override + @JsonKey(ignore: true) + _$$OwnedDHTRecordPointerImplCopyWith<_$OwnedDHTRecordPointerImpl> + get copyWith => throw _privateConstructorUsedError; +} diff --git a/packages/veilid_support/lib/dht_support/src/dht_record/dht_record_pool.g.dart b/packages/veilid_support/lib/dht_support/src/dht_record/dht_record_pool.g.dart index c2c031f..12b3a1e 100644 --- a/packages/veilid_support/lib/dht_support/src/dht_record/dht_record_pool.g.dart +++ b/packages/veilid_support/lib/dht_support/src/dht_record/dht_record_pool.g.dart @@ -6,9 +6,9 @@ part of 'dht_record_pool.dart'; // JsonSerializableGenerator // ************************************************************************** -_DHTRecordPoolAllocations _$DHTRecordPoolAllocationsFromJson( +_$DHTRecordPoolAllocationsImpl _$$DHTRecordPoolAllocationsImplFromJson( Map json) => - _DHTRecordPoolAllocations( + _$DHTRecordPoolAllocationsImpl( childrenByParent: json['children_by_parent'] == null ? const IMapConst>({}) : IMap>>.fromJson( @@ -34,8 +34,8 @@ _DHTRecordPoolAllocations _$DHTRecordPoolAllocationsFromJson( (value) => value as String), ); -Map _$DHTRecordPoolAllocationsToJson( - _DHTRecordPoolAllocations instance) => +Map _$$DHTRecordPoolAllocationsImplToJson( + _$DHTRecordPoolAllocationsImpl instance) => { 'children_by_parent': instance.childrenByParent.toJson( (value) => value, @@ -56,15 +56,15 @@ Map _$DHTRecordPoolAllocationsToJson( ), }; -_OwnedDHTRecordPointer _$OwnedDHTRecordPointerFromJson( +_$OwnedDHTRecordPointerImpl _$$OwnedDHTRecordPointerImplFromJson( Map json) => - _OwnedDHTRecordPointer( + _$OwnedDHTRecordPointerImpl( recordKey: Typed.fromJson(json['record_key']), owner: KeyPair.fromJson(json['owner']), ); -Map _$OwnedDHTRecordPointerToJson( - _OwnedDHTRecordPointer instance) => +Map _$$OwnedDHTRecordPointerImplToJson( + _$OwnedDHTRecordPointerImpl instance) => { 'record_key': instance.recordKey.toJson(), 'owner': instance.owner.toJson(), diff --git a/packages/veilid_support/lib/dht_support/src/dht_record/dht_record_pool_private.dart b/packages/veilid_support/lib/dht_support/src/dht_record/dht_record_pool_private.dart index 05b93b0..b7cbba8 100644 --- a/packages/veilid_support/lib/dht_support/src/dht_record/dht_record_pool_private.dart +++ b/packages/veilid_support/lib/dht_support/src/dht_record/dht_record_pool_private.dart @@ -1,5 +1,12 @@ part of 'dht_record_pool.dart'; +const int _watchBackoffMultiplier = 2; +const int _watchBackoffMax = 30; + +const int? _defaultWatchDurationSecs = null; // 600 +const int _watchRenewalNumerator = 4; +const int _watchRenewalDenominator = 5; + // DHT crypto domain const String _cryptoDomainDHT = 'dht'; @@ -10,17 +17,21 @@ const _sfListen = 'listen'; /// Watch state @immutable class _WatchState extends Equatable { - const _WatchState({ - required this.subkeys, - required this.expiration, - required this.count, - }); + const _WatchState( + {required this.subkeys, + required this.expiration, + required this.count, + this.realExpiration, + this.renewalTime}); final List? subkeys; final Timestamp? expiration; final int? count; + final Timestamp? realExpiration; + final Timestamp? renewalTime; @override - List get props => [subkeys, expiration, count]; + List get props => + [subkeys, expiration, count, realExpiration, renewalTime]; } /// Data shared amongst all DHTRecord instances diff --git a/packages/veilid_support/lib/dht_support/src/dht_record/extensions.dart b/packages/veilid_support/lib/dht_support/src/dht_record/extensions.dart deleted file mode 100644 index b0da9e3..0000000 --- a/packages/veilid_support/lib/dht_support/src/dht_record/extensions.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'package:veilid/veilid.dart'; - -class DHTSeqChange { - const DHTSeqChange(this.subkey, this.oldSeq, this.newSeq); - final int subkey; - final int? oldSeq; - final int newSeq; -} - -extension DHTReportReportExt on DHTRecordReport { - List get newerOnlineSubkeys { - if (networkSeqs.isEmpty || localSeqs.isEmpty || subkeys.isEmpty) { - return []; - } - - final currentSubkeys = []; - - var i = 0; - for (final skr in subkeys) { - for (var sk = skr.low; sk <= skr.high; sk++) { - final nseq = networkSeqs[i]; - final lseq = localSeqs[i]; - - if (nseq != null && (lseq == null || nseq > lseq)) { - if (currentSubkeys.isNotEmpty && - currentSubkeys.last.high == (sk - 1)) { - currentSubkeys.add(ValueSubkeyRange( - low: currentSubkeys.removeLast().low, high: sk)); - } else { - currentSubkeys.add(ValueSubkeyRange.single(sk)); - } - } - - i++; - } - } - - return currentSubkeys; - } - - DHTSeqChange? get firstSeqChange { - if (networkSeqs.isEmpty || localSeqs.isEmpty || subkeys.isEmpty) { - return null; - } - - var i = 0; - for (final skr in subkeys) { - for (var sk = skr.low; sk <= skr.high; sk++) { - final nseq = networkSeqs[i]; - final lseq = localSeqs[i]; - - if (nseq != null && (lseq == null || nseq > lseq)) { - return DHTSeqChange(sk, lseq, nseq); - } - i++; - } - } - - return null; - } -} diff --git a/packages/veilid_support/lib/dht_support/src/dht_record/stats.dart b/packages/veilid_support/lib/dht_support/src/dht_record/stats.dart deleted file mode 100644 index 6388f5c..0000000 --- a/packages/veilid_support/lib/dht_support/src/dht_record/stats.dart +++ /dev/null @@ -1,175 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:indent/indent.dart'; - -import '../../../veilid_support.dart'; - -const maxLatencySamples = 100; -const timeoutDuration = 10; - -extension LatencyStatsExt on LatencyStats { - String debugString() => 'fast($fastest)/avg($average)/slow($slowest)/' - 'tm90($tm90)/tm75($tm75)/p90($p90)/p75($p75)'; -} - -class LatencyStatsAccounting { - LatencyStatsAccounting({required this.maxSamples}); - - LatencyStats record(TimestampDuration dur) { - _samples.add(dur); - if (_samples.length > maxSamples) { - _samples.removeAt(0); - } - - final sortedList = _samples.sorted(); - - final fastest = sortedList.first; - final slowest = sortedList.last; - final average = TimestampDuration( - value: sortedList.fold(BigInt.zero, (acc, x) => acc + x.value) ~/ - BigInt.from(sortedList.length)); - - final tm90len = (sortedList.length * 90 + 99) ~/ 100; - final tm75len = (sortedList.length * 75 + 99) ~/ 100; - final tm90 = TimestampDuration( - value: sortedList - .sublist(0, tm90len) - .fold(BigInt.zero, (acc, x) => acc + x.value) ~/ - BigInt.from(tm90len)); - final tm75 = TimestampDuration( - value: sortedList - .sublist(0, tm75len) - .fold(BigInt.zero, (acc, x) => acc + x.value) ~/ - BigInt.from(tm90len)); - final p90 = sortedList[tm90len - 1]; - final p75 = sortedList[tm75len - 1]; - - final ls = LatencyStats( - fastest: fastest, - slowest: slowest, - average: average, - tm90: tm90, - tm75: tm75, - p90: p90, - p75: p75); - - return ls; - } - - ///////////////////////////// - final int maxSamples; - final _samples = []; -} - -class DHTCallStats { - void record(TimestampDuration dur, Exception? exc) { - final wasTimeout = - exc is VeilidAPIExceptionTimeout || dur.toSecs() >= timeoutDuration; - - calls++; - if (wasTimeout) { - timeouts++; - } else { - successLatency = successLatencyAcct.record(dur); - } - latency = latencyAcct.record(dur); - } - - String debugString() => - ' timeouts/calls: $timeouts/$calls (${(timeouts * 100 / calls).toStringAsFixed(3)}%)\n' - 'success latency: ${successLatency?.debugString()}\n' - ' all latency: ${latency?.debugString()}\n'; - - ///////////////////////////// - int calls = 0; - int timeouts = 0; - LatencyStats? latency; - LatencyStats? successLatency; - final latencyAcct = LatencyStatsAccounting(maxSamples: maxLatencySamples); - final successLatencyAcct = - LatencyStatsAccounting(maxSamples: maxLatencySamples); -} - -class DHTPerKeyStats { - DHTPerKeyStats(this.debugName); - - void record(String func, TimestampDuration dur, Exception? exc) { - final keyFuncStats = _perFuncStats.putIfAbsent(func, DHTCallStats.new); - - _stats.record(dur, exc); - keyFuncStats.record(dur, exc); - } - - String debugString() { - // - final out = StringBuffer() - ..write('Name: $debugName\n') - ..write(_stats.debugString().indent(4)) - ..writeln('Per-Function:'); - for (final entry in _perFuncStats.entries) { - final funcName = entry.key; - final funcStats = entry.value.debugString().indent(4); - out.write('$funcName:\n$funcStats'.indent(4)); - } - - return out.toString(); - } - - ////////////////////////////// - - final String debugName; - final _stats = DHTCallStats(); - final _perFuncStats = {}; -} - -class DHTStats { - DHTStats(); - - Future measure(TypedKey key, String debugName, String func, - Future Function() closure) async { - // - final start = Veilid.instance.now(); - final keyStats = - _statsPerKey.putIfAbsent(key, () => DHTPerKeyStats(debugName)); - final funcStats = _statsPerFunc.putIfAbsent(func, DHTCallStats.new); - - VeilidAPIException? exc; - - try { - final res = await closure(); - - return res; - } on VeilidAPIException catch (e) { - exc = e; - rethrow; - } finally { - final end = Veilid.instance.now(); - final dur = end.diff(start); - - keyStats.record(func, dur, exc); - funcStats.record(dur, exc); - } - } - - String debugString() { - // - final out = StringBuffer()..writeln('Per-Function:'); - for (final entry in _statsPerFunc.entries) { - final funcName = entry.key; - final funcStats = entry.value.debugString().indent(4); - out.write('$funcName:\n$funcStats\n'.indent(4)); - } - out.writeln('Per-Key:'); - for (final entry in _statsPerKey.entries) { - final keyName = entry.key; - final keyStats = entry.value.debugString().indent(4); - out.write('$keyName:\n$keyStats\n'.indent(4)); - } - - return out.toString(); - } - - ////////////////////////////// - - final _statsPerKey = {}; - final _statsPerFunc = {}; -} diff --git a/packages/veilid_support/lib/dht_support/src/dht_short_array/dht_short_array.dart b/packages/veilid_support/lib/dht_support/src/dht_short_array/dht_short_array.dart index ccf7d18..d0d26a8 100644 --- a/packages/veilid_support/lib/dht_support/src/dht_short_array/dht_short_array.dart +++ b/packages/veilid_support/lib/dht_support/src/dht_short_array/dht_short_array.dart @@ -4,7 +4,6 @@ import 'dart:typed_data'; import 'package:async_tools/async_tools.dart'; import 'package:collection/collection.dart'; -import '../../../src/veilid_log.dart'; import '../../../veilid_support.dart'; import '../../proto/proto.dart' as proto; @@ -149,32 +148,33 @@ class DHTShortArray implements DHTDeleteable { /// Add a reference to this shortarray @override - void ref() { - _openCount++; - } + Future ref() async => _mutex.protect(() async { + _openCount++; + }); /// Free all resources for the DHTShortArray @override - Future close() async { - if (_openCount == 0) { - throw StateError('already closed'); - } - _openCount--; - if (_openCount != 0) { - return false; - } + Future close() async => _mutex.protect(() async { + if (_openCount == 0) { + throw StateError('already closed'); + } + _openCount--; + if (_openCount != 0) { + return false; + } - await _watchController?.close(); - _watchController = null; - await _head.close(); - return true; - } + await _watchController?.close(); + _watchController = null; + await _head.close(); + return true; + }); /// Free all resources for the DHTShortArray and delete it from the DHT - /// Returns true if the deletion was processed immediately - /// Returns false if the deletion was marked for later + /// Will wait until the short array is closed to delete it @override - Future delete() async => _head.delete(); + Future delete() async { + await _head.delete(); + } //////////////////////////////////////////////////////////////////////////// // Public API @@ -289,9 +289,10 @@ class DHTShortArray implements DHTDeleteable { // Openable int _openCount; + final _mutex = Mutex(); // Watch mutex to ensure we keep the representation valid - final Mutex _listenMutex = Mutex(debugLockTimeout: kIsDebugMode ? 60 : null); + final Mutex _listenMutex = Mutex(); // Stream of external changes StreamController? _watchController; } diff --git a/packages/veilid_support/lib/dht_support/src/dht_short_array/dht_short_array_cubit.dart b/packages/veilid_support/lib/dht_support/src/dht_short_array/dht_short_array_cubit.dart index 246a990..30309a3 100644 --- a/packages/veilid_support/lib/dht_support/src/dht_short_array/dht_short_array_cubit.dart +++ b/packages/veilid_support/lib/dht_support/src/dht_short_array/dht_short_array_cubit.dart @@ -3,14 +3,28 @@ import 'dart:async'; import 'package:async_tools/async_tools.dart'; import 'package:bloc/bloc.dart'; import 'package:bloc_advanced_tools/bloc_advanced_tools.dart'; +import 'package:equatable/equatable.dart'; import 'package:fast_immutable_collections/fast_immutable_collections.dart'; +import 'package:meta/meta.dart'; import '../../../veilid_support.dart'; +import '../interfaces/refreshable_cubit.dart'; -typedef DHTShortArrayState = AsyncValue>>; -typedef DHTShortArrayCubitState = BlocBusyState>; +@immutable +class DHTShortArrayElementState extends Equatable { + const DHTShortArrayElementState( + {required this.value, required this.isOffline}); + final T value; + final bool isOffline; -class DHTShortArrayCubit extends Cubit> + @override + List get props => [value, isOffline]; +} + +typedef DHTShortArrayState = AsyncValue>>; +typedef DHTShortArrayBusyState = BlocBusyState>; + +class DHTShortArrayCubit extends Cubit> with BlocBusyWrapper>, RefreshableCubit { DHTShortArrayCubit({ required Future Function() open, @@ -32,8 +46,7 @@ class DHTShortArrayCubit extends Cubit> } } } on Exception catch (e, st) { - addError(e, st); - emit(DHTShortArrayCubitState(AsyncValue.error(e, st))); + emit(DHTShortArrayBusyState(AsyncValue.error(e, st))); return; } @@ -62,12 +75,15 @@ class DHTShortArrayCubit extends Cubit> Set? offlinePositions; if (_shortArray.writer != null) { offlinePositions = await reader.getOfflinePositions(); + if (offlinePositions == null) { + return null; + } } // Get the items final allItems = (await reader.getRange(0, forceRefresh: forceRefresh)) ?.indexed - .map((x) => OnlineElementState( + .map((x) => DHTShortArrayElementState( value: _decodeElement(x.$2), isOffline: offlinePositions?.contains(x.$1) ?? false)) .toIList(); @@ -80,9 +96,8 @@ class DHTShortArrayCubit extends Cubit> } emit(AsyncValue.data(newState)); setRefreshed(); - } on Exception catch (e, st) { - addError(e, st); - emit(AsyncValue.error(e, st)); + } on Exception catch (e) { + emit(AsyncValue.error(e)); } } diff --git a/packages/veilid_support/lib/dht_support/src/dht_short_array/dht_short_array_head.dart b/packages/veilid_support/lib/dht_support/src/dht_short_array/dht_short_array_head.dart index 1785b28..ff550e8 100644 --- a/packages/veilid_support/lib/dht_support/src/dht_short_array/dht_short_array_head.dart +++ b/packages/veilid_support/lib/dht_support/src/dht_short_array/dht_short_array_head.dart @@ -5,7 +5,7 @@ class DHTShortArrayHeadLookup { {required this.record, required this.recordSubkey, required this.seq}); final DHTRecord record; final int recordSubkey; - final int? seq; + final int seq; } class _DHTShortArrayHead { @@ -41,7 +41,7 @@ class _DHTShortArrayHead { final head = proto.DHTShortArray(); head.keys.addAll(_linkedRecords.map((lr) => lr.key.toProto())); head.index = List.of(_index); - head.seqs.addAll(_seqs.map((x) => x ?? 0xFFFFFFFF)); + head.seqs.addAll(_seqs); // Do not serialize free list, it gets recreated // Do not serialize local seqs, they are only locally relevant return head; @@ -65,12 +65,18 @@ class _DHTShortArrayHead { }); } - /// Returns true if the deletion was processed immediately - /// Returns false if the deletion was marked for later - Future delete() => _headMutex.protect(_headRecord.delete); + Future delete() async { + await _headMutex.protect(() async { + // Will deep delete all linked records as they are children + await _headRecord.delete(); + }); + } Future operate(Future Function(_DHTShortArrayHead) closure) async => - _headMutex.protect(() async => closure(this)); + // ignore: prefer_expression_function_bodies + _headMutex.protect(() async { + return closure(this); + }); Future operateWrite( Future Function(_DHTShortArrayHead) closure) async => @@ -112,7 +118,7 @@ class _DHTShortArrayHead { late List oldLinkedRecords; late List oldIndex; late List oldFree; - late List oldSeqs; + late List oldSeqs; late T out; try { @@ -194,8 +200,7 @@ class _DHTShortArrayHead { // Get the set of new linked keys and validate it final updatedLinkedKeys = head.keys.map((p) => p.toVeilid()).toList(); final updatedIndex = List.of(head.index); - final updatedSeqs = - List.of(head.seqs.map((x) => x == 0xFFFFFFFF ? null : x)); + final updatedSeqs = List.of(head.seqs); final updatedFree = _makeFreeList(updatedLinkedKeys, updatedIndex); // See which records are actually new @@ -331,7 +336,7 @@ class _DHTShortArrayHead { } Future lookupIndex(int idx, bool allowCreate) async { - final seq = idx < _seqs.length ? _seqs[idx] : null; + final seq = idx < _seqs.length ? _seqs[idx] : 0xFFFFFFFF; final recordNumber = idx ~/ _stride; final record = await _getOrCreateLinkedRecord(recordNumber, allowCreate); final recordSubkey = (idx % _stride) + ((recordNumber == 0) ? 1 : 0); @@ -381,24 +386,6 @@ class _DHTShortArrayHead { // xxx: free list optimization here? } - /// Truncate index to a particular length - void truncate(int newLength) { - if (newLength >= _index.length) { - return; - } else if (newLength == 0) { - clearIndex(); - return; - } else if (newLength < 0) { - throw StateError('can not truncate to negative length'); - } - - final newIndex = _index.sublist(0, newLength); - final freed = _index.sublist(newLength); - - _index = newIndex; - _free.addAll(freed); - } - /// Validate the head from the DHT is properly formatted /// and calculate the free list from it while we're here List _makeFreeList( @@ -443,18 +430,18 @@ class _DHTShortArrayHead { // If our local sequence number is unknown or hasnt been written yet // then a normal DHT operation is going to pull from the network anyway - if (_localSeqs.length < idx || _localSeqs[idx] == null) { + if (_localSeqs.length < idx || _localSeqs[idx] == 0xFFFFFFFF) { return false; } // If the remote sequence number record is unknown or hasnt been written // at this index yet, then we also do not refresh at this time as it // is the first time the index is being written to - if (_seqs.length < idx || _seqs[idx] == null) { + if (_seqs.length < idx || _seqs[idx] == 0xFFFFFFFF) { return false; } - return _localSeqs[idx]! < _seqs[idx]!; + return _localSeqs[idx] < _seqs[idx]; } /// Update the sequence number for a particular index in @@ -464,12 +451,12 @@ class _DHTShortArrayHead { final idx = _index[pos]; while (_localSeqs.length <= idx) { - _localSeqs.add(null); + _localSeqs.add(0xFFFFFFFF); } _localSeqs[idx] = newSeq; if (write) { while (_seqs.length <= idx) { - _seqs.add(null); + _seqs.add(0xFFFFFFFF); } _seqs[idx] = newSeq; } @@ -482,13 +469,13 @@ class _DHTShortArrayHead { Future watch() async { // This will update any existing watches if necessary try { + await _headRecord.watch(subkeys: [ValueSubkeyRange.single(0)]); + // Update changes to the head record // Don't watch for local changes because this class already handles // notifying listeners and knows when it makes local changes _subscription ??= await _headRecord.listen(localChanges: false, _onHeadValueChanged); - - await _headRecord.watch(subkeys: [ValueSubkeyRange.single(0)]); } on Exception { // If anything fails, try to cancel the watches await cancelWatch(); @@ -531,7 +518,7 @@ class _DHTShortArrayHead { //////////////////////////////////////////////////////////////////////////// // Head/element mutex to ensure we keep the representation valid - final Mutex _headMutex = Mutex(debugLockTimeout: kIsDebugMode ? 60 : null); + final Mutex _headMutex = Mutex(); // Subscription to head record internal changes StreamSubscription? _subscription; // Notify closure for external head changes @@ -553,7 +540,7 @@ class _DHTShortArrayHead { // The sequence numbers of each subkey. // Index is by subkey number not by element index. // (n-1 for head record and then the next n for linked records) - List _seqs; + List _seqs; // The local sequence numbers for each subkey. - List _localSeqs; + List _localSeqs; } diff --git a/packages/veilid_support/lib/dht_support/src/dht_short_array/dht_short_array_read.dart b/packages/veilid_support/lib/dht_support/src/dht_short_array/dht_short_array_read.dart index eeb9648..ddfdedc 100644 --- a/packages/veilid_support/lib/dht_support/src/dht_short_array/dht_short_array_read.dart +++ b/packages/veilid_support/lib/dht_support/src/dht_short_array/dht_short_array_read.dart @@ -60,32 +60,17 @@ class _DHTShortArrayRead implements DHTShortArrayReadOperations { final chunks = Iterable.generate(length) .slices(kMaxDHTConcurrency) - .map((chunk) => chunk.map((pos) async { - try { - return await get(pos + start, forceRefresh: forceRefresh); - // Need some way to debug ParallelWaitError - // ignore: avoid_catches_without_on_clauses - } catch (e, st) { - veilidLoggy.error('$e\n$st\n'); - rethrow; - } - })); + .map((chunk) => chunk + .map((pos) async => get(pos + start, forceRefresh: forceRefresh))); for (final chunk in chunks) { - var elems = await chunk.wait; + final elems = await chunk.wait; - // Return only the first contiguous range, anything else is garbage - // due to a representational error in the head or shortarray legnth - final nullPos = elems.indexOf(null); - if (nullPos != -1) { - elems = elems.sublist(0, nullPos); + // If any element was unavailable, return null + if (elems.contains(null)) { + return null; } - out.addAll(elems.cast()); - - if (nullPos != -1) { - break; - } } return out; diff --git a/packages/veilid_support/lib/dht_support/src/dht_short_array/dht_short_array_write.dart b/packages/veilid_support/lib/dht_support/src/dht_short_array/dht_short_array_write.dart index fa3b1c6..c52a7b2 100644 --- a/packages/veilid_support/lib/dht_support/src/dht_short_array/dht_short_array_write.dart +++ b/packages/veilid_support/lib/dht_support/src/dht_short_array/dht_short_array_write.dart @@ -9,7 +9,6 @@ abstract class DHTShortArrayWriteOperations DHTRandomWrite, DHTInsertRemove, DHTAdd, - DHTTruncate, DHTClear {} class _DHTShortArrayWrite extends _DHTShortArrayRead @@ -41,7 +40,7 @@ class _DHTShortArrayWrite extends _DHTShortArrayRead } } if (!success) { - throw const DHTExceptionOutdated(); + throw DHTExceptionOutdated(); } } @@ -73,16 +72,10 @@ class _DHTShortArrayWrite extends _DHTShortArrayRead final value = values[i]; final outSeqNum = outSeqNums[i]; dws.add((_) async { - try { - final outValue = await lookup.record.tryWriteBytes(value, - subkey: lookup.recordSubkey, outSeqNum: outSeqNum); - if (outValue != null) { - success = false; - } - // Need some way to debug ParallelWaitError - // ignore: avoid_catches_without_on_clauses - } catch (e, st) { - veilidLoggy.error('$e\n$st\n'); + final outValue = await lookup.record.tryWriteBytes(value, + subkey: lookup.recordSubkey, outSeqNum: outSeqNum); + if (outValue != null) { + success = false; } }); } @@ -104,7 +97,7 @@ class _DHTShortArrayWrite extends _DHTShortArrayRead } } if (!success) { - throw const DHTExceptionOutdated(); + throw DHTExceptionOutdated(); } } @@ -129,7 +122,7 @@ class _DHTShortArrayWrite extends _DHTShortArrayRead final outSeqNum = Output(); - final result = lookup.seq == null + final result = lookup.seq == 0xFFFFFFFF ? null : await lookup.record.get(subkey: lookup.recordSubkey); @@ -149,11 +142,6 @@ class _DHTShortArrayWrite extends _DHTShortArrayRead _head.clearIndex(); } - @override - Future truncate(int newLength) async { - _head.truncate(newLength); - } - @override Future tryWriteItem(int pos, Uint8List newValue, {Output? output}) async { @@ -163,7 +151,7 @@ class _DHTShortArrayWrite extends _DHTShortArrayRead final lookup = await _head.lookupPosition(pos, true); final outSeqNumRead = Output(); - final oldValue = lookup.seq == null + final oldValue = lookup.seq == 0xFFFFFFFF ? null : await lookup.record .get(subkey: lookup.recordSubkey, outSeqNum: outSeqNumRead); diff --git a/packages/veilid_support/lib/dht_support/src/interfaces/dht_closeable.dart b/packages/veilid_support/lib/dht_support/src/interfaces/dht_closeable.dart index 0fb10ab..c913340 100644 --- a/packages/veilid_support/lib/dht_support/src/interfaces/dht_closeable.dart +++ b/packages/veilid_support/lib/dht_support/src/interfaces/dht_closeable.dart @@ -4,7 +4,7 @@ import 'package:meta/meta.dart'; abstract class DHTCloseable { // Public interface - void ref(); + Future ref(); Future close(); // Internal implementation @@ -15,9 +15,7 @@ abstract class DHTCloseable { } abstract class DHTDeleteable extends DHTCloseable { - /// Returns true if the deletion was processed immediately - /// Returns false if the deletion was marked for later - Future delete(); + Future delete(); } extension DHTCloseableExt on DHTCloseable { diff --git a/packages/veilid_support/lib/dht_support/src/interfaces/dht_random_read.dart b/packages/veilid_support/lib/dht_support/src/interfaces/dht_random_read.dart index d361757..0547332 100644 --- a/packages/veilid_support/lib/dht_support/src/interfaces/dht_random_read.dart +++ b/packages/veilid_support/lib/dht_support/src/interfaces/dht_random_read.dart @@ -14,22 +14,19 @@ abstract class DHTRandomRead { /// is specified, the network will always be checked for newer values /// rather than returning the existing locally stored copy of the elements. /// Throws an IndexError if the 'pos' is not within the length - /// of the container. May return null if the item is not available at this - /// time. + /// of the container. Future get(int pos, {bool forceRefresh = false}); /// Return a list of a range of items in the DHTArray. If 'forceRefresh' /// is specified, the network will always be checked for newer values /// rather than returning the existing locally stored copy of the elements. /// Throws an IndexError if either 'start' or '(start+length)' is not within - /// the length of the container. May return fewer items than the length - /// expected if the requested items are not available, but will always - /// return a contiguous range starting at 'start'. + /// the length of the container. Future?> getRange(int start, {int? length, bool forceRefresh = false}); /// Get a list of the positions that were written offline and not flushed yet - Future> getOfflinePositions(); + Future?> getOfflinePositions(); } extension DHTRandomReadExt on DHTRandomRead { diff --git a/packages/veilid_support/lib/dht_support/src/interfaces/exceptions.dart b/packages/veilid_support/lib/dht_support/src/interfaces/exceptions.dart index 134f5fa..b17dbee 100644 --- a/packages/veilid_support/lib/dht_support/src/interfaces/exceptions.dart +++ b/packages/veilid_support/lib/dht_support/src/interfaces/exceptions.dart @@ -2,32 +2,20 @@ class DHTExceptionOutdated implements Exception { const DHTExceptionOutdated( [this.cause = 'operation failed due to newer dht value']); final String cause; - - @override - String toString() => 'DHTExceptionOutdated: $cause'; } class DHTExceptionInvalidData implements Exception { - const DHTExceptionInvalidData(this.cause); + const DHTExceptionInvalidData([this.cause = 'dht data structure is corrupt']); final String cause; - - @override - String toString() => 'DHTExceptionInvalidData: $cause'; } class DHTExceptionCancelled implements Exception { const DHTExceptionCancelled([this.cause = 'operation was cancelled']); final String cause; - - @override - String toString() => 'DHTExceptionCancelled: $cause'; } class DHTExceptionNotAvailable implements Exception { const DHTExceptionNotAvailable( [this.cause = 'request could not be completed at this time']); final String cause; - - @override - String toString() => 'DHTExceptionNotAvailable: $cause'; } diff --git a/packages/veilid_support/lib/identity_support/account_record_info.dart b/packages/veilid_support/lib/identity_support/account_record_info.dart index c74baac..60accf9 100644 --- a/packages/veilid_support/lib/identity_support/account_record_info.dart +++ b/packages/veilid_support/lib/identity_support/account_record_info.dart @@ -8,7 +8,7 @@ part 'account_record_info.g.dart'; /// AccountRecordInfo is the key and owner info for the account dht record that /// is stored in the identity instance record @freezed -sealed class AccountRecordInfo with _$AccountRecordInfo { +class AccountRecordInfo with _$AccountRecordInfo { const factory AccountRecordInfo({ // Top level account keys and secrets required OwnedDHTRecordPointer accountRecord, diff --git a/packages/veilid_support/lib/identity_support/account_record_info.freezed.dart b/packages/veilid_support/lib/identity_support/account_record_info.freezed.dart index b1796f6..0d5b327 100644 --- a/packages/veilid_support/lib/identity_support/account_record_info.freezed.dart +++ b/packages/veilid_support/lib/identity_support/account_record_info.freezed.dart @@ -1,4 +1,3 @@ -// dart format width=80 // coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND // ignore_for_file: type=lint @@ -10,49 +9,31 @@ part of 'account_record_info.dart'; // FreezedGenerator // ************************************************************************** -// dart format off T _$identity(T value) => value; +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +AccountRecordInfo _$AccountRecordInfoFromJson(Map json) { + return _AccountRecordInfo.fromJson(json); +} + /// @nodoc mixin _$AccountRecordInfo { // Top level account keys and secrets - OwnedDHTRecordPointer get accountRecord; + OwnedDHTRecordPointer get accountRecord => throw _privateConstructorUsedError; - /// Create a copy of AccountRecordInfo - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) $AccountRecordInfoCopyWith get copyWith => - _$AccountRecordInfoCopyWithImpl( - this as AccountRecordInfo, _$identity); - - /// Serializes this AccountRecordInfo to a JSON map. - Map toJson(); - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is AccountRecordInfo && - (identical(other.accountRecord, accountRecord) || - other.accountRecord == accountRecord)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash(runtimeType, accountRecord); - - @override - String toString() { - return 'AccountRecordInfo(accountRecord: $accountRecord)'; - } + throw _privateConstructorUsedError; } /// @nodoc -abstract mixin class $AccountRecordInfoCopyWith<$Res> { +abstract class $AccountRecordInfoCopyWith<$Res> { factory $AccountRecordInfoCopyWith( - AccountRecordInfo value, $Res Function(AccountRecordInfo) _then) = - _$AccountRecordInfoCopyWithImpl; + AccountRecordInfo value, $Res Function(AccountRecordInfo) then) = + _$AccountRecordInfoCopyWithImpl<$Res, AccountRecordInfo>; @useResult $Res call({OwnedDHTRecordPointer accountRecord}); @@ -60,130 +41,130 @@ abstract mixin class $AccountRecordInfoCopyWith<$Res> { } /// @nodoc -class _$AccountRecordInfoCopyWithImpl<$Res> +class _$AccountRecordInfoCopyWithImpl<$Res, $Val extends AccountRecordInfo> implements $AccountRecordInfoCopyWith<$Res> { - _$AccountRecordInfoCopyWithImpl(this._self, this._then); + _$AccountRecordInfoCopyWithImpl(this._value, this._then); - final AccountRecordInfo _self; - final $Res Function(AccountRecordInfo) _then; + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; - /// Create a copy of AccountRecordInfo - /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ Object? accountRecord = null, }) { - return _then(_self.copyWith( + return _then(_value.copyWith( accountRecord: null == accountRecord - ? _self.accountRecord + ? _value.accountRecord : accountRecord // ignore: cast_nullable_to_non_nullable as OwnedDHTRecordPointer, - )); + ) as $Val); } - /// Create a copy of AccountRecordInfo - /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $OwnedDHTRecordPointerCopyWith<$Res> get accountRecord { - return $OwnedDHTRecordPointerCopyWith<$Res>(_self.accountRecord, (value) { - return _then(_self.copyWith(accountRecord: value)); + return $OwnedDHTRecordPointerCopyWith<$Res>(_value.accountRecord, (value) { + return _then(_value.copyWith(accountRecord: value) as $Val); }); } } +/// @nodoc +abstract class _$$AccountRecordInfoImplCopyWith<$Res> + implements $AccountRecordInfoCopyWith<$Res> { + factory _$$AccountRecordInfoImplCopyWith(_$AccountRecordInfoImpl value, + $Res Function(_$AccountRecordInfoImpl) then) = + __$$AccountRecordInfoImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({OwnedDHTRecordPointer accountRecord}); + + @override + $OwnedDHTRecordPointerCopyWith<$Res> get accountRecord; +} + +/// @nodoc +class __$$AccountRecordInfoImplCopyWithImpl<$Res> + extends _$AccountRecordInfoCopyWithImpl<$Res, _$AccountRecordInfoImpl> + implements _$$AccountRecordInfoImplCopyWith<$Res> { + __$$AccountRecordInfoImplCopyWithImpl(_$AccountRecordInfoImpl _value, + $Res Function(_$AccountRecordInfoImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? accountRecord = null, + }) { + return _then(_$AccountRecordInfoImpl( + accountRecord: null == accountRecord + ? _value.accountRecord + : accountRecord // ignore: cast_nullable_to_non_nullable + as OwnedDHTRecordPointer, + )); + } +} + /// @nodoc @JsonSerializable() -class _AccountRecordInfo implements AccountRecordInfo { - const _AccountRecordInfo({required this.accountRecord}); - factory _AccountRecordInfo.fromJson(Map json) => - _$AccountRecordInfoFromJson(json); +class _$AccountRecordInfoImpl implements _AccountRecordInfo { + const _$AccountRecordInfoImpl({required this.accountRecord}); + + factory _$AccountRecordInfoImpl.fromJson(Map json) => + _$$AccountRecordInfoImplFromJson(json); // Top level account keys and secrets @override final OwnedDHTRecordPointer accountRecord; - /// Create a copy of AccountRecordInfo - /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') - _$AccountRecordInfoCopyWith<_AccountRecordInfo> get copyWith => - __$AccountRecordInfoCopyWithImpl<_AccountRecordInfo>(this, _$identity); - - @override - Map toJson() { - return _$AccountRecordInfoToJson( - this, - ); + String toString() { + return 'AccountRecordInfo(accountRecord: $accountRecord)'; } @override bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _AccountRecordInfo && + other is _$AccountRecordInfoImpl && (identical(other.accountRecord, accountRecord) || other.accountRecord == accountRecord)); } - @JsonKey(includeFromJson: false, includeToJson: false) + @JsonKey(ignore: true) @override int get hashCode => Object.hash(runtimeType, accountRecord); - @override - String toString() { - return 'AccountRecordInfo(accountRecord: $accountRecord)'; - } -} - -/// @nodoc -abstract mixin class _$AccountRecordInfoCopyWith<$Res> - implements $AccountRecordInfoCopyWith<$Res> { - factory _$AccountRecordInfoCopyWith( - _AccountRecordInfo value, $Res Function(_AccountRecordInfo) _then) = - __$AccountRecordInfoCopyWithImpl; - @override - @useResult - $Res call({OwnedDHTRecordPointer accountRecord}); - - @override - $OwnedDHTRecordPointerCopyWith<$Res> get accountRecord; -} - -/// @nodoc -class __$AccountRecordInfoCopyWithImpl<$Res> - implements _$AccountRecordInfoCopyWith<$Res> { - __$AccountRecordInfoCopyWithImpl(this._self, this._then); - - final _AccountRecordInfo _self; - final $Res Function(_AccountRecordInfo) _then; - - /// Create a copy of AccountRecordInfo - /// with the given fields replaced by the non-null parameter values. + @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - $Res call({ - Object? accountRecord = null, - }) { - return _then(_AccountRecordInfo( - accountRecord: null == accountRecord - ? _self.accountRecord - : accountRecord // ignore: cast_nullable_to_non_nullable - as OwnedDHTRecordPointer, - )); - } + _$$AccountRecordInfoImplCopyWith<_$AccountRecordInfoImpl> get copyWith => + __$$AccountRecordInfoImplCopyWithImpl<_$AccountRecordInfoImpl>( + this, _$identity); - /// Create a copy of AccountRecordInfo - /// with the given fields replaced by the non-null parameter values. @override - @pragma('vm:prefer-inline') - $OwnedDHTRecordPointerCopyWith<$Res> get accountRecord { - return $OwnedDHTRecordPointerCopyWith<$Res>(_self.accountRecord, (value) { - return _then(_self.copyWith(accountRecord: value)); - }); + Map toJson() { + return _$$AccountRecordInfoImplToJson( + this, + ); } } -// dart format on +abstract class _AccountRecordInfo implements AccountRecordInfo { + const factory _AccountRecordInfo( + {required final OwnedDHTRecordPointer accountRecord}) = + _$AccountRecordInfoImpl; + + factory _AccountRecordInfo.fromJson(Map json) = + _$AccountRecordInfoImpl.fromJson; + + @override // Top level account keys and secrets + OwnedDHTRecordPointer get accountRecord; + @override + @JsonKey(ignore: true) + _$$AccountRecordInfoImplCopyWith<_$AccountRecordInfoImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/packages/veilid_support/lib/identity_support/account_record_info.g.dart b/packages/veilid_support/lib/identity_support/account_record_info.g.dart index 429f9d0..ad9318c 100644 --- a/packages/veilid_support/lib/identity_support/account_record_info.g.dart +++ b/packages/veilid_support/lib/identity_support/account_record_info.g.dart @@ -6,12 +6,14 @@ part of 'account_record_info.dart'; // JsonSerializableGenerator // ************************************************************************** -_AccountRecordInfo _$AccountRecordInfoFromJson(Map json) => - _AccountRecordInfo( +_$AccountRecordInfoImpl _$$AccountRecordInfoImplFromJson( + Map json) => + _$AccountRecordInfoImpl( accountRecord: OwnedDHTRecordPointer.fromJson(json['account_record']), ); -Map _$AccountRecordInfoToJson(_AccountRecordInfo instance) => +Map _$$AccountRecordInfoImplToJson( + _$AccountRecordInfoImpl instance) => { 'account_record': instance.accountRecord.toJson(), }; diff --git a/packages/veilid_support/lib/identity_support/identity.dart b/packages/veilid_support/lib/identity_support/identity.dart index c1c7113..ea9c38c 100644 --- a/packages/veilid_support/lib/identity_support/identity.dart +++ b/packages/veilid_support/lib/identity_support/identity.dart @@ -14,7 +14,7 @@ part 'identity.g.dart'; /// DHT Secret: IdentityInstance Secret Key (stored encrypted with unlock code /// in local table store) @freezed -sealed class Identity with _$Identity { +class Identity with _$Identity { const factory Identity({ // Top level account keys and secrets required IMap> accountRecords, diff --git a/packages/veilid_support/lib/identity_support/identity.freezed.dart b/packages/veilid_support/lib/identity_support/identity.freezed.dart index d9f08f9..5977a26 100644 --- a/packages/veilid_support/lib/identity_support/identity.freezed.dart +++ b/packages/veilid_support/lib/identity_support/identity.freezed.dart @@ -1,4 +1,3 @@ -// dart format width=80 // coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND // ignore_for_file: type=lint @@ -10,68 +9,86 @@ part of 'identity.dart'; // FreezedGenerator // ************************************************************************** -// dart format off T _$identity(T value) => value; +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +Identity _$IdentityFromJson(Map json) { + return _Identity.fromJson(json); +} + /// @nodoc mixin _$Identity { // Top level account keys and secrets - IMap> get accountRecords; + IMap> get accountRecords => + throw _privateConstructorUsedError; - /// Create a copy of Identity - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) $IdentityCopyWith get copyWith => - _$IdentityCopyWithImpl(this as Identity, _$identity); - - /// Serializes this Identity to a JSON map. - Map toJson(); - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is Identity && - (identical(other.accountRecords, accountRecords) || - other.accountRecords == accountRecords)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash(runtimeType, accountRecords); - - @override - String toString() { - return 'Identity(accountRecords: $accountRecords)'; - } + throw _privateConstructorUsedError; } /// @nodoc -abstract mixin class $IdentityCopyWith<$Res> { - factory $IdentityCopyWith(Identity value, $Res Function(Identity) _then) = - _$IdentityCopyWithImpl; +abstract class $IdentityCopyWith<$Res> { + factory $IdentityCopyWith(Identity value, $Res Function(Identity) then) = + _$IdentityCopyWithImpl<$Res, Identity>; @useResult $Res call({IMap> accountRecords}); } /// @nodoc -class _$IdentityCopyWithImpl<$Res> implements $IdentityCopyWith<$Res> { - _$IdentityCopyWithImpl(this._self, this._then); +class _$IdentityCopyWithImpl<$Res, $Val extends Identity> + implements $IdentityCopyWith<$Res> { + _$IdentityCopyWithImpl(this._value, this._then); - final Identity _self; - final $Res Function(Identity) _then; + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; - /// Create a copy of Identity - /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ Object? accountRecords = null, }) { - return _then(_self.copyWith( + return _then(_value.copyWith( accountRecords: null == accountRecords - ? _self.accountRecords + ? _value.accountRecords + : accountRecords // ignore: cast_nullable_to_non_nullable + as IMap>, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$IdentityImplCopyWith<$Res> + implements $IdentityCopyWith<$Res> { + factory _$$IdentityImplCopyWith( + _$IdentityImpl value, $Res Function(_$IdentityImpl) then) = + __$$IdentityImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({IMap> accountRecords}); +} + +/// @nodoc +class __$$IdentityImplCopyWithImpl<$Res> + extends _$IdentityCopyWithImpl<$Res, _$IdentityImpl> + implements _$$IdentityImplCopyWith<$Res> { + __$$IdentityImplCopyWithImpl( + _$IdentityImpl _value, $Res Function(_$IdentityImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? accountRecords = null, + }) { + return _then(_$IdentityImpl( + accountRecords: null == accountRecords + ? _value.accountRecords : accountRecords // ignore: cast_nullable_to_non_nullable as IMap>, )); @@ -80,80 +97,60 @@ class _$IdentityCopyWithImpl<$Res> implements $IdentityCopyWith<$Res> { /// @nodoc @JsonSerializable() -class _Identity implements Identity { - const _Identity({required this.accountRecords}); - factory _Identity.fromJson(Map json) => - _$IdentityFromJson(json); +class _$IdentityImpl implements _Identity { + const _$IdentityImpl({required this.accountRecords}); + + factory _$IdentityImpl.fromJson(Map json) => + _$$IdentityImplFromJson(json); // Top level account keys and secrets @override final IMap> accountRecords; - /// Create a copy of Identity - /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') - _$IdentityCopyWith<_Identity> get copyWith => - __$IdentityCopyWithImpl<_Identity>(this, _$identity); - - @override - Map toJson() { - return _$IdentityToJson( - this, - ); + String toString() { + return 'Identity(accountRecords: $accountRecords)'; } @override bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _Identity && + other is _$IdentityImpl && (identical(other.accountRecords, accountRecords) || other.accountRecords == accountRecords)); } - @JsonKey(includeFromJson: false, includeToJson: false) + @JsonKey(ignore: true) @override int get hashCode => Object.hash(runtimeType, accountRecords); - @override - String toString() { - return 'Identity(accountRecords: $accountRecords)'; - } -} - -/// @nodoc -abstract mixin class _$IdentityCopyWith<$Res> - implements $IdentityCopyWith<$Res> { - factory _$IdentityCopyWith(_Identity value, $Res Function(_Identity) _then) = - __$IdentityCopyWithImpl; - @override - @useResult - $Res call({IMap> accountRecords}); -} - -/// @nodoc -class __$IdentityCopyWithImpl<$Res> implements _$IdentityCopyWith<$Res> { - __$IdentityCopyWithImpl(this._self, this._then); - - final _Identity _self; - final $Res Function(_Identity) _then; - - /// Create a copy of Identity - /// with the given fields replaced by the non-null parameter values. + @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - $Res call({ - Object? accountRecords = null, - }) { - return _then(_Identity( - accountRecords: null == accountRecords - ? _self.accountRecords - : accountRecords // ignore: cast_nullable_to_non_nullable - as IMap>, - )); + _$$IdentityImplCopyWith<_$IdentityImpl> get copyWith => + __$$IdentityImplCopyWithImpl<_$IdentityImpl>(this, _$identity); + + @override + Map toJson() { + return _$$IdentityImplToJson( + this, + ); } } -// dart format on +abstract class _Identity implements Identity { + const factory _Identity( + {required final IMap> + accountRecords}) = _$IdentityImpl; + + factory _Identity.fromJson(Map json) = + _$IdentityImpl.fromJson; + + @override // Top level account keys and secrets + IMap> get accountRecords; + @override + @JsonKey(ignore: true) + _$$IdentityImplCopyWith<_$IdentityImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/packages/veilid_support/lib/identity_support/identity.g.dart b/packages/veilid_support/lib/identity_support/identity.g.dart index 1ee10b8..afc9088 100644 --- a/packages/veilid_support/lib/identity_support/identity.g.dart +++ b/packages/veilid_support/lib/identity_support/identity.g.dart @@ -6,7 +6,8 @@ part of 'identity.dart'; // JsonSerializableGenerator // ************************************************************************** -_Identity _$IdentityFromJson(Map json) => _Identity( +_$IdentityImpl _$$IdentityImplFromJson(Map json) => + _$IdentityImpl( accountRecords: IMap>.fromJson( json['account_records'] as Map, (value) => value as String, @@ -14,7 +15,8 @@ _Identity _$IdentityFromJson(Map json) => _Identity( value, (value) => AccountRecordInfo.fromJson(value))), ); -Map _$IdentityToJson(_Identity instance) => { +Map _$$IdentityImplToJson(_$IdentityImpl instance) => + { 'account_records': instance.accountRecords.toJson( (value) => value, (value) => value.toJson( diff --git a/packages/veilid_support/lib/identity_support/identity_instance.dart b/packages/veilid_support/lib/identity_support/identity_instance.dart index d2bc323..1b6bf1f 100644 --- a/packages/veilid_support/lib/identity_support/identity_instance.dart +++ b/packages/veilid_support/lib/identity_support/identity_instance.dart @@ -10,7 +10,7 @@ part 'identity_instance.freezed.dart'; part 'identity_instance.g.dart'; @freezed -sealed class IdentityInstance with _$IdentityInstance { +class IdentityInstance with _$IdentityInstance { const factory IdentityInstance({ // Private DHT record storing identity account mapping required TypedKey recordKey, diff --git a/packages/veilid_support/lib/identity_support/identity_instance.freezed.dart b/packages/veilid_support/lib/identity_support/identity_instance.freezed.dart index 42522d4..a7c3e78 100644 --- a/packages/veilid_support/lib/identity_support/identity_instance.freezed.dart +++ b/packages/veilid_support/lib/identity_support/identity_instance.freezed.dart @@ -1,4 +1,3 @@ -// dart format width=80 // coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND // ignore_for_file: type=lint @@ -10,76 +9,46 @@ part of 'identity_instance.dart'; // FreezedGenerator // ************************************************************************** -// dart format off T _$identity(T value) => value; +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +IdentityInstance _$IdentityInstanceFromJson(Map json) { + return _IdentityInstance.fromJson(json); +} + /// @nodoc mixin _$IdentityInstance { // Private DHT record storing identity account mapping - TypedKey get recordKey; // Public key of identity instance - PublicKey get publicKey; // Secret key of identity instance + Typed get recordKey => + throw _privateConstructorUsedError; // Public key of identity instance + FixedEncodedString43 get publicKey => + throw _privateConstructorUsedError; // Secret key of identity instance // Encrypted with appended salt, key is DeriveSharedSecret( // password = SuperIdentity.secret, // salt = publicKey) // Used to recover accounts without generating a new instance @Uint8ListJsonConverter() - Uint8List - get encryptedSecretKey; // Signature of SuperInstance recordKey and SuperInstance publicKey + Uint8List get encryptedSecretKey => + throw _privateConstructorUsedError; // Signature of SuperInstance recordKey and SuperInstance publicKey // by publicKey - Signature - get superSignature; // Signature of recordKey, publicKey, encryptedSecretKey, and superSignature + FixedEncodedString86 get superSignature => + throw _privateConstructorUsedError; // Signature of recordKey, publicKey, encryptedSecretKey, and superSignature // by SuperIdentity publicKey - Signature get signature; + FixedEncodedString86 get signature => throw _privateConstructorUsedError; - /// Create a copy of IdentityInstance - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) $IdentityInstanceCopyWith get copyWith => - _$IdentityInstanceCopyWithImpl( - this as IdentityInstance, _$identity); - - /// Serializes this IdentityInstance to a JSON map. - Map toJson(); - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is IdentityInstance && - (identical(other.recordKey, recordKey) || - other.recordKey == recordKey) && - (identical(other.publicKey, publicKey) || - other.publicKey == publicKey) && - const DeepCollectionEquality() - .equals(other.encryptedSecretKey, encryptedSecretKey) && - (identical(other.superSignature, superSignature) || - other.superSignature == superSignature) && - (identical(other.signature, signature) || - other.signature == signature)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash( - runtimeType, - recordKey, - publicKey, - const DeepCollectionEquality().hash(encryptedSecretKey), - superSignature, - signature); - - @override - String toString() { - return 'IdentityInstance(recordKey: $recordKey, publicKey: $publicKey, encryptedSecretKey: $encryptedSecretKey, superSignature: $superSignature, signature: $signature)'; - } + throw _privateConstructorUsedError; } /// @nodoc -abstract mixin class $IdentityInstanceCopyWith<$Res> { +abstract class $IdentityInstanceCopyWith<$Res> { factory $IdentityInstanceCopyWith( - IdentityInstance value, $Res Function(IdentityInstance) _then) = - _$IdentityInstanceCopyWithImpl; + IdentityInstance value, $Res Function(IdentityInstance) then) = + _$IdentityInstanceCopyWithImpl<$Res, IdentityInstance>; @useResult $Res call( {Typed recordKey, @@ -90,15 +59,15 @@ abstract mixin class $IdentityInstanceCopyWith<$Res> { } /// @nodoc -class _$IdentityInstanceCopyWithImpl<$Res> +class _$IdentityInstanceCopyWithImpl<$Res, $Val extends IdentityInstance> implements $IdentityInstanceCopyWith<$Res> { - _$IdentityInstanceCopyWithImpl(this._self, this._then); + _$IdentityInstanceCopyWithImpl(this._value, this._then); - final IdentityInstance _self; - final $Res Function(IdentityInstance) _then; + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; - /// Create a copy of IdentityInstance - /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -108,25 +77,83 @@ class _$IdentityInstanceCopyWithImpl<$Res> Object? superSignature = null, Object? signature = null, }) { - return _then(_self.copyWith( + return _then(_value.copyWith( recordKey: null == recordKey - ? _self.recordKey! + ? _value.recordKey : recordKey // ignore: cast_nullable_to_non_nullable as Typed, publicKey: null == publicKey - ? _self.publicKey! + ? _value.publicKey : publicKey // ignore: cast_nullable_to_non_nullable as FixedEncodedString43, encryptedSecretKey: null == encryptedSecretKey - ? _self.encryptedSecretKey + ? _value.encryptedSecretKey : encryptedSecretKey // ignore: cast_nullable_to_non_nullable as Uint8List, superSignature: null == superSignature - ? _self.superSignature! + ? _value.superSignature : superSignature // ignore: cast_nullable_to_non_nullable as FixedEncodedString86, signature: null == signature - ? _self.signature! + ? _value.signature + : signature // ignore: cast_nullable_to_non_nullable + as FixedEncodedString86, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$IdentityInstanceImplCopyWith<$Res> + implements $IdentityInstanceCopyWith<$Res> { + factory _$$IdentityInstanceImplCopyWith(_$IdentityInstanceImpl value, + $Res Function(_$IdentityInstanceImpl) then) = + __$$IdentityInstanceImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {Typed recordKey, + FixedEncodedString43 publicKey, + @Uint8ListJsonConverter() Uint8List encryptedSecretKey, + FixedEncodedString86 superSignature, + FixedEncodedString86 signature}); +} + +/// @nodoc +class __$$IdentityInstanceImplCopyWithImpl<$Res> + extends _$IdentityInstanceCopyWithImpl<$Res, _$IdentityInstanceImpl> + implements _$$IdentityInstanceImplCopyWith<$Res> { + __$$IdentityInstanceImplCopyWithImpl(_$IdentityInstanceImpl _value, + $Res Function(_$IdentityInstanceImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? recordKey = null, + Object? publicKey = null, + Object? encryptedSecretKey = null, + Object? superSignature = null, + Object? signature = null, + }) { + return _then(_$IdentityInstanceImpl( + recordKey: null == recordKey + ? _value.recordKey + : recordKey // ignore: cast_nullable_to_non_nullable + as Typed, + publicKey: null == publicKey + ? _value.publicKey + : publicKey // ignore: cast_nullable_to_non_nullable + as FixedEncodedString43, + encryptedSecretKey: null == encryptedSecretKey + ? _value.encryptedSecretKey + : encryptedSecretKey // ignore: cast_nullable_to_non_nullable + as Uint8List, + superSignature: null == superSignature + ? _value.superSignature + : superSignature // ignore: cast_nullable_to_non_nullable + as FixedEncodedString86, + signature: null == signature + ? _value.signature : signature // ignore: cast_nullable_to_non_nullable as FixedEncodedString86, )); @@ -135,16 +162,17 @@ class _$IdentityInstanceCopyWithImpl<$Res> /// @nodoc @JsonSerializable() -class _IdentityInstance extends IdentityInstance { - const _IdentityInstance( +class _$IdentityInstanceImpl extends _IdentityInstance { + const _$IdentityInstanceImpl( {required this.recordKey, required this.publicKey, @Uint8ListJsonConverter() required this.encryptedSecretKey, required this.superSignature, required this.signature}) : super._(); - factory _IdentityInstance.fromJson(Map json) => - _$IdentityInstanceFromJson(json); + + factory _$IdentityInstanceImpl.fromJson(Map json) => + _$$IdentityInstanceImplFromJson(json); // Private DHT record storing identity account mapping @override @@ -169,26 +197,16 @@ class _IdentityInstance extends IdentityInstance { @override final FixedEncodedString86 signature; - /// Create a copy of IdentityInstance - /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') - _$IdentityInstanceCopyWith<_IdentityInstance> get copyWith => - __$IdentityInstanceCopyWithImpl<_IdentityInstance>(this, _$identity); - - @override - Map toJson() { - return _$IdentityInstanceToJson( - this, - ); + String toString() { + return 'IdentityInstance(recordKey: $recordKey, publicKey: $publicKey, encryptedSecretKey: $encryptedSecretKey, superSignature: $superSignature, signature: $signature)'; } @override bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _IdentityInstance && + other is _$IdentityInstanceImpl && (identical(other.recordKey, recordKey) || other.recordKey == recordKey) && (identical(other.publicKey, publicKey) || @@ -201,7 +219,7 @@ class _IdentityInstance extends IdentityInstance { other.signature == signature)); } - @JsonKey(includeFromJson: false, includeToJson: false) + @JsonKey(ignore: true) @override int get hashCode => Object.hash( runtimeType, @@ -211,70 +229,52 @@ class _IdentityInstance extends IdentityInstance { superSignature, signature); - @override - String toString() { - return 'IdentityInstance(recordKey: $recordKey, publicKey: $publicKey, encryptedSecretKey: $encryptedSecretKey, superSignature: $superSignature, signature: $signature)'; - } -} - -/// @nodoc -abstract mixin class _$IdentityInstanceCopyWith<$Res> - implements $IdentityInstanceCopyWith<$Res> { - factory _$IdentityInstanceCopyWith( - _IdentityInstance value, $Res Function(_IdentityInstance) _then) = - __$IdentityInstanceCopyWithImpl; - @override - @useResult - $Res call( - {Typed recordKey, - FixedEncodedString43 publicKey, - @Uint8ListJsonConverter() Uint8List encryptedSecretKey, - FixedEncodedString86 superSignature, - FixedEncodedString86 signature}); -} - -/// @nodoc -class __$IdentityInstanceCopyWithImpl<$Res> - implements _$IdentityInstanceCopyWith<$Res> { - __$IdentityInstanceCopyWithImpl(this._self, this._then); - - final _IdentityInstance _self; - final $Res Function(_IdentityInstance) _then; - - /// Create a copy of IdentityInstance - /// with the given fields replaced by the non-null parameter values. + @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - $Res call({ - Object? recordKey = null, - Object? publicKey = null, - Object? encryptedSecretKey = null, - Object? superSignature = null, - Object? signature = null, - }) { - return _then(_IdentityInstance( - recordKey: null == recordKey - ? _self.recordKey - : recordKey // ignore: cast_nullable_to_non_nullable - as Typed, - publicKey: null == publicKey - ? _self.publicKey - : publicKey // ignore: cast_nullable_to_non_nullable - as FixedEncodedString43, - encryptedSecretKey: null == encryptedSecretKey - ? _self.encryptedSecretKey - : encryptedSecretKey // ignore: cast_nullable_to_non_nullable - as Uint8List, - superSignature: null == superSignature - ? _self.superSignature - : superSignature // ignore: cast_nullable_to_non_nullable - as FixedEncodedString86, - signature: null == signature - ? _self.signature - : signature // ignore: cast_nullable_to_non_nullable - as FixedEncodedString86, - )); + _$$IdentityInstanceImplCopyWith<_$IdentityInstanceImpl> get copyWith => + __$$IdentityInstanceImplCopyWithImpl<_$IdentityInstanceImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$IdentityInstanceImplToJson( + this, + ); } } -// dart format on +abstract class _IdentityInstance extends IdentityInstance { + const factory _IdentityInstance( + {required final Typed recordKey, + required final FixedEncodedString43 publicKey, + @Uint8ListJsonConverter() required final Uint8List encryptedSecretKey, + required final FixedEncodedString86 superSignature, + required final FixedEncodedString86 signature}) = _$IdentityInstanceImpl; + const _IdentityInstance._() : super._(); + + factory _IdentityInstance.fromJson(Map json) = + _$IdentityInstanceImpl.fromJson; + + @override // Private DHT record storing identity account mapping + Typed get recordKey; + @override // Public key of identity instance + FixedEncodedString43 get publicKey; + @override // Secret key of identity instance +// Encrypted with appended salt, key is DeriveSharedSecret( +// password = SuperIdentity.secret, +// salt = publicKey) +// Used to recover accounts without generating a new instance + @Uint8ListJsonConverter() + Uint8List get encryptedSecretKey; + @override // Signature of SuperInstance recordKey and SuperInstance publicKey +// by publicKey + FixedEncodedString86 get superSignature; + @override // Signature of recordKey, publicKey, encryptedSecretKey, and superSignature +// by SuperIdentity publicKey + FixedEncodedString86 get signature; + @override + @JsonKey(ignore: true) + _$$IdentityInstanceImplCopyWith<_$IdentityInstanceImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/packages/veilid_support/lib/identity_support/identity_instance.g.dart b/packages/veilid_support/lib/identity_support/identity_instance.g.dart index eddbcf6..cb228e6 100644 --- a/packages/veilid_support/lib/identity_support/identity_instance.g.dart +++ b/packages/veilid_support/lib/identity_support/identity_instance.g.dart @@ -6,8 +6,9 @@ part of 'identity_instance.dart'; // JsonSerializableGenerator // ************************************************************************** -_IdentityInstance _$IdentityInstanceFromJson(Map json) => - _IdentityInstance( +_$IdentityInstanceImpl _$$IdentityInstanceImplFromJson( + Map json) => + _$IdentityInstanceImpl( recordKey: Typed.fromJson(json['record_key']), publicKey: FixedEncodedString43.fromJson(json['public_key']), encryptedSecretKey: @@ -16,7 +17,8 @@ _IdentityInstance _$IdentityInstanceFromJson(Map json) => signature: FixedEncodedString86.fromJson(json['signature']), ); -Map _$IdentityInstanceToJson(_IdentityInstance instance) => +Map _$$IdentityInstanceImplToJson( + _$IdentityInstanceImpl instance) => { 'record_key': instance.recordKey.toJson(), 'public_key': instance.publicKey.toJson(), diff --git a/packages/veilid_support/lib/identity_support/identity_support.dart b/packages/veilid_support/lib/identity_support/identity_support.dart index 68723bf..463be9a 100644 --- a/packages/veilid_support/lib/identity_support/identity_support.dart +++ b/packages/veilid_support/lib/identity_support/identity_support.dart @@ -3,5 +3,4 @@ export 'exceptions.dart'; export 'identity.dart'; export 'identity_instance.dart'; export 'super_identity.dart'; -export 'super_identity_cubit.dart'; export 'writable_super_identity.dart'; diff --git a/packages/veilid_support/lib/identity_support/super_identity.dart b/packages/veilid_support/lib/identity_support/super_identity.dart index c8fd59d..e4ec8fc 100644 --- a/packages/veilid_support/lib/identity_support/super_identity.dart +++ b/packages/veilid_support/lib/identity_support/super_identity.dart @@ -22,8 +22,7 @@ part 'super_identity.g.dart'; /// DHT Owner Secret: SuperIdentity Secret Key (kept offline) /// Encryption: None @freezed -sealed class SuperIdentity with _$SuperIdentity { - @JsonSerializable() +class SuperIdentity with _$SuperIdentity { const factory SuperIdentity({ /// Public DHT record storing this structure for account recovery /// changing this can migrate/forward the SuperIdentity to a new DHT record @@ -64,40 +63,7 @@ sealed class SuperIdentity with _$SuperIdentity { const SuperIdentity._(); - /// Ensure a SuperIdentity is valid - Future validate({required TypedKey superRecordKey}) async { - // Validate current IdentityInstance - if (!await currentInstance.validateIdentityInstance( - superRecordKey: superRecordKey, superPublicKey: publicKey)) { - // Invalid current IdentityInstance signature(s) - throw IdentityException.invalid; - } - - // Validate deprecated IdentityInstances - for (final deprecatedInstance in deprecatedInstances) { - if (!await deprecatedInstance.validateIdentityInstance( - superRecordKey: superRecordKey, superPublicKey: publicKey)) { - // Invalid deprecated IdentityInstance signature(s) - throw IdentityException.invalid; - } - } - - // Validate SuperIdentity - final deprecatedInstancesSignatures = - deprecatedInstances.map((x) => x.signature).toList(); - if (!await _validateSuperIdentitySignature( - recordKey: recordKey, - currentInstanceSignature: currentInstance.signature, - deprecatedInstancesSignatures: deprecatedInstancesSignatures, - deprecatedSuperRecordKeys: deprecatedSuperRecordKeys, - publicKey: publicKey, - signature: signature)) { - // Invalid SuperIdentity signature - throw IdentityException.invalid; - } - } - - /// Opens an existing super identity, validates it, and returns it + /// Opens an existing super identity and validates it static Future open({required TypedKey superRecordKey}) async { final pool = DHTRecordPool.instance; @@ -108,7 +74,37 @@ sealed class SuperIdentity with _$SuperIdentity { final superIdentity = (await superRec.getJson(SuperIdentity.fromJson, refreshMode: DHTRecordRefreshMode.network))!; - await superIdentity.validate(superRecordKey: superRecordKey); + // Validate current IdentityInstance + if (!await superIdentity.currentInstance.validateIdentityInstance( + superRecordKey: superRecordKey, + superPublicKey: superIdentity.publicKey)) { + // Invalid current IdentityInstance signature(s) + throw IdentityException.invalid; + } + + // Validate deprecated IdentityInstances + for (final deprecatedInstance in superIdentity.deprecatedInstances) { + if (!await deprecatedInstance.validateIdentityInstance( + superRecordKey: superRecordKey, + superPublicKey: superIdentity.publicKey)) { + // Invalid deprecated IdentityInstance signature(s) + throw IdentityException.invalid; + } + } + + // Validate SuperIdentity + final deprecatedInstancesSignatures = + superIdentity.deprecatedInstances.map((x) => x.signature).toList(); + if (!await _validateSuperIdentitySignature( + recordKey: superIdentity.recordKey, + currentInstanceSignature: superIdentity.currentInstance.signature, + deprecatedInstancesSignatures: deprecatedInstancesSignatures, + deprecatedSuperRecordKeys: superIdentity.deprecatedSuperRecordKeys, + publicKey: superIdentity.publicKey, + signature: superIdentity.signature)) { + // Invalid SuperIdentity signature + throw IdentityException.invalid; + } return superIdentity; }); diff --git a/packages/veilid_support/lib/identity_support/super_identity.freezed.dart b/packages/veilid_support/lib/identity_support/super_identity.freezed.dart index 3144205..dc1c69a 100644 --- a/packages/veilid_support/lib/identity_support/super_identity.freezed.dart +++ b/packages/veilid_support/lib/identity_support/super_identity.freezed.dart @@ -1,4 +1,3 @@ -// dart format width=80 // coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND // ignore_for_file: type=lint @@ -10,93 +9,61 @@ part of 'super_identity.dart'; // FreezedGenerator // ************************************************************************** -// dart format off T _$identity(T value) => value; +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +SuperIdentity _$SuperIdentityFromJson(Map json) { + return _SuperIdentity.fromJson(json); +} + /// @nodoc mixin _$SuperIdentity { /// Public DHT record storing this structure for account recovery /// changing this can migrate/forward the SuperIdentity to a new DHT record /// Instances should not hash this recordKey, rather the actual record /// key used to store the superIdentity, as this may change. - TypedKey get recordKey; + Typed get recordKey => + throw _privateConstructorUsedError; /// Public key of the SuperIdentity used to sign identity keys for recovery /// This must match the owner of the superRecord DHT record and can not be /// changed without changing the record - PublicKey get publicKey; + FixedEncodedString43 get publicKey => throw _privateConstructorUsedError; /// Current identity instance /// The most recently generated identity instance for this SuperIdentity - IdentityInstance get currentInstance; + IdentityInstance get currentInstance => throw _privateConstructorUsedError; /// Deprecated identity instances /// These may be compromised and should not be considered valid for /// new signatures, but may be used to validate old signatures - List get deprecatedInstances; + List get deprecatedInstances => + throw _privateConstructorUsedError; /// Deprecated superRecords /// These may be compromised and should not be considered valid for /// new signatures, but may be used to validate old signatures - List get deprecatedSuperRecordKeys; + List> get deprecatedSuperRecordKeys => + throw _privateConstructorUsedError; /// Signature of recordKey, currentInstance signature, /// signatures of deprecatedInstances, and deprecatedSuperRecordKeys /// by publicKey - Signature get signature; + FixedEncodedString86 get signature => throw _privateConstructorUsedError; - /// Create a copy of SuperIdentity - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) $SuperIdentityCopyWith get copyWith => - _$SuperIdentityCopyWithImpl( - this as SuperIdentity, _$identity); - - /// Serializes this SuperIdentity to a JSON map. - Map toJson(); - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is SuperIdentity && - (identical(other.recordKey, recordKey) || - other.recordKey == recordKey) && - (identical(other.publicKey, publicKey) || - other.publicKey == publicKey) && - (identical(other.currentInstance, currentInstance) || - other.currentInstance == currentInstance) && - const DeepCollectionEquality() - .equals(other.deprecatedInstances, deprecatedInstances) && - const DeepCollectionEquality().equals( - other.deprecatedSuperRecordKeys, deprecatedSuperRecordKeys) && - (identical(other.signature, signature) || - other.signature == signature)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash( - runtimeType, - recordKey, - publicKey, - currentInstance, - const DeepCollectionEquality().hash(deprecatedInstances), - const DeepCollectionEquality().hash(deprecatedSuperRecordKeys), - signature); - - @override - String toString() { - return 'SuperIdentity(recordKey: $recordKey, publicKey: $publicKey, currentInstance: $currentInstance, deprecatedInstances: $deprecatedInstances, deprecatedSuperRecordKeys: $deprecatedSuperRecordKeys, signature: $signature)'; - } + throw _privateConstructorUsedError; } /// @nodoc -abstract mixin class $SuperIdentityCopyWith<$Res> { +abstract class $SuperIdentityCopyWith<$Res> { factory $SuperIdentityCopyWith( - SuperIdentity value, $Res Function(SuperIdentity) _then) = - _$SuperIdentityCopyWithImpl; + SuperIdentity value, $Res Function(SuperIdentity) then) = + _$SuperIdentityCopyWithImpl<$Res, SuperIdentity>; @useResult $Res call( {Typed recordKey, @@ -110,15 +77,15 @@ abstract mixin class $SuperIdentityCopyWith<$Res> { } /// @nodoc -class _$SuperIdentityCopyWithImpl<$Res> +class _$SuperIdentityCopyWithImpl<$Res, $Val extends SuperIdentity> implements $SuperIdentityCopyWith<$Res> { - _$SuperIdentityCopyWithImpl(this._self, this._then); + _$SuperIdentityCopyWithImpl(this._value, this._then); - final SuperIdentity _self; - final $Res Function(SuperIdentity) _then; + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; - /// Create a copy of SuperIdentity - /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -129,50 +96,114 @@ class _$SuperIdentityCopyWithImpl<$Res> Object? deprecatedSuperRecordKeys = null, Object? signature = null, }) { - return _then(_self.copyWith( + return _then(_value.copyWith( recordKey: null == recordKey - ? _self.recordKey! + ? _value.recordKey : recordKey // ignore: cast_nullable_to_non_nullable as Typed, publicKey: null == publicKey - ? _self.publicKey! + ? _value.publicKey : publicKey // ignore: cast_nullable_to_non_nullable as FixedEncodedString43, currentInstance: null == currentInstance - ? _self.currentInstance + ? _value.currentInstance : currentInstance // ignore: cast_nullable_to_non_nullable as IdentityInstance, deprecatedInstances: null == deprecatedInstances - ? _self.deprecatedInstances + ? _value.deprecatedInstances : deprecatedInstances // ignore: cast_nullable_to_non_nullable as List, deprecatedSuperRecordKeys: null == deprecatedSuperRecordKeys - ? _self.deprecatedSuperRecordKeys! + ? _value.deprecatedSuperRecordKeys : deprecatedSuperRecordKeys // ignore: cast_nullable_to_non_nullable as List>, signature: null == signature - ? _self.signature! + ? _value.signature : signature // ignore: cast_nullable_to_non_nullable as FixedEncodedString86, - )); + ) as $Val); } - /// Create a copy of SuperIdentity - /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $IdentityInstanceCopyWith<$Res> get currentInstance { - return $IdentityInstanceCopyWith<$Res>(_self.currentInstance, (value) { - return _then(_self.copyWith(currentInstance: value)); + return $IdentityInstanceCopyWith<$Res>(_value.currentInstance, (value) { + return _then(_value.copyWith(currentInstance: value) as $Val); }); } } /// @nodoc +abstract class _$$SuperIdentityImplCopyWith<$Res> + implements $SuperIdentityCopyWith<$Res> { + factory _$$SuperIdentityImplCopyWith( + _$SuperIdentityImpl value, $Res Function(_$SuperIdentityImpl) then) = + __$$SuperIdentityImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {Typed recordKey, + FixedEncodedString43 publicKey, + IdentityInstance currentInstance, + List deprecatedInstances, + List> deprecatedSuperRecordKeys, + FixedEncodedString86 signature}); + @override + $IdentityInstanceCopyWith<$Res> get currentInstance; +} + +/// @nodoc +class __$$SuperIdentityImplCopyWithImpl<$Res> + extends _$SuperIdentityCopyWithImpl<$Res, _$SuperIdentityImpl> + implements _$$SuperIdentityImplCopyWith<$Res> { + __$$SuperIdentityImplCopyWithImpl( + _$SuperIdentityImpl _value, $Res Function(_$SuperIdentityImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? recordKey = null, + Object? publicKey = null, + Object? currentInstance = null, + Object? deprecatedInstances = null, + Object? deprecatedSuperRecordKeys = null, + Object? signature = null, + }) { + return _then(_$SuperIdentityImpl( + recordKey: null == recordKey + ? _value.recordKey + : recordKey // ignore: cast_nullable_to_non_nullable + as Typed, + publicKey: null == publicKey + ? _value.publicKey + : publicKey // ignore: cast_nullable_to_non_nullable + as FixedEncodedString43, + currentInstance: null == currentInstance + ? _value.currentInstance + : currentInstance // ignore: cast_nullable_to_non_nullable + as IdentityInstance, + deprecatedInstances: null == deprecatedInstances + ? _value._deprecatedInstances + : deprecatedInstances // ignore: cast_nullable_to_non_nullable + as List, + deprecatedSuperRecordKeys: null == deprecatedSuperRecordKeys + ? _value._deprecatedSuperRecordKeys + : deprecatedSuperRecordKeys // ignore: cast_nullable_to_non_nullable + as List>, + signature: null == signature + ? _value.signature + : signature // ignore: cast_nullable_to_non_nullable + as FixedEncodedString86, + )); + } +} + +/// @nodoc @JsonSerializable() -class _SuperIdentity extends SuperIdentity { - const _SuperIdentity( +class _$SuperIdentityImpl extends _SuperIdentity { + const _$SuperIdentityImpl( {required this.recordKey, required this.publicKey, required this.currentInstance, @@ -183,8 +214,9 @@ class _SuperIdentity extends SuperIdentity { : _deprecatedInstances = deprecatedInstances, _deprecatedSuperRecordKeys = deprecatedSuperRecordKeys, super._(); - factory _SuperIdentity.fromJson(Map json) => - _$SuperIdentityFromJson(json); + + factory _$SuperIdentityImpl.fromJson(Map json) => + _$$SuperIdentityImplFromJson(json); /// Public DHT record storing this structure for account recovery /// changing this can migrate/forward the SuperIdentity to a new DHT record @@ -242,26 +274,16 @@ class _SuperIdentity extends SuperIdentity { @override final FixedEncodedString86 signature; - /// Create a copy of SuperIdentity - /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(includeFromJson: false, includeToJson: false) - @pragma('vm:prefer-inline') - _$SuperIdentityCopyWith<_SuperIdentity> get copyWith => - __$SuperIdentityCopyWithImpl<_SuperIdentity>(this, _$identity); - - @override - Map toJson() { - return _$SuperIdentityToJson( - this, - ); + String toString() { + return 'SuperIdentity(recordKey: $recordKey, publicKey: $publicKey, currentInstance: $currentInstance, deprecatedInstances: $deprecatedInstances, deprecatedSuperRecordKeys: $deprecatedSuperRecordKeys, signature: $signature)'; } @override bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _SuperIdentity && + other is _$SuperIdentityImpl && (identical(other.recordKey, recordKey) || other.recordKey == recordKey) && (identical(other.publicKey, publicKey) || @@ -276,7 +298,7 @@ class _SuperIdentity extends SuperIdentity { other.signature == signature)); } - @JsonKey(includeFromJson: false, includeToJson: false) + @JsonKey(ignore: true) @override int get hashCode => Object.hash( runtimeType, @@ -287,89 +309,72 @@ class _SuperIdentity extends SuperIdentity { const DeepCollectionEquality().hash(_deprecatedSuperRecordKeys), signature); - @override - String toString() { - return 'SuperIdentity(recordKey: $recordKey, publicKey: $publicKey, currentInstance: $currentInstance, deprecatedInstances: $deprecatedInstances, deprecatedSuperRecordKeys: $deprecatedSuperRecordKeys, signature: $signature)'; - } -} - -/// @nodoc -abstract mixin class _$SuperIdentityCopyWith<$Res> - implements $SuperIdentityCopyWith<$Res> { - factory _$SuperIdentityCopyWith( - _SuperIdentity value, $Res Function(_SuperIdentity) _then) = - __$SuperIdentityCopyWithImpl; - @override - @useResult - $Res call( - {Typed recordKey, - FixedEncodedString43 publicKey, - IdentityInstance currentInstance, - List deprecatedInstances, - List> deprecatedSuperRecordKeys, - FixedEncodedString86 signature}); - - @override - $IdentityInstanceCopyWith<$Res> get currentInstance; -} - -/// @nodoc -class __$SuperIdentityCopyWithImpl<$Res> - implements _$SuperIdentityCopyWith<$Res> { - __$SuperIdentityCopyWithImpl(this._self, this._then); - - final _SuperIdentity _self; - final $Res Function(_SuperIdentity) _then; - - /// Create a copy of SuperIdentity - /// with the given fields replaced by the non-null parameter values. + @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - $Res call({ - Object? recordKey = null, - Object? publicKey = null, - Object? currentInstance = null, - Object? deprecatedInstances = null, - Object? deprecatedSuperRecordKeys = null, - Object? signature = null, - }) { - return _then(_SuperIdentity( - recordKey: null == recordKey - ? _self.recordKey - : recordKey // ignore: cast_nullable_to_non_nullable - as Typed, - publicKey: null == publicKey - ? _self.publicKey - : publicKey // ignore: cast_nullable_to_non_nullable - as FixedEncodedString43, - currentInstance: null == currentInstance - ? _self.currentInstance - : currentInstance // ignore: cast_nullable_to_non_nullable - as IdentityInstance, - deprecatedInstances: null == deprecatedInstances - ? _self._deprecatedInstances - : deprecatedInstances // ignore: cast_nullable_to_non_nullable - as List, - deprecatedSuperRecordKeys: null == deprecatedSuperRecordKeys - ? _self._deprecatedSuperRecordKeys - : deprecatedSuperRecordKeys // ignore: cast_nullable_to_non_nullable - as List>, - signature: null == signature - ? _self.signature - : signature // ignore: cast_nullable_to_non_nullable - as FixedEncodedString86, - )); - } + _$$SuperIdentityImplCopyWith<_$SuperIdentityImpl> get copyWith => + __$$SuperIdentityImplCopyWithImpl<_$SuperIdentityImpl>(this, _$identity); - /// Create a copy of SuperIdentity - /// with the given fields replaced by the non-null parameter values. @override - @pragma('vm:prefer-inline') - $IdentityInstanceCopyWith<$Res> get currentInstance { - return $IdentityInstanceCopyWith<$Res>(_self.currentInstance, (value) { - return _then(_self.copyWith(currentInstance: value)); - }); + Map toJson() { + return _$$SuperIdentityImplToJson( + this, + ); } } -// dart format on +abstract class _SuperIdentity extends SuperIdentity { + const factory _SuperIdentity( + {required final Typed recordKey, + required final FixedEncodedString43 publicKey, + required final IdentityInstance currentInstance, + required final List deprecatedInstances, + required final List> + deprecatedSuperRecordKeys, + required final FixedEncodedString86 signature}) = _$SuperIdentityImpl; + const _SuperIdentity._() : super._(); + + factory _SuperIdentity.fromJson(Map json) = + _$SuperIdentityImpl.fromJson; + + @override + + /// Public DHT record storing this structure for account recovery + /// changing this can migrate/forward the SuperIdentity to a new DHT record + /// Instances should not hash this recordKey, rather the actual record + /// key used to store the superIdentity, as this may change. + Typed get recordKey; + @override + + /// Public key of the SuperIdentity used to sign identity keys for recovery + /// This must match the owner of the superRecord DHT record and can not be + /// changed without changing the record + FixedEncodedString43 get publicKey; + @override + + /// Current identity instance + /// The most recently generated identity instance for this SuperIdentity + IdentityInstance get currentInstance; + @override + + /// Deprecated identity instances + /// These may be compromised and should not be considered valid for + /// new signatures, but may be used to validate old signatures + List get deprecatedInstances; + @override + + /// Deprecated superRecords + /// These may be compromised and should not be considered valid for + /// new signatures, but may be used to validate old signatures + List> get deprecatedSuperRecordKeys; + @override + + /// Signature of recordKey, currentInstance signature, + /// signatures of deprecatedInstances, and deprecatedSuperRecordKeys + /// by publicKey + FixedEncodedString86 get signature; + @override + @JsonKey(ignore: true) + _$$SuperIdentityImplCopyWith<_$SuperIdentityImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/packages/veilid_support/lib/identity_support/super_identity.g.dart b/packages/veilid_support/lib/identity_support/super_identity.g.dart index 1b52492..4c4f4f3 100644 --- a/packages/veilid_support/lib/identity_support/super_identity.g.dart +++ b/packages/veilid_support/lib/identity_support/super_identity.g.dart @@ -6,8 +6,8 @@ part of 'super_identity.dart'; // JsonSerializableGenerator // ************************************************************************** -_SuperIdentity _$SuperIdentityFromJson(Map json) => - _SuperIdentity( +_$SuperIdentityImpl _$$SuperIdentityImplFromJson(Map json) => + _$SuperIdentityImpl( recordKey: Typed.fromJson(json['record_key']), publicKey: FixedEncodedString43.fromJson(json['public_key']), currentInstance: IdentityInstance.fromJson(json['current_instance']), @@ -21,7 +21,7 @@ _SuperIdentity _$SuperIdentityFromJson(Map json) => signature: FixedEncodedString86.fromJson(json['signature']), ); -Map _$SuperIdentityToJson(_SuperIdentity instance) => +Map _$$SuperIdentityImplToJson(_$SuperIdentityImpl instance) => { 'record_key': instance.recordKey.toJson(), 'public_key': instance.publicKey.toJson(), diff --git a/packages/veilid_support/lib/identity_support/super_identity_cubit.dart b/packages/veilid_support/lib/identity_support/super_identity_cubit.dart deleted file mode 100644 index 9de55ad..0000000 --- a/packages/veilid_support/lib/identity_support/super_identity_cubit.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:async_tools/async_tools.dart'; - -import '../veilid_support.dart'; - -typedef SuperIdentityState = AsyncValue; - -class SuperIdentityCubit extends DefaultDHTRecordCubit { - SuperIdentityCubit({required TypedKey superRecordKey}) - : super( - open: () => _open(superRecordKey: superRecordKey), - decodeState: (buf) => jsonDecodeBytes(SuperIdentity.fromJson, buf)); - - static Future _open({required TypedKey superRecordKey}) async { - final pool = DHTRecordPool.instance; - - return pool.openRecordRead( - superRecordKey, - debugName: 'SuperIdentityCubit::_open::SuperIdentityRecord', - ); - } -} diff --git a/packages/veilid_support/lib/proto/dht.pb.dart b/packages/veilid_support/lib/proto/dht.pb.dart index b1c0b47..7a9ac9a 100644 --- a/packages/veilid_support/lib/proto/dht.pb.dart +++ b/packages/veilid_support/lib/proto/dht.pb.dart @@ -4,7 +4,7 @@ // // @dart = 2.12 -// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: annotate_overrides, camel_case_types // ignore_for_file: constant_identifier_names, library_prefixes // ignore_for_file: non_constant_identifier_names, prefer_final_fields // ignore_for_file: unnecessary_import, unnecessary_this, unused_import @@ -16,27 +16,7 @@ import 'package:protobuf/protobuf.dart' as $pb; import 'veilid.pb.dart' as $0; class DHTData extends $pb.GeneratedMessage { - factory DHTData({ - $core.Iterable<$0.TypedKey>? keys, - $0.TypedKey? hash, - $core.int? chunk, - $core.int? size, - }) { - final $result = create(); - if (keys != null) { - $result.keys.addAll(keys); - } - if (hash != null) { - $result.hash = hash; - } - if (chunk != null) { - $result.chunk = chunk; - } - if (size != null) { - $result.size = size; - } - return $result; - } + factory DHTData() => create(); DHTData._() : super(); factory DHTData.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory DHTData.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -70,12 +50,9 @@ class DHTData extends $pb.GeneratedMessage { static DHTData getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static DHTData? _defaultInstance; - /// Other keys to concatenate - /// Uses the same writer as this DHTList with SMPL schema @$pb.TagNumber(1) $core.List<$0.TypedKey> get keys => $_getList(0); - /// Hash of reassembled data to verify contents @$pb.TagNumber(2) $0.TypedKey get hash => $_getN(1); @$pb.TagNumber(2) @@ -87,7 +64,6 @@ class DHTData extends $pb.GeneratedMessage { @$pb.TagNumber(2) $0.TypedKey ensureHash() => $_ensure(1); - /// Chunk size per subkey @$pb.TagNumber(3) $core.int get chunk => $_getIZ(2); @$pb.TagNumber(3) @@ -97,7 +73,6 @@ class DHTData extends $pb.GeneratedMessage { @$pb.TagNumber(3) void clearChunk() => clearField(3); - /// Total data size @$pb.TagNumber(4) $core.int get size => $_getIZ(3); @$pb.TagNumber(4) @@ -108,26 +83,8 @@ class DHTData extends $pb.GeneratedMessage { void clearSize() => clearField(4); } -/// DHTLog - represents a ring buffer of many elements with append/truncate semantics -/// Header in subkey 0 of first key follows this structure class DHTLog extends $pb.GeneratedMessage { - factory DHTLog({ - $core.int? head, - $core.int? tail, - $core.int? stride, - }) { - final $result = create(); - if (head != null) { - $result.head = head; - } - if (tail != null) { - $result.tail = tail; - } - if (stride != null) { - $result.stride = stride; - } - return $result; - } + factory DHTLog() => create(); DHTLog._() : super(); factory DHTLog.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory DHTLog.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -160,7 +117,6 @@ class DHTLog extends $pb.GeneratedMessage { static DHTLog getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static DHTLog? _defaultInstance; - /// Position of the start of the log (oldest items) @$pb.TagNumber(1) $core.int get head => $_getIZ(0); @$pb.TagNumber(1) @@ -170,7 +126,6 @@ class DHTLog extends $pb.GeneratedMessage { @$pb.TagNumber(1) void clearHead() => clearField(1); - /// Position of the end of the log (newest items) @$pb.TagNumber(2) $core.int get tail => $_getIZ(1); @$pb.TagNumber(2) @@ -180,7 +135,6 @@ class DHTLog extends $pb.GeneratedMessage { @$pb.TagNumber(2) void clearTail() => clearField(2); - /// Stride of each segment of the dhtlog @$pb.TagNumber(3) $core.int get stride => $_getIZ(2); @$pb.TagNumber(3) @@ -191,32 +145,8 @@ class DHTLog extends $pb.GeneratedMessage { void clearStride() => clearField(3); } -/// DHTShortArray - represents a re-orderable collection of up to 256 individual elements -/// Header in subkey 0 of first key follows this structure -/// -/// stride = descriptor subkey count on first key - 1 -/// Subkeys 1..=stride on the first key are individual elements -/// Subkeys 0..stride on the 'keys' keys are also individual elements -/// -/// Keys must use writable schema in order to make this list mutable class DHTShortArray extends $pb.GeneratedMessage { - factory DHTShortArray({ - $core.Iterable<$0.TypedKey>? keys, - $core.List<$core.int>? index, - $core.Iterable<$core.int>? seqs, - }) { - final $result = create(); - if (keys != null) { - $result.keys.addAll(keys); - } - if (index != null) { - $result.index = index; - } - if (seqs != null) { - $result.seqs.addAll(seqs); - } - return $result; - } + factory DHTShortArray() => create(); DHTShortArray._() : super(); factory DHTShortArray.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory DHTShortArray.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -249,16 +179,9 @@ class DHTShortArray extends $pb.GeneratedMessage { static DHTShortArray getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static DHTShortArray? _defaultInstance; - /// Other keys to concatenate - /// Uses the same writer as this DHTList with SMPL schema @$pb.TagNumber(1) $core.List<$0.TypedKey> get keys => $_getList(0); - /// Item position index (uint8[256./]) - /// Actual item location is: - /// idx = index[n] + 1 (offset for header at idx 0) - /// key = idx / stride - /// subkey = idx % stride @$pb.TagNumber(2) $core.List<$core.int> get index => $_getN(1); @$pb.TagNumber(2) @@ -268,26 +191,12 @@ class DHTShortArray extends $pb.GeneratedMessage { @$pb.TagNumber(2) void clearIndex() => clearField(2); - /// Most recent sequence numbers for elements @$pb.TagNumber(3) $core.List<$core.int> get seqs => $_getList(2); } -/// A pointer to an child DHT record class OwnedDHTRecordPointer extends $pb.GeneratedMessage { - factory OwnedDHTRecordPointer({ - $0.TypedKey? recordKey, - $0.KeyPair? owner, - }) { - final $result = create(); - if (recordKey != null) { - $result.recordKey = recordKey; - } - if (owner != null) { - $result.owner = owner; - } - return $result; - } + factory OwnedDHTRecordPointer() => create(); OwnedDHTRecordPointer._() : super(); factory OwnedDHTRecordPointer.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory OwnedDHTRecordPointer.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -319,7 +228,6 @@ class OwnedDHTRecordPointer extends $pb.GeneratedMessage { static OwnedDHTRecordPointer getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static OwnedDHTRecordPointer? _defaultInstance; - /// DHT Record key @$pb.TagNumber(1) $0.TypedKey get recordKey => $_getN(0); @$pb.TagNumber(1) @@ -331,7 +239,6 @@ class OwnedDHTRecordPointer extends $pb.GeneratedMessage { @$pb.TagNumber(1) $0.TypedKey ensureRecordKey() => $_ensure(0); - /// DHT record owner key @$pb.TagNumber(2) $0.KeyPair get owner => $_getN(1); @$pb.TagNumber(2) diff --git a/packages/veilid_support/lib/proto/dht.pbenum.dart b/packages/veilid_support/lib/proto/dht.pbenum.dart index 7059e85..f76992d 100644 --- a/packages/veilid_support/lib/proto/dht.pbenum.dart +++ b/packages/veilid_support/lib/proto/dht.pbenum.dart @@ -4,7 +4,7 @@ // // @dart = 2.12 -// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: annotate_overrides, camel_case_types // ignore_for_file: constant_identifier_names, library_prefixes // ignore_for_file: non_constant_identifier_names, prefer_final_fields // ignore_for_file: unnecessary_import, unnecessary_this, unused_import diff --git a/packages/veilid_support/lib/proto/dht.pbjson.dart b/packages/veilid_support/lib/proto/dht.pbjson.dart index dd14566..9d505f0 100644 --- a/packages/veilid_support/lib/proto/dht.pbjson.dart +++ b/packages/veilid_support/lib/proto/dht.pbjson.dart @@ -4,7 +4,7 @@ // // @dart = 2.12 -// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: annotate_overrides, camel_case_types // ignore_for_file: constant_identifier_names, library_prefixes // ignore_for_file: non_constant_identifier_names, prefer_final_fields // ignore_for_file: unnecessary_import, unnecessary_this, unused_import diff --git a/packages/veilid_support/lib/proto/dht.pbserver.dart b/packages/veilid_support/lib/proto/dht.pbserver.dart index 02e8c03..ffbf990 100644 --- a/packages/veilid_support/lib/proto/dht.pbserver.dart +++ b/packages/veilid_support/lib/proto/dht.pbserver.dart @@ -4,7 +4,7 @@ // // @dart = 2.12 -// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: annotate_overrides, camel_case_types // ignore_for_file: constant_identifier_names // ignore_for_file: deprecated_member_use_from_same_package, library_prefixes // ignore_for_file: non_constant_identifier_names, prefer_final_fields diff --git a/packages/veilid_support/lib/proto/proto.dart b/packages/veilid_support/lib/proto/proto.dart index 936bbdf..a7a70bb 100644 --- a/packages/veilid_support/lib/proto/proto.dart +++ b/packages/veilid_support/lib/proto/proto.dart @@ -1,6 +1,5 @@ import 'dart:typed_data'; -import '../src/dynamic_debug.dart'; import '../veilid_support.dart' as veilid; import 'veilid.pb.dart' as proto; @@ -151,26 +150,3 @@ extension ProtoKeyPair on proto.KeyPair { veilid.KeyPair toVeilid() => veilid.KeyPair(key: key.toVeilid(), secret: secret.toVeilid()); } - -void registerVeilidProtoToDebug() { - dynamic toDebug(dynamic protoObj) { - if (protoObj is proto.CryptoKey) { - return protoObj.toVeilid(); - } - if (protoObj is proto.Signature) { - return protoObj.toVeilid(); - } - if (protoObj is proto.Nonce) { - return protoObj.toVeilid(); - } - if (protoObj is proto.TypedKey) { - return protoObj.toVeilid(); - } - if (protoObj is proto.KeyPair) { - return protoObj.toVeilid(); - } - return protoObj; - } - - DynamicDebug.registerToDebug(toDebug); -} diff --git a/packages/veilid_support/lib/proto/veilid.pb.dart b/packages/veilid_support/lib/proto/veilid.pb.dart index 5431b80..a53133a 100644 --- a/packages/veilid_support/lib/proto/veilid.pb.dart +++ b/packages/veilid_support/lib/proto/veilid.pb.dart @@ -4,7 +4,7 @@ // // @dart = 2.12 -// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: annotate_overrides, camel_case_types // ignore_for_file: constant_identifier_names, library_prefixes // ignore_for_file: non_constant_identifier_names, prefer_final_fields // ignore_for_file: unnecessary_import, unnecessary_this, unused_import @@ -13,45 +13,8 @@ import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; -/// 32-byte value in bigendian format class CryptoKey extends $pb.GeneratedMessage { - factory CryptoKey({ - $core.int? u0, - $core.int? u1, - $core.int? u2, - $core.int? u3, - $core.int? u4, - $core.int? u5, - $core.int? u6, - $core.int? u7, - }) { - final $result = create(); - if (u0 != null) { - $result.u0 = u0; - } - if (u1 != null) { - $result.u1 = u1; - } - if (u2 != null) { - $result.u2 = u2; - } - if (u3 != null) { - $result.u3 = u3; - } - if (u4 != null) { - $result.u4 = u4; - } - if (u5 != null) { - $result.u5 = u5; - } - if (u6 != null) { - $result.u6 = u6; - } - if (u7 != null) { - $result.u7 = u7; - } - return $result; - } + factory CryptoKey() => create(); CryptoKey._() : super(); factory CryptoKey.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory CryptoKey.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -162,77 +125,8 @@ class CryptoKey extends $pb.GeneratedMessage { void clearU7() => clearField(8); } -/// 64-byte value in bigendian format class Signature extends $pb.GeneratedMessage { - factory Signature({ - $core.int? u0, - $core.int? u1, - $core.int? u2, - $core.int? u3, - $core.int? u4, - $core.int? u5, - $core.int? u6, - $core.int? u7, - $core.int? u8, - $core.int? u9, - $core.int? u10, - $core.int? u11, - $core.int? u12, - $core.int? u13, - $core.int? u14, - $core.int? u15, - }) { - final $result = create(); - if (u0 != null) { - $result.u0 = u0; - } - if (u1 != null) { - $result.u1 = u1; - } - if (u2 != null) { - $result.u2 = u2; - } - if (u3 != null) { - $result.u3 = u3; - } - if (u4 != null) { - $result.u4 = u4; - } - if (u5 != null) { - $result.u5 = u5; - } - if (u6 != null) { - $result.u6 = u6; - } - if (u7 != null) { - $result.u7 = u7; - } - if (u8 != null) { - $result.u8 = u8; - } - if (u9 != null) { - $result.u9 = u9; - } - if (u10 != null) { - $result.u10 = u10; - } - if (u11 != null) { - $result.u11 = u11; - } - if (u12 != null) { - $result.u12 = u12; - } - if (u13 != null) { - $result.u13 = u13; - } - if (u14 != null) { - $result.u14 = u14; - } - if (u15 != null) { - $result.u15 = u15; - } - return $result; - } + factory Signature() => create(); Signature._() : super(); factory Signature.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory Signature.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -423,37 +317,8 @@ class Signature extends $pb.GeneratedMessage { void clearU15() => clearField(16); } -/// 24-byte value in bigendian format class Nonce extends $pb.GeneratedMessage { - factory Nonce({ - $core.int? u0, - $core.int? u1, - $core.int? u2, - $core.int? u3, - $core.int? u4, - $core.int? u5, - }) { - final $result = create(); - if (u0 != null) { - $result.u0 = u0; - } - if (u1 != null) { - $result.u1 = u1; - } - if (u2 != null) { - $result.u2 = u2; - } - if (u3 != null) { - $result.u3 = u3; - } - if (u4 != null) { - $result.u4 = u4; - } - if (u5 != null) { - $result.u5 = u5; - } - return $result; - } + factory Nonce() => create(); Nonce._() : super(); factory Nonce.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory Nonce.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -544,21 +409,8 @@ class Nonce extends $pb.GeneratedMessage { void clearU5() => clearField(6); } -/// 36-byte typed crypto key class TypedKey extends $pb.GeneratedMessage { - factory TypedKey({ - $core.int? kind, - CryptoKey? value, - }) { - final $result = create(); - if (kind != null) { - $result.kind = kind; - } - if (value != null) { - $result.value = value; - } - return $result; - } + factory TypedKey() => create(); TypedKey._() : super(); factory TypedKey.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory TypedKey.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -590,7 +442,6 @@ class TypedKey extends $pb.GeneratedMessage { static TypedKey getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static TypedKey? _defaultInstance; - /// CryptoKind FourCC in bigendian format @$pb.TagNumber(1) $core.int get kind => $_getIZ(0); @$pb.TagNumber(1) @@ -600,7 +451,6 @@ class TypedKey extends $pb.GeneratedMessage { @$pb.TagNumber(1) void clearKind() => clearField(1); - /// Key value @$pb.TagNumber(2) CryptoKey get value => $_getN(1); @$pb.TagNumber(2) @@ -613,21 +463,8 @@ class TypedKey extends $pb.GeneratedMessage { CryptoKey ensureValue() => $_ensure(1); } -/// Key pair class KeyPair extends $pb.GeneratedMessage { - factory KeyPair({ - CryptoKey? key, - CryptoKey? secret, - }) { - final $result = create(); - if (key != null) { - $result.key = key; - } - if (secret != null) { - $result.secret = secret; - } - return $result; - } + factory KeyPair() => create(); KeyPair._() : super(); factory KeyPair.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); factory KeyPair.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); @@ -659,7 +496,6 @@ class KeyPair extends $pb.GeneratedMessage { static KeyPair getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static KeyPair? _defaultInstance; - /// Public key @$pb.TagNumber(1) CryptoKey get key => $_getN(0); @$pb.TagNumber(1) @@ -671,7 +507,6 @@ class KeyPair extends $pb.GeneratedMessage { @$pb.TagNumber(1) CryptoKey ensureKey() => $_ensure(0); - /// Private key @$pb.TagNumber(2) CryptoKey get secret => $_getN(1); @$pb.TagNumber(2) diff --git a/packages/veilid_support/lib/proto/veilid.pbenum.dart b/packages/veilid_support/lib/proto/veilid.pbenum.dart index 89c0019..1ade7e9 100644 --- a/packages/veilid_support/lib/proto/veilid.pbenum.dart +++ b/packages/veilid_support/lib/proto/veilid.pbenum.dart @@ -4,7 +4,7 @@ // // @dart = 2.12 -// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: annotate_overrides, camel_case_types // ignore_for_file: constant_identifier_names, library_prefixes // ignore_for_file: non_constant_identifier_names, prefer_final_fields // ignore_for_file: unnecessary_import, unnecessary_this, unused_import diff --git a/packages/veilid_support/lib/proto/veilid.pbjson.dart b/packages/veilid_support/lib/proto/veilid.pbjson.dart index db8318e..b92b4e5 100644 --- a/packages/veilid_support/lib/proto/veilid.pbjson.dart +++ b/packages/veilid_support/lib/proto/veilid.pbjson.dart @@ -4,7 +4,7 @@ // // @dart = 2.12 -// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: annotate_overrides, camel_case_types // ignore_for_file: constant_identifier_names, library_prefixes // ignore_for_file: non_constant_identifier_names, prefer_final_fields // ignore_for_file: unnecessary_import, unnecessary_this, unused_import diff --git a/packages/veilid_support/lib/proto/veilid.pbserver.dart b/packages/veilid_support/lib/proto/veilid.pbserver.dart index f799a3f..2de2834 100644 --- a/packages/veilid_support/lib/proto/veilid.pbserver.dart +++ b/packages/veilid_support/lib/proto/veilid.pbserver.dart @@ -4,7 +4,7 @@ // // @dart = 2.12 -// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: annotate_overrides, camel_case_types // ignore_for_file: constant_identifier_names // ignore_for_file: deprecated_member_use_from_same_package, library_prefixes // ignore_for_file: non_constant_identifier_names, prefer_final_fields diff --git a/packages/veilid_support/lib/src/async_table_db_backed_cubit.dart b/packages/veilid_support/lib/src/async_table_db_backed_cubit.dart index 313d3e2..d637ee1 100644 --- a/packages/veilid_support/lib/src/async_table_db_backed_cubit.dart +++ b/packages/veilid_support/lib/src/async_table_db_backed_cubit.dart @@ -4,7 +4,6 @@ import 'package:async_tools/async_tools.dart'; import 'package:bloc/bloc.dart'; import 'package:meta/meta.dart'; -import 'config.dart'; import 'table_db.dart'; abstract class AsyncTableDBBackedCubit extends Cubit> @@ -28,9 +27,8 @@ abstract class AsyncTableDBBackedCubit extends Cubit> await _mutex.protect(() async { emit(AsyncValue.data(await load())); }); - } on Exception catch (e, st) { - addError(e, st); - emit(AsyncValue.error(e, st)); + } on Exception catch (e, stackTrace) { + emit(AsyncValue.error(e, stackTrace)); } } @@ -39,12 +37,11 @@ abstract class AsyncTableDBBackedCubit extends Cubit> await _initWait(); try { emit(AsyncValue.data(await store(newState))); - } on Exception catch (e, st) { - addError(e, st); - emit(AsyncValue.error(e, st)); + } on Exception catch (e, stackTrace) { + emit(AsyncValue.error(e, stackTrace)); } } final WaitSet _initWait = WaitSet(); - final Mutex _mutex = Mutex(debugLockTimeout: kIsDebugMode ? 60 : null); + final Mutex _mutex = Mutex(); } diff --git a/packages/veilid_support/lib/src/config.dart b/packages/veilid_support/lib/src/config.dart index 6902479..a8b5ea0 100644 --- a/packages/veilid_support/lib/src/config.dart +++ b/packages/veilid_support/lib/src/config.dart @@ -4,32 +4,27 @@ import 'package:path/path.dart' as p; import 'package:path_provider/path_provider.dart'; import 'package:veilid/veilid.dart'; -// Allowed to pull sentinel value // ignore: do_not_use_environment -const bool kIsReleaseMode = bool.fromEnvironment('dart.vm.product'); -// Allowed to pull sentinel value +const bool _kReleaseMode = bool.fromEnvironment('dart.vm.product'); // ignore: do_not_use_environment -const bool kIsProfileMode = bool.fromEnvironment('dart.vm.profile'); -const bool kIsDebugMode = !kIsReleaseMode && !kIsProfileMode; +const bool _kProfileMode = bool.fromEnvironment('dart.vm.profile'); +const bool _kDebugMode = !_kReleaseMode && !_kProfileMode; Future> getDefaultVeilidPlatformConfig( bool isWeb, String appName) async { final ignoreLogTargetsStr = - // Allowed to change settings // ignore: do_not_use_environment const String.fromEnvironment('IGNORE_LOG_TARGETS').trim(); final ignoreLogTargets = ignoreLogTargetsStr.isEmpty ? [] : ignoreLogTargetsStr.split(',').map((e) => e.trim()).toList(); - // Allowed to change settings // ignore: do_not_use_environment var flamePathStr = const String.fromEnvironment('FLAME').trim(); if (flamePathStr == '1') { flamePathStr = p.join( (await getApplicationSupportDirectory()).absolute.path, '$appName.folded'); - // Allowed for debugging // ignore: avoid_print print('Flame data logged to $flamePathStr'); } @@ -39,7 +34,7 @@ Future> getDefaultVeilidPlatformConfig( logging: VeilidWASMConfigLogging( performance: VeilidWASMConfigLoggingPerformance( enabled: true, - level: kIsDebugMode + level: _kDebugMode ? VeilidConfigLogLevel.debug : VeilidConfigLogLevel.info, logsInTimings: true, @@ -54,8 +49,9 @@ Future> getDefaultVeilidPlatformConfig( return VeilidFFIConfig( logging: VeilidFFIConfigLogging( terminal: VeilidFFIConfigLoggingTerminal( - enabled: false, - level: kIsDebugMode + enabled: + _kDebugMode && (Platform.isIOS || Platform.isAndroid), + level: _kDebugMode ? VeilidConfigLogLevel.debug : VeilidConfigLogLevel.info, ignoreLogTargets: ignoreLogTargets), @@ -78,37 +74,30 @@ Future getVeilidConfig(bool isWeb, String programName) async { var config = await getDefaultVeilidConfig( isWeb: isWeb, programName: programName, - // Allowed to change settings // ignore: avoid_redundant_argument_values, do_not_use_environment namespace: const String.fromEnvironment('NAMESPACE'), - // Allowed to change settings // ignore: avoid_redundant_argument_values, do_not_use_environment bootstrap: const String.fromEnvironment('BOOTSTRAP'), - // Allowed to change settings // ignore: avoid_redundant_argument_values, do_not_use_environment networkKeyPassword: const String.fromEnvironment('NETWORK_KEY'), ); - // Allowed to change settings // ignore: do_not_use_environment if (const String.fromEnvironment('DELETE_TABLE_STORE') == '1') { config = config.copyWith(tableStore: config.tableStore.copyWith(delete: true)); } - // Allowed to change settings // ignore: do_not_use_environment if (const String.fromEnvironment('DELETE_PROTECTED_STORE') == '1') { config = config.copyWith( protectedStore: config.protectedStore.copyWith(delete: true)); } - // Allowed to change settings // ignore: do_not_use_environment if (const String.fromEnvironment('DELETE_BLOCK_STORE') == '1') { config = config.copyWith(blockStore: config.blockStore.copyWith(delete: true)); } - // Allowed to change settings // ignore: do_not_use_environment const envNetwork = String.fromEnvironment('NETWORK'); if (envNetwork.isNotEmpty) { @@ -123,12 +112,10 @@ Future getVeilidConfig(bool isWeb, String programName) async { return config.copyWith( capabilities: - // XXX: Remove DHTV and DHTW after DHT widening (and maybe remote - // rehydration?) + // XXX: Remove DHTV and DHTW when we get background sync implemented const VeilidConfigCapabilities(disable: ['DHTV', 'DHTW', 'TUNL']), protectedStore: // XXX: Linux often does not have a secret storage mechanism installed - config.protectedStore - .copyWith(allowInsecureFallback: !isWeb && Platform.isLinux), + config.protectedStore.copyWith(allowInsecureFallback: Platform.isLinux), ); } diff --git a/packages/veilid_support/lib/src/dynamic_debug.dart b/packages/veilid_support/lib/src/dynamic_debug.dart deleted file mode 100644 index 1c38d96..0000000 --- a/packages/veilid_support/lib/src/dynamic_debug.dart +++ /dev/null @@ -1,130 +0,0 @@ -import 'package:async_tools/async_tools.dart'; -import 'package:bloc_advanced_tools/bloc_advanced_tools.dart'; -import 'package:convert/convert.dart'; -import 'package:fast_immutable_collections/fast_immutable_collections.dart'; - -import 'online_element_state.dart'; - -typedef ToDebugFunction = dynamic Function(dynamic protoObj); - -// This should be implemented to add toDebug capability -// ignore: one_member_abstracts -abstract class ToDebugMap { - Map toDebugMap(); -} - -// We explicitly want this class to avoid having a global function 'toDebug' -// ignore: avoid_classes_with_only_static_members -class DynamicDebug { - /// Add a 'toDebug' handler to the chain - static void registerToDebug(ToDebugFunction toDebugFunction) { - final _oldToDebug = _toDebug; - _toDebug = (obj) => _oldToDebug(toDebugFunction(obj)); - } - - /// Convert a type to a debug version of the same type that - /// has a better `toString` representation and possibly other extra debug - /// information - static dynamic toDebug(dynamic obj) { - try { - return _toDebug(obj); - // In this case we watch to catch everything - // because toDebug need to never fail - // ignore: avoid_catches_without_on_clauses - } catch (e) { - // Ensure this gets printed, but continue - // ignore: avoid_print - print('Exception in toDebug: $e'); - return obj.toString(); - } - } - - ////////////////////////////////////////////////////////////// - static dynamic _baseToDebug(dynamic obj) { - if (obj is AsyncValue) { - if (obj.isLoading) { - return {r'$runtimeType': obj.runtimeType, 'loading': null}; - } - if (obj.isError) { - return { - r'$runtimeType': obj.runtimeType, - 'error': toDebug(obj.asError!.error), - 'stackTrace': toDebug(obj.asError!.stackTrace), - }; - } - if (obj.isData) { - return { - r'$runtimeType': obj.runtimeType, - 'data': toDebug(obj.asData!.value), - }; - } - return obj.toString(); - } - if (obj is IMap) { - // Handled by Map - return _baseToDebug(obj.unlockView); - } - if (obj is IMapOfSets) { - // Handled by Map - return _baseToDebug(obj.unlock); - } - if (obj is ISet) { - // Handled by Iterable - return _baseToDebug(obj.unlockView); - } - if (obj is IList) { - return _baseToDebug(obj.unlockView); - } - if (obj is BlocBusyState) { - return { - r'$runtimeType': obj.runtimeType, - 'busy': obj.busy, - 'state': toDebug(obj.state), - }; - } - if (obj is OnlineElementState) { - return { - r'$runtimeType': obj.runtimeType, - 'isOffline': obj.isOffline, - 'value': toDebug(obj.value), - }; - } - if (obj is List) { - try { - // Do bytes as a hex string for brevity and clarity - return 'List: ${hex.encode(obj)}'; - // One has to be able to catch this - // ignore: avoid_catching_errors - } on RangeError { - // Otherwise directly convert as list of integers - return obj.toString(); - } - } - if (obj is Map) { - return obj.map((k, v) => MapEntry(toDebug(k), toDebug(v))); - } - if (obj is Iterable) { - return obj.map(toDebug).toList(); - } - if (obj is String || obj is bool || obj is num || obj == null) { - return obj; - } - if (obj is ToDebugMap) { - // Handled by Map - return _baseToDebug(obj.toDebugMap()); - } - - try { - // Let's try convering to a json object - // ignore: avoid_dynamic_calls - return obj.toJson(); - - // No matter how this fails, we shouldn't throw - // ignore: avoid_catches_without_on_clauses - } catch (_) {} - - return obj.toString(); - } - - static ToDebugFunction _toDebug = _baseToDebug; -} diff --git a/packages/veilid_support/lib/src/persistent_queue.dart b/packages/veilid_support/lib/src/persistent_queue.dart index 939a5b3..e59a470 100644 --- a/packages/veilid_support/lib/src/persistent_queue.dart +++ b/packages/veilid_support/lib/src/persistent_queue.dart @@ -5,9 +5,7 @@ import 'package:async_tools/async_tools.dart'; import 'package:fast_immutable_collections/fast_immutable_collections.dart'; import 'package:protobuf/protobuf.dart'; -import 'config.dart'; import 'table_db.dart'; -import 'veilid_log.dart'; class PersistentQueue with TableDBBackedFromBuffer> { @@ -47,7 +45,7 @@ class PersistentQueue } } - Future _init(Completer _) async { + Future _init(_) async { // Start the processor unawaited(Future.delayed(Duration.zero, () async { await _initWait(); @@ -183,28 +181,10 @@ class PersistentQueue @override IList valueFromBuffer(Uint8List bytes) { + final reader = CodedBufferReader(bytes); var out = IList(); - try { - final reader = CodedBufferReader(bytes); - while (!reader.isAtEnd()) { - final bytes = reader.readBytesAsView(); - try { - final item = _fromBuffer(bytes); - out = out.add(item); - } on Exception catch (e, st) { - veilidLoggy.debug( - 'Dropping invalid item from persistent queue: $bytes\n' - 'tableName=${tableName()}:tableKeyName=${tableKeyName()}\n', - e, - st); - } - } - } on Exception catch (e, st) { - veilidLoggy.debug( - 'Dropping remainder of invalid persistent queue\n' - 'tableName=${tableName()}:tableKeyName=${tableKeyName()}\n', - e, - st); + while (!reader.isAtEnd()) { + out = out.add(_fromBuffer(reader.readBytesAsView())); } return out; } @@ -223,7 +203,7 @@ class PersistentQueue final T Function(Uint8List) _fromBuffer; final bool _deleteOnClose; final WaitSet _initWait = WaitSet(); - final Mutex _queueMutex = Mutex(debugLockTimeout: kIsDebugMode ? 60 : null); + final Mutex _queueMutex = Mutex(); IList _queue = IList.empty(); final StreamController> _syncAddController = StreamController(); final StreamController _queueReady = StreamController(); diff --git a/packages/veilid_support/lib/src/table_db.dart b/packages/veilid_support/lib/src/table_db.dart index eb7fe99..522a837 100644 --- a/packages/veilid_support/lib/src/table_db.dart +++ b/packages/veilid_support/lib/src/table_db.dart @@ -6,9 +6,6 @@ import 'package:async_tools/async_tools.dart'; import 'package:meta/meta.dart'; import 'package:veilid/veilid.dart'; -import '../veilid_support.dart'; -import 'veilid_log.dart'; - Future tableScope( String name, Future Function(VeilidTableDB tdb) callback, {int columnCount = 1}) async { @@ -51,20 +48,11 @@ abstract mixin class TableDBBackedJson { /// Load things from storage @protected Future load() async { - try { - final obj = await tableScope(tableName(), (tdb) async { - final objJson = await tdb.loadStringJson(0, tableKeyName()); - return valueFromJson(objJson); - }); - return obj; - } on Exception catch (e, st) { - veilidLoggy.debug( - 'Unable to load data from table store: ' - '${tableName()}:${tableKeyName()}', - e, - st); - return null; - } + final obj = await tableScope(tableName(), (tdb) async { + final objJson = await tdb.loadStringJson(0, tableKeyName()); + return valueFromJson(objJson); + }); + return obj; } /// Store things to storage diff --git a/packages/veilid_support/lib/src/table_db_array.dart b/packages/veilid_support/lib/src/table_db_array.dart index 8b59336..7c3d4d4 100644 --- a/packages/veilid_support/lib/src/table_db_array.dart +++ b/packages/veilid_support/lib/src/table_db_array.dart @@ -9,7 +9,6 @@ import 'package:meta/meta.dart'; import 'package:protobuf/protobuf.dart'; import '../veilid_support.dart'; -import 'veilid_log.dart'; @immutable class TableDBArrayUpdate extends Equatable { @@ -263,16 +262,7 @@ class _TableDBArrayBase { final dws = DelayedWaitSet(); while (batchLen > 0) { final entry = await _getIndexEntry(pos); - dws.add((_) async { - try { - return (await _loadEntry(entry))!; - // Need some way to debug ParallelWaitError - // ignore: avoid_catches_without_on_clauses - } catch (e, st) { - veilidLoggy.error('$e\n$st\n'); - rethrow; - } - }); + dws.add((_) async => (await _loadEntry(entry))!); pos++; batchLen--; } @@ -624,7 +614,7 @@ class _TableDBArrayBase { var _initDone = false; final VeilidCrypto _crypto; final WaitSet _initWait = WaitSet(); - final Mutex _mutex = Mutex(debugLockTimeout: kIsDebugMode ? 60 : null); + final Mutex _mutex = Mutex(); // Change tracking int _headDelta = 0; diff --git a/packages/veilid_support/lib/src/table_db_array_protobuf_cubit.dart b/packages/veilid_support/lib/src/table_db_array_protobuf_cubit.dart index 92b920f..89408ac 100644 --- a/packages/veilid_support/lib/src/table_db_array_protobuf_cubit.dart +++ b/packages/veilid_support/lib/src/table_db_array_protobuf_cubit.dart @@ -24,7 +24,7 @@ class TableDBArrayProtobufStateData final IList windowElements; // The length of the entire array final int length; - // One past the end of the last element (modulo length, can be zero) + // One past the end of the last element final int windowTail; // The total number of elements to try to keep in 'elements' final int windowCount; @@ -92,7 +92,6 @@ class TableDBArrayProtobufCubit final avElements = await _loadElements(_tail, _count); final err = avElements.asError; if (err != null) { - addError(err.error, err.stackTrace); emit(AsyncValue.error(err.error, err.stackTrace)); return; } @@ -124,7 +123,6 @@ class TableDBArrayProtobufCubit final allItems = (await _array.getRange(start, end)).toIList(); return AsyncValue.data(allItems); } on Exception catch (e, st) { - addError(e, st); return AsyncValue.error(e, st); } } diff --git a/packages/veilid_support/lib/src/veilid_log.dart b/packages/veilid_support/lib/src/veilid_log.dart index 4c9ad0b..0007754 100644 --- a/packages/veilid_support/lib/src/veilid_log.dart +++ b/packages/veilid_support/lib/src/veilid_log.dart @@ -57,19 +57,23 @@ void processLog(VeilidLog log) { switch (log.logLevel) { case VeilidLogLevel.error: veilidLoggy.error(log.message, error, stackTrace); + break; case VeilidLogLevel.warn: veilidLoggy.warning(log.message, error, stackTrace); + break; case VeilidLogLevel.info: veilidLoggy.info(log.message, error, stackTrace); + break; case VeilidLogLevel.debug: veilidLoggy.debug(log.message, error, stackTrace); + break; case VeilidLogLevel.trace: veilidLoggy.trace(log.message, error, stackTrace); + break; } } void initVeilidLog(bool debugMode) { - // Always allow LOG_TRACE option // ignore: do_not_use_environment const isTrace = String.fromEnvironment('LOG_TRACE') != ''; LogLevel logLevel; diff --git a/packages/veilid_support/lib/veilid_support.dart b/packages/veilid_support/lib/veilid_support.dart index 2f4da90..f48376f 100644 --- a/packages/veilid_support/lib/veilid_support.dart +++ b/packages/veilid_support/lib/veilid_support.dart @@ -1,14 +1,13 @@ /// Dart Veilid Support Library /// Common functionality for interfacing with Veilid -library; +library veilid_support; export 'package:veilid/veilid.dart'; export 'dht_support/dht_support.dart'; export 'identity_support/identity_support.dart'; export 'src/config.dart'; -export 'src/dynamic_debug.dart'; export 'src/json_tools.dart'; export 'src/memory_tools.dart'; export 'src/online_element_state.dart'; diff --git a/packages/veilid_support/pubspec.lock b/packages/veilid_support/pubspec.lock index b4c7eef..5078198 100644 --- a/packages/veilid_support/pubspec.lock +++ b/packages/veilid_support/pubspec.lock @@ -5,114 +5,114 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: e55636ed79578b9abca5fecf9437947798f5ef7456308b5cb85720b793eac92f + sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" url: "https://pub.dev" source: hosted - version: "82.0.0" + version: "67.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "904ae5bb474d32c38fb9482e2d925d5454cda04ddd0e55d2e6826bc72f6ba8c0" + sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" url: "https://pub.dev" source: hosted - version: "7.4.5" + version: "6.4.1" args: dependency: transitive description: name: args - sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" url: "https://pub.dev" source: hosted - version: "2.7.0" + version: "2.5.0" async: dependency: transitive description: name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.11.0" async_tools: dependency: "direct main" description: name: async_tools - sha256: afd5426e76631172f8ce6a6359b264b092fa9d2a52cd2528100115be9525e067 + sha256: "9166e8fe65fc65eb79202a6d540f4de768553d78141b885f5bd3f8d7d30eef5e" url: "https://pub.dev" source: hosted - version: "0.1.9" + version: "0.1.5" bloc: dependency: "direct main" description: name: bloc - sha256: "52c10575f4445c61dd9e0cafcc6356fdd827c4c64dd7945ef3c4105f6b6ac189" + sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e" url: "https://pub.dev" source: hosted - version: "9.0.0" + version: "8.1.4" bloc_advanced_tools: dependency: "direct main" description: name: bloc_advanced_tools - sha256: dfb142569814952af8d93e7fe045972d847e29382471687db59913e253202f6e + sha256: "2b2dd492a350e7192a933d09f15ea04d5d00e7bd3fe2a906fe629cd461ddbf94" url: "https://pub.dev" source: hosted - version: "0.1.12" + version: "0.1.5" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" build: dependency: transitive description: name: build - sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0 + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.4.1" build_config: dependency: transitive description: name: build_config - sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33" + sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.1" build_daemon: dependency: transitive description: name: build_daemon - sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa" + sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9" url: "https://pub.dev" source: hosted - version: "4.0.4" + version: "4.0.2" build_resolvers: dependency: transitive description: name: build_resolvers - sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0 + sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" url: "https://pub.dev" source: hosted - version: "2.4.4" + version: "2.4.2" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99" + sha256: "1414d6d733a85d8ad2f1dfcb3ea7945759e35a123cb99ccfac75d0758f75edfa" url: "https://pub.dev" source: hosted - version: "2.4.15" + version: "2.4.10" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021" + sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799" url: "https://pub.dev" source: hosted - version: "8.0.0" + version: "7.3.0" built_collection: dependency: transitive description: @@ -125,34 +125,34 @@ packages: dependency: transitive description: name: built_value - sha256: ea90e81dc4a25a043d9bee692d20ed6d1c4a1662a28c03a96417446c093ed6b4 + sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb url: "https://pub.dev" source: hosted - version: "8.9.5" + version: "8.9.2" change_case: dependency: transitive description: name: change_case - sha256: e41ef3df58521194ef8d7649928954805aeb08061917cf658322305e61568003 + sha256: "47c48c36f95f20c6d0ba03efabceff261d05026cca322cc2c4c01c343371b5bb" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.0.1" characters: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.0" charcode: dependency: "direct main" description: name: charcode - sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a + sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.1" checked_yaml: dependency: transitive description: @@ -161,102 +161,94 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.3" - cli_config: - dependency: transitive - description: - name: cli_config - sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec - url: "https://pub.dev" - source: hosted - version: "0.2.0" code_builder: dependency: transitive description: name: code_builder - sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e" + sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 url: "https://pub.dev" source: hosted - version: "4.10.1" + version: "4.10.0" collection: dependency: "direct main" description: name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.19.1" + version: "1.18.0" convert: - dependency: "direct main" + dependency: transitive description: name: convert - sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.1.1" coverage: dependency: transitive description: name: coverage - sha256: "802bd084fb82e55df091ec8ad1553a7331b61c08251eef19a508b6f3f3a9858d" + sha256: "3945034e86ea203af7a056d98e98e42a5518fff200d6e8e6647e1886b07e936e" url: "https://pub.dev" source: hosted - version: "1.13.1" + version: "1.8.0" crypto: dependency: transitive description: name: crypto - sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.0.3" dart_style: dependency: transitive description: name: dart_style - sha256: "5b236382b47ee411741447c1f1e111459c941ea1b3f2b540dde54c210a3662af" + sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "2.3.6" equatable: dependency: "direct main" description: name: equatable - sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 url: "https://pub.dev" source: hosted - version: "2.0.7" + version: "2.0.5" fast_immutable_collections: dependency: "direct main" description: name: fast_immutable_collections - sha256: d1aa3d7788fab06cce7f303f4969c7a16a10c865e1bd2478291a8ebcbee084e5 + sha256: "533806a7f0c624c2e479d05d3fdce4c87109a7cd0db39b8cc3830d3a2e8dedc7" url: "https://pub.dev" source: hosted - version: "11.0.4" + version: "10.2.3" ffi: dependency: transitive description: name: ffi - sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.2" file: dependency: transitive description: name: file - sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" url: "https://pub.dev" source: hosted - version: "7.0.1" + version: "7.0.0" fixnum: dependency: transitive description: name: fixnum - sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.0" flutter: dependency: transitive description: flutter @@ -271,18 +263,18 @@ packages: dependency: "direct dev" description: name: freezed - sha256: "6022db4c7bfa626841b2a10f34dd1e1b68e8f8f9650db6112dcdeeca45ca793c" + sha256: a434911f643466d78462625df76fd9eb13e57348ff43fe1f77bbe909522c67a1 url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "2.5.2" freezed_annotation: dependency: "direct main" description: name: freezed_annotation - sha256: c87ff004c8aa6af2d531668b46a4ea379f7191dc6dfa066acd53d506da6e044b + sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "2.4.1" frontend_server_client: dependency: transitive description: @@ -295,10 +287,10 @@ packages: dependency: transitive description: name: glob - sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.2" globbing: dependency: transitive description: @@ -311,58 +303,42 @@ packages: dependency: transitive description: name: graphs - sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" + sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 url: "https://pub.dev" source: hosted - version: "2.3.2" - http: - dependency: transitive - description: - name: http - sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" - url: "https://pub.dev" - source: hosted - version: "1.4.0" + version: "2.3.1" http_multi_server: dependency: transitive description: name: http_multi_server - sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" url: "https://pub.dev" source: hosted - version: "3.2.2" + version: "3.2.1" http_parser: dependency: transitive description: name: http_parser - sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" url: "https://pub.dev" source: hosted - version: "4.1.2" - indent: - dependency: "direct main" - description: - name: indent - sha256: "819319a5c185f7fe412750c798953378b37a0d0d32564ce33e7c5acfd1372d2a" - url: "https://pub.dev" - source: hosted - version: "2.0.0" + version: "4.0.2" io: dependency: transitive description: name: io - sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" url: "https://pub.dev" source: hosted - version: "1.0.5" + version: "1.0.4" js: dependency: transitive description: name: js - sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" + sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf url: "https://pub.dev" source: hosted - version: "0.7.2" + version: "0.7.1" json_annotation: dependency: "direct main" description: @@ -375,26 +351,26 @@ packages: dependency: "direct dev" description: name: json_serializable - sha256: c50ef5fc083d5b5e12eef489503ba3bf5ccc899e487d691584699b4bdefeea8c + sha256: ea1432d167339ea9b5bb153f0571d0039607a873d6e04e0117af043f14a1fd4b url: "https://pub.dev" source: hosted - version: "6.9.5" + version: "6.8.0" lint_hard: dependency: "direct dev" description: name: lint_hard - sha256: "2073d4e83ac4e3f2b87cc615fff41abb5c2c5618e117edcd3d71f40f2186f4d5" + sha256: "44d15ec309b1a8e1aff99069df9dcb1597f49d5f588f32811ca28fb7b38c32fe" url: "https://pub.dev" source: hosted - version: "6.1.1" + version: "4.0.0" logging: dependency: transitive description: name: logging - sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.2.0" loggy: dependency: "direct main" description: @@ -407,34 +383,34 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.11.1" + version: "0.8.0" meta: dependency: "direct main" description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.12.0" mime: dependency: transitive description: name: mime - sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "1.0.5" node_preamble: dependency: transitive description: @@ -447,42 +423,42 @@ packages: dependency: transitive description: name: package_config - sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.1.0" path: dependency: "direct main" description: name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.9.0" path_provider: dependency: "direct main" description: name: path_provider - sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" + sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.1.3" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 + sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d url: "https://pub.dev" source: hosted - version: "2.2.17" + version: "2.2.4" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.0" path_provider_linux: dependency: transitive description: @@ -503,18 +479,18 @@ packages: dependency: transitive description: name: path_provider_windows - sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.2.1" platform: dependency: transitive description: name: platform - sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" url: "https://pub.dev" source: hosted - version: "3.1.6" + version: "3.1.4" plugin_platform_interface: dependency: transitive description: @@ -535,34 +511,34 @@ packages: dependency: "direct main" description: name: protobuf - sha256: "579fe5557eae58e3adca2e999e38f02441d8aa908703854a9e0a0f47fa857731" + sha256: "68645b24e0716782e58948f8467fd42a880f255096a821f9e7d0ec625b00c84d" url: "https://pub.dev" source: hosted - version: "4.1.0" + version: "3.1.0" pub_semver: dependency: transitive description: name: pub_semver - sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.1.4" pubspec_parse: dependency: transitive description: name: pubspec_parse - sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" + sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "1.2.3" shelf: dependency: transitive description: name: shelf - sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 url: "https://pub.dev" source: hosted - version: "1.4.2" + version: "1.4.1" shelf_packages_handler: dependency: transitive description: @@ -575,95 +551,95 @@ packages: dependency: transitive description: name: shelf_static - sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 + sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e url: "https://pub.dev" source: hosted - version: "1.1.3" + version: "1.1.2" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "1.0.4" sky_engine: dependency: transitive description: flutter source: sdk - version: "0.0.0" + version: "0.0.99" source_gen: dependency: transitive description: name: source_gen - sha256: "35c8150ece9e8c8d263337a265153c3329667640850b9304861faea59fc98f6b" + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "1.5.0" source_helper: dependency: transitive description: name: source_helper - sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c" + sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd" url: "https://pub.dev" source: hosted - version: "1.3.5" + version: "1.3.4" source_map_stack_trace: dependency: transitive description: name: source_map_stack_trace - sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b + sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" source_maps: dependency: transitive description: name: source_maps - sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812" + sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" url: "https://pub.dev" source: hosted - version: "0.10.13" + version: "0.10.12" source_span: dependency: transitive description: name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.10.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.12.1" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.2" stream_transform: dependency: transitive description: name: stream_transform - sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.0" string_scanner: dependency: transitive description: name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.2.0" system_info2: dependency: transitive description: @@ -676,58 +652,58 @@ packages: dependency: transitive description: name: system_info_plus - sha256: df94187e95527f9cb459e6a9f6e0b1ea20c157d8029bc233de34b3c1e17e1c48 + sha256: b915c811c6605b802f3988859bc2bb79c95f735762a75b5451741f7a2b949d1b url: "https://pub.dev" source: hosted - version: "0.0.6" + version: "0.0.5" term_glyph: dependency: transitive description: name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.1" test: dependency: "direct dev" description: name: test - sha256: "65e29d831719be0591f7b3b1a32a3cda258ec98c58c7b25f7b84241bc31215bb" + sha256: "7ee446762c2c50b3bd4ea96fe13ffac69919352bd3b4b17bac3f3465edc58073" url: "https://pub.dev" source: hosted - version: "1.26.2" + version: "1.25.2" test_api: dependency: transitive description: name: test_api - sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.7.6" + version: "0.7.0" test_core: dependency: transitive description: name: test_core - sha256: "80bf5a02b60af04b09e14f6fe68b921aad119493e26e490deaca5993fef1b05a" + sha256: "2bc4b4ecddd75309300d8096f781c0e3280ca1ef85beda558d33fcbedc2eead4" url: "https://pub.dev" source: hosted - version: "0.6.11" + version: "0.6.0" timing: dependency: transitive description: name: timing - sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.0.1" typed_data: dependency: transitive description: name: typed_data - sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.2" vector_math: dependency: transitive description: @@ -742,47 +718,39 @@ packages: path: "../../../veilid/veilid-flutter" relative: true source: path - version: "0.4.6" + version: "0.3.3" vm_service: dependency: transitive description: name: vm_service - sha256: "6f82e9ee8e7339f5d8b699317f6f3afc17c80a68ebef1bc0d6f52a678c14b1e6" + sha256: "7475cb4dd713d57b6f7464c0e13f06da0d535d8b2067e188962a59bac2cf280b" url: "https://pub.dev" source: hosted - version: "15.0.1" + version: "14.2.2" watcher: dependency: transitive description: name: watcher - sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104" + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.0" web: dependency: transitive description: name: web - sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" url: "https://pub.dev" source: hosted - version: "1.1.1" - web_socket: - dependency: transitive - description: - name: web_socket - sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" - url: "https://pub.dev" - source: hosted - version: "1.0.1" + version: "0.5.1" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 + sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "2.4.5" webkit_inspection_protocol: dependency: transitive description: @@ -791,22 +759,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.1" + win32: + dependency: transitive + description: + name: win32 + sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4 + url: "https://pub.dev" + source: hosted + version: "5.5.1" xdg_directories: dependency: transitive description: name: xdg_directories - sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.0.4" yaml: dependency: transitive description: name: yaml - sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" url: "https://pub.dev" source: hosted - version: "3.1.3" + version: "3.1.2" sdks: - dart: ">=3.7.0 <4.0.0" - flutter: ">=3.27.0" + dart: ">=3.4.0 <4.0.0" + flutter: ">=3.19.1" diff --git a/packages/veilid_support/pubspec.yaml b/packages/veilid_support/pubspec.yaml index 8864fa6..cec41f7 100644 --- a/packages/veilid_support/pubspec.yaml +++ b/packages/veilid_support/pubspec.yaml @@ -1,42 +1,40 @@ name: veilid_support description: Veilid Support Library -publish_to: "none" +publish_to: 'none' version: 1.0.2+0 environment: - sdk: ">=3.2.0 <4.0.0" + sdk: '>=3.2.0 <4.0.0' dependencies: - async_tools: ^0.1.9 - bloc: ^9.0.0 - bloc_advanced_tools: ^0.1.12 - charcode: ^1.4.0 - collection: ^1.19.1 - convert: ^3.1.2 - equatable: ^2.0.7 - fast_immutable_collections: ^11.0.3 - freezed_annotation: ^3.0.0 - indent: ^2.0.0 + async_tools: ^0.1.5 + bloc: ^8.1.4 + bloc_advanced_tools: ^0.1.5 + charcode: ^1.3.1 + collection: ^1.18.0 + equatable: ^2.0.5 + fast_immutable_collections: ^10.2.3 + freezed_annotation: ^2.4.1 json_annotation: ^4.9.0 loggy: ^2.0.3 - meta: ^1.16.0 + meta: ^1.12.0 - path: ^1.9.1 - path_provider: ^2.1.5 - protobuf: ^4.1.0 + path: ^1.9.0 + path_provider: ^2.1.3 + protobuf: ^3.1.0 veilid: # veilid: ^0.0.1 path: ../../../veilid/veilid-flutter # dependency_overrides: -# async_tools: -# path: ../../../dart_async_tools -# bloc_advanced_tools: -# path: ../../../bloc_advanced_tools +# async_tools: +# path: ../../../dart_async_tools +# bloc_advanced_tools: +# path: ../../../bloc_advanced_tools dev_dependencies: - build_runner: ^2.4.15 - freezed: ^3.0.4 - json_serializable: ^6.9.4 - lint_hard: ^6.0.0 - test: ^1.25.15 + build_runner: ^2.4.10 + freezed: ^2.5.2 + json_serializable: ^6.8.0 + lint_hard: ^4.0.0 + test: ^1.25.2 diff --git a/pubspec.lock b/pubspec.lock index aa97499..2e8947c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: e55636ed79578b9abca5fecf9437947798f5ef7456308b5cb85720b793eac92f + sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" url: "https://pub.dev" source: hosted - version: "82.0.0" + version: "67.0.0" accordion: dependency: "direct main" description: @@ -21,26 +21,18 @@ packages: dependency: transitive description: name: analyzer - sha256: "904ae5bb474d32c38fb9482e2d925d5454cda04ddd0e55d2e6826bc72f6ba8c0" + sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" url: "https://pub.dev" source: hosted - version: "7.4.5" + version: "6.4.1" animated_bottom_navigation_bar: dependency: "direct main" description: name: animated_bottom_navigation_bar - sha256: "94971fdfd53acd443acd0d17ce1cb5219ad833f20c75b50c55b205e54a5d6117" + sha256: "2b04a2ae4b0742669e60ddf309467d6a354cefd2d0cd20f4737b1efaf9834cda" url: "https://pub.dev" source: hosted - version: "1.4.0" - animated_custom_dropdown: - dependency: "direct main" - description: - name: animated_custom_dropdown - sha256: "5a72dc209041bb53f6c7164bc2e366552d5197cdb032b1c9b2c36e3013024486" - url: "https://pub.dev" - source: hosted - version: "3.1.1" + version: "1.3.3" animated_switcher_transitions: dependency: "direct main" description: @@ -61,58 +53,50 @@ packages: dependency: "direct main" description: name: ansicolor - sha256: "50e982d500bc863e1d703448afdbf9e5a72eb48840a4f766fa361ffd6877055f" + sha256: "8bf17a8ff6ea17499e40a2d2542c2f481cd7615760c6d34065cb22bfd22e6880" url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "2.0.2" archive: dependency: "direct main" description: name: archive - sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" + sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d url: "https://pub.dev" source: hosted - version: "4.0.7" + version: "3.6.1" args: dependency: transitive description: name: args - sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" url: "https://pub.dev" source: hosted - version: "2.7.0" + version: "2.5.0" async: dependency: transitive description: name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.11.0" async_tools: dependency: "direct main" description: name: async_tools - sha256: afd5426e76631172f8ce6a6359b264b092fa9d2a52cd2528100115be9525e067 + sha256: "9166e8fe65fc65eb79202a6d540f4de768553d78141b885f5bd3f8d7d30eef5e" url: "https://pub.dev" source: hosted - version: "0.1.9" - auto_size_text: - dependency: "direct main" - description: - name: auto_size_text - sha256: "3f5261cd3fb5f2a9ab4e2fc3fba84fd9fcaac8821f20a1d4e71f557521b22599" - url: "https://pub.dev" - source: hosted - version: "3.0.0" + version: "0.1.5" awesome_extensions: dependency: "direct main" description: name: awesome_extensions - sha256: "9b1693e986e4045141add298fa2d7f9aa6cdd3c125b951e2cde739a5058ed879" + sha256: "07e52221467e651cab9219a26286245760831c3852ea2c54883a48a54f120d7c" url: "https://pub.dev" source: hosted - version: "2.0.21" + version: "2.0.16" badges: dependency: "direct main" description: @@ -125,42 +109,42 @@ packages: dependency: transitive description: name: barcode - sha256: "7b6729c37e3b7f34233e2318d866e8c48ddb46c1f7ad01ff7bb2a8de1da2b9f4" + sha256: ab180ce22c6555d77d45f0178a523669db67f95856e3378259ef2ffeb43e6003 url: "https://pub.dev" source: hosted - version: "2.2.9" + version: "2.2.8" basic_utils: dependency: "direct main" description: name: basic_utils - sha256: "548047bef0b3b697be19fa62f46de54d99c9019a69fb7db92c69e19d87f633c7" + sha256: "2064b21d3c41ed7654bc82cc476fd65542e04d60059b74d5eed490a4da08fc6c" url: "https://pub.dev" source: hosted - version: "5.8.2" + version: "5.7.0" bidi: dependency: transitive description: name: bidi - sha256: "77f475165e94b261745cf1032c751e2032b8ed92ccb2bf5716036db79320637d" + sha256: "1a7d0c696324b2089f72e7671fd1f1f64fef44c980f3cebc84e803967c597b63" url: "https://pub.dev" source: hosted - version: "2.0.13" + version: "2.0.10" bloc: dependency: "direct main" description: name: bloc - sha256: "52c10575f4445c61dd9e0cafcc6356fdd827c4c64dd7945ef3c4105f6b6ac189" + sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e" url: "https://pub.dev" source: hosted - version: "9.0.0" + version: "8.1.4" bloc_advanced_tools: dependency: "direct main" description: name: bloc_advanced_tools - sha256: dfb142569814952af8d93e7fe045972d847e29382471687db59913e253202f6e + sha256: "2b2dd492a350e7192a933d09f15ea04d5d00e7bd3fe2a906fe629cd461ddbf94" url: "https://pub.dev" source: hosted - version: "0.1.12" + version: "0.1.5" blurry_modal_progress_hud: dependency: "direct main" description: @@ -173,58 +157,58 @@ packages: dependency: transitive description: name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" build: dependency: transitive description: name: build - sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0 + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.4.1" build_config: dependency: transitive description: name: build_config - sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33" + sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.1" build_daemon: dependency: transitive description: name: build_daemon - sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa" + sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9" url: "https://pub.dev" source: hosted - version: "4.0.4" + version: "4.0.2" build_resolvers: dependency: transitive description: name: build_resolvers - sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0 + sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" url: "https://pub.dev" source: hosted - version: "2.4.4" + version: "2.4.2" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99" + sha256: "644dc98a0f179b872f612d3eb627924b578897c629788e858157fa5e704ca0c7" url: "https://pub.dev" source: hosted - version: "2.4.15" + version: "2.4.11" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021" + sha256: e3c79f69a64bdfcd8a776a3c28db4eb6e3fb5356d013ae5eb2e52007706d5dbe url: "https://pub.dev" source: hosted - version: "8.0.0" + version: "7.3.1" built_collection: dependency: transitive description: @@ -237,98 +221,98 @@ packages: dependency: transitive description: name: built_value - sha256: ea90e81dc4a25a043d9bee692d20ed6d1c4a1662a28c03a96417446c093ed6b4 + sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb url: "https://pub.dev" source: hosted - version: "8.9.5" + version: "8.9.2" cached_network_image: dependency: transitive description: name: cached_network_image - sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916" + sha256: "4a5d8d2c728b0f3d0245f69f921d7be90cae4c2fd5288f773088672c0893f819" url: "https://pub.dev" source: hosted - version: "3.4.1" + version: "3.4.0" cached_network_image_platform_interface: dependency: transitive description: name: cached_network_image_platform_interface - sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829" + sha256: ff0c949e323d2a1b52be73acce5b4a7b04063e61414c8ca542dbba47281630a7 url: "https://pub.dev" source: hosted - version: "4.1.1" + version: "4.1.0" cached_network_image_web: dependency: transitive description: name: cached_network_image_web - sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062" + sha256: "6322dde7a5ad92202e64df659241104a43db20ed594c41ca18de1014598d7996" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.0" camera: dependency: transitive description: name: camera - sha256: "413d2b34fe28496c35c69ede5b232fb9dd5ca2c3a4cb606b14efc1c7546cc8cb" + sha256: dfa8fc5a1adaeb95e7a54d86a5bd56f4bb0e035515354c8ac6d262e35cec2ec8 url: "https://pub.dev" source: hosted - version: "0.11.1" - camera_android_camerax: + version: "0.10.6" + camera_android: dependency: transitive description: - name: camera_android_camerax - sha256: "9fb44e73e0fea3647a904dc26d38db24055e5b74fc68fd2b6d3abfa1bd20f536" + name: camera_android + sha256: "134b83167cc3c83199e8d75e5bcfde677fec843e7b2ca6b754a5b0b96d00d921" url: "https://pub.dev" source: hosted - version: "0.6.17" + version: "0.10.9+10" camera_avfoundation: dependency: transitive description: name: camera_avfoundation - sha256: ca36181194f429eef3b09de3c96280f2400693f9735025f90d1f4a27465fdd72 + sha256: b5093a82537b64bb88d4244f8e00b5ba69e822a5994f47b31d11400e1db975e5 url: "https://pub.dev" source: hosted - version: "0.9.19" + version: "0.9.17+1" camera_platform_interface: dependency: transitive description: name: camera_platform_interface - sha256: "2f757024a48696ff4814a789b0bd90f5660c0fb25f393ab4564fb483327930e2" + sha256: b3ede1f171532e0d83111fe0980b46d17f1aa9788a07a2fbed07366bbdbb9061 url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.8.0" camera_web: dependency: transitive description: name: camera_web - sha256: "595f28c89d1fb62d77c73c633193755b781c6d2e0ebcd8dc25b763b514e6ba8f" + sha256: b9235ec0a2ce949daec546f1f3d86f05c3921ed31c7d9ab6b7c03214d152fc2d url: "https://pub.dev" source: hosted - version: "0.3.5" + version: "0.3.4" change_case: dependency: "direct main" description: name: change_case - sha256: e41ef3df58521194ef8d7649928954805aeb08061917cf658322305e61568003 + sha256: "99cfdf2018c627c8a3af5a23ea4c414eb69c75c31322d23b9660ebc3cf30b514" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.1.0" characters: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.0" charcode: dependency: "direct main" description: name: charcode - sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a + sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.1" charset: dependency: transitive description: @@ -365,66 +349,66 @@ packages: dependency: transitive description: name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.1" code_builder: dependency: transitive description: name: code_builder - sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e" + sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 url: "https://pub.dev" source: hosted - version: "4.10.1" + version: "4.10.0" collection: dependency: transitive description: name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.19.1" + version: "1.18.0" convert: dependency: transitive description: name: convert - sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" url: "https://pub.dev" source: hosted - version: "3.1.2" - cross_cache: - dependency: transitive + version: "3.1.1" + cool_dropdown: + dependency: "direct main" description: - name: cross_cache - sha256: "007d0340c19d4d201192a3335c4034f4b79eae5ea53f90b69eeb5d239d9fbd1d" + name: cool_dropdown + sha256: "24400f57740b4779407586121e014bef241699ad2a52c506a7e1e7616cb68653" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "2.1.0" cross_file: dependency: transitive description: name: cross_file - sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" + sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32" url: "https://pub.dev" source: hosted - version: "0.3.4+2" + version: "0.3.4+1" crypto: dependency: transitive description: name: crypto - sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.0.3" csslib: dependency: transitive description: name: csslib - sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" + sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.0.0" cupertino_icons: dependency: "direct main" description: @@ -437,10 +421,10 @@ packages: dependency: transitive description: name: dart_style - sha256: "5b236382b47ee411741447c1f1e111459c941ea1b3f2b540dde54c210a3662af" + sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "2.3.6" diffutil_dart: dependency: transitive description: @@ -453,74 +437,74 @@ packages: dependency: transitive description: name: dio - sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9" + sha256: e17f6b3097b8c51b72c74c9f071a605c47bcc8893839bd66732457a5ebe73714 url: "https://pub.dev" source: hosted - version: "5.8.0+1" + version: "5.5.0+1" dio_web_adapter: dependency: transitive description: name: dio_web_adapter - sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" + sha256: "36c5b2d79eb17cdae41e974b7a8284fec631651d2a6f39a8a2ff22327e90aeac" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "1.0.1" equatable: dependency: "direct main" description: name: equatable - sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 url: "https://pub.dev" source: hosted - version: "2.0.7" + version: "2.0.5" expansion_tile_group: dependency: "direct main" description: name: expansion_tile_group - sha256: "894c5088d94dda5d1ddde50463881935ff41b15850fe674605b9003d09716c8e" + sha256: "47615665d4e610dee0b6362de9e81003b56b150b5765ea5444a091762b5dc7d5" url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "1.3.0" fast_immutable_collections: dependency: "direct main" description: name: fast_immutable_collections - sha256: d1aa3d7788fab06cce7f303f4969c7a16a10c865e1bd2478291a8ebcbee084e5 + sha256: c3c73f4f989d3302066e4ec94e6ec73b5dc872592d02194f49f1352d64126b8c url: "https://pub.dev" source: hosted - version: "11.0.4" + version: "10.2.4" ffi: dependency: transitive description: name: ffi - sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.2" file: dependency: transitive description: name: file - sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" url: "https://pub.dev" source: hosted - version: "7.0.1" + version: "7.0.0" file_saver: dependency: "direct main" description: name: file_saver - sha256: "017a127de686af2d2fbbd64afea97052d95f2a0f87d19d25b87e097407bf9c1e" + sha256: d375b351e3331663abbaf99747abd72f159260c58fbbdbca9f926f02c01bdc48 url: "https://pub.dev" source: hosted - version: "0.2.14" + version: "0.2.13" fixnum: dependency: "direct main" description: name: fixnum - sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.0" flutter: dependency: "direct main" description: flutter @@ -530,58 +514,75 @@ packages: dependency: "direct main" description: name: flutter_animate - sha256: "7befe2d3252728afb77aecaaea1dec88a89d35b9b1d2eea6d04479e8af9117b5" + sha256: "7c8a6594a9252dad30cc2ef16e33270b6248c4dedc3b3d06c86c4f3f4dc05ae5" url: "https://pub.dev" source: hosted - version: "4.5.2" + version: "4.5.0" flutter_bloc: dependency: "direct main" description: name: flutter_bloc - sha256: cf51747952201a455a1c840f8171d273be009b932c75093020f9af64f2123e38 + sha256: b594505eac31a0518bdcb4b5b79573b8d9117b193cc80cc12e17d639b10aa27a url: "https://pub.dev" source: hosted - version: "9.1.1" + version: "8.1.6" flutter_cache_manager: dependency: transitive description: name: flutter_cache_manager - sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386" + sha256: a77f77806a790eb9ba0118a5a3a936e81c4fea2b61533033b2b0c3d50bbde5ea url: "https://pub.dev" source: hosted - version: "3.4.1" - flutter_chat_core: + version: "3.4.0" + flutter_chat_types: dependency: "direct main" description: - name: flutter_chat_core - sha256: "7875785bc4aa0b1dce56a76d2a8bd65841c130a3deb2c527878ebfdf8c54f971" + name: flutter_chat_types + sha256: e285b588f6d19d907feb1f6d912deaf22e223656769c34093b64e1c59b094fb9 url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "3.6.2" flutter_chat_ui: dependency: "direct main" description: - name: flutter_chat_ui - sha256: "012aa0d9cc2898b8f89b48f66adb106de9547e466ba21ad54ccef25515f68dcc" - url: "https://pub.dev" - source: hosted - version: "2.3.0" + path: "." + ref: main + resolved-ref: d4b9d507d10f5d640156cacfd754f661f8c0f4c1 + url: "https://gitlab.com/veilid/flutter-chat-ui.git" + source: git + version: "1.6.14" flutter_form_builder: dependency: "direct main" description: name: flutter_form_builder - sha256: aa3901466c70b69ae6c7f3d03fcbccaec5fde179d3fded0b10203144b546ad28 + sha256: "447f8808f68070f7df968e8063aada3c9d2e90e789b5b70f3b44e4b315212656" url: "https://pub.dev" source: hosted - version: "10.0.1" + version: "9.3.0" flutter_hooks: dependency: "direct main" description: name: flutter_hooks - sha256: b772e710d16d7a20c0740c4f855095026b31c7eb5ba3ab67d2bd52021cd9461d + sha256: cde36b12f7188c85286fba9b38cc5a902e7279f36dd676967106c041dc9dde70 url: "https://pub.dev" source: hosted - version: "0.21.2" + version: "0.20.5" + flutter_link_previewer: + dependency: transitive + description: + name: flutter_link_previewer + sha256: "007069e60f42419fb59872beb7a3cc3ea21e9f1bdff5d40239f376fa62ca9f20" + url: "https://pub.dev" + source: hosted + version: "3.2.2" + flutter_linkify: + dependency: transitive + description: + name: flutter_linkify + sha256: "74669e06a8f358fee4512b4320c0b80e51cffc496607931de68d28f099254073" + url: "https://pub.dev" + source: hosted + version: "6.0.0" flutter_localizations: dependency: "direct main" description: flutter @@ -591,34 +592,42 @@ packages: dependency: "direct main" description: name: flutter_native_splash - sha256: "8321a6d11a8d13977fa780c89de8d257cce3d841eecfb7a4cadffcc4f12d82dc" + sha256: aa06fec78de2190f3db4319dd60fdc8d12b2626e93ef9828633928c2dcaea840 url: "https://pub.dev" source: hosted - version: "2.4.6" + version: "2.4.1" + flutter_parsed_text: + dependency: transitive + description: + name: flutter_parsed_text + sha256: "529cf5793b7acdf16ee0f97b158d0d4ba0bf06e7121ef180abe1a5b59e32c1e2" + url: "https://pub.dev" + source: hosted + version: "2.2.1" flutter_plugin_android_lifecycle: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: f948e346c12f8d5480d2825e03de228d0eb8c3a737e4cdaa122267b89c022b5e + sha256: "9d98bd47ef9d34e803d438f17fd32b116d31009f534a6fa5ce3a1167f189a6de" url: "https://pub.dev" source: hosted - version: "2.0.28" + version: "2.0.21" flutter_shaders: dependency: transitive description: name: flutter_shaders - sha256: "34794acadd8275d971e02df03afee3dee0f98dbfb8c4837082ad0034f612a3e2" + sha256: "02750b545c01ff4d8e9bbe8f27a7731aa3778402506c67daa1de7f5fc3f4befe" url: "https://pub.dev" source: hosted - version: "0.1.3" + version: "0.1.2" flutter_slidable: dependency: "direct main" description: name: flutter_slidable - sha256: ab7dbb16f783307c9d7762ede2593ce32c220ba2ba0fd540a3db8e9a3acba71a + sha256: "2c5611c0b44e20d180e4342318e1bbc28b0a44ad2c442f5df16962606fd3e8e3" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "3.1.1" flutter_spinkit: dependency: "direct main" description: @@ -631,18 +640,18 @@ packages: dependency: "direct main" description: name: flutter_sticky_header - sha256: fb4fda6164ef3e5fc7ab73aba34aad253c17b7c6ecf738fa26f1a905b7d2d1e2 + sha256: "017f398fbb45a589e01491861ca20eb6570a763fd9f3888165a978e11248c709" url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.6.5" flutter_svg: dependency: "direct main" description: name: flutter_svg - sha256: d44bf546b13025ec7353091516f6881f1d4c633993cb109c3916c3a0159dadf1 + sha256: "7b4ca6cf3304575fe9c8ec64813c8d02ee41d2afe60bcfe0678bcb5375d596a2" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.0.10+1" flutter_translate: dependency: "direct main" description: @@ -668,26 +677,26 @@ packages: dependency: "direct main" description: name: form_builder_validators - sha256: cd617fa346250293ff3e2709961d0faf7b80e6e4f0ff7b500126b28d7422dd67 + sha256: c61ed7b1deecf0e1ebe49e2fa79e3283937c5a21c7e48e3ed9856a4a14e1191a url: "https://pub.dev" source: hosted - version: "11.1.2" + version: "11.0.0" freezed: dependency: "direct dev" description: name: freezed - sha256: "6022db4c7bfa626841b2a10f34dd1e1b68e8f8f9650db6112dcdeeca45ca793c" + sha256: a434911f643466d78462625df76fd9eb13e57348ff43fe1f77bbe909522c67a1 url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "2.5.2" freezed_annotation: dependency: "direct main" description: name: freezed_annotation - sha256: c87ff004c8aa6af2d531668b46a4ea379f7191dc6dfa066acd53d506da6e044b + sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "2.4.4" frontend_server_client: dependency: transitive description: @@ -700,18 +709,18 @@ packages: dependency: transitive description: name: get - sha256: c79eeb4339f1f3deffd9ec912f8a923834bec55f7b49c9e882b8fef2c139d425 + sha256: e4e7335ede17452b391ed3b2ede016545706c01a02292a6c97619705e7d2a85e url: "https://pub.dev" source: hosted - version: "4.7.2" + version: "4.6.6" glob: dependency: transitive description: name: glob - sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.2" globbing: dependency: transitive description: @@ -724,10 +733,10 @@ packages: dependency: "direct main" description: name: go_router - sha256: "0b1e06223bee260dee31a171fb1153e306907563a0b0225e8c1733211911429a" + sha256: "39dd52168d6c59984454183148dc3a5776960c61083adfc708cc79a7b3ce1ba8" url: "https://pub.dev" source: hosted - version: "15.1.2" + version: "14.2.1" graphs: dependency: transitive description: @@ -736,102 +745,94 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.2" + hive: + dependency: transitive + description: + name: hive + sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941" + url: "https://pub.dev" + source: hosted + version: "2.2.3" html: dependency: transitive description: name: html - sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" + sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" url: "https://pub.dev" source: hosted - version: "0.15.6" + version: "0.15.4" http: dependency: transitive description: name: http - sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" + sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.2.2" http_multi_server: dependency: transitive description: name: http_multi_server - sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" url: "https://pub.dev" source: hosted - version: "3.2.2" + version: "3.2.1" http_parser: dependency: transitive description: name: http_parser - sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" url: "https://pub.dev" source: hosted - version: "4.1.2" + version: "4.0.2" + hydrated_bloc: + dependency: "direct main" + description: + name: hydrated_bloc + sha256: af35b357739fe41728df10bec03aad422cdc725a1e702e03af9d2a41ea05160c + url: "https://pub.dev" + source: hosted + version: "9.1.5" icons_launcher: dependency: "direct dev" description: name: icons_launcher - sha256: "2949eef3d336028d89133f69ef221d877e09deed04ebd8e738ab4a427850a7a2" + sha256: "9b514ffed6ed69b232fd2bf34c44878c8526be71fc74129a658f35c04c9d4a9d" url: "https://pub.dev" source: hosted - version: "3.0.1" - iconsax_flutter: - dependency: transitive - description: - name: iconsax_flutter - sha256: "95b65699da8ea98f87c5d232f06b0debaaf1ec1332b697e4d90969ec9a93037d" - url: "https://pub.dev" - source: hosted - version: "1.0.0" - idb_shim: - dependency: transitive - description: - name: idb_shim - sha256: "40e872276d79a1a97cc2c1ea0ecf046b8e34d788f16a8ea8f0da3e9b337d42da" - url: "https://pub.dev" - source: hosted - version: "2.6.6+1" + version: "2.1.7" image: dependency: "direct main" description: name: image - sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928" + sha256: "2237616a36c0d69aef7549ab439b833fb7f9fb9fc861af2cc9ac3eedddd69ca8" url: "https://pub.dev" source: hosted - version: "4.5.4" - indent: - dependency: transitive - description: - name: indent - sha256: "819319a5c185f7fe412750c798953378b37a0d0d32564ce33e7c5acfd1372d2a" - url: "https://pub.dev" - source: hosted - version: "2.0.0" + version: "4.2.0" intl: dependency: "direct main" description: name: intl - sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.20.2" + version: "0.19.0" io: dependency: transitive description: name: io - sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" url: "https://pub.dev" source: hosted - version: "1.0.5" + version: "1.0.4" js: dependency: transitive description: name: js - sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.7.2" + version: "0.6.7" json_annotation: dependency: "direct main" description: @@ -844,34 +845,34 @@ packages: dependency: "direct dev" description: name: json_serializable - sha256: c50ef5fc083d5b5e12eef489503ba3bf5ccc899e487d691584699b4bdefeea8c + sha256: ea1432d167339ea9b5bb153f0571d0039607a873d6e04e0117af043f14a1fd4b url: "https://pub.dev" source: hosted - version: "6.9.5" - keyboard_avoider: - dependency: "direct main" + version: "6.8.0" + linkify: + dependency: transitive description: - name: keyboard_avoider - sha256: d2917bd52c6612bf8d1ff97f74049ddf3592a81d44e814f0e7b07dcfd245b75c + name: linkify + sha256: "4139ea77f4651ab9c315b577da2dd108d9aa0bd84b5d03d33323f1970c645832" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "5.0.0" lint_hard: dependency: "direct dev" description: name: lint_hard - sha256: "2073d4e83ac4e3f2b87cc615fff41abb5c2c5618e117edcd3d71f40f2186f4d5" + sha256: "44d15ec309b1a8e1aff99069df9dcb1597f49d5f588f32811ca28fb7b38c32fe" url: "https://pub.dev" source: hosted - version: "6.1.1" + version: "4.0.0" logging: dependency: transitive description: name: logging - sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.2.0" loggy: dependency: "direct main" description: @@ -884,42 +885,58 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.11.1" + version: "0.8.0" meta: dependency: "direct main" description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.12.0" mime: dependency: transitive description: name: mime - sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "1.0.5" mobile_scanner: dependency: "direct main" description: name: mobile_scanner - sha256: "72f06a071aa8b14acea3ab43ea7949eefe4a2469731ae210e006ba330a033a8c" + sha256: b8c0e9afcfd52534f85ec666f3d52156f560b5e6c25b1e3d4fe2087763607926 url: "https://pub.dev" source: hosted - version: "7.0.0" + version: "5.1.1" + motion_toast: + dependency: "direct main" + description: + name: motion_toast + sha256: "8dc8af93c606d0a08f2592591164f4a761099c5470e589f25689de6c601f124e" + url: "https://pub.dev" + source: hosted + version: "2.10.0" + native_device_orientation: + dependency: "direct main" + description: + name: native_device_orientation + sha256: "0c330c068575e4be72cce5968ca479a3f8d5d1e5dfce7d89d5c13a1e943b338c" + url: "https://pub.dev" + source: hosted + version: "2.0.3" nested: dependency: transitive description: @@ -940,74 +957,74 @@ packages: dependency: transitive description: name: package_config - sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.1.0" package_info_plus: dependency: "direct main" description: name: package_info_plus - sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191" + sha256: "4de6c36df77ffbcef0a5aefe04669d33f2d18397fea228277b852a2d4e58e860" url: "https://pub.dev" source: hosted - version: "8.3.0" + version: "8.0.1" package_info_plus_platform_interface: dependency: transitive description: name: package_info_plus_platform_interface - sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c" + sha256: ac1f4a4847f1ade8e6a87d1f39f5d7c67490738642e2542f559ec38c37489a66 url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "3.0.1" pasteboard: dependency: "direct main" description: name: pasteboard - sha256: "9ff73ada33f79a59ff91f6c01881fd4ed0a0031cfc4ae2d86c0384471525fca1" + sha256: "1c8b6a8b3f1d12e55d4e9404433cda1b4abe66db6b17bc2d2fb5965772c04674" url: "https://pub.dev" source: hosted - version: "0.4.0" + version: "0.2.0" path: dependency: "direct main" description: name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.9.0" path_parsing: dependency: transitive description: name: path_parsing - sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" + sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.0.1" path_provider: dependency: "direct main" description: name: path_provider - sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" + sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378 url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.1.4" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 + sha256: "490539678396d4c3c0b06efdaab75ae60675c3e0c66f72bc04c2e2c1e0e2abeb" url: "https://pub.dev" source: hosted - version: "2.2.17" + version: "2.2.9" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.0" path_provider_linux: dependency: transitive description: @@ -1032,22 +1049,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.0" - pausable_timer: - dependency: transitive - description: - name: pausable_timer - sha256: "6ef1a95441ec3439de6fb63f39a011b67e693198e7dae14e20675c3c00e86074" - url: "https://pub.dev" - source: hosted - version: "3.1.0+3" pdf: dependency: "direct main" description: name: pdf - sha256: "28eacad99bffcce2e05bba24e50153890ad0255294f4dd78a17075a2ba5c8416" + sha256: "81d5522bddc1ef5c28e8f0ee40b71708761753c163e0c93a40df56fd515ea0f0" url: "https://pub.dev" source: hosted - version: "3.11.3" + version: "3.11.0" pdf_widget_wrapper: dependency: transitive description: @@ -1060,26 +1069,34 @@ packages: dependency: transitive description: name: petitparser - sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "6.0.2" + photo_view: + dependency: transitive + description: + name: photo_view + sha256: "1fc3d970a91295fbd1364296575f854c9863f225505c28c46e0a03e48960c75e" + url: "https://pub.dev" + source: hosted + version: "0.15.0" pinput: dependency: "direct main" description: name: pinput - sha256: "8a73be426a91fefec90a7f130763ca39772d547e92f19a827cf4aa02e323d35a" + sha256: "6d571e38a484f7515a52e89024ef416f11fa6171ac6f32303701374ab9890efa" url: "https://pub.dev" source: hosted - version: "5.0.1" + version: "4.0.0" platform: dependency: transitive description: name: platform - sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" url: "https://pub.dev" source: hosted - version: "3.1.6" + version: "3.1.5" plugin_platform_interface: dependency: transitive description: @@ -1092,10 +1109,10 @@ packages: dependency: transitive description: name: pointycastle - sha256: "92aa3841d083cc4b0f4709b5c74fd6409a3e6ba833ffc7dc6a8fee096366acf5" + sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "3.9.1" pool: dependency: transitive description: @@ -1104,14 +1121,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" - posix: - dependency: transitive - description: - name: posix - sha256: f0d7856b6ca1887cfa6d1d394056a296ae33489db914e365e2044fdada449e62 - url: "https://pub.dev" - source: hosted - version: "6.0.2" preload_page_view: dependency: "direct main" description: @@ -1124,42 +1133,42 @@ packages: dependency: "direct main" description: name: printing - sha256: "482cd5a5196008f984bb43ed0e47cbfdca7373490b62f3b27b3299275bf22a93" + sha256: cc4b256a5a89d5345488e3318897b595867f5181b8c5ed6fc63bfa5f2044aec3 url: "https://pub.dev" source: hosted - version: "5.14.2" + version: "5.13.1" protobuf: dependency: "direct main" description: name: protobuf - sha256: "579fe5557eae58e3adca2e999e38f02441d8aa908703854a9e0a0f47fa857731" + sha256: "68645b24e0716782e58948f8467fd42a880f255096a821f9e7d0ec625b00c84d" url: "https://pub.dev" source: hosted - version: "4.1.0" + version: "3.1.0" provider: dependency: "direct main" description: name: provider - sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84" + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c url: "https://pub.dev" source: hosted - version: "6.1.5" + version: "6.1.2" pub_semver: dependency: transitive description: name: pub_semver - sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.1.4" pubspec_parse: dependency: transitive description: name: pubspec_parse - sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" + sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8 url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "1.3.0" qr: dependency: transitive description: @@ -1168,22 +1177,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.2" - qr_code_dart_decoder: - dependency: transitive - description: - name: qr_code_dart_decoder - sha256: "6da7eda27726d504bed3c30eabf78ddca3eb9265e1c8dc49b30ef5974b9c267f" - url: "https://pub.dev" - source: hosted - version: "0.0.5" qr_code_dart_scan: dependency: "direct main" description: name: qr_code_dart_scan - sha256: bc4fc6f400b4350c6946d123c7871e156459703a61f8fa57d7144df9bbb46610 + sha256: "52912da40f5e40a197b890108af9d2a6baa0c5812b77bfb085c8ee9e3c4f1f52" url: "https://pub.dev" source: hosted - version: "0.10.0" + version: "0.8.1" qr_flutter: dependency: "direct main" description: @@ -1192,14 +1193,22 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.0" + quickalert: + dependency: "direct main" + description: + name: quickalert + sha256: b5d62b1e20b08cc0ff5f40b6da519bdc7a5de6082f13d90572cf4e72eea56c5e + url: "https://pub.dev" + source: hosted + version: "1.1.0" quiver: dependency: transitive description: name: quiver - sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2 + sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47 url: "https://pub.dev" source: hosted - version: "3.2.2" + version: "3.2.1" radix_colors: dependency: "direct main" description: @@ -1216,14 +1225,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.10" - rflutter_alert: - dependency: "direct main" - description: - name: rflutter_alert - sha256: "8ff35e3f9712ba24c746499cfa95bf320385edf38901a1a4eab0fe555867f66c" - url: "https://pub.dev" - source: hosted - version: "2.0.7" rxdart: dependency: transitive description: @@ -1236,42 +1237,10 @@ packages: dependency: transitive description: name: screen_retriever - sha256: "570dbc8e4f70bac451e0efc9c9bb19fa2d6799a11e6ef04f946d7886d2e23d0c" + sha256: "6ee02c8a1158e6dae7ca430da79436e3b1c9563c8cf02f524af997c201ac2b90" url: "https://pub.dev" source: hosted - version: "0.2.0" - screen_retriever_linux: - dependency: transitive - description: - name: screen_retriever_linux - sha256: f7f8120c92ef0784e58491ab664d01efda79a922b025ff286e29aa123ea3dd18 - url: "https://pub.dev" - source: hosted - version: "0.2.0" - screen_retriever_macos: - dependency: transitive - description: - name: screen_retriever_macos - sha256: "71f956e65c97315dd661d71f828708bd97b6d358e776f1a30d5aa7d22d78a149" - url: "https://pub.dev" - source: hosted - version: "0.2.0" - screen_retriever_platform_interface: - dependency: transitive - description: - name: screen_retriever_platform_interface - sha256: ee197f4581ff0d5608587819af40490748e1e39e648d7680ecf95c05197240c0 - url: "https://pub.dev" - source: hosted - version: "0.2.0" - screen_retriever_windows: - dependency: transitive - description: - name: screen_retriever_windows - sha256: "449ee257f03ca98a57288ee526a301a430a344a161f9202b4fcc38576716fe13" - url: "https://pub.dev" - source: hosted - version: "0.2.0" + version: "0.1.9" screenshot: dependency: "direct main" description: @@ -1288,79 +1257,63 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.1" - scrollview_observer: - dependency: transitive - description: - name: scrollview_observer - sha256: "174d4efe7b79459a07662175c4db42c9862dcf78d3978e6e9c2d6c0d8137f4ca" - url: "https://pub.dev" - source: hosted - version: "1.26.1" searchable_listview: dependency: "direct main" description: path: "." ref: main - resolved-ref: f367c2f713dcc0c965a4f7af5952d94b2f699998 + resolved-ref: db0f7b6f1baec0250ecba82f3d161bac1cf23d7d url: "https://gitlab.com/veilid/Searchable-Listview.git" source: git - version: "2.16.0" - sembast: - dependency: transitive - description: - name: sembast - sha256: d3f0d0ba501a5f1fd7d6c8532ee01385977c8a069c334635dae390d059ae3d6d - url: "https://pub.dev" - source: hosted - version: "3.8.5" + version: "2.14.1" share_plus: dependency: "direct main" description: name: share_plus - sha256: b2961506569e28948d75ec346c28775bb111986bb69dc6a20754a457e3d97fa0 + sha256: ef3489a969683c4f3d0239010cc8b7a2a46543a8d139e111c06c558875083544 url: "https://pub.dev" source: hosted - version: "11.0.0" + version: "9.0.0" share_plus_platform_interface: dependency: transitive description: name: share_plus_platform_interface - sha256: "1032d392bc5d2095a77447a805aa3f804d2ae6a4d5eef5e6ebb3bd94c1bc19ef" + sha256: "0f9e4418835d1b2c3ae78fdb918251959106cefdbc4dd43526e182f80e82f6d4" url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "4.0.0" shared_preferences: dependency: "direct main" description: name: shared_preferences - sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + sha256: c272f9cabca5a81adc9b0894381e9c1def363e980f960fa903c604c471b22f68 url: "https://pub.dev" source: hosted - version: "2.5.3" + version: "2.3.1" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" + sha256: "041be4d9d2dc6079cf342bc8b761b03787e3b71192d658220a56cac9c04a0294" url: "https://pub.dev" source: hosted - version: "2.4.10" + version: "2.3.0" shared_preferences_foundation: dependency: transitive description: name: shared_preferences_foundation - sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" + sha256: "671e7a931f55a08aa45be2a13fe7247f2a41237897df434b30d2012388191833" url: "https://pub.dev" source: hosted - version: "2.5.4" + version: "2.5.0" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux - sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + sha256: "2ba0510d3017f91655b7543e9ee46d48619de2a2af38e5c790423f7007c7ccc1" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.0" shared_preferences_platform_interface: dependency: transitive description: @@ -1373,34 +1326,34 @@ packages: dependency: transitive description: name: shared_preferences_web - sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + sha256: "3a293170d4d9403c3254ee05b84e62e8a9b3c5808ebd17de6a33fe9ea6457936" url: "https://pub.dev" source: hosted - version: "2.4.3" + version: "2.4.0" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows - sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + sha256: "398084b47b7f92110683cac45c6dc4aae853db47e470e5ddcd52cab7f7196ab2" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.0" shelf: dependency: transitive description: name: shelf - sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 url: "https://pub.dev" source: hosted - version: "1.4.2" + version: "1.4.1" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" + sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "2.0.0" signal_strength_indicator: dependency: "direct main" description: @@ -1413,15 +1366,15 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.0" + version: "0.0.99" sliver_expandable: dependency: "direct main" description: name: sliver_expandable - sha256: "046d8912ebd072bf9d8e8161e50a4669c520f691fce8bfcbae4ada6982b18ba3" + sha256: ae20eb848bd0ba9dd704732ad654438ac5a5bea2b023fa3cf80a086166d96d97 url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.1" sliver_fill_remaining_box_adapter: dependency: "direct main" description: @@ -1438,6 +1391,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.12" + smart_auth: + dependency: transitive + description: + name: smart_auth + sha256: "88aa8fe66e951c78a307f26d1c29672dce2e9eb3da2e12e853864d0e615a73ad" + url: "https://pub.dev" + source: hosted + version: "2.0.0" sorted_list: dependency: "direct main" description: @@ -1451,26 +1412,26 @@ packages: dependency: transitive description: name: source_gen - sha256: "35c8150ece9e8c8d263337a265153c3329667640850b9304861faea59fc98f6b" + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "1.5.0" source_helper: dependency: transitive description: name: source_helper - sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c" + sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd" url: "https://pub.dev" source: hosted - version: "1.3.5" + version: "1.3.4" source_span: dependency: transitive description: name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.10.1" + version: "1.10.0" split_view: dependency: "direct main" description: @@ -1491,50 +1452,26 @@ packages: dependency: transitive description: name: sqflite - sha256: e2297b1da52f127bc7a3da11439985d9b536f75070f3325e62ada69a5c585d03 + sha256: a43e5a27235518c03ca238e7b4732cf35eabe863a369ceba6cbefa537a66f16d url: "https://pub.dev" source: hosted - version: "2.4.2" - sqflite_android: - dependency: transitive - description: - name: sqflite_android - sha256: "2b3070c5fa881839f8b402ee4a39c1b4d561704d4ebbbcfb808a119bc2a1701b" - url: "https://pub.dev" - source: hosted - version: "2.4.1" + version: "2.3.3+1" sqflite_common: dependency: transitive description: name: sqflite_common - sha256: "84731e8bfd8303a3389903e01fb2141b6e59b5973cacbb0929021df08dddbe8b" + sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4" url: "https://pub.dev" source: hosted - version: "2.5.5" - sqflite_darwin: - dependency: transitive - description: - name: sqflite_darwin - sha256: "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3" - url: "https://pub.dev" - source: hosted - version: "2.4.2" - sqflite_platform_interface: - dependency: transitive - description: - name: sqflite_platform_interface - sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920" - url: "https://pub.dev" - source: hosted - version: "2.4.0" + version: "2.5.4" stack_trace: dependency: "direct main" description: name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.12.1" + version: "1.11.1" star_menu: dependency: "direct main" description: @@ -1547,34 +1484,34 @@ packages: dependency: transitive description: name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.2" stream_transform: dependency: "direct main" description: name: stream_transform - sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.0" string_scanner: dependency: transitive description: name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.3.0" synchronized: dependency: transitive description: name: synchronized - sha256: "0669c70faae6270521ee4f05bffd2919892d42d1276e6c495be80174b6bc0ef6" + sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558" url: "https://pub.dev" source: hosted - version: "3.3.1" + version: "3.1.0+1" system_info2: dependency: transitive description: @@ -1587,42 +1524,34 @@ packages: dependency: transitive description: name: system_info_plus - sha256: df94187e95527f9cb459e6a9f6e0b1ea20c157d8029bc233de34b3c1e17e1c48 + sha256: b915c811c6605b802f3988859bc2bb79c95f735762a75b5451741f7a2b949d1b url: "https://pub.dev" source: hosted - version: "0.0.6" + version: "0.0.5" term_glyph: dependency: transitive description: name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.1" test_api: dependency: transitive description: name: test_api - sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.7.6" + version: "0.7.0" timing: dependency: transitive description: name: timing - sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" url: "https://pub.dev" source: hosted - version: "1.0.2" - toastification: - dependency: "direct main" - description: - name: toastification - sha256: "9713989549d60754fd0522425d1251501919cfb7bab4ffbbb36ef40de5ea72b9" - url: "https://pub.dev" - source: hosted - version: "3.0.2" + version: "1.0.1" transitioned_indexed_stack: dependency: "direct main" description: @@ -1635,10 +1564,10 @@ packages: dependency: transitive description: name: typed_data - sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.2" universal_io: dependency: transitive description: @@ -1659,42 +1588,42 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603" + sha256: "21b704ce5fa560ea9f3b525b43601c678728ba46725bab9b01187b4831377ed3" url: "https://pub.dev" source: hosted - version: "6.3.1" + version: "6.3.0" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: "8582d7f6fe14d2652b4c45c9b6c14c0b678c2af2d083a11b604caeba51930d79" + sha256: "94d8ad05f44c6d4e2ffe5567ab4d741b82d62e3c8e288cc1fcea45965edf47c9" url: "https://pub.dev" source: hosted - version: "6.3.16" + version: "6.3.8" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "7f2022359d4c099eea7df3fdf739f7d3d3b9faf3166fb1dd390775176e0b76cb" + sha256: e43b677296fadce447e987a2f519dcf5f6d1e527dc35d01ffab4fff5b8a7063e url: "https://pub.dev" source: hosted - version: "6.3.3" + version: "6.3.1" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935" + sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811 url: "https://pub.dev" source: hosted - version: "3.2.1" + version: "3.1.1" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2" + sha256: "9a1a42d5d2d95400c795b2914c36fdcb525870c752569438e4ebb09a2b5d90de" url: "https://pub.dev" source: hosted - version: "3.2.2" + version: "3.2.0" url_launcher_platform_interface: dependency: transitive description: @@ -1707,58 +1636,58 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2" + sha256: "8d9e750d8c9338601e709cd0885f95825086bd8b642547f26bda435aade95d8a" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.3.1" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77" + sha256: "49c10f879746271804767cb45551ec5592cdab00ee105c06dddde1a98f73b185" url: "https://pub.dev" source: hosted - version: "3.1.4" + version: "3.1.2" uuid: dependency: "direct main" description: name: uuid - sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + sha256: "83d37c7ad7aaf9aa8e275490669535c8080377cfa7a7004c24dfac53afffaa90" url: "https://pub.dev" source: hosted - version: "4.5.1" + version: "4.4.2" value_layout_builder: dependency: transitive description: name: value_layout_builder - sha256: ab4b7d98bac8cefeb9713154d43ee0477490183f5aa23bb4ffa5103d9bbf6275 + sha256: "98202ec1807e94ac72725b7f0d15027afde513c55c69ff3f41bcfccb950831bc" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.3.1" vector_graphics: dependency: transitive description: name: vector_graphics - sha256: "44cc7104ff32563122a929e4620cf3efd584194eec6d1d913eb5ba593dbcf6de" + sha256: "32c3c684e02f9bc0afb0ae0aa653337a2fe022e8ab064bcd7ffda27a74e288e3" url: "https://pub.dev" source: hosted - version: "1.1.18" + version: "1.1.11+1" vector_graphics_codec: dependency: transitive description: name: vector_graphics_codec - sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146" + sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da url: "https://pub.dev" source: hosted - version: "1.1.13" + version: "1.1.11+1" vector_graphics_compiler: dependency: transitive description: name: vector_graphics_compiler - sha256: "557a315b7d2a6dbb0aaaff84d857967ce6bdc96a63dc6ee2a57ce5a6ee5d3331" + sha256: "12faff3f73b1741a36ca7e31b292ddeb629af819ca9efe9953b70bd63fc8cd81" url: "https://pub.dev" source: hosted - version: "1.1.17" + version: "1.1.11+1" vector_math: dependency: transitive description: @@ -1773,7 +1702,7 @@ packages: path: "../veilid/veilid-flutter" relative: true source: path - version: "0.4.6" + version: "0.3.3" veilid_support: dependency: "direct main" description: @@ -1781,62 +1710,70 @@ packages: relative: true source: path version: "1.0.2+0" + visibility_detector: + dependency: transitive + description: + name: visibility_detector + sha256: dd5cc11e13494f432d15939c3aa8ae76844c42b723398643ce9addb88a5ed420 + url: "https://pub.dev" + source: hosted + version: "0.4.0+2" watcher: dependency: transitive description: name: watcher - sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104" + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.0" web: dependency: transitive description: name: web - sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "0.5.1" web_socket: dependency: transitive description: name: web_socket - sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" + sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "0.1.6" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 + sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.1" win32: dependency: transitive description: name: win32 - sha256: "329edf97fdd893e0f1e3b9e88d6a0e627128cc17cc316a8d67fda8f1451178ba" + sha256: "015002c060f1ae9f41a818f2d5640389cc05283e368be19dc8d77cecb43c40c9" url: "https://pub.dev" source: hosted - version: "5.13.0" + version: "5.5.3" window_manager: dependency: "direct main" description: name: window_manager - sha256: "732896e1416297c63c9e3fb95aea72d0355f61390263982a47fd519169dc5059" + sha256: "8699323b30da4cdbe2aa2e7c9de567a6abd8a97d9a5c850a3c86dcd0b34bbfbf" url: "https://pub.dev" source: hosted - version: "0.4.3" + version: "0.3.9" xdg_directories: dependency: transitive description: name: xdg_directories - sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.0.4" xml: dependency: transitive description: @@ -1857,10 +1794,10 @@ packages: dependency: transitive description: name: yaml - sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" url: "https://pub.dev" source: hosted - version: "3.1.3" + version: "3.1.2" zmodem: dependency: transitive description: @@ -1886,5 +1823,5 @@ packages: source: hosted version: "1.1.2" sdks: - dart: ">=3.7.0 <4.0.0" - flutter: ">=3.32.0" + dart: ">=3.4.0 <4.0.0" + flutter: ">=3.22.1" diff --git a/pubspec.yaml b/pubspec.yaml index 519714b..e4ae2fc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,90 +1,94 @@ name: veilidchat description: VeilidChat -publish_to: "none" -version: 0.4.7+20 +publish_to: 'none' +version: 0.4.0+13 environment: - sdk: ">=3.2.0 <4.0.0" - flutter: ">=3.32.0" + sdk: '>=3.2.0 <4.0.0' + flutter: '>=3.22.1' dependencies: accordion: ^2.6.0 - animated_bottom_navigation_bar: ^1.4.0 - animated_custom_dropdown: ^3.1.1 + animated_bottom_navigation_bar: ^1.3.3 animated_switcher_transitions: ^1.0.0 animated_theme_switcher: ^2.0.10 - ansicolor: ^2.0.3 - archive: ^4.0.4 - async_tools: ^0.1.9 - auto_size_text: ^3.0.0 - awesome_extensions: ^2.0.21 + ansicolor: ^2.0.2 + archive: ^3.6.1 + async_tools: ^0.1.5 + awesome_extensions: ^2.0.16 badges: ^3.1.2 - basic_utils: ^5.8.2 - bloc: ^9.0.0 - bloc_advanced_tools: ^0.1.12 + basic_utils: ^5.7.0 + bloc: ^8.1.4 + bloc_advanced_tools: ^0.1.5 blurry_modal_progress_hud: ^1.1.1 - change_case: ^2.2.0 - charcode: ^1.4.0 + change_case: ^2.1.0 + charcode: ^1.3.1 circular_profile_avatar: ^2.0.5 circular_reveal_animation: ^2.0.1 + cool_dropdown: ^2.1.0 cupertino_icons: ^1.0.8 - equatable: ^2.0.7 - expansion_tile_group: ^2.2.0 - fast_immutable_collections: ^11.0.3 - file_saver: ^0.2.14 - fixnum: ^1.1.1 + equatable: ^2.0.5 + expansion_tile_group: ^1.2.4 + fast_immutable_collections: ^10.2.4 + file_saver: ^0.2.13 + fixnum: ^1.1.0 flutter: sdk: flutter - flutter_animate: ^4.5.2 - flutter_bloc: ^9.1.0 - flutter_chat_core: ^2.2.0 - flutter_chat_ui: ^2.2.0 - flutter_form_builder: ^10.0.1 - flutter_hooks: ^0.21.2 + flutter_animate: ^4.5.0 + flutter_bloc: ^8.1.5 + flutter_chat_types: ^3.6.2 + flutter_chat_ui: + git: + url: https://gitlab.com/veilid/flutter-chat-ui.git + ref: main + flutter_form_builder: ^9.3.0 + flutter_hooks: ^0.20.5 flutter_localizations: sdk: flutter - flutter_native_splash: ^2.4.5 - flutter_slidable: ^4.0.0 + flutter_native_splash: ^2.4.0 + flutter_slidable: ^3.1.0 flutter_spinkit: ^5.2.1 - flutter_sticky_header: ^0.8.0 - flutter_svg: ^2.0.17 + flutter_sticky_header: ^0.6.5 + flutter_svg: ^2.0.10+1 flutter_translate: ^4.1.0 flutter_zoom_drawer: ^3.2.0 - form_builder_validators: ^11.1.2 - freezed_annotation: ^3.0.0 - go_router: ^15.1.2 - image: ^4.5.3 + form_builder_validators: ^11.0.0 + freezed_annotation: ^2.4.1 + go_router: ^14.1.4 + hydrated_bloc: ^9.1.5 + image: ^4.2.0 intl: ^0.19.0 json_annotation: ^4.9.0 - keyboard_avoider: ^0.2.0 loggy: ^2.0.3 - meta: ^1.16.0 - mobile_scanner: ^7.0.0 - package_info_plus: ^8.3.0 - pasteboard: ^0.4.0 - path: ^1.9.1 - path_provider: ^2.1.5 - pdf: ^3.11.3 - pinput: ^5.0.1 + meta: ^1.12.0 + mobile_scanner: ^5.1.1 + motion_toast: ^2.10.0 + native_device_orientation: ^2.0.3 + package_info_plus: ^8.0.0 + pasteboard: ^0.2.0 + path: ^1.9.0 + path_provider: ^2.1.3 + pdf: ^3.11.0 + pinput: ^4.0.0 preload_page_view: ^0.2.0 - printing: ^5.14.2 - protobuf: ^4.1.0 + printing: ^5.13.1 + protobuf: ^3.1.0 provider: ^6.1.2 - qr_code_dart_scan: ^0.10.0 + qr_code_dart_scan: ^0.8.0 qr_flutter: ^4.1.0 + quickalert: ^1.1.0 radix_colors: ^1.0.4 reorderable_grid: ^1.0.10 - rflutter_alert: ^2.0.7 screenshot: ^3.0.0 scroll_to_index: ^3.0.1 searchable_listview: git: url: https://gitlab.com/veilid/Searchable-Listview.git ref: main - share_plus: ^11.0.0 - shared_preferences: ^2.5.2 + share_plus: ^9.0.0 + shared_preferences: ^2.2.3 signal_strength_indicator: ^0.4.1 - sliver_expandable: ^1.1.2 + sliver_expandable: ^1.1.1 sliver_fill_remaining_box_adapter: ^1.0.0 sliver_tools: ^0.2.12 sorted_list: @@ -92,60 +96,56 @@ dependencies: url: https://gitlab.com/veilid/dart-sorted-list-improved.git ref: main split_view: ^3.2.1 - stack_trace: ^1.12.1 + stack_trace: ^1.11.1 star_menu: ^4.0.1 - stream_transform: ^2.1.1 - toastification: ^3.0.2 + stream_transform: ^2.1.0 transitioned_indexed_stack: ^1.0.2 - url_launcher: ^6.3.1 - uuid: ^4.5.1 + url_launcher: ^6.3.0 + uuid: ^4.4.0 veilid: # veilid: ^0.0.1 path: ../veilid/veilid-flutter veilid_support: path: packages/veilid_support - window_manager: ^0.4.3 + window_manager: ^0.3.9 xterm: ^4.0.0 zxing2: ^0.2.3 -dependency_overrides: - intl: ^0.20.2 # Until flutter_translate updates intl -# async_tools: -# path: ../dart_async_tools -# bloc_advanced_tools: -# path: ../bloc_advanced_tools -# searchable_listview: -# path: ../Searchable-Listview -# flutter_chat_core: -# path: ../flutter_chat_ui/packages/flutter_chat_core -# flutter_chat_ui: -# path: ../flutter_chat_ui/packages/flutter_chat_ui +# dependency_overrides: +# async_tools: +# path: ../dart_async_tools +# bloc_advanced_tools: +# path: ../bloc_advanced_tools +# searchable_listview: +# path: ../Searchable-Listview +# flutter_chat_ui: +# path: ../flutter_chat_ui dev_dependencies: - build_runner: ^2.4.15 - freezed: ^3.0.4 - icons_launcher: ^3.0.1 - json_serializable: ^6.9.4 - lint_hard: ^6.0.0 + build_runner: ^2.4.11 + freezed: ^2.5.2 + icons_launcher: ^2.1.7 + json_serializable: ^6.8.0 + lint_hard: ^4.0.0 flutter_native_splash: color: "#8588D0" - + icons_launcher: - image_path: "assets/launcher/icon.png" + image_path: 'assets/launcher/icon.png' platforms: android: enable: true - adaptive_background_color: "#ffffff" - adaptive_foreground_image: "assets/launcher/icon.png" - adaptive_round_image: "assets/launcher/icon.png" + adaptive_background_color: '#ffffff' + adaptive_foreground_image: 'assets/launcher/icon.png' + adaptive_round_image: 'assets/launcher/icon.png' ios: enable: true web: enable: true macos: enable: true - image_path: "assets/launcher/macos_icon.png" + image_path: 'assets/launcher/macos_icon.png' windows: enable: true linux: @@ -158,27 +158,14 @@ flutter: - assets/i18n/en.json # Launcher icon - assets/launcher/icon.png - # Theme wallpaper - - assets/images/wallpaper/arctic.svg - - assets/images/wallpaper/babydoll.svg - - assets/images/wallpaper/eggplant.svg - - assets/images/wallpaper/elite.svg - - assets/images/wallpaper/forest.svg - - assets/images/wallpaper/garden.svg - - assets/images/wallpaper/gold.svg - - assets/images/wallpaper/grim.svg - - assets/images/wallpaper/lapis.svg - - assets/images/wallpaper/lime.svg - - assets/images/wallpaper/scarlet.svg - - assets/images/wallpaper/vapor.svg - # Vector Images - - assets/images/icon.svg + # Images - assets/images/splash.svg + - assets/images/icon.svg - assets/images/title.svg - assets/images/vlogo.svg - - assets/images/toilet.svg - # Raster Images - assets/images/ellet.png + - assets/images/toilet.png + - assets/images/handshake.png # Printing - assets/js/pdf/3.2.146/pdf.min.js # Sounds @@ -193,7 +180,7 @@ flutter: - asset: assets/fonts/SourceCodePro-Regular.ttf - asset: assets/fonts/SourceCodePro-Bold.ttf weight: 700 - + # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware diff --git a/web/index.html b/web/index.html index 99eac34..dfd1680 100644 --- a/web/index.html +++ b/web/index.html @@ -1,5 +1,5 @@ - + - + - + @@ -33,16 +34,19 @@ VeilidChat + + + + - window.dartPdfJsBaseUrl = "assets/js/pdf/3.2.146/"; + - + \ No newline at end of file diff --git a/windows/flutter/CMakeLists.txt b/windows/flutter/CMakeLists.txt index 903f489..930d207 100644 --- a/windows/flutter/CMakeLists.txt +++ b/windows/flutter/CMakeLists.txt @@ -10,11 +10,6 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") -# Set fallback configurations for older versions of the flutter tool. -if (NOT DEFINED FLUTTER_TARGET_PLATFORM) - set(FLUTTER_TARGET_PLATFORM "windows-x64") -endif() - # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -97,7 +92,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - ${FLUTTER_TARGET_PLATFORM} $ + windows-x64 $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index bb1eee2..041a716 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -9,8 +9,9 @@ #include #include #include -#include +#include #include +#include #include #include #include @@ -22,10 +23,12 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("PasteboardPlugin")); PrintingPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("PrintingPlugin")); - ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar( - registry->GetRegistrarForPlugin("ScreenRetrieverWindowsPluginCApi")); + ScreenRetrieverPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ScreenRetrieverPlugin")); SharePlusWindowsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); + SmartAuthPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SmartAuthPlugin")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); VeilidPluginRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 6cba61e..17a8144 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -6,8 +6,9 @@ list(APPEND FLUTTER_PLUGIN_LIST file_saver pasteboard printing - screen_retriever_windows + screen_retriever share_plus + smart_auth url_launcher_windows veilid window_manager