diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 00000000..8ea23a42 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,205 @@ +kind: pipeline +type: exec +name: Nightly + +steps: + - name: Prepare + commands: + - ln -s /var/lib/drone-runner-exec/.local $HOME/.local + - ln -s /var/lib/drone-runner-exec/fdroid $HOME/fdroid + + - name: Run Tests + commands: + - ./ci.py --ci-run-tests + - sonar-scanner -Dsonar.projectKey=SelfPrivacy-Flutter-App -Dsonar.sources=. -Dsonar.host.url=http://analyzer.lan:9000 -Dsonar.login="$SONARQUBE_TOKEN" + environment: + SONARQUBE_TOKEN: + from_secret: SONARQUBE_TOKEN + + - name: Build Nightly Linux Release Artifact (Binary) + commands: + - ./ci.py --ci-build-linux + + - name: Build Nightly Release Artifact (.APK) + commands: + - ./ci.py --ci-build-apk + + - name: Sign and Commit Nightly Android Release Artifact (.APK) for F-Droid Repository + commands: + - rm -rf $HOME/fdroid/build/org.selfprivacy.app.nightly && mkdir $HOME/fdroid/build/org.selfprivacy.app.nightly + - git archive --format=tar HEAD | tar x -C $HOME/fdroid/build/org.selfprivacy.app.nightly + - ./ci.py --sign-apk-fdroid-nightly + environment: + STANDALONE_KEYSTORE_PASS: + from_secret: STANDALONE_KEYSTORE_PASS + FDROID_KEYSTORE_PASS: + from_secret: FDROID_KEYSTORE_PASS + GOOGLE_KEYSTORE_PASS: + from_secret: GOOGLE_KEYSTORE_PASS + + - name: Deploy F-Droid Repository + commands: + - ./ci.py --deploy-fdroid-repo + environment: + SSH_PRIVATE_KEY: + from_secret: SSH_PRIVATE_KEY + +trigger: + event: + - push + branch: + - master + +node: + server: builder + +--- + +kind: pipeline +type: exec +name: Release + +steps: + - name: Prepare + commands: + - ln -s /var/lib/drone-runner-exec/.local $HOME/.local + - ln -s /var/lib/drone-runner-exec/fdroid $HOME/fdroid + - if podman volume exists release; then podman volume rm -f release; podman volume create release; else podman volume create release; fi + - git config user.email "builder@selfprivacy.org" + - git config user.name "Builder" + + - name: Create an Empty Gitea Release + commands: + - ./ci.py --gitea-create-release + environment: + GITEA_RELEASE_TOKEN: + from_secret: GITEA_RELEASE_TOKEN + + - name: Build Intermediate Linux Release Artifact (Binary) + commands: + - ./ci.py --build-linux + environment: + STANDALONE_KEYSTORE_PASS: + from_secret: STANDALONE_KEYSTORE_PASS + FDROID_KEYSTORE_PASS: + from_secret: FDROID_KEYSTORE_PASS + GOOGLE_KEYSTORE_PASS: + from_secret: GOOGLE_KEYSTORE_PASS + + - name: Build Intermediate Android Release Artifact (.APK) + commands: + - ./ci.py --build-apk + environment: + STANDALONE_KEYSTORE_PASS: + from_secret: STANDALONE_KEYSTORE_PASS + FDROID_KEYSTORE_PASS: + from_secret: FDROID_KEYSTORE_PASS + GOOGLE_KEYSTORE_PASS: + from_secret: GOOGLE_KEYSTORE_PASS + + - name: Build Intermediate Android Release Artifact (Bundle) + commands: + - ./ci.py --build-bundle + environment: + STANDALONE_KEYSTORE_PASS: + from_secret: STANDALONE_KEYSTORE_PASS + FDROID_KEYSTORE_PASS: + from_secret: FDROID_KEYSTORE_PASS + GOOGLE_KEYSTORE_PASS: + from_secret: GOOGLE_KEYSTORE_PASS + + - name: Sign Android Release Artifact (.APK) for Standalone Use + commands: + - ./ci.py --sign-apk-standalone + environment: + STANDALONE_KEYSTORE_PASS: + from_secret: STANDALONE_KEYSTORE_PASS + FDROID_KEYSTORE_PASS: + from_secret: FDROID_KEYSTORE_PASS + GOOGLE_KEYSTORE_PASS: + from_secret: GOOGLE_KEYSTORE_PASS + + - name: Sign and Commit Android Release Artifact (.APK) for F-Droid Repository + commands: + - rm -rf $HOME/fdroid/build/org.selfprivacy.app && mkdir $HOME/fdroid/build/org.selfprivacy.app + - git archive --format=tar HEAD | tar x -C $HOME/fdroid/build/org.selfprivacy.app + - ./ci.py --sign-apk-fdroid + environment: + STANDALONE_KEYSTORE_PASS: + from_secret: STANDALONE_KEYSTORE_PASS + FDROID_KEYSTORE_PASS: + from_secret: FDROID_KEYSTORE_PASS + GOOGLE_KEYSTORE_PASS: + from_secret: GOOGLE_KEYSTORE_PASS + + - name: Sign Android Release Artifact (Bundle) for Google Play + commands: + - ./ci.py --sign-bundle + environment: + STANDALONE_KEYSTORE_PASS: + from_secret: STANDALONE_KEYSTORE_PASS + FDROID_KEYSTORE_PASS: + from_secret: FDROID_KEYSTORE_PASS + GOOGLE_KEYSTORE_PASS: + from_secret: GOOGLE_KEYSTORE_PASS + + - name: Package Linux AppImage Artifact + commands: + - ./ci.py --package-linux-appimage + + - name: Package Linux Flatpak Artifact + commands: + - ./ci.py --package-linux-flatpak + + - name: Package Linux Archive Artifact + commands: + - ./ci.py --package-linux-archive + + - name: Push Artifacts to the Release Volume + commands: + - git add -v *.AppImage *.AppImage.zsync *.flatpak *.apk *.apk.idsig *.aab *.tar.zstd + - git commit -m Release + - git archive --format=tar HEAD | podman volume import release - + +trigger: + event: + - tag + +node: + server: builder + +--- + +kind: pipeline +type: exec +name: Deploy + +steps: + - name: Prepare + commands: + - ln -s /var/lib/drone-runner-exec/.local $HOME/.local + - podman unshare podman volume mount release + + - name: Deploy Artifacts to Gitea + commands: + - ./ci.py --deploy-gitea-release + environment: + GITEA_RELEASE_TOKEN: + from_secret: GITEA_RELEASE_TOKEN + + - name: Deploy F-Droid Repository + commands: + - ./ci.py --deploy-fdroid-repo + environment: + SSH_PRIVATE_KEY: + from_secret: SSH_PRIVATE_KEY + +trigger: + event: + - tag + +node: + server: builder + +depends_on: + - Release diff --git a/.editorconfig b/.editorconfig index 80a3e35b..206f5ceb 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,3 +14,6 @@ max_line_length = 150 [*.md] trim_trailing_whitespace = false + +[*.json] +indent_size = 4 diff --git a/.gitea/ISSUE_TEMPLATE/bug.yaml b/.gitea/ISSUE_TEMPLATE/bug.yaml new file mode 100644 index 00000000..f9441722 --- /dev/null +++ b/.gitea/ISSUE_TEMPLATE/bug.yaml @@ -0,0 +1,68 @@ +name: Bug report +about: File a bug report +labels: + - Bug +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! Please provide a short but a descriptive title for your issue. + - type: textarea + id: expected-behaviour + attributes: + label: Expected Behavior + description: What did you expect to happen? + validations: + required: true + - type: textarea + id: actual-behaviour + attributes: + label: Actual Behavior + description: What actually happened? + validations: + required: true + - type: textarea + id: steps-to-reproduce + attributes: + label: Steps to Reproduce + description: What steps can we follow to reproduce this issue? + placeholder: | + 1. First step + 2. Second step + 3. and so on... + validations: + required: true + - type: textarea + id: context + attributes: + label: Context and notes + description: Additional information about environment or what were you trying to do. If you have an idea how to fix this issue, please describe it here too. + - type: textarea + id: logs + attributes: + label: Relevant log output + description: Please copy and paste any relevant log output, if you have any. This will be automatically formatted into code, so no need for backticks. + render: shell + - type: input + id: app-version + attributes: + label: App Version + description: What version of SelfPrivacy app are you running? You can find it in the "About" section of the app. + validations: + required: true + - type: input + id: api-version + attributes: + label: Server API Version + description: What version of SelfPrivacy API are you running? You can find it in the "About" section of the app. Leave it empty if your app is not connected to the server yet. + - type: dropdown + id: os + attributes: + label: Operating System + description: What operating system are you using? + options: + - Android + - iOS + - Linux + - macOS + - Windows diff --git a/.gitea/ISSUE_TEMPLATE/feature.yaml b/.gitea/ISSUE_TEMPLATE/feature.yaml new file mode 100644 index 00000000..8ff40a0f --- /dev/null +++ b/.gitea/ISSUE_TEMPLATE/feature.yaml @@ -0,0 +1,23 @@ +name: Feature request +about: Suggest an idea for this project +label: + - Feature request +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this feature request! Please provide a short but a descriptive title for your issue. + - type: textarea + id: description + attributes: + label: Description + description: Describe the feature you'd like to see. + placeholder: | + As a user, I want to be able to... + validations: + required: true + - type: textarea + id: context + attributes: + label: Context and notes + description: Additional information about environment and what were you trying to do. If you have an idea how to implement this feature, please describe it here too. diff --git a/.gitea/ISSUE_TEMPLATE/translation.yaml b/.gitea/ISSUE_TEMPLATE/translation.yaml new file mode 100644 index 00000000..c7348b94 --- /dev/null +++ b/.gitea/ISSUE_TEMPLATE/translation.yaml @@ -0,0 +1,29 @@ +name: Translation issue +about: File a translation (localization) issue +labels: + - Translations +body: + - type: markdown + attributes: + value: | + Translations can be modified and discussed on [Weblate](https://weblate.selfprivacy.org/projects/selfprivacy/). You can fix the mistranslation issue yourself there. Using the search, you can also find the string ID of the mistranslated string. If your issue is more complex, please file it here + + If you are a member of SelfPrivacy core team, you **must** fix the issue yourself on Weblate. + - type: input + id: language + attributes: + label: Language + description: What language is affected? + placeholder: | + English + validations: + required: true + - type: textarea + id: description + attributes: + label: Description + description: Describe the issue in detail. If you have an idea how to fix this issue, please describe it here too. Include the string ID of the mistranslated string, if possible. + placeholder: | + The string `string.id` is translated as "foo", but it should be "bar". + validations: + required: true diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml new file mode 100644 index 00000000..c52968cd --- /dev/null +++ b/.github/workflows/windows.yml @@ -0,0 +1,29 @@ +name: Windows Builder + +on: tag + +jobs: + build-windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v3 + + # Install Python dependencies + - run: pip install requests pyyaml + + # Install Flutter + - uses: subosito/flutter-action@v2 + with: + flutter-version: '3.3.10' + channel: 'stable' + + # Build Windows artifact + - run: flutter build windows + + # Archive the build + - run: python ci.py --package-windows-archive + + # Upload the build + - run: python ci.py --deploy-windows-archive + env: + GITEA_RELEASE_TOKEN: ${{ secrets.GITEA_RELEASE_TOKEN }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..32bc427d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,58 @@ +FROM ubuntu:22.04 + +ARG PACKAGES="build-essential openjdk-11-jdk-headless clang cmake curl git jq libblkid1 \ + libblkid-dev libc6 libc6-dev libc-bin libcrypt1 libdbus-1-3 libexpat1 libffi7 \ + libgcc-s1 libgcrypt20 libgcrypt20-dev libglib2.0-0 libglib2.0-dev libglu1-mesa \ + libgpg-error0 libgtk-3-0 libgtk-3-dev liblz4-1 liblz4-dev liblzma5 liblzma-dev \ + libmount1 libpcre3 libselinux1 libsepol2 libstdc++-10-dev libstdc++6 libuuid1 \ + ninja-build pkg-config rsync unzip xz-utils zlib1g unzip libsecret-1-dev libsecret-tools \ + libsecret-1-0 libjsoncpp-dev fuse flatpak-builder binutils coreutils desktop-file-utils \ + fakeroot fuse libgdk-pixbuf2.0-dev patchelf python3-pip python3-setuptools squashfs-tools \ + strace util-linux zsync" +ARG ANDROID_SDK_TOOLS_VERSION="8512546" +ARG ANDROID_SDK_TOOLS_URL="https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_SDK_TOOLS_VERSION}_latest.zip" +ARG FLUTTER_VERSION="3.3.1" +ARG FLUTTER_URL="https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz" +ARG FREEDESKTOP_SDK_VERSION="22.08" + +# Update packages +RUN apt-get update && apt-get upgrade -y && apt-get install -y $PACKAGES + +WORKDIR /opt + +# Install Android SDK +ADD $ANDROID_SDK_TOOLS_URL . +RUN mkdir -p android-sdk/cmdline-tools && unzip commandlinetools-linux-${ANDROID_SDK_TOOLS_VERSION}_latest.zip \ + && rm commandlinetools-linux-${ANDROID_SDK_TOOLS_VERSION}_latest.zip && mv cmdline-tools android-sdk/cmdline-tools/latest + +# Install Flutter +ENV FLUTTER_HOME "/opt/flutter" +ADD $FLUTTER_URL . +RUN tar -vxf flutter_linux_${FLUTTER_VERSION}-stable.tar.xz && \ + rm flutter_linux_${FLUTTER_VERSION}-stable.tar.xz +# Flutter doesn't work without write permissions, so fuck it, fuck +RUN chmod -R 777 $FLUTTER_HOME +RUN git config --system --add safe.directory $FLUTTER_HOME + +ENV ANDROID_HOME "/opt/android-sdk" +ENV ANDROID_SDK_ROOT "${ANDROID_HOME}" +ENV PATH "$PATH:$ANDROID_SDK_ROOT/cmdline-tools/latest/bin:$ANDROID_SDK_ROOT/build-tools/30.0.3:/opt/flutter/bin" + +# Install needed Android SDK packages +RUN yes | sdkmanager 'build-tools;30.0.3' 'platforms;android-29' 'platforms;android-30' 'platforms;android-31' + +WORKDIR /tmp + +# Prepare dependencies for offline build +ENV PUB_CACHE "/tmp/flutter_pub_cache" +ENV GRADLE_USER_HOME "/tmp/gradle" +RUN git clone --depth=1 --single-branch https://git.selfprivacy.org/kherel/selfprivacy.org.app.git deps +WORKDIR /tmp/deps +RUN flutter build linux +RUN flutter build apk --flavor production +WORKDIR /tmp +RUN rm -rf deps +RUN find $GRADLE_USER_HOME/daemon -exec chmod 777 {} \; + +# Install AppImage Builder and F-Droid Server +RUN pip3 install appimage-builder fdroidserver diff --git a/README.md b/README.md index 35c31b44..1e132b91 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,57 @@ # SelfPrivacy App -- [Official site](https://selfprivacy.org) -- [SelfPrivacy App (F-Droid)](https://f-droid.org/en/packages/pro.kherel.selfprivacy/) +SelfPrivacy — is a platform on your cloud hosting, that allows to deploy your own private services and control them using mobile application. -## Getting Started +To use this application, you'll be required to create accounts of different service providers. Please reffer to this manual: https://selfprivacy.org/docs/getting-started/ -This project is a starting point for a Flutter application. +Application will do the following things for you: -A few resources to get you started if this is your first Flutter project: +1. Create your personal server +2. Setup NixOS +3. Bring all services to the ready-to-use state. Services include: -- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) +* E-mail, ready to use with DeltaChat +* NextCloud - your personal cloud storage +* Bitwarden — secure and private password manager +* Pleroma — your private fediverse space for blogging +* Jitsi — awesome Zoom alternative +* Gitea — your own Git server +* OpenConnect — Personal VPN server -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. +**Project is currently in open beta state**. Feel free to try it. It would be much appreciated if you would provide us with some feedback. + +## Building + +Supported platforms are Android, Linux, and Windows. We are looking forward to support iOS and macOS builds. + +For Linux builds, make sure you have these packages installed: +|Arch-based|Debian-based| +|----------|------------| +|pacman -S ninja xdg-user-dirs gnome-keyring unzip xz-utils zip|apt install ninja-build xdg-user-dirs gnome-keyring unzip xz-utils zip| + +Install [Flutter](https://docs.flutter.dev/get-started/install/linux) and [Android SDK tools](https://developer.android.com/studio/command-line/sdkmanager), then try your setup: + +``` +flutter pub get + +# Build .APK for Android +flutter build --flavor production apk +# Build AAB bundle for Google Play +flutter build --flavor production aab +# Build Linux binaries +flutter build linux +# Build Windows binaries +flutter build windows + +# Package AppImage +appimage-builder --recipe appimage.yml +# Package Flatpak +flatpak-builder --force-clean --repo=flatpak-repo flatpak-build flatpak.yml +flatpak build-bundle flatpak-repo org.selfprivacy.app.flatpak org.selfprivacy.app +``` + +## Translations + +[![Translation status](http://weblate.selfprivacy.org/widgets/selfprivacy/-/selfprivacy-app/multi-auto.svg)](http://weblate.selfprivacy.org/engage/selfprivacy/) + +Translations are stored in `assets/translations/*.json` and can be edited on . diff --git a/analysis_options.yaml b/analysis_options.yaml index 9d16cb20..ff0f9356 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -13,6 +13,7 @@ analyzer: exclude: - lib/generated_plugin_registrant.dart - lib/**.g.dart + - lib/**.graphql.dart linter: # The lint rules applied to this project can be customized in the @@ -28,17 +29,16 @@ linter: # producing the lint. rules: avoid_print: false # Uncomment to disable the `avoid_print` rule - prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule - always_use_package_imports: true - invariant_booleans: true - no_adjacent_strings_in_list: true - unnecessary_statements: true always_declare_return_types: true - always_put_required_named_parameters_first: true always_put_control_body_on_new_line: true + always_put_required_named_parameters_first: true + always_use_package_imports: true avoid_escaping_inner_quotes: true avoid_setters_without_getters: true + collection_methods_unrelated_type: true + combinators_ordering: true eol_at_end_of_file: true + no_adjacent_strings_in_list: true prefer_constructors_over_static_methods: true prefer_expression_function_bodies: true prefer_final_in_for_each: true @@ -48,12 +48,18 @@ linter: prefer_if_elements_to_conditional_expressions: true prefer_mixin: true prefer_null_aware_method_calls: true + prefer_single_quotes: true require_trailing_commas: true sized_box_shrink_expand: true sort_constructors_first: true + unawaited_futures: true unnecessary_await_in_return: true + unnecessary_null_aware_operator_on_extension_on_nullable: true unnecessary_null_checks: true unnecessary_parenthesis: true + unnecessary_statements: true + unnecessary_to_list_in_spreads: true + unreachable_from_main: true use_enums: true use_if_null_to_convert_nulls_to_bools: true use_is_even_rather_than_modulo: true @@ -61,6 +67,7 @@ linter: use_named_constants: true use_setters_to_change_properties: true use_string_buffers: true + use_string_in_part_of_directives: true use_super_parameters: true use_to_and_as_if_applicable: true diff --git a/android/app/build.gradle b/android/app/build.gradle index 0963724e..9d678878 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -26,7 +26,9 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion flutter.compileSdkVersion + namespace 'org.selfprivacy.app' + + compileSdkVersion flutter.compileSdkVersion ndkVersion flutter.ndkVersion sourceSets { @@ -48,18 +50,41 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "pro.kherel.selfprivacy" + applicationId "org.selfprivacy.app" minSdkVersion 21 - targetSdkVersion 31 + targetSdkVersion 33 + compileSdkVersion 33 versionCode flutterVersionCode.toInteger() versionName flutterVersionName } - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug + flavorDimensions "default" + productFlavors { + fdroid { + applicationId "pro.kherel.selfprivacy" + } + production { + applicationIdSuffix "" + } + nightly { + applicationIdSuffix ".nightly" + versionCode project.getVersionCode() + versionName "nightly-" + project.getVersionCode() + } + } + + flavorDimensions "default" + productFlavors { + fdroid { + applicationId "pro.kherel.selfprivacy" + } + production { + applicationIdSuffix "" + } + nightly { + applicationIdSuffix ".nightly" + versionCode project.getVersionCode() + versionName "nightly-" + project.getVersionCode() } } } diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml index 27e1af1d..dddeb01f 100644 --- a/android/app/src/debug/AndroidManifest.xml +++ b/android/app/src/debug/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="org.selfprivacy.app"> diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 33c2ba9a..50f47475 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="org.selfprivacy.app"> diff --git a/android/build.gradle b/android/build.gradle index 31e95773..13645686 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,18 @@ buildscript { ext.kotlin_version = '1.6.10' + ext.getVersionCode = { -> + try { + def stdout = new ByteArrayOutputStream() + exec { + commandLine 'git', 'rev-list', '--first-parent', '--count', 'HEAD' + standardOutput = stdout + } + return Integer.parseInt(stdout.toString().trim()) + } + catch (ignored) { + return -1 + } + } repositories { google() jcenter() @@ -26,6 +39,6 @@ subprojects { project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } diff --git a/android/gradle.properties b/android/gradle.properties index a6738207..c396be2a 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,4 +1,4 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true -android.enableR8=true +android.bundle.enableUncompressedNativeLibs=false diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index cc5527d7..02e5f581 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip diff --git a/appimage.yml b/appimage.yml new file mode 100644 index 00000000..40bcc3c3 --- /dev/null +++ b/appimage.yml @@ -0,0 +1,53 @@ +# appimage-builder recipe see https://appimage-builder.readthedocs.io for details +version: 1 +script: + - rm -rf AppDir || true + - cp -r build/linux/x64/release/bundle AppDir + - install -Dm644 assets/images/icon/logo.svg AppDir/usr/share/icons/hicolor/scalable/apps/org.selfprivacy.app.svg +AppDir: + path: AppDir + app_info: + id: org.selfprivacy.app + name: SelfPrivacy + icon: org.selfprivacy.app + version: 0.9.1 + exec: selfprivacy + exec_args: $@ + apt: + arch: + - amd64 + allow_unauthenticated: true + sources: + - sourceline: deb http://archive.ubuntu.com/ubuntu/ jammy main restricted + - sourceline: deb http://archive.ubuntu.com/ubuntu/ jammy-updates main restricted + - sourceline: deb http://archive.ubuntu.com/ubuntu/ jammy universe + - sourceline: deb http://archive.ubuntu.com/ubuntu/ jammy-updates universe + - sourceline: deb http://archive.ubuntu.com/ubuntu/ jammy multiverse + - sourceline: deb http://archive.ubuntu.com/ubuntu/ jammy-updates multiverse + - sourceline: deb http://archive.ubuntu.com/ubuntu/ jammy-backports main restricted universe multiverse + - sourceline: deb http://security.ubuntu.com/ubuntu/ jammy-security main restricted + - sourceline: deb http://security.ubuntu.com/ubuntu/ jammy-security universe + - sourceline: deb http://security.ubuntu.com/ubuntu/ jammy-security multiverse + include: + - libjsoncpp-dev + - libsecret-1-0 + - xdg-desktop-portal + test: + fedora-30: + image: appimagecrafters/tests-env:fedora-30 + command: ./AppRun + debian-stable: + image: appimagecrafters/tests-env:debian-stable + command: ./AppRun + archlinux-latest: + image: appimagecrafters/tests-env:archlinux-latest + command: ./AppRun + centos-7: + image: appimagecrafters/tests-env:centos-7 + command: ./AppRun + ubuntu-xenial: + image: appimagecrafters/tests-env:ubuntu-xenial + command: ./AppRun +AppImage: + arch: x86_64 + update-information: guess diff --git a/assets/images/icon/logo.svg b/assets/images/icon/logo.svg new file mode 100644 index 00000000..881a57fe --- /dev/null +++ b/assets/images/icon/logo.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/images/logos/cloudflare.svg b/assets/images/logos/cloudflare.svg new file mode 100644 index 00000000..03a60465 --- /dev/null +++ b/assets/images/logos/cloudflare.svg @@ -0,0 +1 @@ + diff --git a/assets/images/logos/desec.svg b/assets/images/logos/desec.svg new file mode 100644 index 00000000..cb54b268 --- /dev/null +++ b/assets/images/logos/desec.svg @@ -0,0 +1,89 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/assets/images/logos/digital_ocean.png b/assets/images/logos/digital_ocean.png new file mode 100644 index 00000000..04f5efd5 Binary files /dev/null and b/assets/images/logos/digital_ocean.png differ diff --git a/assets/images/logos/digital_ocean.svg b/assets/images/logos/digital_ocean.svg new file mode 100644 index 00000000..98b04c36 --- /dev/null +++ b/assets/images/logos/digital_ocean.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/images/logos/hetzner.svg b/assets/images/logos/hetzner.svg new file mode 100644 index 00000000..5cafc101 --- /dev/null +++ b/assets/images/logos/hetzner.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/markdown/about-be.md b/assets/markdown/about-be.md new file mode 100644 index 00000000..71a9ef3e --- /dev/null +++ b/assets/markdown/about-be.md @@ -0,0 +1,12 @@ +### Пра нас + +Усё больш арганізацый жадаюць валодаць нашымі дадзенымі +Праект дазваляе толькі Вам у поўнай меры распараджацца ўласнымі **дадзенымі** на сваім сэрвэры. + +### Наша місія + +Лічбавая незалежнасць і прыватнасць, даступныя кожнаму + +### Мэта + +Распрацаваць праграму, якая дазволіць кожнаму разгарнуць свае прыватныя паслугі для сябе і сваіх суседзяў. \ No newline at end of file diff --git a/assets/markdown/about-cs.md b/assets/markdown/about-cs.md new file mode 100644 index 00000000..4d0a01e2 --- /dev/null +++ b/assets/markdown/about-cs.md @@ -0,0 +1,12 @@ +### O nás + +More and more corporations want to control our data. +We want to have full control of our **data** on our own. + +### Naše poslání + +Digitální nezávislost a soukromí dostupné všem + +### Cíl + +Rozvíjet program, který umožní každému nasadit své soukromé služby pro sebe a své sousedy. \ No newline at end of file diff --git a/assets/markdown/about-de.md b/assets/markdown/about-de.md new file mode 100644 index 00000000..401af514 --- /dev/null +++ b/assets/markdown/about-de.md @@ -0,0 +1,12 @@ +### Über uns + +Immer mehr Unternehmen wollen unsere Daten kontrollieren. +Wir wollen selbst die volle Kontrolle über unsere **data** haben. + +### Unsere Mission + +Digitale Unabhängigkeit und Privatsphäre für alle verfügbar + +### Ziel + +Das Programm entwickeln, das es jedem ermöglicht, seine privaten Dienste für sich und seine Nachbarn einzusetzen. \ No newline at end of file diff --git a/assets/markdown/about-es.md b/assets/markdown/about-es.md new file mode 100644 index 00000000..3963aa6e --- /dev/null +++ b/assets/markdown/about-es.md @@ -0,0 +1,12 @@ +### About us + +More and more corporations want to control our data. +We want to have full control of our **data** on our own. + +### Our mission + +Digital independence and privacy, available to everyone + +### Target + +Develop the program, which will allow everyone to deploy their private services for themselves and their neighbours. \ No newline at end of file diff --git a/assets/markdown/about-fr.md b/assets/markdown/about-fr.md new file mode 100644 index 00000000..3963aa6e --- /dev/null +++ b/assets/markdown/about-fr.md @@ -0,0 +1,12 @@ +### About us + +More and more corporations want to control our data. +We want to have full control of our **data** on our own. + +### Our mission + +Digital independence and privacy, available to everyone + +### Target + +Develop the program, which will allow everyone to deploy their private services for themselves and their neighbours. \ No newline at end of file diff --git a/assets/markdown/about-it.md b/assets/markdown/about-it.md new file mode 100644 index 00000000..3963aa6e --- /dev/null +++ b/assets/markdown/about-it.md @@ -0,0 +1,12 @@ +### About us + +More and more corporations want to control our data. +We want to have full control of our **data** on our own. + +### Our mission + +Digital independence and privacy, available to everyone + +### Target + +Develop the program, which will allow everyone to deploy their private services for themselves and their neighbours. \ No newline at end of file diff --git a/assets/markdown/about-ja.md b/assets/markdown/about-ja.md new file mode 100644 index 00000000..3963aa6e --- /dev/null +++ b/assets/markdown/about-ja.md @@ -0,0 +1,12 @@ +### About us + +More and more corporations want to control our data. +We want to have full control of our **data** on our own. + +### Our mission + +Digital independence and privacy, available to everyone + +### Target + +Develop the program, which will allow everyone to deploy their private services for themselves and their neighbours. \ No newline at end of file diff --git a/assets/markdown/about-ka.md b/assets/markdown/about-ka.md new file mode 100644 index 00000000..3963aa6e --- /dev/null +++ b/assets/markdown/about-ka.md @@ -0,0 +1,12 @@ +### About us + +More and more corporations want to control our data. +We want to have full control of our **data** on our own. + +### Our mission + +Digital independence and privacy, available to everyone + +### Target + +Develop the program, which will allow everyone to deploy their private services for themselves and their neighbours. \ No newline at end of file diff --git a/assets/markdown/about-nl.md b/assets/markdown/about-nl.md new file mode 100644 index 00000000..3963aa6e --- /dev/null +++ b/assets/markdown/about-nl.md @@ -0,0 +1,12 @@ +### About us + +More and more corporations want to control our data. +We want to have full control of our **data** on our own. + +### Our mission + +Digital independence and privacy, available to everyone + +### Target + +Develop the program, which will allow everyone to deploy their private services for themselves and their neighbours. \ No newline at end of file diff --git a/assets/markdown/about-pl.md b/assets/markdown/about-pl.md new file mode 100644 index 00000000..ebd41246 --- /dev/null +++ b/assets/markdown/about-pl.md @@ -0,0 +1,12 @@ +### About us + +More and more corporations want to control our data. +We want to have full control of our **data** on our own. + +### Misja projektu + +Niezależność i prywatność cyfrowa dostępna dla wszystkich + +### Cel + +Opracuj program, dzięki któremu każdy będzie mógł stworzyć prywatne usługi dla siebie i swoich bliskich. \ No newline at end of file diff --git a/assets/markdown/about-ru.md b/assets/markdown/about-ru.md index c93abce2..15c0f237 100644 --- a/assets/markdown/about-ru.md +++ b/assets/markdown/about-ru.md @@ -5,7 +5,7 @@ ### Миссия проекта -Цифровая независимость и приватность доступная каждому. +Цифровая независимость и приватность, доступная каждому ### Цель diff --git a/assets/markdown/about-sk.md b/assets/markdown/about-sk.md new file mode 100644 index 00000000..d3135d3b --- /dev/null +++ b/assets/markdown/about-sk.md @@ -0,0 +1,12 @@ +### O nás + +More and more corporations want to control our data. +We want to have full control of our **data** on our own. + +### Naše poslanie + +Digitálna nezávislosť a súkromie dostupné pre každého + +### Cieľ + +Vytvorte program, ktorý umožní každému vytvoriť súkromné služby pre seba a svojich blízkych. \ No newline at end of file diff --git a/assets/markdown/about-th.md b/assets/markdown/about-th.md new file mode 100644 index 00000000..3963aa6e --- /dev/null +++ b/assets/markdown/about-th.md @@ -0,0 +1,12 @@ +### About us + +More and more corporations want to control our data. +We want to have full control of our **data** on our own. + +### Our mission + +Digital independence and privacy, available to everyone + +### Target + +Develop the program, which will allow everyone to deploy their private services for themselves and their neighbours. \ No newline at end of file diff --git a/assets/markdown/about-uk.md b/assets/markdown/about-uk.md new file mode 100644 index 00000000..fee81d64 --- /dev/null +++ b/assets/markdown/about-uk.md @@ -0,0 +1,12 @@ +### Про нас + +Все більше корпорацій хочуть контролювати свої дані. +Ми хочемо мати повний контроль над нашими. + +### Наша місія + +Цифрова незалежність і конфіденційність доступні кожному + +### Ціль + +Розробити програму, яка дозволить кожному розгорнути свої приватні послуги для себе та їх сусідів. \ No newline at end of file diff --git a/assets/markdown/how_backblaze-be.md b/assets/markdown/how_backblaze-be.md new file mode 100644 index 00000000..76e364e3 --- /dev/null +++ b/assets/markdown/how_backblaze-be.md @@ -0,0 +1,8 @@ +### Як атрымаць Backblaze API Token +1. Перайдзіце па спасылцы https://secure.backblaze.com/user_signin.htm і аўтарызуйцеся +2. У левай частцы інтэрфейсу абярыце **App Keys** y **B2 Cloud Storage** падкатэгорыі. +3. Націсніце на сінюю кнопку **Generate New Master Application Key**. +4. Пацвердзіце стварэнне ва ўсплываючым акне. +5. Схавайце _keyID_ і _applicationKey_ у бяспечным месце. Напрыклад, у мэнеджэры пароляў. + +![Атрыманіе токена Backblaze](resource:assets/images/gifs/Backblaze.gif) diff --git a/assets/markdown/how_backblaze-cs.md b/assets/markdown/how_backblaze-cs.md new file mode 100644 index 00000000..f1a87840 --- /dev/null +++ b/assets/markdown/how_backblaze-cs.md @@ -0,0 +1,8 @@ +### Jak získat Backblaze API Token +1. Navštivte následující odkaz a autorizujte se: https://secure.backblaze.com/user_signin.htm +2. V levé části rozhraní vyberte položku **App Keys** v podkategorii **B2 Cloud Storage**. +3. Klikněte na modré tlačítko **Generate New Master Application Key** (Vygenerovat nový hlavní aplikační klíč**). +4. V zobrazeném vyskakovacím okně potvrďte vygenerování. +5. Save _keyID_ and _applicationKey_ in the safe place.. Například ve správci hesel. + +![Nastavení tokenu Backblaze](resource:assets/images/gifs/Backblaze.gif) diff --git a/assets/markdown/how_backblaze-de.md b/assets/markdown/how_backblaze-de.md new file mode 100644 index 00000000..a9ef0c1c --- /dev/null +++ b/assets/markdown/how_backblaze-de.md @@ -0,0 +1,8 @@ +### So erhalten Sie das Backblaze-API-Token +1. Besuchen Sie den folgenden Link und melden Sie sich an: https://secure.backblaze.com/user_signin.htm +2. Wählen Sie auf der linken Seite der Benutzeroberfläche **App Keys** in der Unterkategorie **B2 Cloud Storage** aus. +3. Klicken Sie auf die blaue Schaltfläche **Generate New Master Application Key**. +4. Bestätigen Sie im erscheinenden Popup-Fenster die Generierung. +5. Speichern Sie _keyID_ und _applicationKey_ an einem sicheren Ort. Zum Beispiel im Passwort-Manager. + +![Backblaze token Einrichtung](resource:assets/images/gifs/Backblaze.gif) diff --git a/assets/markdown/how_backblaze-es.md b/assets/markdown/how_backblaze-es.md new file mode 100644 index 00000000..b1e341d1 --- /dev/null +++ b/assets/markdown/how_backblaze-es.md @@ -0,0 +1,8 @@ +### How to get Backblaze API Token +1. Visit the following link and authorize: https://secure.backblaze.com/user_signin.htm +2. On the left side of the interface, select **App Keys** in the **B2 Cloud Storage** subcategory. +3. Click on the blue **Generate New Master Application Key** button. +4. In the appeared pop-up window confirm the generation. +5. Save _keyID_ and _applicationKey_ in the safe place. For example, in the password manager. + +![Backblaze token setup](resource:assets/images/gifs/Backblaze.gif) diff --git a/assets/markdown/how_backblaze-fr.md b/assets/markdown/how_backblaze-fr.md new file mode 100644 index 00000000..b1e341d1 --- /dev/null +++ b/assets/markdown/how_backblaze-fr.md @@ -0,0 +1,8 @@ +### How to get Backblaze API Token +1. Visit the following link and authorize: https://secure.backblaze.com/user_signin.htm +2. On the left side of the interface, select **App Keys** in the **B2 Cloud Storage** subcategory. +3. Click on the blue **Generate New Master Application Key** button. +4. In the appeared pop-up window confirm the generation. +5. Save _keyID_ and _applicationKey_ in the safe place. For example, in the password manager. + +![Backblaze token setup](resource:assets/images/gifs/Backblaze.gif) diff --git a/assets/markdown/how_backblaze-it.md b/assets/markdown/how_backblaze-it.md new file mode 100644 index 00000000..b1e341d1 --- /dev/null +++ b/assets/markdown/how_backblaze-it.md @@ -0,0 +1,8 @@ +### How to get Backblaze API Token +1. Visit the following link and authorize: https://secure.backblaze.com/user_signin.htm +2. On the left side of the interface, select **App Keys** in the **B2 Cloud Storage** subcategory. +3. Click on the blue **Generate New Master Application Key** button. +4. In the appeared pop-up window confirm the generation. +5. Save _keyID_ and _applicationKey_ in the safe place. For example, in the password manager. + +![Backblaze token setup](resource:assets/images/gifs/Backblaze.gif) diff --git a/assets/markdown/how_backblaze-ja.md b/assets/markdown/how_backblaze-ja.md new file mode 100644 index 00000000..b1e341d1 --- /dev/null +++ b/assets/markdown/how_backblaze-ja.md @@ -0,0 +1,8 @@ +### How to get Backblaze API Token +1. Visit the following link and authorize: https://secure.backblaze.com/user_signin.htm +2. On the left side of the interface, select **App Keys** in the **B2 Cloud Storage** subcategory. +3. Click on the blue **Generate New Master Application Key** button. +4. In the appeared pop-up window confirm the generation. +5. Save _keyID_ and _applicationKey_ in the safe place. For example, in the password manager. + +![Backblaze token setup](resource:assets/images/gifs/Backblaze.gif) diff --git a/assets/markdown/how_backblaze-ka.md b/assets/markdown/how_backblaze-ka.md new file mode 100644 index 00000000..b1e341d1 --- /dev/null +++ b/assets/markdown/how_backblaze-ka.md @@ -0,0 +1,8 @@ +### How to get Backblaze API Token +1. Visit the following link and authorize: https://secure.backblaze.com/user_signin.htm +2. On the left side of the interface, select **App Keys** in the **B2 Cloud Storage** subcategory. +3. Click on the blue **Generate New Master Application Key** button. +4. In the appeared pop-up window confirm the generation. +5. Save _keyID_ and _applicationKey_ in the safe place. For example, in the password manager. + +![Backblaze token setup](resource:assets/images/gifs/Backblaze.gif) diff --git a/assets/markdown/how_backblaze-nl.md b/assets/markdown/how_backblaze-nl.md new file mode 100644 index 00000000..b1e341d1 --- /dev/null +++ b/assets/markdown/how_backblaze-nl.md @@ -0,0 +1,8 @@ +### How to get Backblaze API Token +1. Visit the following link and authorize: https://secure.backblaze.com/user_signin.htm +2. On the left side of the interface, select **App Keys** in the **B2 Cloud Storage** subcategory. +3. Click on the blue **Generate New Master Application Key** button. +4. In the appeared pop-up window confirm the generation. +5. Save _keyID_ and _applicationKey_ in the safe place. For example, in the password manager. + +![Backblaze token setup](resource:assets/images/gifs/Backblaze.gif) diff --git a/assets/markdown/how_backblaze-pl.md b/assets/markdown/how_backblaze-pl.md new file mode 100644 index 00000000..a14ec3ed --- /dev/null +++ b/assets/markdown/how_backblaze-pl.md @@ -0,0 +1,8 @@ +### Jak otrzymać Backblaze API Token +1. Odwiedź poniższy link i autoryzuj: https://secure.backblaze.com/user_signin.htm +2. Po lewej stronie wybierz **App Keys** w podkategorіі **B2 Cloud Storage**. +3. Naciśnij **Generate New Master Application Key**. +4. W wyświetlonym oknie podręcznym potwierdź generację. +5. Zapisz _keyID_ i _applicationKey_ w bezpiecznym miejscu. Na przykład w menedżerze haseł. + +![Otrzymanie tokena Backblaze](resource:assets/images/gifs/Backblaze.gif) diff --git a/assets/markdown/how_backblaze-ru.md b/assets/markdown/how_backblaze-ru.md index 2938f1cb..27beb7e7 100644 --- a/assets/markdown/how_backblaze-ru.md +++ b/assets/markdown/how_backblaze-ru.md @@ -5,4 +5,4 @@ 4. Во всплывающем окне подтверждаем генерацию. 5. Сохраняем _keyID_ и _applicationKey_ в надёжном месте. Например в менеджере паролей. -![Backblaze token setup](resource:assets/images/gifs/Backblaze.gif) +![Получение токена Backblaze](resource:assets/images/gifs/Backblaze.gif) diff --git a/assets/markdown/how_backblaze-sk.md b/assets/markdown/how_backblaze-sk.md new file mode 100644 index 00000000..7736c858 --- /dev/null +++ b/assets/markdown/how_backblaze-sk.md @@ -0,0 +1,8 @@ +### Ako získať token API spoločnosti Backblaze +1. Navštívte nasledujúci odkaz a autorizujte sa: https://secure.backblaze.com/user_signin.htm +2. Na ľavej strane rozhrania vyberte položku **App Keys** v podkategórii **B2 Cloud Storage** podkategória. +3. Kliknite na modré tlačidlo **Generate New Master Application Key**. +4. V zobrazenom pop-up okne potvrďte generovanie. +5. Uložte _keyID_ and _applicationKey_ in bezpečné miesto. Napríklad v správcovi hesiel. + +![Prijatie tokenu Backblaze](resource:assets/images/gifs/Backblaze.gif) diff --git a/assets/markdown/how_backblaze-th.md b/assets/markdown/how_backblaze-th.md new file mode 100644 index 00000000..b1e341d1 --- /dev/null +++ b/assets/markdown/how_backblaze-th.md @@ -0,0 +1,8 @@ +### How to get Backblaze API Token +1. Visit the following link and authorize: https://secure.backblaze.com/user_signin.htm +2. On the left side of the interface, select **App Keys** in the **B2 Cloud Storage** subcategory. +3. Click on the blue **Generate New Master Application Key** button. +4. In the appeared pop-up window confirm the generation. +5. Save _keyID_ and _applicationKey_ in the safe place. For example, in the password manager. + +![Backblaze token setup](resource:assets/images/gifs/Backblaze.gif) diff --git a/assets/markdown/how_backblaze-uk.md b/assets/markdown/how_backblaze-uk.md new file mode 100644 index 00000000..115aeec9 --- /dev/null +++ b/assets/markdown/how_backblaze-uk.md @@ -0,0 +1,8 @@ +## Як отримати Backblaze API токен +1. Перейдіть за посиланням і авторизуйте: https://secure.backblaze.com/user_signin.htm +2. У лівій частині інтерфейсу виберіть **App Keys** у підкатегорії **B2 Cloud Storage**. +3. Натисніть кнопку синій **Створити новий майстер-ключ програми**. +4. У з'явилось спливаюче вікно підтверджують покоління. +5. Save _KeyID_ and _applicationKey_ in the safe place. Наприклад, в менеджері паролів. + +![Backblaze токен налаштування](resource:assets/images/gifs/Backblaze.gif) diff --git a/assets/markdown/how_cloudflare-be.md b/assets/markdown/how_cloudflare-be.md new file mode 100644 index 00000000..7cf9c11f --- /dev/null +++ b/assets/markdown/how_cloudflare-be.md @@ -0,0 +1,17 @@ +### Як атрымаць токен Cloudflare API +1. Перайдзіце па наступнай спасылцы: https://dash.cloudflare.com/ +2. У правым куце націсніце на значок профілю (чалавек у крузе). Для мабільнай версіі сайта ў левым верхнім куце націсніце кнопку **Menu** (тры гарызантальныя палоскі), у выпадальным меню націсніце **My Profile**. +3. Ёсць чатыры катэгорыі канфігурацыі на выбар: *Communication*, *Authentication*, **API Tokens**, *Session*. Выберыце **API Tokens**. +4. Націсніце на кнопку **Create Token**. +5. Спусціцеся ўніз і паглядзіце поле **Create Custom Token** і націсніце кнопку **Get Started** справа. +6. У полі **Token Name** дайце назву токену. +7. Далей у нас налады. У крайнім левым полі абярыце **Zone**. У самым доўгім полі па цэнтры абярыце **DNS**. У крайнім правым полі абярыце **Edit**. +8. Далей прама пад гэтым радком націсніце «Дадаць яшчэ». З'явіцца аналагічнае поле. +9. У самым левым полі новага радка выберыце аналагічна папярэдняму радку — **Zone**. У цэнтры — крыху іншае. Тут абярыце тое ж, што і злева — **Zone**. У крайнім правым полі абярыце **Read**. +10. Далей паглядзіце на **Resources Zone**. Пад гэтым надпісам радок з двума палямі. Злева павінна быць **Include**, а справа павінна быць **Specific Zone**. Пасля таго як вы выберыце канкрэтную зону, справа з'явіцца іншае поле. Выберыце ў ім свой дамен. +11. Прагартайце ўніз і націсніце сінюю кнопку **Continue to Summary**. +12. Правер, ці ўсё ў цябе правільна. Аналагічны радок павінен прысутнічаць: *Domain — DNS:Edit, Zone:Read*. +13. Націсніце на **Create Token**. +14. Створаны токен капіюем і захоўваем у надзейным месцы (пажадана ў мэнэджару пароляў). + +![Настройка токену Cloudfire](resource:assets/images/gifs/CloudFlare.gif) diff --git a/assets/markdown/how_cloudflare-cs.md b/assets/markdown/how_cloudflare-cs.md new file mode 100644 index 00000000..8dc86bb4 --- /dev/null +++ b/assets/markdown/how_cloudflare-cs.md @@ -0,0 +1,17 @@ +### Jak získat token API Cloudflare +1. Přejděte na následující odkaz: https://dash.cloudflare.com/. +2. V pravém rohu klikněte na ikonu profilu (mužík v kruhu). V případě mobilní verze webu klikněte v levém horním rohu na **Menu** (tři vodorovné pruhy), v rozbalovací nabídce klikněte na **My profile**. +Na výběr jsou čtyři kategorie nastavení: *Communication*, *Authentication*, **API Tokens**, *Session*. Vyberte možnost **API Tokens**. +4. Klikněte na tlačítko **Create Token**. +5. Přejděte dolů, zobrazte pole **Create Custom Token** a klikněte na tlačítko **Get Started** vpravo. +6. V poli **Token Name** pojmenujte svůj token. +7. Dále máme přístupová práva. V poli zcela vlevo vyberte možnost **Zone**. V nejdelším poli uprostřed vyberte položku **DNS**. V pravém krajním poli vyberte možnost **Edit**. +8. Poté hned pod tímto řádkem klepněte na tlačítko Přidat další. Zobrazí se podobné pole. +9. Na zcela levé straně nového řádku vyberte totéž co u předchozího řádku - **Zone**. Prostřední pole se mírně liší. Zde vyberte totéž co na levém okraji - **Zone**. Na pravém okraji zcela vpravo vyberte možnost **Read**. +10. Dále se podívejte na položku **Zone Resources**. Pod ním se nachází řádek se dvěma poli. V levém z nich by mělo být uvedeno **Include** a v pravém **Specific Zone**. Jakmile vyberete možnost Specifická zóna, objeví se vpravo další pole. V něm vyberte svou doménu. +11. Přejděte úplně dolů a klikněte na modré tlačítko **Continue to Summary**. +12. Zkontrolujte, zda jste vše provedli správně. Měl by se objevit podobný řádek: *Domain - DNS:Edit, Zone:Read*. +13. Klepněte na tlačítko **Create Token**. +14. Zkopírujte vytvořený token a uložte jej na bezpečné místo (nejlépe do správce hesel). + +![Cloudflare token setup](resource:assets/images/gifs/CloudFlare.gif) diff --git a/assets/markdown/how_cloudflare-de.md b/assets/markdown/how_cloudflare-de.md new file mode 100644 index 00000000..4b81e00f --- /dev/null +++ b/assets/markdown/how_cloudflare-de.md @@ -0,0 +1,17 @@ +### Wie man Cloudflare API Token bekommt +1. Besuchen Sie den folgenden link: https://dash.cloudflare.com/ +2. Klicken Sie in der rechten Ecke auf das Profilsymbol (ein Mann in einem Kreis). Bei der mobilen Version der Website klicken Sie in der oberen linken Ecke auf die Schaltfläche **Menu** (drei horizontale Balken), im Dropdown-Menü klicken Sie auf **My Profile** +3. Es gibt vier Konfigurationskategorien, aus denen Sie wählen können: *Communication*, *Authentication*, **API Tokens**, *Session*. Wählen Sie **API Tokens**. +4. Klicken Sie auf die Schaltfläche **Create Token**. +5. Gehen Sie nach unten und sehen Sie das Feld **Create Custom Token** und klicken Sie auf der rechten Seite auf die Schaltfläche **Get Started**. +6. Geben Sie in das Feld **Token Name** einen Namen für Ihr Token ein. +7. Als nächstes folgen die Berechtigungen. Wählen Sie im Feld ganz links **Zone**. Wählen Sie im längsten Feld in der Mitte **DNS**. Wählen Sie im Feld ganz rechts **Edit**. +8. Klicken Sie anschließend direkt unter dieser Zeile auf Add More. Ein ähnliches Feld wird angezeigt. +9. Wählen Sie im ganz linken Feld der neuen Zeile, ähnlich wie in der letzten Zeile, **Zone**. In der Mitte - ein wenig anders. Wählen Sie hier dasselbe wie in der linken Zeile - **Zone**. Im Feld ganz rechts wählen Sie **Read**. +10. Als nächstes sehen Sie sich **Zone Resources** an. Unter dieser Aufschrift befindet sich eine Zeile mit zwei Feldern. Auf der linken Seite muss **Include** und auf der rechten Seite **Specific Zone** stehen. Sobald Sie Spezifische Zone auswählen, erscheint rechts ein weiteres Feld. Wählen Sie dort Ihre Domain aus. +11. Streichen Sie nach unten und drücken Sie die blaue Schaltfläche **Continue to Summary**. +12. Überprüfen Sie, ob Sie alles richtig gemacht haben. Eine ähnliche Zeichenfolge muss vorhanden sein: *Domain — DNS:Edit, Zone:Read*. +13. Klicken Sie auf **Create Token**. +14. Kopieren Sie das erstellte Token und speichern Sie es an einem zuverlässigen Ort (vorzugsweise im Passwort-Manager). + +![Cloudflare token Einrichtung](resource:assets/images/gifs/CloudFlare.gif) diff --git a/assets/markdown/how_cloudflare-es.md b/assets/markdown/how_cloudflare-es.md new file mode 100644 index 00000000..61fb4b4e --- /dev/null +++ b/assets/markdown/how_cloudflare-es.md @@ -0,0 +1,17 @@ +### How to get Cloudflare API Token +1. Visit the following link: https://dash.cloudflare.com/ +2. the right corner, click on the profile icon (a man in a circle). For the mobile version of the site, in the upper left corner, click the **Menu** button (three horizontal bars), in the dropdown menu, click on **My Profile** +3. There are four configuration categories to choose from: *Communication*, *Authentication*, **API Tokens**, *Session*. Choose **API Tokens**. +4. Click on **Create Token** button. +5. Go down to the bottom and see the **Create Custom Token** field and press **Get Started** button on the right side. +6. In the **Token Name** field, give your token a name. +7. Next we have Permissions. In the leftmost field, select **Zone**. In the longest field, center, select **DNS**. In the rightmost field, select **Edit**. +8. Next, right under this line, click Add More. Similar field will appear. +9. In the leftmost field of the new line, select, similar to the last line — **Zone**. In the center — a little different. Here choose the same as in the left — **Zone**. In the rightmost field, select **Read**. +10. Next look at **Zone Resources**. Under this inscription there is a line with two fields. The left must have **Include** and the right must have **Specific Zone**. Once you select Specific Zone, another field appears on the right. Choose your domain in it. +11. Flick to the bottom and press the blue **Continue to Summary** button. +12. Check if you got everything right. A similar string must be present: *Domain — DNS:Edit, Zone:Read*. +13. Click on **Create Token**. +14. We copy the created token, and save it in a reliable place (preferably in the password manager). + +![Cloudflare token setup](resource:assets/images/gifs/CloudFlare.gif) diff --git a/assets/markdown/how_cloudflare-fr.md b/assets/markdown/how_cloudflare-fr.md new file mode 100644 index 00000000..61fb4b4e --- /dev/null +++ b/assets/markdown/how_cloudflare-fr.md @@ -0,0 +1,17 @@ +### How to get Cloudflare API Token +1. Visit the following link: https://dash.cloudflare.com/ +2. the right corner, click on the profile icon (a man in a circle). For the mobile version of the site, in the upper left corner, click the **Menu** button (three horizontal bars), in the dropdown menu, click on **My Profile** +3. There are four configuration categories to choose from: *Communication*, *Authentication*, **API Tokens**, *Session*. Choose **API Tokens**. +4. Click on **Create Token** button. +5. Go down to the bottom and see the **Create Custom Token** field and press **Get Started** button on the right side. +6. In the **Token Name** field, give your token a name. +7. Next we have Permissions. In the leftmost field, select **Zone**. In the longest field, center, select **DNS**. In the rightmost field, select **Edit**. +8. Next, right under this line, click Add More. Similar field will appear. +9. In the leftmost field of the new line, select, similar to the last line — **Zone**. In the center — a little different. Here choose the same as in the left — **Zone**. In the rightmost field, select **Read**. +10. Next look at **Zone Resources**. Under this inscription there is a line with two fields. The left must have **Include** and the right must have **Specific Zone**. Once you select Specific Zone, another field appears on the right. Choose your domain in it. +11. Flick to the bottom and press the blue **Continue to Summary** button. +12. Check if you got everything right. A similar string must be present: *Domain — DNS:Edit, Zone:Read*. +13. Click on **Create Token**. +14. We copy the created token, and save it in a reliable place (preferably in the password manager). + +![Cloudflare token setup](resource:assets/images/gifs/CloudFlare.gif) diff --git a/assets/markdown/how_cloudflare-it.md b/assets/markdown/how_cloudflare-it.md new file mode 100644 index 00000000..61fb4b4e --- /dev/null +++ b/assets/markdown/how_cloudflare-it.md @@ -0,0 +1,17 @@ +### How to get Cloudflare API Token +1. Visit the following link: https://dash.cloudflare.com/ +2. the right corner, click on the profile icon (a man in a circle). For the mobile version of the site, in the upper left corner, click the **Menu** button (three horizontal bars), in the dropdown menu, click on **My Profile** +3. There are four configuration categories to choose from: *Communication*, *Authentication*, **API Tokens**, *Session*. Choose **API Tokens**. +4. Click on **Create Token** button. +5. Go down to the bottom and see the **Create Custom Token** field and press **Get Started** button on the right side. +6. In the **Token Name** field, give your token a name. +7. Next we have Permissions. In the leftmost field, select **Zone**. In the longest field, center, select **DNS**. In the rightmost field, select **Edit**. +8. Next, right under this line, click Add More. Similar field will appear. +9. In the leftmost field of the new line, select, similar to the last line — **Zone**. In the center — a little different. Here choose the same as in the left — **Zone**. In the rightmost field, select **Read**. +10. Next look at **Zone Resources**. Under this inscription there is a line with two fields. The left must have **Include** and the right must have **Specific Zone**. Once you select Specific Zone, another field appears on the right. Choose your domain in it. +11. Flick to the bottom and press the blue **Continue to Summary** button. +12. Check if you got everything right. A similar string must be present: *Domain — DNS:Edit, Zone:Read*. +13. Click on **Create Token**. +14. We copy the created token, and save it in a reliable place (preferably in the password manager). + +![Cloudflare token setup](resource:assets/images/gifs/CloudFlare.gif) diff --git a/assets/markdown/how_cloudflare-ja.md b/assets/markdown/how_cloudflare-ja.md new file mode 100644 index 00000000..61fb4b4e --- /dev/null +++ b/assets/markdown/how_cloudflare-ja.md @@ -0,0 +1,17 @@ +### How to get Cloudflare API Token +1. Visit the following link: https://dash.cloudflare.com/ +2. the right corner, click on the profile icon (a man in a circle). For the mobile version of the site, in the upper left corner, click the **Menu** button (three horizontal bars), in the dropdown menu, click on **My Profile** +3. There are four configuration categories to choose from: *Communication*, *Authentication*, **API Tokens**, *Session*. Choose **API Tokens**. +4. Click on **Create Token** button. +5. Go down to the bottom and see the **Create Custom Token** field and press **Get Started** button on the right side. +6. In the **Token Name** field, give your token a name. +7. Next we have Permissions. In the leftmost field, select **Zone**. In the longest field, center, select **DNS**. In the rightmost field, select **Edit**. +8. Next, right under this line, click Add More. Similar field will appear. +9. In the leftmost field of the new line, select, similar to the last line — **Zone**. In the center — a little different. Here choose the same as in the left — **Zone**. In the rightmost field, select **Read**. +10. Next look at **Zone Resources**. Under this inscription there is a line with two fields. The left must have **Include** and the right must have **Specific Zone**. Once you select Specific Zone, another field appears on the right. Choose your domain in it. +11. Flick to the bottom and press the blue **Continue to Summary** button. +12. Check if you got everything right. A similar string must be present: *Domain — DNS:Edit, Zone:Read*. +13. Click on **Create Token**. +14. We copy the created token, and save it in a reliable place (preferably in the password manager). + +![Cloudflare token setup](resource:assets/images/gifs/CloudFlare.gif) diff --git a/assets/markdown/how_cloudflare-ka.md b/assets/markdown/how_cloudflare-ka.md new file mode 100644 index 00000000..61fb4b4e --- /dev/null +++ b/assets/markdown/how_cloudflare-ka.md @@ -0,0 +1,17 @@ +### How to get Cloudflare API Token +1. Visit the following link: https://dash.cloudflare.com/ +2. the right corner, click on the profile icon (a man in a circle). For the mobile version of the site, in the upper left corner, click the **Menu** button (three horizontal bars), in the dropdown menu, click on **My Profile** +3. There are four configuration categories to choose from: *Communication*, *Authentication*, **API Tokens**, *Session*. Choose **API Tokens**. +4. Click on **Create Token** button. +5. Go down to the bottom and see the **Create Custom Token** field and press **Get Started** button on the right side. +6. In the **Token Name** field, give your token a name. +7. Next we have Permissions. In the leftmost field, select **Zone**. In the longest field, center, select **DNS**. In the rightmost field, select **Edit**. +8. Next, right under this line, click Add More. Similar field will appear. +9. In the leftmost field of the new line, select, similar to the last line — **Zone**. In the center — a little different. Here choose the same as in the left — **Zone**. In the rightmost field, select **Read**. +10. Next look at **Zone Resources**. Under this inscription there is a line with two fields. The left must have **Include** and the right must have **Specific Zone**. Once you select Specific Zone, another field appears on the right. Choose your domain in it. +11. Flick to the bottom and press the blue **Continue to Summary** button. +12. Check if you got everything right. A similar string must be present: *Domain — DNS:Edit, Zone:Read*. +13. Click on **Create Token**. +14. We copy the created token, and save it in a reliable place (preferably in the password manager). + +![Cloudflare token setup](resource:assets/images/gifs/CloudFlare.gif) diff --git a/assets/markdown/how_cloudflare-nl.md b/assets/markdown/how_cloudflare-nl.md new file mode 100644 index 00000000..61fb4b4e --- /dev/null +++ b/assets/markdown/how_cloudflare-nl.md @@ -0,0 +1,17 @@ +### How to get Cloudflare API Token +1. Visit the following link: https://dash.cloudflare.com/ +2. the right corner, click on the profile icon (a man in a circle). For the mobile version of the site, in the upper left corner, click the **Menu** button (three horizontal bars), in the dropdown menu, click on **My Profile** +3. There are four configuration categories to choose from: *Communication*, *Authentication*, **API Tokens**, *Session*. Choose **API Tokens**. +4. Click on **Create Token** button. +5. Go down to the bottom and see the **Create Custom Token** field and press **Get Started** button on the right side. +6. In the **Token Name** field, give your token a name. +7. Next we have Permissions. In the leftmost field, select **Zone**. In the longest field, center, select **DNS**. In the rightmost field, select **Edit**. +8. Next, right under this line, click Add More. Similar field will appear. +9. In the leftmost field of the new line, select, similar to the last line — **Zone**. In the center — a little different. Here choose the same as in the left — **Zone**. In the rightmost field, select **Read**. +10. Next look at **Zone Resources**. Under this inscription there is a line with two fields. The left must have **Include** and the right must have **Specific Zone**. Once you select Specific Zone, another field appears on the right. Choose your domain in it. +11. Flick to the bottom and press the blue **Continue to Summary** button. +12. Check if you got everything right. A similar string must be present: *Domain — DNS:Edit, Zone:Read*. +13. Click on **Create Token**. +14. We copy the created token, and save it in a reliable place (preferably in the password manager). + +![Cloudflare token setup](resource:assets/images/gifs/CloudFlare.gif) diff --git a/assets/markdown/how_cloudflare-pl.md b/assets/markdown/how_cloudflare-pl.md new file mode 100644 index 00000000..2cfb615b --- /dev/null +++ b/assets/markdown/how_cloudflare-pl.md @@ -0,0 +1,17 @@ +### Jak uzyskać token API Cloudflare'a +1. Przejdź pod następujący link: https://dash.cloudflare.com/. +2. W prawym rogu kliknij ikonę profilu (człowiek w kółku). W przypadku wersji mobilnej strony, w lewym górnym rogu kliknij **Menu** (trzy poziome paski), w rozwijanym menu kliknij **My Profile**. +Do wyboru są cztery kategorie ustawień: *Communication*, *Authentication*, **API Tokens**, *Session*. Należy wybrać **API Tokens**. +4. Kliknij przycisk **Create token**. +5. Zjedź na dół, zobacz pole **Create Custom Token** i kliknij przycisk **Get Started** po prawej stronie. +6. W polu **Token Name** nadaj nazwę swojemu tokenowi. +7. Następnie mamy prawa dostępu. W skrajnie lewym polu wybierz **Zone**. W najdłuższym polu, środkowym, wybierz **DNS**. W skrajnie prawym polu wybierz **Edit**. +8. Następnie, tuż pod tym wierszem, kliknij Add more. Pojawi się podobne pole. +9. W skrajnie lewej części nowej linii wybierz to samo, co w poprzedniej linii - **Zone**. Środkowe pole jest nieco inne. Tutaj wybierz to samo, co na lewym marginesie - **Zone**. W skrajnie prawym marginesie wybierz **Read**. +10. Następnie spójrz na **Strefę zasobów**. Poniżej tego znajduje się linia z dwoma polami. W lewym powinno być **Include**, a w prawym **Specific Zone**. Gdy wybierzesz Specific Zone, po prawej stronie pojawi się kolejne pole. Wybierz w nim swoją domenę. +11. Przejdź na sam dół i kliknij niebieski przycisk **Continue to Summary**. +12. Sprawdź, czy wszystko zrobiłeś poprawnie. Powinna pojawić się podobna linia: *Domain - DNS:Edit, Zone:Read*. +13. Kliknij na **Create Token**. +14. Skopiuj utworzony token i zapisz go w bezpiecznym miejscu (najlepiej w menedżerze haseł). + +![Cloudflare token setup](resource:assets/images/gifs/CloudFlare.gif) diff --git a/assets/markdown/how_cloudflare-ru.md b/assets/markdown/how_cloudflare-ru.md index d6cc0072..0eb2fca9 100644 --- a/assets/markdown/how_cloudflare-ru.md +++ b/assets/markdown/how_cloudflare-ru.md @@ -1,18 +1,17 @@ -### Как получить Cloudflare API Token -1. Переходим по [ссылке](https://dash.cloudflare.com/) и авторизуемся в ранее созданном аккаунте. https://dash.cloudflare.com/ -2. В правом верхнем углу кликаем на иконку профиля (для мобильной версии сайта: в верхнем левом углу нажимаем кнопку **Меню** с тремя горизонтальными полосками). В выпавшем меню кликаем на пункт **My Profile**. -![My profile](resource:assets/images/pics/myprofile.png) -3. Нам предлагается на выбор, четыре категории настройки: **Preferences**, **Authentication**, **API Tokens**, **Sessions**. Выбираем **API Tokens**. -4. Самым первым пунктом видим кнопку **Create Token**. С полной уверенностью в себе и желанием обрести приватность, нажимаем на неё. -5. Спускаемся в самый низ и видим поле **Create Custom Token** и кнопку **Get Started** с правой стороны. Нажимаем. -6. В поле **Token Name** даём своему токену имя. Можете покреативить и отнестись к этому как к наименованию домашнего зверька :) -7. Далее, у нас **Permissions**. В первом поле выбираем **Zone**. Во втором поле, по центру, выбираем **DNS**. В последнем поле выбираем **Edit**. -8. Нажимаем на синюю надпись снизу **+ Add more** (сразу же под левым полем которое мы заполняли ранее). Вуаля, у нас появились новые поля. Заполняем по аналогии с предыдущим пунктом, в первом поле выбираем **Zone**, во-втором тоже **Zone**. А уже в третьем нажимаем на **Read**. Давайте сверим с тем, что у вас получилось: -![Permissions](resource:assets/images/pics/permissions.png) -8. Далее смотрим на **Zone Resources**. Под этой надписью есть строка с двумя полями. В первом должно быть **Include**, а во втором — **Specific Zone**. Как только Вы выберите **Specific Zone**, справа появится ещё одно поле. В нём выбираем наш домен. -9. Листаем в самый низ и нажимаем на синюю кнопку **Continue to Summary**. -10. Проверяем, всё ли мы правильно выбрали. Должна присутствовать подобная строка: ваш.домен — **DNS:Edit, Zone:Read**. -11. Нажимаем **Create Token**. -12. Копируем созданный токен, и сохраняем его в надёжном месте (желательно — в менеджере паролей). +### Как получить токен API Cloudflare +1. Перейдите по следующей ссылке: https://dash.cloudflare.com/. +2. В правом углу нажмите на значок профиля (человечек в круге). Для мобильной версии сайта: в левом верхнем углу нажмите на кнопку **Menu** (три горизонтальные полоски), в выпадающем меню нажмите на **My Profile**. +3. На выбор предлагаются четыре категории настроек: *Communication*, *Authentication*, **API Tokens**, *Session*. Выберите **API Tokens**. +4. Нажмите на кнопку **Create token**. +5. Спуститесь в самый низ, увидите поле **Create Custom Token** и нажмите кнопку **Get Started** справа. +6. В поле **Token Name** дайте имя вашему токену. +7. Далее у нас есть права доступа. В крайнем левом поле выберите **Zone**. В самом длинном поле, по центру, выберите **DNS**. В крайнем правом поле выберите **Edit**. +8. Далее, прямо под этой строкой, нажмите **Добавить еще**. Появится аналогичное поле. +9. В крайнем левом поле новой строки выберите, аналогично предыдущей строке - **Zone**. В центральном - немного иначе. Здесь выберите то же, что и в левом - **Zone**. В крайнем правом поле выберите **Read**. +10. Далее посмотрите на **Resources Zone**. Под этой надписью находится строка с двумя полями. В левом должно быть **Include**, а в правом - **Specific Zone**. Как только вы выберете Specific Zone, справа появится еще одно поле. Выберите в нем свой домен. +11. Перейдите в самый низ и нажмите синюю кнопку **Continue to Summary**. +12. Проверьте, все ли вы сделали правильно. Должна присутствовать аналогичная строка: *Domain - DNS:Edit, Zone:Read*. +13. Нажмите на **Create Token**. +14. Копируем созданный токен и сохраняем его в надежном месте (желательно в менеджере паролей). ![Cloudflare token setup](resource:assets/images/gifs/CloudFlare.gif) diff --git a/assets/markdown/how_cloudflare-sk.md b/assets/markdown/how_cloudflare-sk.md new file mode 100644 index 00000000..63df2bd4 --- /dev/null +++ b/assets/markdown/how_cloudflare-sk.md @@ -0,0 +1,17 @@ +### Ako získať token API Cloudflare +1. Navštívte nasledujúce prepojenie: https://dash.cloudflare.com/ +2. V pravom rohu kliknite na ikonu profilu (muž v kruhu). V prípade mobilnej verzie stránky kliknite v ľavom hornom rohu na tlačidlo **Menu** (tri vodorovné pruhy), v rozbaľovacej ponuke kliknite na **My profile** +3. Na výber sú štyri kategórie konfigurácie: *Communication*, *Authentication*, **Toky API**, *Session*. Vyberte **API Tokens**. +4. Kliknite na tlačidlo **Create Token**. +5. Prejdite na spodnú časť a zobrazte pole **Create Custom Token** a stlačte tlačidlo **Get Started** na pravej strane. +6. V poli **Token Name** zadajte svoj token. +7. Ďalej tu máme položku Permissions. V ľavom krajnom poli vyberte položku **Zone**. V najdlhšom poli uprostred vyberte položku **DNS**. V krajnom pravom poli vyberte položku **Edit**. +8. Ďalej priamo pod týmto riadkom kliknite na položku **Pridať ďalšie**. Zobrazí sa podobné pole. +9. V krajnom ľavom poli nového riadku vyberte podobne ako v predchádzajúcom riadku - **Zone**. V strede - trochu inak. Tu vyberte rovnako ako v ľavom poli - **Zone**. V krajnom pravom poli vyberte položku **Read**. +10. Ďalej sa pozrite na položku **Zone Resources**. Pod týmto nápisom sa nachádza riadok s dvoma poľami. V ľavom musí byť uvedené **Include** a v pravom **Specific Zone**. Keď vyberiete položku **Špecifická zóna**, vpravo sa objaví ďalšie pole. V ňom vyberte svoju doménu. +11. Prejdite na spodnú časť a stlačte modré tlačidlo **Continue to Summary**. +12. Skontrolujte, či máte všetko správne. Musí sa vyskytovať podobný reťazec: *Domain — DNS:Edit, Zone:Read*. +13. Kliknite na **Create Token**. +14. Vytvorený token skopírujeme a uložíme ho na spoľahlivé miesto (najlepšie do správcu hesiel). + +![Cloudflare token setup](resource:assets/images/gifs/CloudFlare.gif) diff --git a/assets/markdown/how_cloudflare-th.md b/assets/markdown/how_cloudflare-th.md new file mode 100644 index 00000000..61fb4b4e --- /dev/null +++ b/assets/markdown/how_cloudflare-th.md @@ -0,0 +1,17 @@ +### How to get Cloudflare API Token +1. Visit the following link: https://dash.cloudflare.com/ +2. the right corner, click on the profile icon (a man in a circle). For the mobile version of the site, in the upper left corner, click the **Menu** button (three horizontal bars), in the dropdown menu, click on **My Profile** +3. There are four configuration categories to choose from: *Communication*, *Authentication*, **API Tokens**, *Session*. Choose **API Tokens**. +4. Click on **Create Token** button. +5. Go down to the bottom and see the **Create Custom Token** field and press **Get Started** button on the right side. +6. In the **Token Name** field, give your token a name. +7. Next we have Permissions. In the leftmost field, select **Zone**. In the longest field, center, select **DNS**. In the rightmost field, select **Edit**. +8. Next, right under this line, click Add More. Similar field will appear. +9. In the leftmost field of the new line, select, similar to the last line — **Zone**. In the center — a little different. Here choose the same as in the left — **Zone**. In the rightmost field, select **Read**. +10. Next look at **Zone Resources**. Under this inscription there is a line with two fields. The left must have **Include** and the right must have **Specific Zone**. Once you select Specific Zone, another field appears on the right. Choose your domain in it. +11. Flick to the bottom and press the blue **Continue to Summary** button. +12. Check if you got everything right. A similar string must be present: *Domain — DNS:Edit, Zone:Read*. +13. Click on **Create Token**. +14. We copy the created token, and save it in a reliable place (preferably in the password manager). + +![Cloudflare token setup](resource:assets/images/gifs/CloudFlare.gif) diff --git a/assets/markdown/how_cloudflare-uk.md b/assets/markdown/how_cloudflare-uk.md new file mode 100644 index 00000000..27e3a5c4 --- /dev/null +++ b/assets/markdown/how_cloudflare-uk.md @@ -0,0 +1,17 @@ +### Як отримати токен Cloudflare API +1. Перейдіть за цим посиланням: https://dash.cloudflare.com/ +2. В правому куті натисніть на іконку профілю (людина в колі). Для мобільної версії сайту, у верхньому лівому кутку натисніть кнопку **Menu** (три горизонтальні смужки), у випадаючому меню натисніть **My profile** +3. Є чотири категорії конфігурації на вибір: *Communication*, *Authentication*, **API Tokens**, *Session*. Виберіть **API Tokens**. +4. Натисніть кнопку **Create Token**. +5. Спустіться вниз і перегляньте поле **Create Custom Token** і натисніть кнопку **Get Started** праворуч. +6. У полі **Назва токена** введіть назву токена. +7. Далі у нас є Дозволи. У крайньому лівому полі виберіть **Zone**. У найдовшому полі по центру виберіть **DNS**. У крайньому правому полі виберіть **Edit**. +8. Далі прямо під цим рядком натисніть «Add more». З'явиться аналогічне поле. +9. У крайньому лівому полі нового рядка виберіть, як і в останньому рядку, — **Zone**. У центрі — трохи інше. Тут вибираємо те саме, що й зліва — **Zone**. У крайньому правому полі виберіть **Read**. +10. Далі подивіться на **Zone Resources**. Під цим написом — рядок із двома полями. Ліворуч має бути **Include**, а праворуч має бути **Specific Zone**. Після вибору «Specific Zone» праворуч з’явиться ще одне поле. Виберіть у ньому свій домен. +11. Прокрутіть униз і натисніть синю кнопку **Continue to Summary**. +12. Перевірте, чи все ви зробили правильно. Подібний рядок має бути присутнім: *Domain — DNS:Edit, Zone:Read*. +13. Натисніть **Create Token**. +14. Копіюємо створений токен і зберігаємо в надійному місці (бажано в менеджері паролів). + +![Cloudflare налаштування токена](resource:assets/images/gifs/CloudFlare.gif) diff --git a/assets/markdown/how_desec-en.md b/assets/markdown/how_desec-en.md new file mode 100644 index 00000000..b5bac67c --- /dev/null +++ b/assets/markdown/how_desec-en.md @@ -0,0 +1,9 @@ +### How to get deSEC API Token +1. Log in at: https://desec.io/login +2. Go to **Domains** page at: https://desec.io/domains +3. Go to **Token management** tab. +4. Click on the round "plus" button in the upper right corner. +5. **"Generate New Token"** dialogue must be displayed. Enter any **Token name** you wish. *Advanced settings* are not required, so do not touch anything there. +6. Click on **Save**. +7. Make sure you save the token's "**secret value**" as it will only be displayed once. +8. Now you can safely **close** the dialogue. \ No newline at end of file diff --git a/assets/markdown/how_desec-ru.md b/assets/markdown/how_desec-ru.md new file mode 100644 index 00000000..a93acc77 --- /dev/null +++ b/assets/markdown/how_desec-ru.md @@ -0,0 +1,9 @@ +### Как получить deSEC API Токен +1. Авторизуемся в deSEC: https://desec.io/login +2. Переходим на страницу **Domains** по ссылке: https://desec.io/domains +3. Переходим на вкладку **Token management**. +4. Нажимаем на большую кнопку с плюсом в правом верхнем углу страницы. +5. Должен был появиться **"Generate New Token"** диалог. Вводим любое имя токена в **Token name**. *Advanced settings* необязательны, так что ничего там не трогаем. +6. Кликаем **Save**. +7. Обязательно сохраняем "**secret value**" ключ токена, потому что он отображается исключительно один раз. +8. Теперь спокойно закрываем диалог, нажав **close**. \ No newline at end of file diff --git a/assets/markdown/how_digital_ocean-be.md b/assets/markdown/how_digital_ocean-be.md new file mode 100644 index 00000000..d817db12 --- /dev/null +++ b/assets/markdown/how_digital_ocean-be.md @@ -0,0 +1,12 @@ +### Як атрымаць токен Digital Ocean API +1. Перайдзіце па наступнай [link](https://cloud.digitalocean.com/) і падпішыцеся + у толькі што створаны ўліковы запіс. +2. Увайдзіце ў раней створаны праект. Калі вы яго не стварылі, + тады, калі ласка, працягвайце. +3. Перайдзіце па спасылцы "API" на панэлі злева. +4. Націсніце на кнопку "Generation New Token". +5. Увядзіце любую назву токена. +6. Усталюйце час заканчэння тэрміну дзеяння на "No Expiry". +7. Усталюйце сцяжок "Write (optional)". +8. Цяпер націсніце на кнопку "Generate Token". +9. Пасля гэтага будзе паказаны токен. Захоўвайце яго ў любым надзейным месцы, пажадана ў менеджэры пароляў. \ No newline at end of file diff --git a/assets/markdown/how_digital_ocean-cs.md b/assets/markdown/how_digital_ocean-cs.md new file mode 100644 index 00000000..9d20f2dd --- /dev/null +++ b/assets/markdown/how_digital_ocean-cs.md @@ -0,0 +1,12 @@ +### Jak získat token API Digital Ocean +1. Navštivte následující [link](https://cloud.digitalocean.com/) a podepište se. + k nově vytvořenému účtu. +2. Vstupte do dříve vytvořeného projektu. Pokud jste žádný nevytvořili, + pak prosím pokračujte. +3. Přejděte na odkaz "API" na levé liště. +4. Klikněte na tlačítko "Generate New Token" (Vygenerovat nový token). +5. Zadejte libovolný název tokenu. +6. Nastavte dobu platnosti na "No expiry". +7. Zaškrtněte políčko "Write (optional)". +8. Nyní klikněte na tlačítko "Generate Token". +9. Poté se zobrazí token. Uložte jej na libovolné spolehlivé místo, nejlépe do správce hesel. \ No newline at end of file diff --git a/assets/markdown/how_digital_ocean-de.md b/assets/markdown/how_digital_ocean-de.md new file mode 100644 index 00000000..79c04259 --- /dev/null +++ b/assets/markdown/how_digital_ocean-de.md @@ -0,0 +1,12 @@ +### So erhalten Sie das Digital Ocean API-Token +1. Besuchen Sie den folgenden [link](https://cloud.digitalocean.com/) und melden Sie sich + in das neu erstellte Konto an. +2. Gehen Sie in ein zuvor erstelltes Projekt. Wenn Sie noch keine erstellt haben, + dann bitte fortfahren. +3. Gehen Sie zum Link "API" in der linken Leiste. +4. Klicken Sie auf "Generate New Token". +5. Geben Sie einen beliebigen Namen für das Token ein. +6. Setzen Sie die Ablaufzeit auf "No expiry". +7. Aktivieren Sie das Kontrollkästchen "Write (optional)". +8. Klicken Sie nun auf die Schaltfläche "Generate Token". +9. Danach wird der Token angezeigt. Bewahren Sie es an einem zuverlässigen Ort auf, vorzugsweise in einem Passwort-Manager. \ No newline at end of file diff --git a/assets/markdown/how_digital_ocean-en.md b/assets/markdown/how_digital_ocean-en.md new file mode 100644 index 00000000..dc6355c2 --- /dev/null +++ b/assets/markdown/how_digital_ocean-en.md @@ -0,0 +1,12 @@ +### How to get Digital Ocean API Token +1. Visit the following [link](https://cloud.digitalocean.com/) and sign + into newly created account. +2. Enter into previously created project. If you haven't created one, + then please proceed. +3. Go to the "API" link on the left bar. +4. Click on the "Generate New Token". +5. Enter any name for the token. +6. Put expiration time to "No expiry". +7. Check the "Write (optional)" checkbox. +8. Now click on the "Generate Token" button. +9. After that, the token will be shown. Store it in any reliable place, preferably a password manager. \ No newline at end of file diff --git a/assets/markdown/how_digital_ocean-es.md b/assets/markdown/how_digital_ocean-es.md new file mode 100644 index 00000000..dc6355c2 --- /dev/null +++ b/assets/markdown/how_digital_ocean-es.md @@ -0,0 +1,12 @@ +### How to get Digital Ocean API Token +1. Visit the following [link](https://cloud.digitalocean.com/) and sign + into newly created account. +2. Enter into previously created project. If you haven't created one, + then please proceed. +3. Go to the "API" link on the left bar. +4. Click on the "Generate New Token". +5. Enter any name for the token. +6. Put expiration time to "No expiry". +7. Check the "Write (optional)" checkbox. +8. Now click on the "Generate Token" button. +9. After that, the token will be shown. Store it in any reliable place, preferably a password manager. \ No newline at end of file diff --git a/assets/markdown/how_digital_ocean-fr.md b/assets/markdown/how_digital_ocean-fr.md new file mode 100644 index 00000000..dc6355c2 --- /dev/null +++ b/assets/markdown/how_digital_ocean-fr.md @@ -0,0 +1,12 @@ +### How to get Digital Ocean API Token +1. Visit the following [link](https://cloud.digitalocean.com/) and sign + into newly created account. +2. Enter into previously created project. If you haven't created one, + then please proceed. +3. Go to the "API" link on the left bar. +4. Click on the "Generate New Token". +5. Enter any name for the token. +6. Put expiration time to "No expiry". +7. Check the "Write (optional)" checkbox. +8. Now click on the "Generate Token" button. +9. After that, the token will be shown. Store it in any reliable place, preferably a password manager. \ No newline at end of file diff --git a/assets/markdown/how_digital_ocean-it.md b/assets/markdown/how_digital_ocean-it.md new file mode 100644 index 00000000..dc6355c2 --- /dev/null +++ b/assets/markdown/how_digital_ocean-it.md @@ -0,0 +1,12 @@ +### How to get Digital Ocean API Token +1. Visit the following [link](https://cloud.digitalocean.com/) and sign + into newly created account. +2. Enter into previously created project. If you haven't created one, + then please proceed. +3. Go to the "API" link on the left bar. +4. Click on the "Generate New Token". +5. Enter any name for the token. +6. Put expiration time to "No expiry". +7. Check the "Write (optional)" checkbox. +8. Now click on the "Generate Token" button. +9. After that, the token will be shown. Store it in any reliable place, preferably a password manager. \ No newline at end of file diff --git a/assets/markdown/how_digital_ocean-ja.md b/assets/markdown/how_digital_ocean-ja.md new file mode 100644 index 00000000..dc6355c2 --- /dev/null +++ b/assets/markdown/how_digital_ocean-ja.md @@ -0,0 +1,12 @@ +### How to get Digital Ocean API Token +1. Visit the following [link](https://cloud.digitalocean.com/) and sign + into newly created account. +2. Enter into previously created project. If you haven't created one, + then please proceed. +3. Go to the "API" link on the left bar. +4. Click on the "Generate New Token". +5. Enter any name for the token. +6. Put expiration time to "No expiry". +7. Check the "Write (optional)" checkbox. +8. Now click on the "Generate Token" button. +9. After that, the token will be shown. Store it in any reliable place, preferably a password manager. \ No newline at end of file diff --git a/assets/markdown/how_digital_ocean-ka.md b/assets/markdown/how_digital_ocean-ka.md new file mode 100644 index 00000000..dc6355c2 --- /dev/null +++ b/assets/markdown/how_digital_ocean-ka.md @@ -0,0 +1,12 @@ +### How to get Digital Ocean API Token +1. Visit the following [link](https://cloud.digitalocean.com/) and sign + into newly created account. +2. Enter into previously created project. If you haven't created one, + then please proceed. +3. Go to the "API" link on the left bar. +4. Click on the "Generate New Token". +5. Enter any name for the token. +6. Put expiration time to "No expiry". +7. Check the "Write (optional)" checkbox. +8. Now click on the "Generate Token" button. +9. After that, the token will be shown. Store it in any reliable place, preferably a password manager. \ No newline at end of file diff --git a/assets/markdown/how_digital_ocean-nl.md b/assets/markdown/how_digital_ocean-nl.md new file mode 100644 index 00000000..dc6355c2 --- /dev/null +++ b/assets/markdown/how_digital_ocean-nl.md @@ -0,0 +1,12 @@ +### How to get Digital Ocean API Token +1. Visit the following [link](https://cloud.digitalocean.com/) and sign + into newly created account. +2. Enter into previously created project. If you haven't created one, + then please proceed. +3. Go to the "API" link on the left bar. +4. Click on the "Generate New Token". +5. Enter any name for the token. +6. Put expiration time to "No expiry". +7. Check the "Write (optional)" checkbox. +8. Now click on the "Generate Token" button. +9. After that, the token will be shown. Store it in any reliable place, preferably a password manager. \ No newline at end of file diff --git a/assets/markdown/how_digital_ocean-pl.md b/assets/markdown/how_digital_ocean-pl.md new file mode 100644 index 00000000..42496f98 --- /dev/null +++ b/assets/markdown/how_digital_ocean-pl.md @@ -0,0 +1,12 @@ +### Jak uzyskać token API Digital Ocean +1. Wejdź na stronę [link](https://cloud.digitalocean.com/) i zaloguj się + na konto, które właśnie utworzyłeś. +2. Zaloguj się do wcześniej utworzonego projektu. Jeśli jeszcze nie utworzyłeś projektu, + to przejdź dalej. +3. Kliknij na link "API" w lewym panelu. +4. Kliknij na przycisk "Generate New Token". +5. Wprowadź dowolną nazwę dla tokena. +6. Ustaw datę wygaśnięcia na "No expiry". +7. Zaznacz pole wyboru "Write (optional)". +8. Teraz kliknij przycisk "Generate Token". +9. Na ekranie pojawi się wówczas token. Przechowuj go w dowolnym bezpiecznym miejscu, najlepiej w menedżerze haseł. \ No newline at end of file diff --git a/assets/markdown/how_digital_ocean-ru.md b/assets/markdown/how_digital_ocean-ru.md new file mode 100644 index 00000000..ad3ddaa3 --- /dev/null +++ b/assets/markdown/how_digital_ocean-ru.md @@ -0,0 +1,12 @@ +### Как получить API-токен Digital Ocean +1. Перейдите по следующей [link](https://cloud.digitalocean.com/) и войдите + в только что созданную учетную запись. +2. Войдите в ранее созданный проект. Если вы еще не создали проект, + тогда приступайте. +3. Перейдите по ссылке "API" на левой панели. +4. Нажмите на кнопку "Generate New Token". +5. Введите любое имя для токена. +6. Установите срок действия на "No expiry". +7. Установите флажок "Write (optional)". +8. Теперь нажмите на кнопку "Generate Token". +9. После этого на экране появится токен. Сохраните его в любом надежном месте, лучше всего в менеджере паролей. \ No newline at end of file diff --git a/assets/markdown/how_digital_ocean-sk.md b/assets/markdown/how_digital_ocean-sk.md new file mode 100644 index 00000000..92b7561f --- /dev/null +++ b/assets/markdown/how_digital_ocean-sk.md @@ -0,0 +1,12 @@ +### Ako získať token API Digital Ocean +1. Navštívte nasledujúci [link](https://cloud.digitalocean.com/) a prihláste sa + do novo vytvoreného účtu. +2. Vstúpte do predtým vytvoreného projektu. Ak ste ho ešte nevytvorili, + potom pokračujte ďalej. +3. Prejdite na odkaz "API" na ľavej lište. +4. Kliknite na tlačidlo "Generate New Token". +5. Zadajte ľubovoľný názov tokenu. +6. Nastavte čas vypršania platnosti na "No expiry". +7. Začiarknite políčko "Write (optional)" zaškrtávacie políčko. +8. Teraz kliknite na tlačidlo "Generate Token" tlačidlo. +9. Potom sa zobrazí token. Uložte ho na akékoľvek spoľahlivé miesto, najlepšie do správcu hesiel. \ No newline at end of file diff --git a/assets/markdown/how_digital_ocean-th.md b/assets/markdown/how_digital_ocean-th.md new file mode 100644 index 00000000..dc6355c2 --- /dev/null +++ b/assets/markdown/how_digital_ocean-th.md @@ -0,0 +1,12 @@ +### How to get Digital Ocean API Token +1. Visit the following [link](https://cloud.digitalocean.com/) and sign + into newly created account. +2. Enter into previously created project. If you haven't created one, + then please proceed. +3. Go to the "API" link on the left bar. +4. Click on the "Generate New Token". +5. Enter any name for the token. +6. Put expiration time to "No expiry". +7. Check the "Write (optional)" checkbox. +8. Now click on the "Generate Token" button. +9. After that, the token will be shown. Store it in any reliable place, preferably a password manager. \ No newline at end of file diff --git a/assets/markdown/how_digital_ocean-uk.md b/assets/markdown/how_digital_ocean-uk.md new file mode 100644 index 00000000..dc6355c2 --- /dev/null +++ b/assets/markdown/how_digital_ocean-uk.md @@ -0,0 +1,12 @@ +### How to get Digital Ocean API Token +1. Visit the following [link](https://cloud.digitalocean.com/) and sign + into newly created account. +2. Enter into previously created project. If you haven't created one, + then please proceed. +3. Go to the "API" link on the left bar. +4. Click on the "Generate New Token". +5. Enter any name for the token. +6. Put expiration time to "No expiry". +7. Check the "Write (optional)" checkbox. +8. Now click on the "Generate Token" button. +9. After that, the token will be shown. Store it in any reliable place, preferably a password manager. \ No newline at end of file diff --git a/assets/markdown/how_fallback_old-be.md b/assets/markdown/how_fallback_old-be.md new file mode 100644 index 00000000..0f3b619d --- /dev/null +++ b/assets/markdown/how_fallback_old-be.md @@ -0,0 +1,3 @@ +У наступным акне увядзіце токен, атрыманы з кансолі папярэдняй версіі прыкладання. + +Увядзіце яго без слова *Bearer*. diff --git a/assets/markdown/how_fallback_old-cs.md b/assets/markdown/how_fallback_old-cs.md new file mode 100644 index 00000000..2080eded --- /dev/null +++ b/assets/markdown/how_fallback_old-cs.md @@ -0,0 +1,3 @@ +V dalším okně zadejte token získaný z konzoly předchozí verze aplikace. + +Zadejte ji bez slova *Bearer*. diff --git a/assets/markdown/how_fallback_old-de.md b/assets/markdown/how_fallback_old-de.md new file mode 100644 index 00000000..c00d7799 --- /dev/null +++ b/assets/markdown/how_fallback_old-de.md @@ -0,0 +1,3 @@ +Geben Sie im nächsten Fenster das Token ein, das Sie von der Konsole der vorherigen Version der Anwendung erhalten haben. + +Geben Sie es ohne das Wort *Bearer* ein. diff --git a/assets/markdown/how_fallback_old-es.md b/assets/markdown/how_fallback_old-es.md new file mode 100644 index 00000000..c12504e7 --- /dev/null +++ b/assets/markdown/how_fallback_old-es.md @@ -0,0 +1,3 @@ +In the next window, enter the token obtained from the console of the previous version of the application. + +Enter it without the word *Bearer*. diff --git a/assets/markdown/how_fallback_old-fr.md b/assets/markdown/how_fallback_old-fr.md new file mode 100644 index 00000000..c12504e7 --- /dev/null +++ b/assets/markdown/how_fallback_old-fr.md @@ -0,0 +1,3 @@ +In the next window, enter the token obtained from the console of the previous version of the application. + +Enter it without the word *Bearer*. diff --git a/assets/markdown/how_fallback_old-it.md b/assets/markdown/how_fallback_old-it.md new file mode 100644 index 00000000..c12504e7 --- /dev/null +++ b/assets/markdown/how_fallback_old-it.md @@ -0,0 +1,3 @@ +In the next window, enter the token obtained from the console of the previous version of the application. + +Enter it without the word *Bearer*. diff --git a/assets/markdown/how_fallback_old-ja.md b/assets/markdown/how_fallback_old-ja.md new file mode 100644 index 00000000..c12504e7 --- /dev/null +++ b/assets/markdown/how_fallback_old-ja.md @@ -0,0 +1,3 @@ +In the next window, enter the token obtained from the console of the previous version of the application. + +Enter it without the word *Bearer*. diff --git a/assets/markdown/how_fallback_old-ka.md b/assets/markdown/how_fallback_old-ka.md new file mode 100644 index 00000000..c12504e7 --- /dev/null +++ b/assets/markdown/how_fallback_old-ka.md @@ -0,0 +1,3 @@ +In the next window, enter the token obtained from the console of the previous version of the application. + +Enter it without the word *Bearer*. diff --git a/assets/markdown/how_fallback_old-nl.md b/assets/markdown/how_fallback_old-nl.md new file mode 100644 index 00000000..c12504e7 --- /dev/null +++ b/assets/markdown/how_fallback_old-nl.md @@ -0,0 +1,3 @@ +In the next window, enter the token obtained from the console of the previous version of the application. + +Enter it without the word *Bearer*. diff --git a/assets/markdown/how_fallback_old-pl.md b/assets/markdown/how_fallback_old-pl.md new file mode 100644 index 00000000..e5c2f114 --- /dev/null +++ b/assets/markdown/how_fallback_old-pl.md @@ -0,0 +1,3 @@ +W kolejnym oknie wprowadź token uzyskany z konsoli poprzedniej wersji aplikacji. + +Należy wpisać bez słowa *Bearer*. diff --git a/assets/markdown/how_fallback_old-sk.md b/assets/markdown/how_fallback_old-sk.md new file mode 100644 index 00000000..2c307149 --- /dev/null +++ b/assets/markdown/how_fallback_old-sk.md @@ -0,0 +1,3 @@ +V ďalšom okne zadajte token získaný z konzoly predchádzajúcej verzie aplikácie. + +Musíte vstúpiť bez slova *Bearer*. diff --git a/assets/markdown/how_fallback_old-th.md b/assets/markdown/how_fallback_old-th.md new file mode 100644 index 00000000..c12504e7 --- /dev/null +++ b/assets/markdown/how_fallback_old-th.md @@ -0,0 +1,3 @@ +In the next window, enter the token obtained from the console of the previous version of the application. + +Enter it without the word *Bearer*. diff --git a/assets/markdown/how_fallback_old-uk.md b/assets/markdown/how_fallback_old-uk.md new file mode 100644 index 00000000..2d02d3fe --- /dev/null +++ b/assets/markdown/how_fallback_old-uk.md @@ -0,0 +1,3 @@ +У наступне вікно введіть токен, отриманий з консолі попередньої версії програми. + +Введіть його без слова *bearer*. diff --git a/assets/markdown/how_fallback_ssh-be.md b/assets/markdown/how_fallback_ssh-be.md new file mode 100644 index 00000000..3ad871bc --- /dev/null +++ b/assets/markdown/how_fallback_ssh-be.md @@ -0,0 +1,19 @@ +Увайдзіце на ваш сервер як root карыстальнік і праглядзіце змесціва файла `/etc/nixos/userdata/tokens.json` + +```sh +cat /etc/nixos/userdata/tokens.json +``` + +Гэты файл будзе мець падобную канструкцыю: + +```json +{ + "tokens": [ + { + "token": "token_to_copy", + "name": "device_name", + "date": "date" + } +``` + +Скапіруйце токен з файла і ўстаўце яго ў наступнае акно. diff --git a/assets/markdown/how_fallback_ssh-cs.md b/assets/markdown/how_fallback_ssh-cs.md new file mode 100644 index 00000000..7b74ac74 --- /dev/null +++ b/assets/markdown/how_fallback_ssh-cs.md @@ -0,0 +1,19 @@ +Přihlaste se k serveru jako uživatel root a podívejte se na obsah souboru `/etc/nixos/userdata/tokens.json` + +```sh +cat /etc/nixos/userdata/tokens.json +``` + +Tento soubor bude mít podobnou konstrukci: + +```json +{ + "tokens": [ + { + "token": "token_ke_zkopírování", + "name": "název_zařízení", + "date": "datum" + } +``` + +Zkopírujte token ze souboru a vložte jej do dalšího okna. diff --git a/assets/markdown/how_fallback_ssh-de.md b/assets/markdown/how_fallback_ssh-de.md new file mode 100644 index 00000000..f6da0343 --- /dev/null +++ b/assets/markdown/how_fallback_ssh-de.md @@ -0,0 +1,19 @@ +Melden Sie sich als Root-Benutzer auf Ihrem Server an und sehen Sie sich den Inhalt der Datei `/etc/nixos/userdata/tokens.json` an + +```sh +cat /etc/nixos/userdata/tokens.json +``` + +Diese Datei hat einen ähnlichen Aufbau: + +```json +{ + "tokens": [ + { + "token": "token_zum_Kopieren", + "name": "Gerätname", + "date": "Datum" + } +``` + +Kopieren Sie das Token aus der Datei und fügen Sie es im nächsten Fenster ein. diff --git a/assets/markdown/how_fallback_ssh-es.md b/assets/markdown/how_fallback_ssh-es.md new file mode 100644 index 00000000..ce90e76a --- /dev/null +++ b/assets/markdown/how_fallback_ssh-es.md @@ -0,0 +1,19 @@ +Login as root user to your server and look at the contents of the file `/etc/nixos/userdata/tokens.json` + +```sh +cat /etc/nixos/userdata/tokens.json +``` + +This file will have a similar construction: + +```json +{ + "tokens": [ + { + "token": "token_to_copy", + "name": "device_name", + "date": "date" + } +``` + +Copy the token from the file and paste it in the next window. diff --git a/assets/markdown/how_fallback_ssh-fr.md b/assets/markdown/how_fallback_ssh-fr.md new file mode 100644 index 00000000..ce90e76a --- /dev/null +++ b/assets/markdown/how_fallback_ssh-fr.md @@ -0,0 +1,19 @@ +Login as root user to your server and look at the contents of the file `/etc/nixos/userdata/tokens.json` + +```sh +cat /etc/nixos/userdata/tokens.json +``` + +This file will have a similar construction: + +```json +{ + "tokens": [ + { + "token": "token_to_copy", + "name": "device_name", + "date": "date" + } +``` + +Copy the token from the file and paste it in the next window. diff --git a/assets/markdown/how_fallback_ssh-it.md b/assets/markdown/how_fallback_ssh-it.md new file mode 100644 index 00000000..ce90e76a --- /dev/null +++ b/assets/markdown/how_fallback_ssh-it.md @@ -0,0 +1,19 @@ +Login as root user to your server and look at the contents of the file `/etc/nixos/userdata/tokens.json` + +```sh +cat /etc/nixos/userdata/tokens.json +``` + +This file will have a similar construction: + +```json +{ + "tokens": [ + { + "token": "token_to_copy", + "name": "device_name", + "date": "date" + } +``` + +Copy the token from the file and paste it in the next window. diff --git a/assets/markdown/how_fallback_ssh-ja.md b/assets/markdown/how_fallback_ssh-ja.md new file mode 100644 index 00000000..ce90e76a --- /dev/null +++ b/assets/markdown/how_fallback_ssh-ja.md @@ -0,0 +1,19 @@ +Login as root user to your server and look at the contents of the file `/etc/nixos/userdata/tokens.json` + +```sh +cat /etc/nixos/userdata/tokens.json +``` + +This file will have a similar construction: + +```json +{ + "tokens": [ + { + "token": "token_to_copy", + "name": "device_name", + "date": "date" + } +``` + +Copy the token from the file and paste it in the next window. diff --git a/assets/markdown/how_fallback_ssh-ka.md b/assets/markdown/how_fallback_ssh-ka.md new file mode 100644 index 00000000..ce90e76a --- /dev/null +++ b/assets/markdown/how_fallback_ssh-ka.md @@ -0,0 +1,19 @@ +Login as root user to your server and look at the contents of the file `/etc/nixos/userdata/tokens.json` + +```sh +cat /etc/nixos/userdata/tokens.json +``` + +This file will have a similar construction: + +```json +{ + "tokens": [ + { + "token": "token_to_copy", + "name": "device_name", + "date": "date" + } +``` + +Copy the token from the file and paste it in the next window. diff --git a/assets/markdown/how_fallback_ssh-nl.md b/assets/markdown/how_fallback_ssh-nl.md new file mode 100644 index 00000000..ce90e76a --- /dev/null +++ b/assets/markdown/how_fallback_ssh-nl.md @@ -0,0 +1,19 @@ +Login as root user to your server and look at the contents of the file `/etc/nixos/userdata/tokens.json` + +```sh +cat /etc/nixos/userdata/tokens.json +``` + +This file will have a similar construction: + +```json +{ + "tokens": [ + { + "token": "token_to_copy", + "name": "device_name", + "date": "date" + } +``` + +Copy the token from the file and paste it in the next window. diff --git a/assets/markdown/how_fallback_ssh-pl.md b/assets/markdown/how_fallback_ssh-pl.md new file mode 100644 index 00000000..0e18b3e5 --- /dev/null +++ b/assets/markdown/how_fallback_ssh-pl.md @@ -0,0 +1,19 @@ +Zaloguj się jako użytkownik root na swoim serwerze i przejrzyj zawartość pliku `/etc/nixos/userdata/tokens.json` + +```sh +cat /etc/nixos/userdata/tokens.json +``` + +W tym pliku będzie podobny wzór: + +`json +{ + { "tokens": [ + { + { "token": "token_który_był_skopedowany", + { "name": "device_name", + { "data": "data" + } +``` + +Skopiuj token z pliku i wklej w kolejnym oknie. diff --git a/assets/markdown/how_fallback_ssh-sk.md b/assets/markdown/how_fallback_ssh-sk.md new file mode 100644 index 00000000..bd5d4a86 --- /dev/null +++ b/assets/markdown/how_fallback_ssh-sk.md @@ -0,0 +1,19 @@ +Prihláste sa ako používateľ root na server a pozrite sa na obsah súboru `/etc/nixos/userdata/tokens.json + +```sh +cat /etc/nixos/userdata/tokens.json +``` + +V tomto súbore bude podobný dizajn: + +``json +{ + {"tokens": [ + { + {"token": "token_which_has_been_scoped", + {"name": "device_name", + {"date": "date" + } +``` + +Skopírujte token zo súboru a vložte ho do ďalšieho okna. diff --git a/assets/markdown/how_fallback_ssh-th.md b/assets/markdown/how_fallback_ssh-th.md new file mode 100644 index 00000000..ce90e76a --- /dev/null +++ b/assets/markdown/how_fallback_ssh-th.md @@ -0,0 +1,19 @@ +Login as root user to your server and look at the contents of the file `/etc/nixos/userdata/tokens.json` + +```sh +cat /etc/nixos/userdata/tokens.json +``` + +This file will have a similar construction: + +```json +{ + "tokens": [ + { + "token": "token_to_copy", + "name": "device_name", + "date": "date" + } +``` + +Copy the token from the file and paste it in the next window. diff --git a/assets/markdown/how_fallback_ssh-uk.md b/assets/markdown/how_fallback_ssh-uk.md new file mode 100644 index 00000000..568d3264 --- /dev/null +++ b/assets/markdown/how_fallback_ssh-uk.md @@ -0,0 +1,19 @@ +Увійдіть від користувача root до вашого сервера і подивіться на вміст файла`/etc/nixos/userdata/tokens.json` + +```sh +cat /etc/nixos/userdata/tokens.json +``` + +Цей файл буде мати подібну конструкцію: + +```json +{ + "tokens": [ + { + "token": "token_to_copy", + "name": "device_name", + "date": "date" + } +``` + +Скопіюйте токен з файла і вставте його в наступне вікно. diff --git a/assets/markdown/how_fallback_terminal-be.md b/assets/markdown/how_fallback_terminal-be.md new file mode 100644 index 00000000..675d3902 --- /dev/null +++ b/assets/markdown/how_fallback_terminal-be.md @@ -0,0 +1,26 @@ +У панэлі кіравання серверам Hetzner перайдзіце на ўкладку **Rescue**. Затым націсніце на **Enable rescue & power cycle**. + +У полі *Choose a Recue OS* выберыце **linux64**, а ў *SSH Key* выберыце свой ключ, калі ён быў дададзены ў ваш уліковы запіс Hetzner. + +Націсніце **Enable rescue & power cycle** і дачакайцеся перазагрузкі сервера. На экране адлюструюцца лагін і пароль. Увайдзіце ў сістэму root, выкарыстоўваючы свой лагін і пароль. + +Падключыце файлавую сістэму сервера і паглядзіце змесціва файла токена: + +```sh +mount /dev/sda1 /mnt +cat /mnt/etc/nixos/userdata/tokens.json +``` + +Гэты файл будзе мець падобную канструкцыю: + +```json +{ + "tokens": [ + { + "token": "token_to_copy", + "name": "device_name", + "date": "date" + } +``` + +Скапіруйце токен з файла і ўстаўце яго ў наступнае акно. diff --git a/assets/markdown/how_fallback_terminal-cs.md b/assets/markdown/how_fallback_terminal-cs.md new file mode 100644 index 00000000..16a993a1 --- /dev/null +++ b/assets/markdown/how_fallback_terminal-cs.md @@ -0,0 +1,26 @@ +V ovládacím panelu serveru Hetzner přejděte na kartu **Rescue**. Poté klikněte na možnost **Enable rescue & power cycle**. + +V části *Choose a Recue OS* vyberte **linux64** a v části *SSH Key* vyberte svůj klíč, pokud byl přidán do vašeho účtu Hetzner. + +Klikněte na tlačítko **Enable rescue & power cycle** a počkejte, až se server restartuje. Na obrazovce se zobrazí přihlašovací jméno a heslo. Přihlaste se k uživateli root pomocí přihlašovacích údajů a hesla. + +Připojte souborový systém serveru a zobrazte obsah souboru token: + +```sh +mount /dev/sda1 /mnt +cat /mnt/etc/nixos/userdata/tokens.json +``` + +Tento soubor bude mít podobnou konstrukci: + +```json +{ + "tokens": [ + { + "token": "token_ke_zkopírování", + "name": "název_zařízení", + "date": "datum" + } +``` + +Zkopírujte token ze souboru a vložte jej do dalšího okna. diff --git a/assets/markdown/how_fallback_terminal-de.md b/assets/markdown/how_fallback_terminal-de.md new file mode 100644 index 00000000..7031612a --- /dev/null +++ b/assets/markdown/how_fallback_terminal-de.md @@ -0,0 +1,26 @@ +Gehen Sie im Control Panel des Hetzner-Servers auf den Tab **Rescue**. Klicken Sie dann auf **Enable rescue & power cycle**. + +In *Choose a Recue OS* wählen Sie **linux64** aus und in *SSH Key* wählen Sie Ihren Schlüssel aus, wen er zu ihrem Htzner-Konto hinzugefügt wurde. + +Klicken Sie auf **Enable rescue & power cycle** und warten Sie, bis der Server neu gestartet wird. Login und Passwort werden auf dem Bildschirm angezeigt. Melden Sie sich mit Ihren Anmelde- und Kennwortinformationen beim Root-Benutzer an. + +Mounten Sie Ihr Server-Dateisystem und sehen Sie sich den Inhalt der Token-Datei an: + +```sh +mount /dev/sda1 /mnt +cat /mnt/etc/nixos/userdata/tokens.json +``` + +Diese Datei hat einen ähnlichen Aufbau: + +```json +{ + "tokens": [ + { + "token": "token_zum_Kopieren", + "name": "Gerätname", + "date": "Datum" + } +``` + +Kopieren Sie das Token aus der Datei und fügen Sie es im nächsten Fenster ein. diff --git a/assets/markdown/how_fallback_terminal-es.md b/assets/markdown/how_fallback_terminal-es.md new file mode 100644 index 00000000..77c97efa --- /dev/null +++ b/assets/markdown/how_fallback_terminal-es.md @@ -0,0 +1,26 @@ +In the Hetzner server control panel, go to the **Rescue** tab. Then, click on **Enable rescue & power cycle**. + +In *Choose a Recue OS* select **linux64**, and in *SSH Key* select your key if it has been added to your Hetzner account. + +Click **Enable rescue & power cycle** and wait for the server to reboot. The login and password will be displayed on the screen. Login to the root user using your login and password information. + +Mount your server file system and see the contents of the token file: + +```sh +mount /dev/sda1 /mnt +cat /mnt/etc/nixos/userdata/tokens.json +``` + +This file will have a similar construction: + +```json +{ + "tokens": [ + { + "token": "token_to_copy", + "name": "device_name", + "date": "date" + } +``` + +Copy the token from the file and paste it in the next window. diff --git a/assets/markdown/how_fallback_terminal-fr.md b/assets/markdown/how_fallback_terminal-fr.md new file mode 100644 index 00000000..77c97efa --- /dev/null +++ b/assets/markdown/how_fallback_terminal-fr.md @@ -0,0 +1,26 @@ +In the Hetzner server control panel, go to the **Rescue** tab. Then, click on **Enable rescue & power cycle**. + +In *Choose a Recue OS* select **linux64**, and in *SSH Key* select your key if it has been added to your Hetzner account. + +Click **Enable rescue & power cycle** and wait for the server to reboot. The login and password will be displayed on the screen. Login to the root user using your login and password information. + +Mount your server file system and see the contents of the token file: + +```sh +mount /dev/sda1 /mnt +cat /mnt/etc/nixos/userdata/tokens.json +``` + +This file will have a similar construction: + +```json +{ + "tokens": [ + { + "token": "token_to_copy", + "name": "device_name", + "date": "date" + } +``` + +Copy the token from the file and paste it in the next window. diff --git a/assets/markdown/how_fallback_terminal-it.md b/assets/markdown/how_fallback_terminal-it.md new file mode 100644 index 00000000..77c97efa --- /dev/null +++ b/assets/markdown/how_fallback_terminal-it.md @@ -0,0 +1,26 @@ +In the Hetzner server control panel, go to the **Rescue** tab. Then, click on **Enable rescue & power cycle**. + +In *Choose a Recue OS* select **linux64**, and in *SSH Key* select your key if it has been added to your Hetzner account. + +Click **Enable rescue & power cycle** and wait for the server to reboot. The login and password will be displayed on the screen. Login to the root user using your login and password information. + +Mount your server file system and see the contents of the token file: + +```sh +mount /dev/sda1 /mnt +cat /mnt/etc/nixos/userdata/tokens.json +``` + +This file will have a similar construction: + +```json +{ + "tokens": [ + { + "token": "token_to_copy", + "name": "device_name", + "date": "date" + } +``` + +Copy the token from the file and paste it in the next window. diff --git a/assets/markdown/how_fallback_terminal-ja.md b/assets/markdown/how_fallback_terminal-ja.md new file mode 100644 index 00000000..77c97efa --- /dev/null +++ b/assets/markdown/how_fallback_terminal-ja.md @@ -0,0 +1,26 @@ +In the Hetzner server control panel, go to the **Rescue** tab. Then, click on **Enable rescue & power cycle**. + +In *Choose a Recue OS* select **linux64**, and in *SSH Key* select your key if it has been added to your Hetzner account. + +Click **Enable rescue & power cycle** and wait for the server to reboot. The login and password will be displayed on the screen. Login to the root user using your login and password information. + +Mount your server file system and see the contents of the token file: + +```sh +mount /dev/sda1 /mnt +cat /mnt/etc/nixos/userdata/tokens.json +``` + +This file will have a similar construction: + +```json +{ + "tokens": [ + { + "token": "token_to_copy", + "name": "device_name", + "date": "date" + } +``` + +Copy the token from the file and paste it in the next window. diff --git a/assets/markdown/how_fallback_terminal-ka.md b/assets/markdown/how_fallback_terminal-ka.md new file mode 100644 index 00000000..77c97efa --- /dev/null +++ b/assets/markdown/how_fallback_terminal-ka.md @@ -0,0 +1,26 @@ +In the Hetzner server control panel, go to the **Rescue** tab. Then, click on **Enable rescue & power cycle**. + +In *Choose a Recue OS* select **linux64**, and in *SSH Key* select your key if it has been added to your Hetzner account. + +Click **Enable rescue & power cycle** and wait for the server to reboot. The login and password will be displayed on the screen. Login to the root user using your login and password information. + +Mount your server file system and see the contents of the token file: + +```sh +mount /dev/sda1 /mnt +cat /mnt/etc/nixos/userdata/tokens.json +``` + +This file will have a similar construction: + +```json +{ + "tokens": [ + { + "token": "token_to_copy", + "name": "device_name", + "date": "date" + } +``` + +Copy the token from the file and paste it in the next window. diff --git a/assets/markdown/how_fallback_terminal-nl.md b/assets/markdown/how_fallback_terminal-nl.md new file mode 100644 index 00000000..77c97efa --- /dev/null +++ b/assets/markdown/how_fallback_terminal-nl.md @@ -0,0 +1,26 @@ +In the Hetzner server control panel, go to the **Rescue** tab. Then, click on **Enable rescue & power cycle**. + +In *Choose a Recue OS* select **linux64**, and in *SSH Key* select your key if it has been added to your Hetzner account. + +Click **Enable rescue & power cycle** and wait for the server to reboot. The login and password will be displayed on the screen. Login to the root user using your login and password information. + +Mount your server file system and see the contents of the token file: + +```sh +mount /dev/sda1 /mnt +cat /mnt/etc/nixos/userdata/tokens.json +``` + +This file will have a similar construction: + +```json +{ + "tokens": [ + { + "token": "token_to_copy", + "name": "device_name", + "date": "date" + } +``` + +Copy the token from the file and paste it in the next window. diff --git a/assets/markdown/how_fallback_terminal-pl.md b/assets/markdown/how_fallback_terminal-pl.md new file mode 100644 index 00000000..2abbe479 --- /dev/null +++ b/assets/markdown/how_fallback_terminal-pl.md @@ -0,0 +1,26 @@ +W panelu sterowania serwera Hetzner przejdź do zakładki **Rescue**. Następnie kliknij **Enable rescue & power cycle**. + +W *Choose a Recue OS* wybierz **linux64**, a w *SSH Key* swój klucz, jeśli został dodany do konta Hetznera. + +Naciśnij **Enable rescue & power cycle** i poczekaj na ponowne uruchomienie serwera. Zostanie wyświetlone hasło logowania. Zaloguj się na użytkownika root, używając informacji o loginie i haśle. + +Zamontuj system plików swojego serwera i spójrz na zawartość pliku token: + +```sh +mount /dev/sda1 /mnt +cat /mnt/etc/nixos/userdata/tokens.json +``` + +W tym pliku będzie podobny wzór: + +`json +{ + { "tokens": [ + { + { "token": "token_który_był_skopedowany", + { "name": "device_name", + { "data": "data" + } +``` + +Skopiuj token z pliku i wklej w kolejnym oknie. diff --git a/assets/markdown/how_fallback_terminal-sk.md b/assets/markdown/how_fallback_terminal-sk.md new file mode 100644 index 00000000..40a8f231 --- /dev/null +++ b/assets/markdown/how_fallback_terminal-sk.md @@ -0,0 +1,26 @@ +V ovládacom paneli servera Hetzner prejdite na kartu **Rescue**. Potom kliknite na **Enable rescue & power cycle**. + +V časti *Choose a Recue OS* vyberte **linux64** a v časti *SSH Key* vyberte svoj kľúč, ak bol pridaný do vášho účtu Hetzner. + +Stlačte tlačidlo **Enable rescue & power cycle** a počkajte, kým sa server reštartuje. Zobrazí sa prihlasovacie heslo. Prihláste sa ako používateľ root pomocou prihlasovacích údajov a hesla. + +Pripojte súborový systém servera a pozrite si obsah súboru token: + +```sh +mount /dev/sda1 /mnt +cat /mnt/etc/nixos/userdata/tokens.json +``` + +V tomto súbore bude podobný dizajn: + +``json +{ + {"tokens": [ + { + {"token": "token_which_has_been_scoped", + {"name": "device_name", + {"date": "date" + } +``` + +Skopírujte token zo súboru a vložte ho do ďalšieho okna. diff --git a/assets/markdown/how_fallback_terminal-th.md b/assets/markdown/how_fallback_terminal-th.md new file mode 100644 index 00000000..77c97efa --- /dev/null +++ b/assets/markdown/how_fallback_terminal-th.md @@ -0,0 +1,26 @@ +In the Hetzner server control panel, go to the **Rescue** tab. Then, click on **Enable rescue & power cycle**. + +In *Choose a Recue OS* select **linux64**, and in *SSH Key* select your key if it has been added to your Hetzner account. + +Click **Enable rescue & power cycle** and wait for the server to reboot. The login and password will be displayed on the screen. Login to the root user using your login and password information. + +Mount your server file system and see the contents of the token file: + +```sh +mount /dev/sda1 /mnt +cat /mnt/etc/nixos/userdata/tokens.json +``` + +This file will have a similar construction: + +```json +{ + "tokens": [ + { + "token": "token_to_copy", + "name": "device_name", + "date": "date" + } +``` + +Copy the token from the file and paste it in the next window. diff --git a/assets/markdown/how_fallback_terminal-uk.md b/assets/markdown/how_fallback_terminal-uk.md new file mode 100644 index 00000000..c72f9f7e --- /dev/null +++ b/assets/markdown/how_fallback_terminal-uk.md @@ -0,0 +1,26 @@ +На панелі керування сервером Hetzner перейдіть на вкладку **Rescue**. Після цього натисніть кнопку **Enable rescue & power cycle**. + +У полі *Choose a Recue OS* виберіть **linux64**, а в *SSH Key* свій ключ, якщо він був доданий у ваш обліковий запис Hetzner. + +Натисніть **Enable rescue & power cycle** і зачекайте, поки сервер перезавантажить. На екрані з'явиться логін і пароль. Увійдіть до користувача root за допомогою вашої реєстраційних даних і паролів. + +Змонтувати файлову систему вашого сервера і переглянути вміст файла токенів: + +```sh +mount /dev/sda1 /mnt +cat /mnt/etc/nixos/userdata/tokens.json +``` + +Цей файл буде мати подібну конструкцію: + +```json +{ + "tokens": [ + { + "token": "token_to_copy", + "name": "device_name", + "date": "date" + } +``` + +Скопіюйте токен з файла і вставте його в наступне вікно. diff --git a/assets/markdown/how_hetzner-be.md b/assets/markdown/how_hetzner-be.md new file mode 100644 index 00000000..758f6ca0 --- /dev/null +++ b/assets/markdown/how_hetzner-be.md @@ -0,0 +1,21 @@ +### Як атрымаць Hetzner API Token +1. Перайдзіце па наступнай [спасылцы](https://console.hetzner.cloud/) і падпішыцеся + у толькі што створаны ўліковы запіс. +2. Увайдзіце ў раней створаны праект. Калі вы яго не стварылі, + тады, калі ласка, працягвайце. +3. Навядзіце курсор мышы на бакавую панэль. Панэль павінна разгарнуцца і паказаць нас + меню. Нас цікавіць апошні — **Security** (значок а ключ). +4. Далей у верхняй частцы інтэрфейсу мы бачым прыблізна + наступнае: **SSH Keys, API Tokens, Certificates, Members.** Вы + патрэбныя **API Tokens**. Націсніце на яго. +5. У правай частцы інтэрфейсу павінна быць **Generate API token** button. Калі вы карыстаецеся мабільнай версіяй вэб-старонкі, у + у правым ніжнім куце вы ўбачыце **red cross**. Націсніце гэтую кнопку. +6. У полі **Description** дайце нашаму токену імя (гэта можа быць любое + імя, якое вам падабаецца. На сутнасць гэта не ўплывае. +7. Пад полем **permissions** мы бачым магчымасць выбару + **дазволы**. Выберыце **Read & Write**. +8. Націсніце **Generate API Token.** +9. Пасля гэтага будзе паказаны наш ключ. Захоўвайце яго ў надзейным месцы, + або ў менеджэры пароляў, што лепш. + +![Наладжванне маркера Hetzner](рэсурс:assets/images/gifs/Hetzner.gif) diff --git a/assets/markdown/how_hetzner-cs.md b/assets/markdown/how_hetzner-cs.md new file mode 100644 index 00000000..500efe54 --- /dev/null +++ b/assets/markdown/how_hetzner-cs.md @@ -0,0 +1,23 @@ +### Jak získat Hetzner API Token +1. Navštivte následující [link](https://console.hetzner.cloud/) a podepište se. + k nově vytvořenému účtu. +2. Vstupte do dříve vytvořeného projektu. Pokud jste žádný nevytvořili, + pak prosím pokračujte. +3. Kurzorem myši najeďte na boční panel. Panel by se měl rozbalit a zobrazit nám + nabídku. Nás zajímá poslední z nich - **Security** (ikona tzv. + klávesy). +4. Dále v horní části rozhraní vidíme přibližně následující položky + následující: ***SSH Keys, API Tokens, Certificates, Members**. + potřebujete **API Tokens**. Klepněte na něj. +5. V pravé části rozhraní by se mělo nacházet **Generate API + token** tlačítko. Pokud používáte mobilní verzi og webové stránky, v části + pravém dolním rohu uvidíte **červený křížek**. Stiskněte toto tlačítko. +6. V poli **Description** pojmenujte náš token (může to být libovolný název). + název, který se vám líbí. Na podstatu to nemá vliv. +7. Pod polem **Description** vidíme možnost zvolit si. + **Permissions**. Vyberte možnost **Read && Write**. +8. Klikněte na tlačítko **Generate API Token**. +9. Poté se zobrazí náš klíč. Uložte jej na spolehlivé místo, + nebo ve správci hesel, což je lepší. + +![Nastavení tokenu Hetzner](resource:assets/images/gifs/Hetzner.gif) diff --git a/assets/markdown/how_hetzner-de.md b/assets/markdown/how_hetzner-de.md new file mode 100644 index 00000000..5430c70b --- /dev/null +++ b/assets/markdown/how_hetzner-de.md @@ -0,0 +1,23 @@ +### So erhalten Sie das Hetzner-API-Token +1. Besuchen Sie den folgenden [link](https://console.hetzner.cloud/) und melden Sie sich + in das neu erstellte Konto an. +2. Gehen Sie in ein zuvor erstelltes Projekt. Wenn Sie noch keine erstellt haben, + dann bitte fortfahren. +3. Bewegen Sie den Mauszeiger über das Seitenpanel. Panel sollte sich erweitern und uns + Ein Menü zeigen. Uns interessiert letzteres — **Security** (Symbol eines + Schlüssels). +4. Als nächstes können wir im oberen Teil der Oberfläache ungefähr sehen + Folgendes: **SSH Keys, API Tokens, Certificates, Members.** Sie + benötigen **API Tokens**. Klicken Sie es an. +5. Im rechten Teil der Benutzeroberfläche sollte **Generate API + Token** stehen. Wenn Sie die mobile Version der Webseite verwenden, in der + In der unteren rechten Ecke sehen Sie **red cross**. Drücken Sie diesen Knopf. +6. Geben Sie im Feld **Description** unserem Token einen Namen (dies kann ein beliebiger Name + sein). Es beeinflusst nicht die Essenz. +7. Unter dem Feld **Description** sehen wir eine Auswahlmöglichkeit + **permissions**. Wählen Sie **Read & Write**. +8. Klicken Sie auf **Generate API Token.** +9. Danach wird unser Schlüssel angezeigt. Bewahren Sie es an einem zuverlässigen Ort auf, + oder im Passwortmanager, was besser ist. + +![Hetzner token Einrichtung](resource:assets/images/gifs/Hetzner.gif) diff --git a/assets/markdown/how_hetzner-es.md b/assets/markdown/how_hetzner-es.md new file mode 100644 index 00000000..6f859c18 --- /dev/null +++ b/assets/markdown/how_hetzner-es.md @@ -0,0 +1,23 @@ +### How to get Hetzner API Token +1. Visit the following [link](https://console.hetzner.cloud/) and sign + into newly created account. +2. Enter into previously created project. If you haven't created one, + then please proceed. +3. Hover side panel with mouse cursor. Panel should expand and show us + a menu. We're interested in the last one — **Security** (icon of a + key). +4. Next, in the upper part of an interface, we can see approximately + the following: **SSH Keys, API Tokens, Certificates, Members.** You + need **API Tokens**. Click on it. +5. In the right part of the interface, there should be **Generate API + token** button. If you're using mobile version og a webpage, in the + lower right corner you'll see **red cross**. Push that button. +6. In the **Description** field, give our token a name (this can be any + name that you like. It doesn't influence the essence. +7. Under the **Description** field we can see a possibility to choose + **permissions**. Pick **Read & Write**. +8. Click **Generate API Token.** +9. After that, our key will be shown. Store it in the reliable place, + or in the password manager, which is better. + +![Hetzner token setup](resource:assets/images/gifs/Hetzner.gif) diff --git a/assets/markdown/how_hetzner-fr.md b/assets/markdown/how_hetzner-fr.md new file mode 100644 index 00000000..6f859c18 --- /dev/null +++ b/assets/markdown/how_hetzner-fr.md @@ -0,0 +1,23 @@ +### How to get Hetzner API Token +1. Visit the following [link](https://console.hetzner.cloud/) and sign + into newly created account. +2. Enter into previously created project. If you haven't created one, + then please proceed. +3. Hover side panel with mouse cursor. Panel should expand and show us + a menu. We're interested in the last one — **Security** (icon of a + key). +4. Next, in the upper part of an interface, we can see approximately + the following: **SSH Keys, API Tokens, Certificates, Members.** You + need **API Tokens**. Click on it. +5. In the right part of the interface, there should be **Generate API + token** button. If you're using mobile version og a webpage, in the + lower right corner you'll see **red cross**. Push that button. +6. In the **Description** field, give our token a name (this can be any + name that you like. It doesn't influence the essence. +7. Under the **Description** field we can see a possibility to choose + **permissions**. Pick **Read & Write**. +8. Click **Generate API Token.** +9. After that, our key will be shown. Store it in the reliable place, + or in the password manager, which is better. + +![Hetzner token setup](resource:assets/images/gifs/Hetzner.gif) diff --git a/assets/markdown/how_hetzner-it.md b/assets/markdown/how_hetzner-it.md new file mode 100644 index 00000000..6f859c18 --- /dev/null +++ b/assets/markdown/how_hetzner-it.md @@ -0,0 +1,23 @@ +### How to get Hetzner API Token +1. Visit the following [link](https://console.hetzner.cloud/) and sign + into newly created account. +2. Enter into previously created project. If you haven't created one, + then please proceed. +3. Hover side panel with mouse cursor. Panel should expand and show us + a menu. We're interested in the last one — **Security** (icon of a + key). +4. Next, in the upper part of an interface, we can see approximately + the following: **SSH Keys, API Tokens, Certificates, Members.** You + need **API Tokens**. Click on it. +5. In the right part of the interface, there should be **Generate API + token** button. If you're using mobile version og a webpage, in the + lower right corner you'll see **red cross**. Push that button. +6. In the **Description** field, give our token a name (this can be any + name that you like. It doesn't influence the essence. +7. Under the **Description** field we can see a possibility to choose + **permissions**. Pick **Read & Write**. +8. Click **Generate API Token.** +9. After that, our key will be shown. Store it in the reliable place, + or in the password manager, which is better. + +![Hetzner token setup](resource:assets/images/gifs/Hetzner.gif) diff --git a/assets/markdown/how_hetzner-ja.md b/assets/markdown/how_hetzner-ja.md new file mode 100644 index 00000000..6f859c18 --- /dev/null +++ b/assets/markdown/how_hetzner-ja.md @@ -0,0 +1,23 @@ +### How to get Hetzner API Token +1. Visit the following [link](https://console.hetzner.cloud/) and sign + into newly created account. +2. Enter into previously created project. If you haven't created one, + then please proceed. +3. Hover side panel with mouse cursor. Panel should expand and show us + a menu. We're interested in the last one — **Security** (icon of a + key). +4. Next, in the upper part of an interface, we can see approximately + the following: **SSH Keys, API Tokens, Certificates, Members.** You + need **API Tokens**. Click on it. +5. In the right part of the interface, there should be **Generate API + token** button. If you're using mobile version og a webpage, in the + lower right corner you'll see **red cross**. Push that button. +6. In the **Description** field, give our token a name (this can be any + name that you like. It doesn't influence the essence. +7. Under the **Description** field we can see a possibility to choose + **permissions**. Pick **Read & Write**. +8. Click **Generate API Token.** +9. After that, our key will be shown. Store it in the reliable place, + or in the password manager, which is better. + +![Hetzner token setup](resource:assets/images/gifs/Hetzner.gif) diff --git a/assets/markdown/how_hetzner-ka.md b/assets/markdown/how_hetzner-ka.md new file mode 100644 index 00000000..6f859c18 --- /dev/null +++ b/assets/markdown/how_hetzner-ka.md @@ -0,0 +1,23 @@ +### How to get Hetzner API Token +1. Visit the following [link](https://console.hetzner.cloud/) and sign + into newly created account. +2. Enter into previously created project. If you haven't created one, + then please proceed. +3. Hover side panel with mouse cursor. Panel should expand and show us + a menu. We're interested in the last one — **Security** (icon of a + key). +4. Next, in the upper part of an interface, we can see approximately + the following: **SSH Keys, API Tokens, Certificates, Members.** You + need **API Tokens**. Click on it. +5. In the right part of the interface, there should be **Generate API + token** button. If you're using mobile version og a webpage, in the + lower right corner you'll see **red cross**. Push that button. +6. In the **Description** field, give our token a name (this can be any + name that you like. It doesn't influence the essence. +7. Under the **Description** field we can see a possibility to choose + **permissions**. Pick **Read & Write**. +8. Click **Generate API Token.** +9. After that, our key will be shown. Store it in the reliable place, + or in the password manager, which is better. + +![Hetzner token setup](resource:assets/images/gifs/Hetzner.gif) diff --git a/assets/markdown/how_hetzner-nl.md b/assets/markdown/how_hetzner-nl.md new file mode 100644 index 00000000..6f859c18 --- /dev/null +++ b/assets/markdown/how_hetzner-nl.md @@ -0,0 +1,23 @@ +### How to get Hetzner API Token +1. Visit the following [link](https://console.hetzner.cloud/) and sign + into newly created account. +2. Enter into previously created project. If you haven't created one, + then please proceed. +3. Hover side panel with mouse cursor. Panel should expand and show us + a menu. We're interested in the last one — **Security** (icon of a + key). +4. Next, in the upper part of an interface, we can see approximately + the following: **SSH Keys, API Tokens, Certificates, Members.** You + need **API Tokens**. Click on it. +5. In the right part of the interface, there should be **Generate API + token** button. If you're using mobile version og a webpage, in the + lower right corner you'll see **red cross**. Push that button. +6. In the **Description** field, give our token a name (this can be any + name that you like. It doesn't influence the essence. +7. Under the **Description** field we can see a possibility to choose + **permissions**. Pick **Read & Write**. +8. Click **Generate API Token.** +9. After that, our key will be shown. Store it in the reliable place, + or in the password manager, which is better. + +![Hetzner token setup](resource:assets/images/gifs/Hetzner.gif) diff --git a/assets/markdown/how_hetzner-pl.md b/assets/markdown/how_hetzner-pl.md new file mode 100644 index 00000000..f16aec17 --- /dev/null +++ b/assets/markdown/how_hetzner-pl.md @@ -0,0 +1,23 @@ +### Jak uzyskać Hetzner API Token +1. Odwiedź następujący [link](https://console.hetzner.cloud/) i zaloguj się + na nowo utworzone konto. +2. Wejdź do wcześniej utworzonego projektu. Jeśli jeszcze go nie stworzyłeś, + to proszę przejść dalej. +3. Najedź kursorem myszy na panel boczny. Panel powinien się rozwinąć i pokazać nam + menu. Nas interesuje ostatnie z nich - **Security** (ikona klucza). + . +4. Następnie w górnej części interfejsu możemy zobaczyć ok. + następujące elementy: ***SSH Keys, API Tokens, Certificates, Members.** +Klikamy na nie. +5. W prawej części interfejsu powinien znajdować się przycisk **Generate API + token**. Jeśli korzystasz z mobilnej wersji strony, w prawym dolnym rogu pojawi się + dolnym prawym rogu pojawi się **red cross**. Wciśnij ten przycisk. +6. W polu **Description** nadaj naszemu tokenowi nazwę (może to być dowolna nazwa). + nazwa, która Ci się podoba. Nie ma ona wpływu na istotę. +7. Pod polem **Description** widzimy możliwość wyboru. + **permissions**. Pick **Read & Write**. +8. Klikamy **Generate API Token**. +9. Po tej czynności pojawi się nasz klucz. Przechowuj go w wiarygodnym miejscu, +lub w menedżerze haseł, co jest lepsze. + +![Ustawienie tokena Hetznera](resource:assets/images/gifs/Hetzner.gif) diff --git a/assets/markdown/how_hetzner-sk.md b/assets/markdown/how_hetzner-sk.md new file mode 100644 index 00000000..f5ab24dc --- /dev/null +++ b/assets/markdown/how_hetzner-sk.md @@ -0,0 +1,23 @@ +### Ako získať Hetzner API Token +1. Navštívte nasledujúci [link](https://console.hetzner.cloud/) a prihláste sa + do novo vytvoreného účtu. +2. Vstúpte do predtým vytvoreného projektu. Ak ste ho ešte nevytvorili, + potom pokračujte ďalej. +3. Kurzorom myši prejdite na bočný panel. Panel by sa mal rozbaliť a zobraziť nám + ponuku. Zaujíma nás posledné z nich - **Security** (ikona + klávesu). +4. Ďalej v hornej časti rozhrania vidíme približne + nasledovné: **SSH Keys, API Tokens, Certificates, Members.**. + potrebujete **API Tokens**. Kliknite naň. +5. V pravej časti rozhrania by sa malo nachádzať **Generate API + token** tlačidlo. Ak používate mobilnú verziu og webovej stránky, v časti + pravom dolnom rohu uvidíte **red cross**. Stlačte toto tlačidlo. +6. V poli **Description** uveďte názov nášho tokenu (môže to byť akýkoľvek názov + , ktorý sa vám páči. Nemá to vplyv na podstatu. +7. Pod poľom **Description** vidíme možnosť výberu + **permissions**. Vyberte možnosť **Read & Write**. +8. Kliknite na tlačidlo **Generate API Token.**. +9. Potom sa zobrazí náš kľúč. Uložte ho na spoľahlivé miesto, + alebo v správcovi hesiel, čo je lepšie. + +![Nastavenie tokenu Hetzner](resource:assets/images/gifs/Hetzner.gif) diff --git a/assets/markdown/how_hetzner-th.md b/assets/markdown/how_hetzner-th.md new file mode 100644 index 00000000..6f859c18 --- /dev/null +++ b/assets/markdown/how_hetzner-th.md @@ -0,0 +1,23 @@ +### How to get Hetzner API Token +1. Visit the following [link](https://console.hetzner.cloud/) and sign + into newly created account. +2. Enter into previously created project. If you haven't created one, + then please proceed. +3. Hover side panel with mouse cursor. Panel should expand and show us + a menu. We're interested in the last one — **Security** (icon of a + key). +4. Next, in the upper part of an interface, we can see approximately + the following: **SSH Keys, API Tokens, Certificates, Members.** You + need **API Tokens**. Click on it. +5. In the right part of the interface, there should be **Generate API + token** button. If you're using mobile version og a webpage, in the + lower right corner you'll see **red cross**. Push that button. +6. In the **Description** field, give our token a name (this can be any + name that you like. It doesn't influence the essence. +7. Under the **Description** field we can see a possibility to choose + **permissions**. Pick **Read & Write**. +8. Click **Generate API Token.** +9. After that, our key will be shown. Store it in the reliable place, + or in the password manager, which is better. + +![Hetzner token setup](resource:assets/images/gifs/Hetzner.gif) diff --git a/assets/markdown/how_hetzner-uk.md b/assets/markdown/how_hetzner-uk.md new file mode 100644 index 00000000..82f15ce9 --- /dev/null +++ b/assets/markdown/how_hetzner-uk.md @@ -0,0 +1,23 @@ +## як отримати Hetzner API токен +1. Завітайте на наступний [посилання](https://console.hetzner.cloud/) і підпишіться +на новостворений рахунок. +2. Введіть раніше створений проект. Якщо ви не створили його, +тоді, будь ласка, продовжуйте. +3. Наведіть вказівник на бічну панель з курсором миші. Панель повинна розгорнути і показати нам +меню. Нас цікавить останній — **БЕЗПЕКА** (значок а +ключ). +4. Далі, у верхній частині інтерфейсу, ми можемо побачити приблизно +Нижче наведено **ключі SSH, ключі API, сертифікати, члени.** Ви +Потрібно **API-Токени**. Клікніть на нього. +5. У правій частині інтерфейсу має бути **Generate API + токен**. Якщо ви користуєтеся мобільною версією, або веб-сторінкою, в +у нижньому правому куті ви побачите **червоний хрест**. Натисніть цю кнопку. +6. У полі **Опис** надайте нашому жетону назву (це може бути будь-яка +ім'я, яке тобі подобається. Це не впливає на суть. +7. У полі **Опис** ми можемо побачити можливість вибору +**дозволи**. Виберіть **Читати і писати**. +8. Натисніть **Створити ключ API.** +9. Після цього буде показано наш ключ. Зберігайте його в надійному місці, +або в менеджері паролів, що краще. + +![Hetzner токен налаштування](resource:assets/images/gifs/Hetzner.gif) diff --git a/assets/translations/az.json b/assets/translations/az.json new file mode 100644 index 00000000..763d6b61 --- /dev/null +++ b/assets/translations/az.json @@ -0,0 +1,501 @@ +{ + "test": "az-test", + "locale": "az", + "basis": { + "providers": "Provayderlər", + "providers_title": "Sizin məlumat mərkəziniz", + "select": "Seçin", + "services": "Xidmətlər", + "users": "İstifadəçilər", + "more": "Çox", + "next": "Sonrakı", + "got_it": "Anladım", + "settings": "Parametrlər", + "password": "Parol", + "create": "Yeni əlavə et", + "confirmation": "Təsdiq", + "cancel": "Ləğv edin", + "delete": "Silin", + "close": "Bağla", + "connect": "Qoşulmaq", + "domain": "Domen", + "saving": "Qənaət…", + "username": "İstifadəçilər adı", + "later": "Keçidin, sonra tənzimləyin", + "connect_to_existing": "Mövcud serverə qoşulun", + "reset": "Sıfırlayın", + "details": "Təfərrüatlar", + "no_data": "İnformasiya yoxdur", + "wait": "Gözləyin", + "remove": "Silin", + "done": "Hazır", + "continue": "Davam et", + "alert": "Bildiriş", + "services_title": "Şəxsi, şəxsi və müstəqil xidmətləriniz.", + "loading": "Yüklənir…", + "apply": "Müraciət edin", + "app_name": "SelfPrivacy" + }, + "more_page": { + "configuration_wizard": "Quraşdırma Sihirbazı", + "onboarding": "Salamlar", + "create_ssh_key": "SSH admin açarları", + "console": "Konsol", + "application_settings": "Tətbiq parametrləri", + "about_project": "SelfPrivacy haqqında", + "about_application": "Proqram haqqında" + }, + "console_page": { + "title": "Konsol", + "waiting": "Başlama gözlənilir…", + "copy": "Kopyalayın" + }, + "about_application_page": { + "application_version_text": "Tətbiq versiyası {}", + "api_version_text": "Server API versiyası {}", + "privacy_policy": "Gizlilik Siyasəti", + "title": "Tətbiq haqqında" + }, + "application_settings": { + "title": "Tətbiq parametrləri", + "dark_theme_title": "Qaranlıq mövzu", + "reset_config_title": "Tətbiq Sıfırlayın", + "reset_config_description": "API və Super İstifadəçi Açarlarını sıfırlayın.", + "delete_server_title": "Serveri silin", + "dark_theme_description": "Rəng mövzusunu dəyişdirin", + "delete_server_description": "Əməliyyat serveri siləcək. Bundan sonra o, əlçatmaz olacaq.", + "system_dark_theme_title": "Defolt sistem mövzusu", + "system_dark_theme_description": "Sistem parametrlərindən asılı olaraq açıq və ya qaranlıq mövzudan istifadə edin", + "dangerous_settings": "Təhlükəli Parametrlər" + }, + "ssh": { + "title": "SSH açarları", + "create": "SSH açarı yaradın", + "delete": "SSH açarını silin", + "delete_confirm_question": "Növbəti SSH açarı silmək istədiyinizə əminsiniz?", + "subtitle_with_keys": "{} açarlar", + "subtitle_without_keys": "Açar yoxdur", + "no_key_name": "Adsız açar", + "root_title": "Bunlar super istifadəçi açarlarıdır", + "input_label": "İctimai ED25519 və ya RSA açarı", + "root_subtitle": "Burada göstərilən açarların sahibləri server məlumatlarına və parametrlərinə tam giriş əldə edirlər. Yalnız açarlarınızı əlavə edin." + }, + "onboarding": { + "page1_title": "Rəqəmsal müstəqillik hər kəs üçün əlçatandır", + "page1_text": "Mail, VPN, Messenger, sosial şəbəkə və daha çox şəxsi serverinizdə, tam nəzarətiniz altında.", + "page2_title": "SelfPrivacy bulud deyil, şəxsi məlumat mərkəzinizdir", + "page2_server_provider_title": "Server provayderi", + "page2_dns_provider_title": "DNS provayderi", + "page2_dns_provider_text": "İnternetdə olmaq üçün sizə domen lazımdır. Domenin serverinizə işarə etməsi üçün sizə etibarlı DNS server lazımdır. Biz sizə dəstəklənən DNS serverlərindən birini seçməyi və bütün qeydləri avtomatik konfiqurasiya etməyi təklif edəcəyik. Onları əl ilə qurmaq istəyirsiniz? Bu da mümkündür.", + "page2_backup_provider_title": "Yedək provayderi", + "page2_text": "SelfPrivacy yalnız seçdiyiniz xidmət təminatçıları ilə işləyir. Hesablarınız yoxdursa, onları yaratmağınıza kömək edəcəyik.", + "page2_server_provider_text": "Server provayderi öz məlumat mərkəzində serverinizə xidmət göstərəcək. SelfPrivacy avtomatik olaraq ona qoşulacaq və sizin üçün server quracaq.", + "page2_backup_provider_text": "Bəs serverə bir şey olarsa? Haker hücumu, xidmətdən imtina və ya sadəcə məlumatların təsadüfən silinməsi? Məlumatlarınız ehtiyat yaddaş provayderinizlə başqa yerdə təhlükəsiz olacaq. Onların hamısı təhlükəsiz şəkildə şifrələnib və siz serverinizi bərpa edə bilərsiniz." + }, + "about_us_page": { + "title": "SelfPrivacy haqqında" + }, + "resource_chart": { + "month": "Ay", + "day": "Gün", + "hour": "Saat", + "cpu_title": "CPU İstifadəsi", + "network_title": "Şəbəkə istifadəsi", + "in": "Alındı", + "out": "Göndərildi" + }, + "server": { + "card_title": "Server", + "general_information": "Ümumi məlumat", + "resource_usage": "Resurs istehlakı", + "allow_autoupgrade": "Avtomatik yeniləmələrə icazə verin", + "allow_autoupgrade_hint": "Serverdə yeniləmələrin avtomatik quraşdırılmasına icazə verin", + "reboot_after_upgrade": "Yeniləmələrdən sonra yenidən başladın", + "description": "Bu, bütün xidmətlərinizin işlədiyi virtual kompüterdir", + "reboot_after_upgrade_hint": "Yeniləmələri tətbiq etdikdən sonra serveri avtomatik yenidən başladın", + "server_timezone": "Server saat qurşağı", + "select_timezone": "Saat qurşağınızı seçin", + "timezone_search_bar": "Saat qurşağı adı və ya vaxt ofset dəyəri", + "server_id": "Server İD", + "status": "Status", + "cpu": "CPU", + "ram": "RAM yaddaşı", + "disk": "Disk", + "monthly_cost": "Aylıq xərc", + "location": "Yerləşdirmə", + "core_count": { + "one": "{} nüvəs", + "two": "{} nüvələr", + "few": "{} nüvələr", + "other": "{} nüvələr", + "many": "{} nüvələr" + } + }, + "record": { + "root": "Kök domen", + "api": "SelfPrivacy APİ", + "cloud": "Fayl buludu", + "git": "Git Server", + "meet": "Video konfrans", + "social": "Sosial Mediya", + "password": "Parol meneceri", + "vpn": "VPN", + "mx": "MX rekordu", + "spf": "SPF rekordu", + "dkim": "DKIM açarı", + "dmarc": "DMARC rekordu" + }, + "domain": { + "card_title": "Domen", + "screen_title": "Domain və DNS", + "ok": "Qeydlər qaydasındadır", + "error": "Problemlər tapıldı", + "refreshing": "Data yenilənməsi…", + "uninitialized": "Data hələ alınmayıb", + "services_title": "Servislər", + "email_title": "E-poçt", + "email_subtitle": "Təhlükəsiz e-poçt mübadiləsi üçün tələb olunan qeydlər.", + "update_list": "Siyahını yeniləyin", + "error_subtitle": "Düzəltmək üçün bura klikləyin", + "services_subtitle": "Xidmətlərin fəaliyyəti üçün “A” tipli qeydlər lazımdır." + }, + "backup": { + "card_title": "Ehtiyat nüsxəsi", + "reupload_key": "Açar yeniləməsini məcbur edin", + "reuploaded_key": "Server açarı yeniləndi", + "initialize": "Qurunmaq", + "waiting_for_rebuild": "Bir neçə dəqiqədən sonra ilk nüsxəni yarada biləcəksiniz.", + "restore": "Kopyadan bərpa edin", + "no_backups": "Yedəkləmə hələ yoxdur", + "create_new": "Yeni nüsxə yaradın", + "creating": "Surəti çıxarmaq: {}%", + "restoring": "Kopyadan bərpa edilir", + "error_pending": "Server xəta verdi: aşağıda yoxlayın", + "restore_alert": "Siz {} tərəfindən yaradılmış nüsxədən bərpa etmək üzrəsiniz. Bütün cari məlumatlar itiriləcək. Sən əminsən?", + "refresh": "Statusu yenilə", + "refetch_backups": "Kopya siyahısını yeniləyin", + "refetching_list": "Siyahı bir neçə dəqiqəyə yenilənəcək", + "description": "İstənilən vəziyyətdə sizə kömək edəcək: haker hücumu, serverin silinməsi və s." + }, + "storage": { + "card_title": "Server yaddaşı", + "status_ok": "Heç bir disk problemi tapılmadı", + "status_error": "Disk sahəsi tükənir", + "disk_usage": "{} istifadə olunur", + "disk_total": "{} ümumi · {}", + "gb": "{} GB", + "mb": "{} MB", + "kb": "{} KB", + "bytes": "Bayt", + "extend_volume_button": "Yaddaşı genişləndirin", + "extending_volume_title": "Yaddaş genişləndirilməsi", + "extending_volume_price_info": "Qiymətə ƏDV daxildir və Hetzner tərəfindən təqdim edilən qiymət məlumatlarına əsaslanır. Proses zamanı server yenidən işə salınacaq.", + "extending_volume_error": "Yaddaş genişləndirilməsinə başlamaq alınmadı.", + "size": "Ölçüsü", + "extending_volume_description": "Yaddaş ölçüsünün dəyişdirilməsi, serverin özünü genişləndirmədən serverinizdə daha çox məlumat saxlamağa imkan verəcəkdir. Həcmi yalnız artırmaq olar, onu azaltmaq olmaz.", + "data_migration_title": "Məlumat miqrasiya", + "data_migration_notice": "Məlumatların köçürülməsi zamanı bütün xidmətlər deaktiv ediləcək.", + "start_migration_button": "Miqrasiyaya başlayın", + "migration_process": "Köçür…", + "migration_done": "Tamamlayın" + }, + "service_page": { + "move": "Başqa sürücüyə keçin", + "open_in_browser": "Brauzerdə açın", + "restart": "Xidməti yenidən başladın", + "disable": "Xidməti söndürün", + "enable": "Xidməti aktivləşdirin", + "uses": "{volume} üzərində {usage} istifadə edir", + "status": { + "active": "Açılır və qaçır", + "inactive": "Dayandı", + "failed": "Başlamaq alınmadı", + "off": "Əlil", + "activating": "Yandırılır", + "deactivating": "Söndürür", + "reloading": "Yenidən başladıldı" + } + }, + "mail": { + "login_info": "İstifadəçilər sekmesinden istifadəçi adı və paroldan istifadə edin. IMAP portu: 143, STARTTLS. SMTP portu: 587, STARTTLS.", + "title": "E-poçt", + "subtitle": "Ailə və ya şirkət üçün e-poçt." + }, + "video": { + "subtitle": "Jitsi meet Zoom və Google meet-in əla analoqudur və bu, rahatlıqla yanaşı, yüksək keyfiyyətli video konfransların təhlükəsizliyini təmin edir.", + "title": "video konfrans", + "login_info": "Hesab tələb olunmur." + }, + "cloud": { + "login_info": "Administrator girişi: admin, parol əsas istifadəçi ilə eynidir. NextCloud admin interfeysində yeni istifadəçilər yaradın.", + "title": "Fayl bulud", + "subtitle": "Bulud xidmətlərinin məlumatlarınıza baxmasının qarşısını alın. NextCloud istifadə edin - bütün məlumatlarınız üçün təhlükəsiz ev." + }, + "git": { + "login_info": "Saytda hesab yaradılmalıdır. İlk qeydiyyatdan keçmiş istifadəçi administrator olur.", + "title": "Git Server", + "subtitle": "Microsoft-a deyil, sizə məxsus şəxsi Github alternativi." + }, + "users": { + "not_ready": "Birinci istifadəçini əlavə etmək üçün Provayderlər bölməsində server, domen və DNS-ni birləşdirin", + "could_not_fetch_description": "İnternet bağlantısını yoxlayın və yenidən cəhd edin", + "username_rule": "Adda yalnız kiçik Latın hərfləri, rəqəmlər, alt xətt ola bilər, rəqəmlərlə başlaya bilməz", + "add_new_user": "İlk istifadəçini əlavə edin", + "new_user": "Yeni istifadəçi", + "delete_user": "İstifadəçini silin", + "nobody_here": "İstifadəçilər burada göstəriləcək", + "login": "Daxil ol", + "new_user_info_note": "Yeni istifadəçi avtomatik olaraq bütün xidmətlərə çıxış əldə edəcək", + "delete_confirm_question": "Hesabınızı silmək istədiyinizə əminsiniz?", + "reset_password": "Parolu sıfırlayın", + "account": "Hesab", + "send_registration_data": "Detalları paylaşın", + "could_not_fetch_users": "İstifadəçiləri əldə etmək alınmadı", + "refresh_users": "İstifadəçi siyahısını yeniləyin", + "could_not_create_user": "İstifadəçi yaratmaq alınmadı", + "could_not_delete_user": "İstifadəçini silmək alınmadı", + "could_not_add_ssh_key": "SSH açarı yaratmaq alınmadı", + "email_login": "E-poçt Avtorizasiyası", + "no_ssh_notice": "Bu istifadəçi üçün yalnız SSH və E-poçt hesabları yaradılmışdır. Bütün xidmətlər üçün vahid icazə hələ tətbiq edilməyib.", + "details_title": "İstifadəçi məlumatları" + }, + "initializing": { + "select_provider": "Aşağıda SelfPrivacy tərəfindən dəstəklənən provayderlərin seçimi var", + "select_provider_countries_text_do": "ABŞ, Hollandiya, Sinqapur, Böyük Britaniya, Almaniya, Kanada, Hindistan, Avstraliya", + "connect_to_server_provider_text": "Token API-dən istifadə edərək SelfPrivacy proqramı sizin adınıza server sifariş edə və konfiqurasiya edə biləcək", + "no_locations_found": "Heç bir məkan tapılmadı, lütfən, hesabınızın mövcud olduğundan əmin olun", + "choose_server_type_text": "Server resursları hansı xidmətlərin başlaya biləcəyini müəyyənləşdirir. İstənilən vaxt serveri genişləndirə bilərsiniz", + "no_server_types_found": "Heç bir mövcud server növləri tapılmadı! Lütfən, server provayderinə girişinizin olduğundan əmin olun...", + "server_rebooted": "Server yenidən başladıldı, son yoxlamanı gözləyirik…", + "one_more_restart": "İndi təhlükəsizlik sertifikatlarını aktivləşdirmək üçün əlavə reboot olacaq.", + "connect_to_server": "Serverdən başlayaq.", + "select_provider_notice": "“Kiçik server” dedikdə biz iki prosessor xətti və iki giqabayt operativ yaddaşa malik serveri nəzərdə tuturuq.", + "select_provider_countries_title": "Mövcud ölkələr", + "select_provider_countries_text_hetzner": "Almaniya, Finlandiya, ABŞ", + "select_provider_price_title": "Orta qiymət", + "select_provider_price_text_hetzner": "Kiçik bir server və 50 GB disk sahəsi üçün ayda € 8", + "select_provider_price_text_do": "Kiçik server və 50 GB disk sahəsi üçün ayda $17", + "select_provider_payment_title": "Ödəmə metodları", + "select_provider_payment_text_hetzner": "Bank kartları, SWIFT, SEPA, PayPal", + "select_provider_payment_text_do": "Bank kartları, Google Pay, PayPal", + "select_provider_email_notice": "Yeni müştərilər üçün e-poçt hostinqi mövcud deyil. İlk ödənişdən sonra onu açmaq mümkün olacaq.", + "select_provider_site_button": "Saytı ziyarət edin", + "connect_to_server_provider": "Giriş ", + "how": "API Tokenini necə əldə etmək olar", + "provider_bad_key_error": "Provayder API açarı yanlışdır", + "could_not_connect": "Provayderə qoşulmaq alınmadı.", + "choose_location_type": "Harada server sifariş etmək olar?", + "choose_location_type_text": "Mövcud konfiqurasiyalar, qiymətlər və serverə qoşulma sürətiniz yer seçimindən asılı olacaq.", + "locations_not_found": "Vay!", + "locations_not_found_text": "Bu məkanda icarəyə verilə bilən server yoxdur", + "back_to_locations": "Başqasını seçək", + "choose_server_type": "Hansı server növünü seçmək lazımdır?", + "choose_server_type_notice": "Diqqət etməli olduğunuz əsas şey prosessor iplərinin sayı və RAM miqdarıdır. Xidmət məlumatları ayrıca ödənilən və asanlıqla genişləndirilə bilən ayrıca diskdə yerləşdiriləcək.", + "choose_server_type_ram": "RAM üçün {} GB", + "choose_server_type_storage": "{} GB sistem yaddaşı", + "choose_server_type_payment_per_month": "{} aylıq", + "backblaze_bad_key_error": "Backblaze vault haqqında məlumat yanlışdır", + "select_dns": "İndi DNS provayderini seçək", + "manage_domain_dns": "Domeninizin DNS-ni idarə etmək üçün", + "use_this_domain": "Biz bu domendən istifadə edirik?", + "use_this_domain_text": "Göstərdiyiniz token bu domen üzərində nəzarəti təmin edir", + "connect_backblaze_storage": "Backblaze bulud yaddaşınızı birləşdirin", + "no_connected_domains": "Hazırda heç bir bağlı domen yoxdur", + "loading_domain_list": "Domenlərin siyahısı yüklənir", + "found_more_domains": "Birdən çox domen tapıldı, təhlükəsizliyiniz üçün lazımsız domenləri silin", + "save_domain": "Domeni yadda saxla", + "final": "Son addım", + "create_server": "Server yaradın", + "what": "Bunun mənası nədi?", + "server_started": "Server işləyir. İndi yoxlanılacaq və yenidən işə salınacaq…", + "server_created": "Server yaradıldı. DNS ünvanları yoxlanılır və server işə salınır…", + "until_the_next_check": "Növbəti yoxlamaya qədər: ", + "check": "Yoxlanış", + "create_master_account": "Əsas hesab yaradın", + "enter_username_and_password": "İstifadəçi adı və mürəkkəb parol daxil edin", + "finish": "Hər şey işə salınıb", + "checks": "Yoxlamalar aparıldı:. \n{} / {}", + "steps": { + "hosting": "Hostinq", + "server_type": "Server növü", + "dns_provider": "DNS provayderi", + "backups_provider": "Yedəkləmələr", + "domain": "Domen", + "master_account": "Master hesab", + "server": "Server", + "dns_setup": "DNS qurulması", + "nixos_installation": "NixOS quraşdırılması", + "server_reboot": "Serverin yenidən yüklənməsi", + "final_checks": "Son yoxlamalar" + } + }, + "recovering": { + "domain_recovery_description": "Serverə daxil olmaq istədiyiniz domeni daxil edin:", + "method_device_description": "Proqramı başqa cihazda açın və cihaz idarəetmə ekranını açın. Avtorizasiya nişanı əldə etmək üçün \"Cihaz əlavə et\" üzərinə klikləyin.", + "modal_confirmation_title": "Bu həqiqətən sizin serverinizdir?", + "modal_confirmation_description": "Yanlış serverə qoşulmaq dağıdıcı ola bilər.", + "modal_confirmation_ip_invalid": "IP DNS qeydində göstərilənə uyğun gəlmir", + "generic_error": "Əməliyyat uğursuz oldu, yenidən cəhd edin.", + "recovery_main_header": "Mövcud serverə qoşulun", + "domain_recover_placeholder": "Sizin domeniniz", + "domain_recover_error": "Bu domenlə server tapmaq mümkün olmadı", + "method_select_description": "Giriş metodunu seçin:", + "method_select_other_device": "Başqa cihazda girişim var", + "method_select_recovery_key": "Məndə bərpa açarı var", + "method_select_nothing": "Məndə bunların heç biri yoxdur", + "method_device_button": "Token aldım", + "method_device_input_description": "İcazə nişanınızı daxil edin", + "method_device_input_placeholder": "Token", + "method_recovery_input_description": "Bərpa nişanınızı daxil edin", + "fallback_select_description": "Bundan sənə nə var? İlk uyğun gələni seçin:", + "fallback_select_token_copy": "Tətbiqin başqa versiyasından icazə nişanının surəti.", + "fallback_select_root_ssh": "SSH vasitəsilə serverə Root girişi.", + "fallback_select_provider_console": "Hostinq konsoluna giriş.", + "authorization_failed": "Bu açarla daxil olmaq alınmadı", + "fallback_select_provider_console_hint": "Məsələn: Hetzner.", + "provider_connected": "{} qoşulur", + "provider_connected_description": "Əlaqə quruldu. {} girişi ilə nişanınızı daxil edin:", + "provider_connected_placeholder": "{} nişanı", + "confirm_server": "Serveri təsdiqləyin", + "confirm_server_description": "Server tapdım! Onun olduğunu təsdiq edin:", + "confirm_server_accept": "Bəli, odur", + "confirm_server_decline": "Başqa server seçin", + "choose_server": "Server seçin", + "choose_server_description": "Hansı serverlə əlaqə saxladığınızı müəyyən etmək mümkün olmadı.", + "no_servers": "Hesabınızda mövcud server yoxdur.", + "domain_not_available_on_token": "Daxil edilmiş işarənin tələb olunan domenə girişi yoxdur.", + "modal_confirmation_dns_valid": "Əks DNS düzgündür", + "modal_confirmation_dns_invalid": "Əks DNS fərqli domenə işarə edir", + "modal_confirmation_ip_valid": "IP DNS qeydində göstərilənə uyğun gəlir" + }, + "devices": { + "main_screen": { + "description": "Bu cihazların SelfPrivacy proqramı vasitəsilə server idarəçiliyinə tam çıxışı var.", + "header": "Cihazlar", + "this_device": "Bu cihaz", + "other_devices": "Digər cihazlar", + "authorize_new_device": "Yeni cihaza icazə verin", + "access_granted_on": "Giriş verilmişdir {}", + "tip": "Girişi ləğv etmək üçün cihaza klikləyin." + }, + "revoke_device_alert": { + "description": "{} cihazı artıq serveri idarə edə bilməyəcək.", + "header": "Giriş ləğv edilsin?", + "yes": "Geri çəkilmək", + "no": "Ləğv et" + }, + "add_new_device_screen": { + "header": "Yeni Cihaz Avtorizasiyası", + "description": "Bu açarı yeni cihaza daxil edin:", + "please_wait": "Zəhmət olmasa, gözləyin", + "tip": "Açar 10 dəqiqə etibarlıdır.", + "expired": "Açarın müddəti bitib.", + "get_new_key": "Yeni açar alın" + } + }, + "recovery_key": { + "key_main_description": "Səlahiyyətli cihazlar mövcud olmadıqda SelfPrivacy avtorizasiyası üçün tələb olunur.", + "key_receiving_description": "Bu açarı təhlükəsiz yerə yazın. O, serverinizə tam giriş imkanı verir:", + "key_connection_error": "Serverə qoşulmaq alınmadı.", + "key_synchronizing": "Sinxronizasiya…", + "key_main_header": "Bərpa açarı", + "key_amount_toggle": "İstifadəsini məhdudlaşdırın", + "key_amount_field_title": "Maks. istifadə sayı", + "key_duedate_toggle": "İstifadə müddətini məhdudlaşdırın", + "key_duedate_field_title": "Son istifadə tarixi", + "key_receive_button": "Açar alın", + "key_valid": "Açarınız etibarlıdır", + "key_invalid": "Açarınız artıq etibarlı deyil", + "key_valid_until": "{} tarixinə qədər etibarlıdır", + "key_valid_for": "Daha {} dəfə istifadə edə bilərsiniz", + "key_creation_date": "Yaradılıb {}", + "key_replace_button": "Yeni açar yaradın", + "key_receiving_info": "Bu açar artıq göstərilməyəcək, lakin siz onu yenisi ilə əvəz edə bilərsiniz.", + "key_receiving_done": "Hazır!", + "generation_error": "Açar yaratmaq alınmadı. {}" + }, + "modals": { + "unexpected_error": "Provayder tərəfindən gözlənilməz xəta.", + "dns_removal_error": "DNS qeydlərini silmək mümkün deyil.", + "server_deletion_error": "Server silinə bilməz.", + "server_validators_error": "Server siyahısını əldə etmək alınmadı.", + "already_exists": "Belə bir server artıq mövcuddur.", + "destroy_server": "Serveri məhv edib yenisini yaratmaq?", + "try_again": "Bir daha cəhd etmək üçün?", + "are_you_sure": "Sən əminsən?", + "purge_all_keys": "Bütün avtorizasiya açarları silinsin?", + "purge_all_keys_confirm": "Bəli, bütün düymələri silin", + "delete_server_volume": "Server və yaddaş silinsin?", + "reboot": "Yenidən yükləyin", + "you_cant_use_this_api": "Siz oxşar TLD ilə domen üçün bu API istifadə edə bilməzsiniz.", + "yes": "Bəli", + "no": "Yox" + }, + "jobs": { + "create_ssh_key": "{} üçün SSH açarı yaradın", + "title": "Tapşırıqlar", + "start": "İcra etməyə başlayın", + "empty": "Tapşırıq yoxdur", + "create_user": "İstifadəçi yaradın", + "delete_user": "İstifadəçini silin", + "service_turn_off": "Dayan", + "service_turn_on": "Başlayın", + "job_added": "Tapşırıq əlavə edildi", + "run_jobs": "Tapşırıqları yerinə yetirin", + "reboot_success": "Server yenidən işə salınır", + "reboot_failed": "Serveri yenidən başlatmaq alınmadı, qeydləri yoxlayın.", + "config_pull_failed": "Server konfiqurasiyasını yeniləmək alınmadı. Proqram təminatı yeniləməsi başladı.", + "upgrade_success": "Server yeniləməsi başladı", + "upgrade_failed": "Server yeniləməsi uğursuz oldu", + "upgrade_server": "Serveri yeniləyin", + "reboot_server": "Serveri yenidən başladın", + "delete_ssh_key": "{} üçün SSH açarını silin", + "server_jobs": "Serverdəki tapşırıqlar", + "reset_user_password": "İstifadəçi parolunu sıfırlayın", + "generic_error": "Serverə qoşulmaq alınmadı!" + }, + "validations": { + "already_exist": "Artıq mövcuddur", + "length_not_equal": "[] sətirinin uzunluğu {}-ə bərabər olmalıdır", + "required": "Məcburi sahə", + "invalid_format": "Səhv format", + "invalid_format_password": "İçərisində boş simvol olmamalıdır", + "invalid_format_ssh": "SSH açar formatına əməl edilməlidir", + "root_name": "İstifadəçi adı 'root' ola bilməz", + "length_longer": "[] sətirinin uzunluğu {}-dən kiçik və ya ona bərabər olmalıdır" + }, + "not_ready_card": { + "in_menu": "Server hələ konfiqurasiya edilməyib, əlaqə sihirbazından istifadə edin." + }, + "password_manager": { + "title": "Parol Meneceri", + "subtitle": "Bu, təhlükəsizliyinizin əsasıdır. Bitwarden sizə cihazlar arasında parollar yaratmağa, saxlamağa, köçürməyə və onları formalara çevirməyə kömək edəcək.", + "login_info": "Saytda hesab yaradılmalıdır." + }, + "social_network": { + "title": "sosial Mediya", + "subtitle": "İnanmaq çətindir, lakin öz qaydaları və auditoriyası olan öz sosial şəbəkənizi yaratmaq mümkün oldu.", + "login_info": "Аккаунт нужно создать на сайте." + }, + "vpn": { + "title": "VPN server", + "subtitle": "Bağlı VPN serveri" + }, + "timer": { + "sec": "{} san" + }, + "support": { + "title": "SelfPrivacy Dəstək" + }, + "developer_settings": { + "title": "Tərtibatçı Seçimləri\naçar", + "subtitle": "Bu parametrlər yalnız sazlama üçündür. Nə etdiyinizi bilmirsinizsə, onları dəyişməyin.", + "server_setup": "Server Quraşdırma Sihirbazı", + "use_staging_acme": "Test ACME serverindən istifadə", + "use_staging_acme_description": "Bu dəyəri dəyişdirmək üçün tətbiqi yenidən qurun.", + "routing": "Roulinq tətbiqləri", + "reset_onboarding": "Xoş gəldin ziyarət bayrağını sıfırlayın", + "reset_onboarding_description": "Enerji ekranını yenidən göstərmək üçün güc açarının sıfırlanması", + "cubit_statuses": "Yükləmə kubitlərinin cari vəziyyəti" + } +} diff --git a/assets/translations/be.json b/assets/translations/be.json new file mode 100644 index 00000000..d634d64b --- /dev/null +++ b/assets/translations/be.json @@ -0,0 +1,511 @@ +{ + "initializing": { + "how": "Як атрымаць API токен", + "provider_bad_key_error": "Няверны API ключ правайдэра", + "could_not_connect": "Не ўдалося злучыцца з правайдэрам.", + "choose_location_type": "Дзе замовіць сервер?", + "choose_location_type_text": "Ад выбару лакацыі будуць залежаць даступныя канфігурацыі, кошты і хуткасць вашага злучэння з серверам.", + "locations_not_found": "Ой!", + "back_to_locations": "Выбраць іншы", + "no_locations_found": "Не знойдзена лакацый, пераканайцеся, што ваш рахунак даступны", + "choose_server_type": "Які тып сервера выбраць?", + "choose_server_type_ram": "{} GB RAM", + "choose_server_type_payment_per_month": "{} у месяц", + "no_server_types_found": "Не знойдзена даступных тыпаў сервера! Калі ласка, пераканайцеся, што ў вас ёсць доступ да правайдэра сервера...", + "backblaze_bad_key_error": "Інфармацыя аб Backblaze сховішча няслушная", + "select_dns": "Цяпер давайце выберам DNS-правайдэра", + "what": "Што гэта значыць?", + "server_rebooted": "Сервер перазагружаны, чакаем апошнюю праверку…", + "server_created": "Сервер створаны. Ідзе праверка DNS адрасоў і запуск сервера…", + "until_the_next_check": "Да наступнай праверкі: ", + "check": "Праверка", + "create_master_account": "Стварыце галоўны ўліковы запіс", + "enter_username_and_password": "Увядзіце імя карыстальніка і складаны пароль", + "finish": "Усё ініцыялізавана", + "checks": "Праверак выканана: \n{} / {}", + "choose_server_type_storage": "{} GB сістэмнага сховішча", + "locations_not_found_text": "У гэтым месцы не аказалася даступных сервераў для арэнды", + "choose_server_type_text": "Ад рэсурсаў сервера залежыць, якія сервісы змогуць запусціцца. Пашырыць сервер можна будзе ў любы час", + "choose_server_type_notice": "Галоўнае, на што варта звярнуць увагу - колькасць патокаў працэсара і аб'ём аператыўнай памяці. Дадзеныя сэрвісаў будуць размешчаны на асобным дыску, які аплачваецца асобна і лёгка пашыраем.", + "server_started": "Сервер запушчаны. Цяпер ён будзе правераны і перагружаны…", + "one_more_restart": "Цяпер будзе дадатковая перазагрузка для актывацыі сертыфікатаў бяспекі.", + "manage_domain_dns": "Для кіравання DNS вашага дамена", + "use_this_domain": "Ужываем гэты дамен?", + "use_this_domain_text": "Указаны вамі токен дае кантроль над гэтым даменам", + "connect_backblaze_storage": "Падлучыце хмарнае сховішча Backblaze", + "no_connected_domains": "У дадзены момант падлучаных даменаў няма", + "loading_domain_list": "Загружаем спіс даменаў", + "found_more_domains": "Знойдзена больш аднаго дамена, для вашай бяспекі, просім Вам выдаліць не патрэбныя дамены", + "save_domain": "Захаваць дамен", + "final": "Апошні крок", + "create_server": "Стварыць сервер", + "connect_to_server": "Пачнём з сервера.", + "select_provider": "Ніжэй падборка правайдэраў, якіх падтрымлівае SelfPrivacy", + "select_provider_notice": "Пад 'Невялікім серверам' маецца на ўвазе сервер з двума струменямі працэсара і двума гігабайтамі аператыўнай памяці.", + "select_provider_countries_title": "Даступныя краіны", + "select_provider_countries_text_hetzner": "Германія, Фінляндыя, ЗША", + "select_provider_countries_text_do": "ЗША, Нідэрланды, Сінгапур, Вялікабрытанія, Германія, Канада, Індыя, Аўстралія", + "select_provider_price_title": "Сярэдні кошт", + "select_provider_price_text_hetzner": "€8 у месяц за невялікі сервер і 50GB месцы на дыску", + "select_provider_price_text_do": "$17 у месяц за невялікі сервер і 50GB месцы на дыску", + "select_provider_payment_title": "Метады аплаты", + "select_provider_payment_text_hetzner": "Банкаўскія карты, SWIFT, SEPA, PayPal", + "select_provider_payment_text_do": "Банкаўскія карты, Google Pay, PayPal", + "select_provider_email_notice": "Хостынг электроннай пошты недаступны для новых кліентаў. Разблакаваць можна будзе пасля першай аплаты.", + "select_provider_site_button": "Наведаць сайт", + "connect_to_server_provider": "Аўтарызавацца ў ", + "connect_to_server_provider_text": "З дапамогай API токена праграма SelfPrivacy зможа ад вашага імя замовіць і наладзіць сервер", + "steps": { + "nixos_installation": "Ўстаноўка NixOS", + "hosting": "Хостынг", + "server_type": "Тып сервера", + "dns_provider": "DNS правайдэр", + "backups_provider": "Рэзервовыя копіі", + "domain": "Дамен", + "master_account": "Майстар акаўнт", + "server": "Сервер", + "dns_setup": "Устаноўка DNS", + "server_reboot": "Перазагрузка сервера", + "final_checks": "Фінальныя праверкі" + }, + "server_provider_description": "Месца, дзе будуць знаходзіцца вашыя дадзеныя і службы SelfPrivacy:", + "dns_provider_description": "Гэта дазволіць звязаць ваш дамен з IP адрасам:", + "connect_to_dns": "Падлучыце DNS правайдэра", + "select_provider_payment_text_cloudflare": "Банкаўскія карты", + "dns_provider_bad_key_error": "API ключ няслушны", + "select_provider_price_free": "Бясплатна", + "connect_to_dns_provider_text": "З дапамогай API токена прыкладанне SelfPrivacy наладзіць DNS запісы" + }, + "recovering": { + "generic_error": "Памылка правядзення аперацыі, паспрабуйце яшчэ раз.", + "recovery_main_header": "Падключыцца да існуючага сервера", + "domain_recovery_description": "Увядзіце дамен, па якім вы жадаеце атрымаць доступ да сервера:", + "domain_recover_placeholder": "Дамен", + "domain_recover_error": "Не удалося знайсці сервер з такім даменам", + "method_select_description": "Выбярыце спосаб уваходу:", + "method_select_other_device": "У мяне ёсць доступ на іншай прыладзе", + "method_select_recovery_key": "У мяне ёсць ключ аднаўлення", + "method_select_nothing": "У мяне нічога з гэтага няма", + "method_device_description": "Адкрыйце прыкладанне на іншай прыладзе і адкрыйце экран кіравання прыладамі. Націсніце \"Дадаць прыладу\" каб атрымаць токен для аўтарызацыі.", + "method_device_button": "Я атрымаў токен", + "method_device_input_placeholder": "Токен", + "method_recovery_input_description": "Увядзіце ваш токен аднаўлення", + "method_device_input_description": "Увядзіце ваш токен аўтарызацыі", + "fallback_select_description": "Што ў вас з гэтага ёсць? Абярыце першае, што падыходзіць:", + "fallback_select_token_copy": "Копія токена аўтарызацыі з іншай версіі праграмы.", + "fallback_select_root_ssh": "Каранёвы доступ да сервера праз SSH.", + "authorization_failed": "Не ўдалося ўвайсці з гэтым ключом", + "fallback_select_provider_console_hint": "Напрыклад, Hetzner.", + "provider_connected": "Падлучэнне да вашага {}", + "provider_connected_placeholder": "Токен {}", + "confirm_server": "Пацвердзіце сервер", + "confirm_server_description": "Знайшлі сервер! Пацьвердзіце, што гэта ён:", + "confirm_server_accept": "Да, гэта ён", + "confirm_server_decline": "Выбраць іншы сервер", + "choose_server_description": "Не атрымалася вызначыць, з якім серверам вы ўсталёўваеце сувязь.", + "domain_not_available_on_token": "Уведзены токен не мае доступу да патрэбнага дамену.", + "modal_confirmation_title": "Гэта сапраўды ваш сэрвер?", + "modal_confirmation_dns_valid": "Зваротны DNS карэктны", + "modal_confirmation_dns_invalid": "Зваротны DNS паказвае на іншы дамен", + "modal_confirmation_ip_invalid": "IP не супадае з паказаным у DNS запісу", + "fallback_select_provider_console": "Доступ да кансолі хостынгу.", + "provider_connected_description": "Сувязь устаноўлена. Увядзіце свой токен з доступам да {}:", + "choose_server": "Выберыце сервер", + "no_servers": "На вашым акаўнце няма даступных сэрвэраў.", + "modal_confirmation_description": "Падлучэнне да няправільнага сервера можа прывесці да дэструктыўных наступстваў.", + "modal_confirmation_ip_valid": "IP супадае з паказаным у DNS запісу" + }, + "devices": { + "main_screen": { + "header": "Прылады", + "this_device": "Гэта прылада", + "other_devices": "Іншыя прылады", + "authorize_new_device": "Аўтарызаваць новую прыладу", + "access_granted_on": "Доступ выдадзены {}", + "tip": "Націсніце на прыладу, каб адклікаць доступ.", + "description": "Гэтыя прылады маюць поўны доступ да кіравання серверам праз прыкладанне SelfPrivacy." + }, + "add_new_device_screen": { + "description": "Увядзіце гэты ключ на новай прыладзе:", + "expired": "Тэрмін дзеяння ключа скончыўся.", + "get_new_key": "Атрымаць новы ключ", + "header": "Аўтарызацыя новай прылады", + "please_wait": "Калі ласка, пачакайце", + "tip": "Ключ сапраўдны 10 хвілін." + }, + "revoke_device_alert": { + "header": "Адклікаць доступ?", + "yes": "Адклікаць", + "no": "Адменіць", + "description": "Прылада {} больш не зможа кіраваць серверам." + } + }, + "test": "by-test", + "locale": "by", + "basis": { + "providers": "Правайдэр", + "providers_title": "Ваш Дата Цэнтр", + "select": "Выбраць", + "services_title": "Вашы асабістыя, прыватныя і незалежныя сэрвісы.", + "next": "Далей", + "delete": "Выдаліць", + "later": "Прапусціць і наладзіць потым", + "no_data": "Няма дадзеных", + "services": "Сэрвісы", + "users": "Ужыткоўнікі", + "more": "Дадаткова", + "got_it": "Зразумеў", + "settings": "Налады", + "password": "Пароль", + "create": "Cтварыць", + "confirmation": "Пацверджанне", + "cancel": "Адмяніць", + "close": "Закрыць", + "connect": "Падключыць", + "domain": "Дамен", + "saving": "Захаванне…", + "username": "Імя ўжыткоўніка", + "loading": "Загрузка…", + "connect_to_existing": "Падключыцца да існуючага сервера", + "reset": "Скінуць", + "details": "Падрабязная інфармацыя", + "wait": "Пачакайце", + "remove": "Выдаліць", + "apply": "Ужыць", + "done": "Гатова", + "continue": "Працягнуць", + "alert": "Паведамленне", + "app_name": "SelfPrivacy" + }, + "recovery_key": { + "key_connection_error": "Не ўдалося злучыцца з серверам.", + "key_synchronizing": "Сінхранізацыя…", + "key_main_header": "Ключ аднаўлення", + "key_amount_toggle": "Абмежаваць выкарыстання", + "key_amount_field_title": "Макс. кольк-ць ужыванняў", + "key_duedate_toggle": "Абмежаваць тэрмін выкарыстання", + "key_duedate_field_title": "Дата заканчэння тэрміну дзеяння", + "key_receive_button": "Атрымаць ключ", + "key_valid": "Ваш ключ сапраўдны", + "key_invalid": "Ваш ключ больш не сапраўдны", + "key_valid_until": "Дзейнічае да {}", + "key_creation_date": "Створаны {}", + "key_replace_button": "Згенераваць новы ключ", + "key_receiving_done": "Зроблена!", + "generation_error": "Не ўдалося згенераваць ключ. {}", + "key_main_description": "Неабходна для аўтарызацыі SelfPrivacy, калі аўтарызаваныя прылады недаступныя.", + "key_valid_for": "Можна выкарыстоўваць яшчэ {} раз", + "key_receiving_description": "Запішыце гэты ключ у бяспечным месцы. Ён дае поўны доступ да вашага сервера:", + "key_receiving_info": "Гэты ключ больш не будзе паказаны, але вы зможаце замяніць яго новым." + }, + "modals": { + "server_validators_error": "Не ўдалося атрымаць спіс сервераў.", + "already_exists": "Такі сервер ужо існуе.", + "destroy_server": "Знішчыць сервер і стварыць новы?", + "try_again": "Паспрабаваць яшчэ раз?", + "are_you_sure": "Вы ўпэўнены?", + "purge_all_keys": "Сцерці ўсе ключы аўтарызацыі?", + "purge_all_keys_confirm": "Так, сцерці ўсе ключы", + "reboot": "Перазагрузіць", + "you_cant_use_this_api": "Нельга выкарыстоўваць гэты API для дамен з падобным TLD.", + "yes": "Да", + "no": "Не", + "dns_removal_error": "Немагчыма выдаліць запісы DNS.", + "server_deletion_error": "Немагчыма выдаліць сервер.", + "unexpected_error": "Непрадбачаная памылка з боку правайдэра.", + "delete_server_volume": "Выдаліць сервер і сховішча?", + "volume_creation_error": "Не ўдалося стварыць том." + }, + "timer": { + "sec": "{} сек" + }, + "jobs": { + "title": "Задачы", + "start": "Пачаць выкананне", + "empty": "Задач няма", + "delete_user": "Выдаліць карыстальніка", + "service_turn_off": "Спыніць", + "service_turn_on": "Запусціць", + "job_added": "Задача дададзена", + "run_jobs": "Запусціце задачы", + "reboot_success": "Сервер перазагружаецца", + "reboot_failed": "Не ўдалося перазагрузіць сервер, праверце логі.", + "upgrade_success": "Запушчана абнаўленне сервера", + "upgrade_failed": "Абнавіць сервер не выйшла", + "upgrade_server": "Абнавіць сервер", + "create_ssh_key": "Стварыць SSH ключ для {}", + "delete_ssh_key": "Выдаліць SSH ключ для {}", + "server_jobs": "Задачы на серверы", + "generic_error": "Не ўдалося падключыцца да сервера!", + "create_user": "Стварыць карыстальніка", + "config_pull_failed": "Не ўдалося абнавіць канфігурацыю сервера. Абнаўленне ПЗ запушчана.", + "reset_user_password": "Скінуць пароль карыстальніка", + "reboot_server": "Перазагрузіць сервер" + }, + "more_page": { + "configuration_wizard": "Майстар наладкі", + "application_settings": "Налады праграмы", + "about_project": "Аб праекце SelfPrivacy", + "about_application": "Аб праграме", + "onboarding": "Прівітанне", + "create_ssh_key": "SSH ключы адміністратара", + "console": "Кансоль" + }, + "about_application_page": { + "application_version_text": "Версія праграмы {}", + "title": "Аб праграме", + "api_version_text": "Версія API сервера {}", + "privacy_policy": "Палітыка прыватнасці" + }, + "application_settings": { + "reset_config_description": "Скінуць API ключы i суперкарыстальніка.", + "delete_server_description": "Дзеянне прывядзе да выдалення сервера. Пасля гэтага ён будзе недаступны.", + "title": "Налады праграмы", + "dark_theme_title": "Цёмная тэма", + "dark_theme_description": "Змяніць каляровую тэму", + "reset_config_title": "Скід налад", + "delete_server_title": "Выдаліць сервер", + "system_dark_theme_title": "Сістэмная тэма па-змаўчанні", + "system_dark_theme_description": "Выкарыстоўвайце светлую ці цёмную тэмы ў залежнасці ад сістэмных налад", + "dangerous_settings": "Небяспечныя наладкі" + }, + "ssh": { + "root_subtitle": "Уладальнікі паказаных тут ключоў атрымліваюць поўны доступ да дадзеных і налад сервера. Дадавайце выключна свае ключы.", + "title": "SSH ключы", + "create": "Дадаць SSH ключ", + "delete": "Выдаліць SSH ключ", + "delete_confirm_question": "Вы ўпэўненыя, што хочаце выдаліць наступны ключ?", + "subtitle_with_keys": "Ключэй: {}", + "subtitle_without_keys": "Ключэй няма", + "no_key_name": "Безназоўны ключ", + "root_title": "Гэта ключы суперкарыстальніка", + "input_label": "Публічны ED25519 або RSA ключ" + }, + "onboarding": { + "page1_title": "Лічбавая незалежнасць, даступная ўсім нам", + "page1_text": "Почта, VPN, Пасыльнік, социальная сеть и многое другое на Вашем личном сервере, под Вашим полным контролем.", + "page2_server_provider_text": "Сервер-правайдэр будзе абслугоўваць ваш сервер у сваім дата-цэнтры. SelfPrivacy аўтаматычна далучыцца да яго і наладзіць вам сервер.", + "page2_dns_provider_text": "Вам патрэбен дамен, каб мець месца ў Інтэрнеце. Вам таксама патрэбен надзейны пастаўшчык DNS, каб дамен паказваў на ваш сервер. Мы прапануем вам выбраць падтрымоўванага пастаўшчыка DNS для аўтаматычнай налады сеткі.", + "page2_backup_provider_text": "Што рабіць, калі нешта здарыцца з вашым серверам? Уявіце сабе хакерскую атаку, выпадковае выдаленне даных або адмову ў абслугоўванні? Вашы дадзеныя будуць захоўвацца ў бяспецы ў пастаўшчыка рэзервовых копій. Яны будуць надзейна зашыфраваны і даступныя для аднаўлення вашага сервера ў любы час.", + "page2_title": "SelfPrivacy - гэта не воблака, а ваш асабісты дата-цэнтр", + "page2_text": "SelfPrivacy працуе толькі з сэрвіс-правайдэрамі на ваш выбар. Калі ў Вас няма ўліковых запісаў, мы дапаможам іх стварыць.", + "page2_server_provider_title": "Сервер-правайдэр", + "page2_dns_provider_title": "DNS-правайдэр", + "page2_backup_provider_title": "Бэкап-правайдэр" + }, + "record": { + "api": "SelfPrivacy API", + "social": "Сацыяльная сетка", + "password": "Менеджэр пароляў", + "root": "Каранёвы дамен", + "cloud": "Файлавае воблака", + "git": "Git сервер", + "meet": "Відэаканферэнцыі", + "vpn": "VPN", + "mx": "Запіс MX", + "dmarc": "Запіс DMARC", + "spf": "Запіс SPF", + "dkim": "DKIM ключ" + }, + "domain": { + "error": "Праблемы знойдзены", + "uninitialized": "Дадзеныя яшчэ не атрыманы", + "email_subtitle": "Запісы неабходныя для бяспечнага абмену электроннай поштай.", + "card_title": "Дамен", + "screen_title": "Дамен і DNS", + "ok": "Запісы ў парадку", + "error_subtitle": "Націсніце тут, каб выправіць іх", + "refreshing": "Абнаўленне дадзеных…", + "services_title": "Сэрвісы", + "services_subtitle": "Запісы тыпу \"A\" неабходныя для працы сэрвісаў.", + "email_title": "Электронная пошта", + "update_list": "Абнавіць спіс" + }, + "backup": { + "description": "Выручыць Вас у любой сітуацыі: хакерская атака, выдаленне сервера і г.д.", + "no_backups": "Рэзервовых копій пакуль няма", + "error_pending": "Сервер вярнуў памылку, праверце яе ніжэй", + "restore_alert": "Вы збіраецеся аднавіць з рэзервовай копіі, створанай {}. Усе бягучыя даныя будуць страчаны. Вы ўпэўнены?", + "card_title": "Рэзервовае капіраванне", + "reupload_key": "Прымусова абнавіць ключ", + "reuploaded_key": "Ключ на серверы абноўлены", + "initialize": "Ініцыялізаваць", + "waiting_for_rebuild": "Вы зможаце стварыць першую рэзервовую копію праз некалькі хвілін.", + "restore": "Аднавіць з рэзервовай копіі", + "create_new": "Стварыць новую рэзервовую копію", + "creating": "Стварэнне новай рэзервовай копіі: {}%", + "restoring": "Аднаўленне з рэзервовай копіі", + "refresh": "Абнавіць статус", + "refetch_backups": "Абнавіць спіс копій", + "refetching_list": "Праз некалькі хвілін спіс будзе абноўлены" + }, + "validations": { + "required": "Абавязковае поле", + "already_exist": "Ужо існуе", + "invalid_format_password": "Пароль не павінен змяшчаць прабелы", + "root_name": "Імя карыстальніка не можа быць 'root'", + "length_not_equal": "Даўжыня радка [], павінна быць роўна {}", + "length_longer": "Даўжыня радка [], павінна быць менш або роўна {}", + "invalid_format": "Няправільны фармат", + "invalid_format_ssh": "Павінен прытрымлівацца фармату SSH ключоў" + }, + "storage": { + "extending_volume_title": "Пашырэнне сховішча", + "extending_volume_description": "Змяненне памеру сховішча дазволіць вам трымаць больш дадзеных на вашым серверы без пашырэння самога сервера. Аб'ём можна толькі павялічыць: зменшыць aб'ём не мажліва.", + "status_ok": "Праблем на дыску не знайдзена", + "status_error": "Мала месца на дыску", + "disk_usage": "{} выкарыстана", + "disk_total": "{} усяго · {}", + "gb": "{} GB", + "mb": "{} MB", + "kb": "{} KB", + "bytes": "Байт", + "extend_volume_button": "Пашырыць сховішча", + "extending_volume_price_info": "Кошт уключае ПДВ і ацэньваецца на аснове дадзеных аб цэнах, прадстаўленых Hetzner. Сервер будзе перагружаны падчас працэсу.", + "extending_volume_error": "Не ўдалося ініцыялізаваць пашырэнне тома.", + "size": "Памер", + "data_migration_title": "Міграцыя дадзеных", + "card_title": "Сховішча", + "data_migration_notice": "Падчас міграцыі ўсе службы будуць адключаны.", + "start_migration_button": "Пачаць міграцыю", + "migration_process": "Мігруем…", + "migration_done": "Скончыць" + }, + "service_page": { + "status": { + "active": "Запушчаны і працуе", + "inactive": "Астаноўлены", + "failed": "Не ўдалося запусціць", + "off": "Адключана", + "activating": "Уключаецца", + "deactivating": "Выключаецца", + "reloading": "Перазапускаецца" + }, + "open_in_browser": "Адкрыць у браўзэры", + "restart": "Перазапусціць сэрвіс", + "disable": "Выключыць сэрвіс", + "enable": "Уключыць сэрвіс", + "move": "Перамясціць на іншы дыск", + "uses": "Выкарыстоўвае {usage} на {volume}" + }, + "console_page": { + "title": "Кансоль", + "waiting": "Чакаем ініцыялізацыі…", + "copy": "Капіяваць" + }, + "about_us_page": { + "title": "Аб праекце SelfPrivacy" + }, + "resource_chart": { + "month": "Месяц", + "day": "Дзень", + "hour": "Гадзіна", + "cpu_title": "Выкарыстанне працэсара", + "network_title": "Выкарыстанне сеткі", + "in": "Атрымлена", + "out": "Адпраўлена" + }, + "server": { + "card_title": "Сервер", + "description": "Гэта віртуальны кампутар на якім працуюць усе вашыя сэрвісы", + "general_information": "Агульная інфармацыя", + "resource_usage": "Ужыванне рэсурсаў", + "allow_autoupgrade": "Дазволіць аўта-абнаўленні", + "allow_autoupgrade_hint": "Дазволіць аўтаматычную ўстаноўку абнаўленняў на сервер", + "reboot_after_upgrade": "Перазагружаць пасля абнаўленняў", + "reboot_after_upgrade_hint": "Аўтаматычна перазагружаць сервер пасля прымянення абнаўленняў", + "server_timezone": "Часавы пояс сервера", + "select_timezone": "Абярыце гадзінны пояс", + "timezone_search_bar": "Назва гадзіннага пояса або значэнне зруху часу", + "server_id": "ID сервера", + "status": "Статус", + "cpu": "Працэсар", + "ram": "Аператыўная памяць", + "disk": "Дыск", + "monthly_cost": "Штомесячны кошт", + "location": "Размяшчэнне", + "core_count": { + "one": "{} ядро", + "two": "{} ядра", + "few": "{} ядра", + "many": "{} ядраў", + "other": "{} ядраў" + } + }, + "not_ready_card": { + "in_menu": "Сервер яшчэ не наладжаны. Калі ласка, завяршыце наладку з дапамогай майстра наладкі для далейшай працы." + }, + "mail": { + "login_info": "Выкарыстоўвайце лагін і пароль з укладкі ўжытоўнікаў. IMAP порт: 143, STARTTLS. SMTP порт: 587, STARTTLS.", + "title": "Пошта", + "subtitle": "Электронная пошта для сям'і або кампаніі." + }, + "password_manager": { + "subtitle": "Гэта база вашай бяспекі. Bitwarden дапаможа вам ствараць, захоўваць і перамяшчаць паролі паміж прыладамі, а таксама ўводзіць іх па запыце з дапамогай аўтазапаўнення.", + "title": "Менеджэр пароляў", + "login_info": "Акаўнт трэба стварыць на сайце." + }, + "cloud": { + "subtitle": "Не дазваляйце хмарным сэрвісам праглядаць вашыя дадзеныя. Выкарыстоўвайце NextCloud - надзейны дом для ўсіх Вашых дадзеных.", + "title": "Файлавае воблака", + "login_info": "Лагін адміністратара: admin, пароль такі ж як у асноўнага карыстальніка. Стварайце новых карыстальнікаў у інтэрфейсе адміністратара NextCloud." + }, + "social_network": { + "subtitle": "Складана паверыць, але стала магчымым стварыць сваю ўласную сацыяльную сетку, са сваімі правіламі і аўдыторыяй.", + "title": "Сацыяльная сетка", + "login_info": "Акаўнт трэба стварыць на сайце." + }, + "video": { + "title": "Відэаканферэнцыя", + "subtitle": "Zoom і Google Meet - гэта добра, але Jitsi Meet - вартая альтэрнатыва, якая таксама дае вам упэўненасць, што вас не слухаюць.", + "login_info": "Акаўнт не патрабуецца." + }, + "git": { + "title": "Git-сервер", + "subtitle": "Прыватная альтэрнатыва Github, якая належыць вам, а не Microsoft.", + "login_info": "Акаўнт трэба стварыць на сайце. Першы зарэгістраваны карыстач становіцца адміністратарам." + }, + "vpn": { + "title": "VPN сервер", + "subtitle": "Закрыты VPN сервер" + }, + "users": { + "add_new_user": "Дадайце першага карыстальніка", + "new_user": "Новы карыстальнік", + "delete_user": "Выдаліць карыстальніка", + "not_ready": "Падлучыце сервер, дамен і DNS у раздзеле Правайдэры каб дадаць першага карыстальніка", + "nobody_here": "Тут будуць адлюстроўвацца карыстальнікі", + "login": "Логін", + "new_user_info_note": "Новы карыстальнік аўтаматычна атрымае доступ да ўсіх сэрвісаў", + "delete_confirm_question": "Вы напраўда хочаце выдаліць уліковы запіс?", + "reset_password": "Скінуць пароль", + "account": "Уліковы запіс", + "send_registration_data": "Падзяліцца рэквізітамі", + "could_not_fetch_users": "Не ўдалося атрымаць карыстальнікаў", + "could_not_fetch_description": "Праверце інтэрнэт злучэнне і паспрабуйце зноў", + "refresh_users": "Абнавіць спіс карыстальнікаў", + "could_not_create_user": "Не ўдалося стварыць карыстальніка", + "could_not_delete_user": "Не ўдалося выдаліць карыстальніка", + "could_not_add_ssh_key": "Не ўдалося стварыць SSH ключ", + "username_rule": "Імя можа мець толькі маленькія лацінскія літары, лічбы, падкрэслення і не можа пачынацца з лічбы", + "email_login": "Аўтарызацыя па Email", + "no_ssh_notice": "Для гэтага карыстальніка створаны толькі SSH і Email акаўнты. Адзіная аўтарызацыя для ўсіх сэрвісаў яшчэ не рэалізавана.", + "details_title": "Карыстальніцкія дадзеныя" + }, + "developer_settings": { + "reset_onboarding_description": "Скід перамыкача ўключэння для паўторнага адлюстравання экрана ўключэння", + "title": "Налады распрацоўніка", + "subtitle": "Гэтыя налады прызначаны толькі для адладкі. Не мяняйце іх, калі не ведаеце, што робіце.", + "server_setup": "Майстар усталёўкі сервера", + "use_staging_acme": "Выкарыстанне тэставага ACME сервера", + "use_staging_acme_description": "Ужываецца пры наладзе новага сервера.", + "routing": "Маршрутызацыя прыкладанняў", + "reset_onboarding": "Скінуць сцяг наведвання прывітання", + "cubit_statuses": "Бягучы статут кубітаў загрузкі", + "ignore_tls": "Не правяраць сертыфікаты TLS", + "ignore_tls_description": "Прыкладанне не будзе правяраць сертыфікаты TLS пры падключэнні да сервера." + }, + "support": { + "title": "Падтрымка SelfPrivacy" + } +} diff --git a/assets/translations/cs.json b/assets/translations/cs.json new file mode 100644 index 00000000..d989c463 --- /dev/null +++ b/assets/translations/cs.json @@ -0,0 +1,511 @@ +{ + "test": "cz-test", + "locale": "cz", + "basis": { + "providers": "Poskytovatelé", + "providers_title": "Vaše Datové Centrum", + "select": "Vybrat", + "services": "Služby", + "services_title": "Vaše osobní, soukromé a nezávislé služby.", + "users": "Uživatelé", + "more": "Více", + "next": "Další", + "got_it": "Mám to", + "settings": "Nastavení", + "password": "Heslo", + "create": "Přidat nový", + "confirmation": "Potvrzení", + "cancel": "Zrušit", + "delete": "Vymazat", + "close": "Zavřít", + "connect": "Připojit", + "domain": "Doména", + "saving": "Ukládání…", + "username": "Uživatelské Jméno", + "loading": "Načítání…", + "connect_to_existing": "Připojte se k existujícímu serveru", + "reset": "Resetovat", + "details": "Podrobnosti", + "no_data": "Žádná data", + "wait": "Počkat", + "remove": "Vymazat", + "apply": "Použít", + "done": "Hotovo", + "continue": "Pokračovat", + "alert": "Upozornění", + "later": "Přeskočit na nastavení později", + "app_name": "SelfPrivacy" + }, + "about_application_page": { + "title": "O příloze", + "privacy_policy": "Zásady ochrany osobních údajů", + "api_version_text": "Verze API serveru {}", + "application_version_text": "Verze aplikace {}" + }, + "more_page": { + "about_project": "O nás", + "about_application": "O příloze", + "onboarding": "Pozdravy", + "console": "Konzole", + "application_settings": "Nastavení aplikace", + "configuration_wizard": "Průvodce nastavením", + "create_ssh_key": "Superuživatelské klíče SSH" + }, + "console_page": { + "title": "Konzole", + "waiting": "Čekání na inicializaci…", + "copy": "Kopírovat" + }, + "application_settings": { + "title": "Nastavení aplikace", + "dark_theme_title": "Tmavé téma", + "reset_config_title": "Obnovení konfigurace aplikace", + "reset_config_description": "Obnovení klíčů API a uživatele root.", + "delete_server_title": "Odstranit server", + "dark_theme_description": "Přepnutí tématu aplikace", + "delete_server_description": "Tím odstraníte svůj server. Nebude již přístupný.", + "system_dark_theme_title": "Výchozí téma systému", + "system_dark_theme_description": "Použití světlého nebo tmavého motivu v závislosti na nastavení systému", + "dangerous_settings": "Nebezpečná nastavení" + }, + "ssh": { + "title": "Klíče SSH", + "create": "Vytvoření klíče SSH", + "delete": "Odstranění klíče SSH", + "delete_confirm_question": "Jste si jisti, že chcete odstranit klíč SSH?", + "subtitle_with_keys": "{} klíče", + "subtitle_without_keys": "Žádné klíče", + "no_key_name": "Nejmenovaný klíč", + "root_title": "Jedná se o klíče superuživatele", + "input_label": "Veřejný klíč ED25519 nebo RSA", + "root_subtitle": "Majitelé těchto klíčů získají plný přístup k serveru a mohou na něm dělat cokoli. Na server můžete přidávat pouze své vlastní klíče." + }, + "onboarding": { + "page1_title": "Digitální nezávislost dostupná nám všem", + "page1_text": "Mail, VPN, Messenger, sociální sítě a mnoho dalšího na vašem soukromém serveru, který máte pod kontrolou.", + "page2_text": "Služba SelfPrivacy spolupracuje pouze s poskytovateli, které si vyberete. Pokud u nich nemáte požadované účty, pomůžeme vám je vytvořit.", + "page2_server_provider_title": "Poskytovatel serveru", + "page2_dns_provider_title": "Poskytovatel DNS", + "page2_backup_provider_title": "Poskytovatel zálohování", + "page2_backup_provider_text": "Co když se něco stane s vaším serverem? Představte si útok hackera, náhodné smazání dat nebo odepření služby? Vaše data budou v bezpečí u poskytovatele záloh. Budou bezpečně zašifrovány a kdykoli přístupné pro obnovení vašeho serveru.", + "page2_title": "SelfPrivacy není cloud, je to vaše osobní datové centrum", + "page2_server_provider_text": "Poskytovatel serveru udržuje váš server ve vlastním datovém centru. SelfPrivacy se automaticky připojí k poskytovateli a nastaví vše potřebné.", + "page2_dns_provider_text": "Abyste měli místo na internetu, potřebujete doménu. A také potřebujete spolehlivého poskytovatele DNS, aby doména směřovala na váš server. Doporučíme vám vybrat podporovaného poskytovatele DNS pro automatické nastavení sítě." + }, + "resource_chart": { + "month": "Měsíc", + "day": "Den", + "hour": "Hodina", + "cpu_title": "Využití CPU", + "network_title": "Používání sítě", + "in": "Převzato z", + "out": "Odesláno" + }, + "server": { + "card_title": "Server", + "description": "Všechny vaše služby jsou k dispozici zde", + "general_information": "Obecné informace", + "resource_usage": "Využití zdrojů", + "allow_autoupgrade": "Povolit automatickou aktualizaci", + "allow_autoupgrade_hint": "Povolení automatických aktualizací balíčků na serveru", + "reboot_after_upgrade": "Restart po aktualizaci", + "reboot_after_upgrade_hint": "Restartování bez výzvy po použití změn na serveru", + "timezone_search_bar": "Název časového pásma nebo hodnota časového posunu", + "server_id": "ID serveru", + "status": "Stav", + "cpu": "CPU", + "ram": "Paměť", + "disk": "Místní disk", + "monthly_cost": "Měsíční náklady", + "location": "Umístění", + "core_count": { + "two": "{} jádra", + "few": "{} jádra", + "many": "{} jádra", + "other": "{} jádra", + "one": "{} jádro" + }, + "server_timezone": "Časové pásmo serveru", + "select_timezone": "Časové pásmo serveru" + }, + "initializing": { + "locations_not_found": "Ups!", + "connect_to_server": "Začněme serverem.", + "select_provider": "Vyberte si libovolného poskytovatele z následujícího seznamu, všichni podporují službu SelfPrivacy", + "select_provider_notice": "Pod pojmem \"relativně malý\" rozumíme počítač se dvěma jádry procesoru a dvěma gigabajty paměti RAM.", + "select_provider_countries_text_do": "USA, Nizozemsko, Singapur, Velká Británie, Německo, Kanada, Indie, Austrálie", + "select_provider_email_notice": "E-mailový hosting nebude pro nové klienty k dispozici. Nicméně bude odemčen, jakmile dokončíte první platbu.", + "choose_location_type_text": "Různá místa poskytují různé konfigurace serverů, ceny a rychlosti připojení.", + "choose_server_type_text": "Různé možnosti prostředků podporují různé služby. Nebojte se, svůj server můžete kdykoli rozšířit", + "manage_domain_dns": "Správa domény DNS", + "use_this_domain_text": "Vámi zadaný token poskytuje přístup k následující doméně", + "no_connected_domains": "V současné době nejsou připojeny žádné domény", + "found_more_domains": "Nalezeno více než jedna doména. V zájmu vlastní bezpečnosti vás prosíme o odstranění nepotřebných domén", + "server_created": "Vytvořený server. Probíhá kontrola DNS a spouštění serveru…", + "choose_server_type_notice": "Důležité je zaměřit se na procesor a paměť RAM. Data vašich služeb budou uložena na připojeném svazku, který lze snadno rozšířit a za který se platí zvlášť.", + "connect_backblaze_storage": "Připojení úložiště Backblaze", + "save_domain": "Uložit doménu", + "final": "Závěrečný krok", + "create_server": "Vytvořit server", + "what": "Co to znamená?", + "server_rebooted": "Server byl restartován. Čeká se na poslední ověření…", + "select_provider_countries_title": "Dostupné země", + "select_provider_countries_text_hetzner": "Německo, Finsko, USA", + "select_provider_price_title": "Průměrná cena", + "select_provider_price_text_hetzner": "8 € měsíčně za relativně malý server a 50 GB diskového úložiště", + "select_provider_price_text_do": "17 dolarů měsíčně za relativně malý server a 50 GB diskového úložiště", + "select_provider_payment_title": "Platební metody", + "select_provider_payment_text_hetzner": "Kreditní karty, SWIFT, SEPA, PayPal", + "select_provider_payment_text_do": "Kreditní karty, Google Pay, PayPal", + "select_provider_site_button": "Navštivte stránku", + "connect_to_server_provider": "Nyní se přihlaste ", + "connect_to_server_provider_text": "S tokenem API si SelfPrivacy bude moci pronajmout počítač a nastavit na něm svůj server", + "how": "Jak získat token API", + "provider_bad_key_error": "Klíč API poskytovatele je neplatný", + "could_not_connect": "Nelze se připojit k poskytovateli.", + "choose_location_type": "Kde si chcete objednat server?", + "locations_not_found_text": "Nejsou k dispozici žádné servery k pronájmu", + "back_to_locations": "Vyberte něco jiného", + "no_locations_found": "Nebyla nalezena žádná dostupná místa, ujistěte se, že je váš účet přístupný", + "choose_server_type": "Jaký typ serveru potřebujete?", + "choose_server_type_ram": "{} GB paměti RAM", + "choose_server_type_storage": "{} GB systémového úložiště", + "choose_server_type_payment_per_month": "{} měsíčně", + "no_server_types_found": "Nebyly nalezeny žádné dostupné typy serverů. Ujistěte se, že je váš účet přístupný, a zkuste změnit umístění serveru.", + "backblaze_bad_key_error": "Informace o úložišti Backblaze jsou neplatné", + "select_dns": "Nyní vybereme poskytovatele DNS", + "use_this_domain": "Použít tuto doménu?", + "server_started": "Server byl spuštěn. Nyní bude ověřen a restartován…", + "until_the_next_check": "Do příští kontroly: ", + "check": "Podívejte se na stránky", + "one_more_restart": "Ještě jeden restart pro použití bezpečnostních certifikátů.", + "create_master_account": "Vytvoření hlavního účtu", + "loading_domain_list": "Načítání seznamu domén", + "enter_username_and_password": "Zadejte uživatelské jméno a silné heslo", + "finish": "Vše je inicializováno", + "checks": "Kontroly byly dokončeny\n{} z {}", + "steps": { + "server": "Server", + "dns_setup": "Instalace služby DNS", + "hosting": "Hostování", + "server_type": "Typ serveru", + "dns_provider": "Poskytovatel DNS", + "backups_provider": "Zálohování", + "domain": "Doména", + "master_account": "Hlavní účet", + "nixos_installation": "Instalace systému NixOS", + "server_reboot": "Restartování serveru", + "final_checks": "Závěrečné kontroly" + }, + "server_provider_description": "Místo, kde budou umístěna vaše data a služby SelfPrivacy:", + "dns_provider_description": "Toto propojí vaši doménu s IP adresou:", + "dns_provider_bad_key_error": "Klíč API je neplatný", + "connect_to_dns": "Připojte poskytovatele DNS", + "select_provider_price_free": "Zdarma", + "select_provider_payment_text_cloudflare": "Bankovní karty", + "connect_to_dns_provider_text": "Pomocí rozhraní token API nakonfiguruje aplikace SelfPrivacy záznamy DNS" + }, + "about_us_page": { + "title": "O nás" + }, + "users": { + "no_ssh_notice": "Pro tohoto uživatele jsou vytvořeny pouze účty e-mailu a SSH. Jednotné přihlašování pro všechny služby se chystá brzy.", + "add_new_user": "Přidání prvního uživatele", + "new_user": "Nový uživatel", + "nobody_here": "Nikdo zde není", + "login": "Přihlášení", + "new_user_info_note": "Novému uživateli bude automaticky přidělen přístup ke všem službám", + "delete_confirm_question": "Jste si jistý?", + "reset_password": "Obnovení hesla", + "account": "Účet", + "send_registration_data": "Sdílení přihlašovacích údajů", + "could_not_fetch_users": "Nepodařilo se načíst seznam uživatelů", + "could_not_fetch_description": "Zkontrolujte prosím své internetové připojení a zkuste to znovu", + "refresh_users": "Obnovení seznamu uživatelů", + "could_not_create_user": "Nepodařilo se vytvořit uživatele", + "could_not_delete_user": "Nepodařilo se odstranit uživatele", + "email_login": "Přihlášení e-mailem", + "delete_user": "Odstranění uživatele", + "not_ready": "Připojte prosím server, doménu a DNS na kartě Poskytovatelé, abyste mohli přidat prvního uživatele", + "could_not_add_ssh_key": "Nepodařilo se přidat klíč SSH", + "username_rule": "Uživatelské jméno musí obsahovat pouze malá písmena latinky, číslice a podtržítka, nesmí začínat číslicí", + "details_title": "Údaje o uživateli" + }, + "record": { + "root": "Kořenová doména", + "api": "API SelfPrivacy", + "cloud": "Oblak souborů", + "git": "Server Git", + "social": "Sociální síť", + "password": "Správce hesel", + "vpn": "VPN", + "mx": "Záznam MX", + "dmarc": "Záznam DMARC", + "spf": "Záznam SPF", + "dkim": "Klíč DKIM", + "meet": "Videokonference" + }, + "domain": { + "card_title": "Doména", + "ok": "Záznamy jsou v pořádku", + "error": "Zjištěné problémy", + "error_subtitle": "Klepnutím sem je opravíte", + "refreshing": "Obnovení stavu…", + "services_title": "Služby", + "services_subtitle": "Pro každou službu jsou vyžadovány záznamy typu \"A\".", + "email_title": "Email", + "email_subtitle": "Záznamy nezbytné pro bezpečnou výměnu emailů.", + "update_list": "Aktualizace seznamu", + "screen_title": "Doména a DNS", + "uninitialized": "Data ještě nejsou načtena" + }, + "backup": { + "card_title": "Záloha", + "reupload_key": "Vynutit klíč k opětovnému nahrání", + "reuploaded_key": "Znovu nahraný klíč", + "initialize": "Inicializace", + "waiting_for_rebuild": "První zálohu budete moci vytvořit během několika minut.", + "restore": "Obnovení ze zálohy", + "no_backups": "Zatím nejsou k dispozici žádné zálohy", + "creating": "Vytvoření nové zálohy: {}%", + "restoring": "Obnovení ze zálohy", + "error_pending": "Server vrátil chybu, zkontrolujte ji níže", + "refresh": "Stav obnovení", + "refetch_backups": "Opětovné načtení seznamu záloh", + "description": "Zachrání vás v případě incidentu: útoku hackerů, vymazání serveru atd.", + "create_new": "Vytvoření nové zálohy", + "restore_alert": "Chystáte se obnovit ze zálohy vytvořené dne {}. Všechna aktuální data budou ztracena. Jste si jisti?", + "refetching_list": "Za několik minut bude seznam aktualizován" + }, + "storage": { + "card_title": "Úložiště serveru", + "status_ok": "Využití disku je v pořádku", + "status_error": "Málo místa na disku", + "disk_usage": "{} použito", + "disk_total": "{} celkem - {}", + "gb": "{} GB", + "mb": "{} MB", + "kb": "{} KB", + "bytes": "Byty", + "extend_volume_button": "Rozšíření objemu", + "extending_volume_title": "Rozšíření objemu", + "extending_volume_price_info": "Cena je uvedena včetně DPH a je odhadnuta na základě cenových údajů poskytnutých společností Hetzner. Po změně velikosti bude server restartován.", + "extending_volume_error": "Nepodařilo se inicializovat rozšíření svazku.", + "size": "Velikost", + "data_migration_title": "Migrace dat", + "data_migration_notice": "Během migrace budou všechny služby vypnuty.", + "start_migration_button": "Zahájení migrace", + "migration_process": "Migrace…", + "migration_done": "Dokončení", + "extending_volume_description": "Změna velikosti svazku vám umožní uložit na server více dat, aniž byste museli rozšiřovat samotný server. Svazek lze pouze rozšířit: zmenšení není možné." + }, + "service_page": { + "open_in_browser": "Otevřít v prohlížeči", + "disable": "Zakázat službu", + "enable": "Povolení služby", + "move": "Přesun do jiného svazku", + "status": { + "active": "Zprovoznění a provoz", + "inactive": "Zastaveno", + "off": "Bezbariérový", + "activating": "Aktivace", + "reloading": "Restartování", + "failed": "Nepodařilo se spustit", + "deactivating": "Deaktivace" + }, + "restart": "Restartování služby", + "uses": "Používá {usage} na {volume}" + }, + "mail": { + "title": "E-mail", + "subtitle": "E-mail pro společnost a rodinu.", + "login_info": "Použijte uživatelské jméno a heslo z karty uživatelů. Port IMAP je 143 se STARTTLS, port SMTP je 587 se STARTTLS." + }, + "cloud": { + "title": "Cloudové úložiště", + "subtitle": "Nedovolte cloudovým službám číst vaše data pomocí služby NextCloud.", + "login_info": "Přihlašovací jméno je admin, heslo je stejné jako u hlavního uživatele. Vytvoření nových účtů v rozhraní Nextcloud." + }, + "password_manager": { + "title": "Správce hesel", + "login_info": "Na webových stránkách si musíte vytvořit účet.", + "subtitle": "Základna vašeho zabezpečení. Bitwarden vám pomůže vytvářet, ukládat a přesouvat hesla mezi zařízeními a také je zadávat na vyžádání pomocí automatického doplňování." + }, + "video": { + "title": "Video konference", + "subtitle": "Zoom a Google Meet jsou dobré, ale Jitsi Meet je hodnotná alternativa, která vám navíc dává jistotu, že vás nikdo neposlouchá.", + "login_info": "Není potřeba žádný účet." + }, + "social_network": { + "title": "Sociální síť", + "login_info": "Na webových stránkách si musíte vytvořit účet.", + "subtitle": "Je těžké tomu uvěřit, ale je možné vytvořit si vlastní sociální síť s vlastními pravidly a cílovou skupinou." + }, + "git": { + "title": "Server Git", + "subtitle": "Soukromá alternativa ke Githubu, která patří vám, ale ne Microsoftu.", + "login_info": "Na webových stránkách si musíte vytvořit účet. První uživatel se stane administrátorem." + }, + "vpn": { + "title": "Server VPN", + "subtitle": "Soukromý server VPN" + }, + "recovering": { + "recovery_main_header": "Připojení k existujícímu serveru", + "domain_recover_placeholder": "Vaše doména", + "domain_recover_error": "Server s takovou doménou nebyl nalezen", + "method_select_description": "Vyberte metodu obnovy:", + "method_select_other_device": "Mám přístup na jiném zařízení", + "method_select_recovery_key": "Mám klíč pro obnovení", + "method_select_nothing": "Nic z toho nemám", + "method_device_button": "Obdržel jsem svůj žeton", + "method_device_input_description": "Zadejte svůj autorizační token", + "method_device_input_placeholder": "Token", + "method_recovery_input_description": "Zadejte svůj klíč k obnovení", + "fallback_select_description": "Co přesně máte? Vyberte první dostupnou možnost:", + "fallback_select_token_copy": "Kopie tokenu autentizace z jiné verze aplikace.", + "fallback_select_root_ssh": "Kořenový přístup SSH k serveru.", + "fallback_select_provider_console": "Přístup ke konzole serveru mého prodiveru.", + "authorization_failed": "Nelze se přihlásit pomocí tohoto klíče", + "provider_connected": "Připojení k poskytovateli {}", + "provider_connected_description": "Komunikace navázána. Zadejte svůj token s přístupem k {}:", + "provider_connected_placeholder": "Token {}", + "confirm_server": "Potvrzení serveru", + "confirm_server_accept": "Ano! To je ono", + "confirm_server_decline": "Výběr jiného serveru", + "choose_server": "Výběr serveru", + "choose_server_description": "Nepodařilo se nám zjistit, ke kterému serveru se snažíte připojit.", + "no_servers": "Na vašem účtu nejsou k dispozici žádné servery.", + "modal_confirmation_title": "Je to skutečně váš server?", + "modal_confirmation_description": "Pokud se připojíte k nesprávnému serveru, můžete přijít o všechna data.", + "modal_confirmation_dns_valid": "Reverzní DNS je platný", + "modal_confirmation_dns_invalid": "Reverzní DNS ukazuje na jinou doménu", + "modal_confirmation_ip_invalid": "IP není stejná jako v záznamu DNS", + "generic_error": "Operace se nezdařila, zkuste to prosím znovu.", + "domain_recovery_description": "Zadejte doménu serveru, ke které chcete získat přístup:", + "method_device_description": "Otevřete aplikaci v jiném zařízení a přejděte na stránku zařízení. Stisknutím tlačítka \"Přidat zařízení\" získáte token.", + "fallback_select_provider_console_hint": "Například: Hetzner.", + "confirm_server_description": "Našel jsem váš server! Potvrďte, že je to ten správný:", + "domain_not_available_on_token": "Vybraná doména není na tomto tokenu k dispozici.", + "modal_confirmation_ip_valid": "IP je stejná jako v záznamu DNS" + }, + "devices": { + "main_screen": { + "header": "Zařízení", + "description": "Tato zařízení mají plný přístup k serveru prostřednictvím aplikace SelfPrivacy.", + "tip": "Stisknutím tlačítka na zařízení zrušíte přístup.", + "this_device": "Toto zařízení", + "other_devices": "Ostatní zařízení", + "authorize_new_device": "Autorizace nového zařízení", + "access_granted_on": "Přístup udělen na {}" + }, + "revoke_device_alert": { + "header": "Odvolání přístup?", + "description": "Zařízení {} již nebude mít přístup k serveru.", + "yes": "Odvolání", + "no": "Zrušit" + }, + "add_new_device_screen": { + "header": "Autorizace nového zařízení", + "description": "Zadejte klíč zařízení, které chcete autorizovat:", + "please_wait": "Počkejte prosím", + "tip": "Klíč je platný po dobu 10 minut.", + "expired": "Platnost klíče vypršela.", + "get_new_key": "Získat nový klíč" + } + }, + "not_ready_card": { + "in_menu": "Server ještě není nastaven. Pro další práci dokončete nastavení pomocí průvodce nastavením." + }, + "recovery_key": { + "key_connection_error": "Nepodařilo se připojit k serveru.", + "key_synchronizing": "Synchronizace…", + "key_receiving_done": "Hotovo!", + "generation_error": "Nepodařilo se vygenerovat klíč pro obnovení. {}", + "key_main_header": "Klíč pro obnovu", + "key_main_description": "Vyžaduje se pro autorizaci SelfPrivacy, pokud nejsou k dispozici autorizovaná zařízení.", + "key_amount_toggle": "Omezit používání", + "key_amount_field_title": "Maximální počet použití", + "key_duedate_toggle": "Omezit dobu používání", + "key_duedate_field_title": "Datum vypršení platnosti", + "key_receive_button": "Přijmout klíč", + "key_valid": "Váš klíč je platný", + "key_invalid": "Váš klíč již není platný", + "key_valid_until": "Platí do {}", + "key_valid_for": "{} můžete použít vícekrát", + "key_creation_date": "Vytvořeno {}", + "key_replace_button": "Generování nového klíče", + "key_receiving_description": "Tento klíč si zapište na bezpečné místo. Umožní vám plný přístup k serveru:", + "key_receiving_info": "Tento klíč se již nebude zobrazovat, ale můžete jej nahradit novým." + }, + "timer": { + "sec": "{} sek" + }, + "jobs": { + "title": "Seznam pracovních míst", + "start": "Start", + "empty": "Žádná pracovní místa", + "create_user": "Vytvoření uživatele", + "delete_user": "Odstranění uživatele", + "service_turn_off": "Zastavte", + "service_turn_on": "Zapnout", + "job_added": "Přidaná práce", + "run_jobs": "Spouštění úloh", + "reboot_success": "Server se restartuje", + "reboot_failed": "Server se nepodařilo restartovat. Zkontrolujte protokoly aplikace.", + "config_pull_failed": "Nepodařilo se stáhnout aktualizaci konfigurace. Přesto byla spuštěna aktualizace softwaru.", + "upgrade_success": "Zahájena aktualizace serveru", + "upgrade_failed": "Aktualizace serveru nefungovala", + "upgrade_server": "Aktualizace serveru", + "reboot_server": "Restartování serveru", + "create_ssh_key": "Vytvoření klíče SSH pro {}", + "delete_ssh_key": "Odstranění klíče SSH pro {}", + "server_jobs": "Úlohy na serveru", + "reset_user_password": "Obnovení hesla uživatele", + "generic_error": "Nelze se připojit k serveru!" + }, + "validations": { + "already_exist": "Již existuje", + "invalid_format": "Nesprávný formát", + "root_name": "Nemůže být 'root'", + "required": "Povinné pole", + "invalid_format_password": "Heslo nesmí obsahovat mezery", + "invalid_format_ssh": "Musí dodržovat formát klíče SSH", + "length_not_equal": "Délka je [], mělo by být {}", + "length_longer": "Délka řetězce [] musí být menší nebo rovna {}" + }, + "modals": { + "dns_removal_error": "Nepodařilo se odstranit záznamy DNS.", + "server_deletion_error": "Nepodařilo se odstranit aktivní server.", + "server_validators_error": "Nelze načíst seznam serverů.", + "already_exists": "Takový server již existuje.", + "unexpected_error": "Neočekávaná chyba při umisťování na straně poskytovatele.", + "destroy_server": "Zničit server a vytvořit nový?", + "try_again": "Mám to zkusit znovu?", + "are_you_sure": "Jste si jistý?", + "purge_all_keys": "Vyčistit všechny ověřovací klíče?", + "purge_all_keys_confirm": "Ano, vyčistěte všechny mé žetony", + "delete_server_volume": "Smazat server a svazek?", + "reboot": "Restartovat", + "you_cant_use_this_api": "Toto rozhraní API nelze použít pro doménu s podobnou TLD.", + "yes": "Ano", + "no": "Ne", + "volume_creation_error": "Svazek se nepodařilo vytvořit." + }, + "support": { + "title": "Podpora SelfPrivacy" + }, + "developer_settings": { + "title": "Nastavení vývojáře", + "subtitle": "Tato nastavení slouží pouze k ladění. Neměňte je, pokud nevíte, co děláte.", + "server_setup": "Nastavení serveru", + "use_staging_acme": "Použití testovacího serveru ACME", + "use_staging_acme_description": "Používá se při nastavování nového serveru.", + "routing": "Směrování aplikací", + "reset_onboarding": "Obnovení uvítací vlajky pro návštěvu", + "reset_onboarding_description": "Resetování vypínače pro opětovné zobrazení obrazovky pro zapnutí napájení", + "cubit_statuses": "Stavy nakládky zařízení Cubit", + "ignore_tls": "Nekontrolujte certifikáty TLS", + "ignore_tls_description": "Aplikace nebude při připojování k serveru ověřovat certifikáty TLS." + } +} diff --git a/assets/translations/de.json b/assets/translations/de.json new file mode 100644 index 00000000..4a39b818 --- /dev/null +++ b/assets/translations/de.json @@ -0,0 +1,511 @@ +{ + "test": "de-test", + "locale": "de", + "basis": { + "providers": "Provider", + "password": "Passwort", + "create": "Hinzufügen", + "confirmation": "Bestätigung", + "providers_title": "Ihr Rechenzentrum", + "select": "Auswählen", + "services": "Dienste", + "users": "Benutzer", + "more": "Mehr", + "next": "Weiter", + "got_it": "Verstanden", + "settings": "Einstellungen", + "cancel": "Abbrechen", + "delete": "Löschen", + "close": "Schließen", + "connect": "Verbinden", + "domain": "Domäne", + "saving": "Speichern…", + "username": "Benutzername", + "loading": "Laden…", + "later": "Überspringen und später einstellen", + "connect_to_existing": "Mit einem existierenden Server verbinden", + "reset": "Zurücksetzen", + "details": "Einzelheiten", + "no_data": "Keine Daten", + "wait": "Warten", + "remove": "Entfernen", + "done": "Fertig", + "continue": "Fortfahren", + "alert": "Alarm", + "services_title": "Ihre persönliche, private und unabhängige Dienste.", + "apply": "Anwenden", + "app_name": "SelfPrivacy" + }, + "more_page": { + "configuration_wizard": "Setup-Assistent", + "about_project": "Über uns", + "about_application": "Über", + "onboarding": "Onboarding", + "create_ssh_key": "Superuser SSH Schlüssel", + "console": "Konsole", + "application_settings": "Anwendungseinstellungen" + }, + "console_page": { + "title": "Konsole", + "waiting": "Warten auf Initialisierung…", + "copy": "Kopieren" + }, + "about_us_page": { + "title": "Über uns" + }, + "about_application_page": { + "title": "Über", + "application_version_text": "Anwendungsversion {}", + "api_version_text": "Server API Version {}", + "privacy_policy": "Datenschutzerklärung" + }, + "application_settings": { + "title": "Anwendungseinstellungen", + "dark_theme_title": "Dunkles Thema", + "dark_theme_description": "Ihr Anwendungsdesign wechseln", + "reset_config_title": "Anwendungseinstellungen zurücksetzen", + "reset_config_description": "API Sclüssel und root Benutzer zurücksetzen.", + "delete_server_title": "Server löschen", + "delete_server_description": "Das wird Ihren Server löschen. Es wird nicht mehr zugänglich sein.", + "system_dark_theme_title": "Standard-Systemthema", + "system_dark_theme_description": "Verwenden Sie je nach Systemeinstellungen ein helles oder dunkles Thema", + "dangerous_settings": "Gefährliche Einstellungen" + }, + "ssh": { + "title": "SSH Schlüssel", + "create": "SSH Schlüssel erstellen", + "delete": "SSH Schlüssel entfernen", + "delete_confirm_question": "Sind Sie sicher, dass Sie SSH SChlüssel entfernen wollen?", + "subtitle_with_keys": "{} Schlüssel", + "subtitle_without_keys": "Keine Slüssel", + "no_key_name": "Unbennanter Schlüssel", + "root_title": "Das sind superuser Schlüsseln", + "root_subtitle": "Besitzer dieser Schlüssel erhalten vollen Zugriff auf den Server und können alles darauf tun. Fügen Sie dem Server nur Ihre eigenen Schlüssel hinzu.", + "input_label": "Öffentlicher ED25519 oder RSA Schlüssel" + }, + "onboarding": { + "page2_server_provider_text": "Ein Serveranbieter unterhält Ihren Server in einem eigenen Rechenzentrum. SelfPrivacy verbindet sich automatisch mit dem Anbieter und richtet alle notwendigen Dinge ein.", + "page1_title": "Digitale Unabhägigkeit ist für jeden von uns verfügbar", + "page1_text": "E-Mail, VPN, Messenger, soziales Netzwerk und viel mehr auf Ihrem privaten Server unter Ihrer Kontrolle.", + "page2_title": "SelfPrivacy ist keine Cloud, sondern Ihr perönliches Rechenzentrum", + "page2_text": "SelfPrivacy funktioniert nur mit Providern Ihrer Wahl. Wenn Sie dort keine erforderlichen Konten haben, helfen wir Ihnen, sie zu erstellen.", + "page2_server_provider_title": "Serveranbieter", + "page2_dns_provider_title": "DNS-Provider", + "page2_backup_provider_title": "Backup-Provider", + "page2_dns_provider_text": "Sie brauchen einen Domainnamen, um im Internet zu sein. Sie benötigen außerdem einen zuverlässigen DNS-Provider, damit die Domain auf ihren Server verweist. Wir empfehlen Ihnen, einen unterstützten DNS-Anbieter auszuwählen, um das Netzwerk automatisch einzurichten. Wollen Sie es manuell einstellen? Das geht auch.", + "page2_backup_provider_text": "Was ist, wenn etwas mit Ihrem Server passiert? Stellen Sie sich einen Hackerangriff, eine versehentliche Datenlöschung oder DOS-Angriff vor? Ihre Daten werden bei Ihrem Anbieter von Backups sicher aufbewahrt. Sie werden sicher verschlüsselt und sind jederzeit zugänglich, um Ihren Server damit wiederherzustellen." + }, + "resource_chart": { + "month": "Monat", + "day": "Tag", + "hour": "Stunde", + "cpu_title": "CPU Auslastung", + "network_title": "Netzwerknutzung", + "in": "Empfangen", + "out": "Gesendet" + }, + "server": { + "card_title": "Server", + "description": "Dies ist ein virtueller Computer, auf dem alle Ihre Dienste ausgeführt werden", + "general_information": "Allgemeine Information", + "resource_usage": "Ressourcenverwendung", + "allow_autoupgrade": "Automatische Aktualisierung erlauben", + "allow_autoupgrade_hint": "Automatische Installation der Updates auf dem Server erlauben", + "reboot_after_upgrade": "Neustart nach der Aktualisierung", + "reboot_after_upgrade_hint": "Automatisch den Server neustarten nach der Anwendung der Aktualisierungen", + "server_timezone": "Server Zeitzone", + "select_timezone": "Zeitzone auswählen", + "timezone_search_bar": "Zeitzonenname oder Zeitverschiebungswert", + "server_id": "Server ID", + "status": "Status", + "cpu": "CPU", + "ram": "Arbeitsspeicher", + "disk": "Festplatte", + "monthly_cost": "Monatliche Kosten", + "location": "Standort", + "core_count": { + "one": "{} Kern", + "two": "{} Kerne", + "few": "{} Kerne", + "many": "{} Kerne", + "other": "{} Kerne" + } + }, + "record": { + "root": "Root Domain", + "api": "SelfPrivacy API", + "git": "Git Server", + "meet": "Videokonferenz", + "social": "Soziales Netzwerk", + "password": "Passwortmanager", + "vpn": "VPN", + "dkim": "DKIM Shlüssel", + "mx": "MX Eintrag", + "dmarc": "DMARC Eintrag", + "spf": "SPF Eintrag", + "cloud": "Datencloud" + }, + "domain": { + "screen_title": "Domäne und DNS", + "ok": "Aufnahmen sind in Ordnung", + "error": "Probleme gefunden", + "error_subtitle": "Tippen sie hier, um sie zu beheben. Dadurch werden auch benutzerdefinierte DNS-Einträge entfernt.", + "uninitialized": "Daten sind noch nicht vorhanden", + "services_title": "Dienste", + "email_title": "E-Mail", + "email_subtitle": "Für sicheren E-Mail-Austauch benötigte Einträge.", + "update_list": "Liste aktualisieren", + "card_title": "Domäne", + "refreshing": "Statusaktualisierung…", + "services_subtitle": "Einträge vom Typ \"A\", die für jeden Dienst erfordelich sind." + }, + "backup": { + "card_title": "Backup", + "description": "Retten Sie im Falle eines Hackerangriffs, Serverlöschung usw.", + "reupload_key": "Erneute Hochladung des Schlüssels erzwingen", + "reuploaded_key": "Schlüssel auf dem Server wurde Aktualisiert", + "initialize": "Initialisieren", + "restore": "Von der Sicherungskopie wiederherstellen", + "no_backups": "Backups sind noch nicht vorhanden", + "create_new": "Neuen Backup erstellen", + "creating": "Erstellung eines neuen Backups: {}%", + "restoring": "Wiederherstellung aus Backups", + "restore_alert": "Sie sind dabei die am {} erstellten Backup wiederherzutellen. Alle aktuelle Daten gehen dabei verloren. Sind Sie sicher?", + "refresh": "Status Aktualisieren", + "refetch_backups": "Backupliste neuladen", + "waiting_for_rebuild": "In wenigen Minuten können Sie Ihren ersten Backup erstellen.", + "error_pending": "Server hat einen Fehler zurückgegeben, überprüfen Sie es unten", + "refetching_list": "In wenigen Minuten wird die Liste neugeladen" + }, + "storage": { + "card_title": "Serverspeicher", + "status_ok": "Die Festplattennutzung ist in Ordnung", + "status_error": "Geringer Speicherplatz", + "disk_usage": "{} belegt", + "disk_total": "{} insgesamt · {}", + "gb": "{} GB", + "mb": "{} MB", + "kb": "{} KB", + "bytes": "Byte", + "extend_volume_button": "Speicher erweitern", + "extending_volume_title": "Speichererweiterung", + "extending_volume_description": "Durch die Speichererweiterung können Sie mehr Daten auf Ihrem Server speichern, ohne den Server selbst zu erweitern. Speicher kann nur erweitert werden, Verringerung ist nicht möglich.", + "extending_volume_error": "Speichererweiterung konnte nicht initialisiert werden.", + "size": "Größe", + "data_migration_title": "Datenmigration", + "data_migration_notice": "Während der Migration werden alle Dienste ausgeschaltet.", + "start_migration_button": "Migration starten", + "migration_process": "Migrieren…", + "migration_done": "Beenden", + "extending_volume_price_info": "Der Preis enthält die gesetzliche Mehrwertsteuer und wird anhand der von Hetzner bereitgestellten Preisangaben geschätzt. Der Server wird nach der Größenänderung neu gestartet." + }, + "not_ready_card": { + "in_menu": "Server ist noch nicht eingerichtet. Bitte beenden Sie die Einrichtung mit dem Einrichtungsassistenten, um fortzufahren." + }, + "service_page": { + "enable": "Dienst aktivieren", + "status": { + "deactivating": "Wird deaktiviert", + "active": "In Betrieb", + "inactive": "Gestoppt", + "failed": "Fehler beim Starten", + "off": "Deaktiviert", + "activating": "Wird aktiviert", + "reloading": "Wird neugestartet" + }, + "open_in_browser": "Im Browser öffnen", + "restart": "Dienst neustarten", + "disable": "Dienst ausschalten", + "move": "Zu einer anderen Festplatte wechseln", + "uses": "Belegt {usage} auf {volume}" + }, + "mail": { + "login_info": "Verwenden Sie den Benutzernamen und das Passwort von dem Benutzertab. IMAP-Port ist 143 mit STARTTLS, SMTP-Port ist 587 mit STARTTLS.", + "title": "E-Mail", + "subtitle": "E-Mail für Familie und Unternehmen." + }, + "password_manager": { + "title": "Passwortmanager", + "subtitle": "Basis Ihrer Sicherheit. Bitwarden hilft Ihnen beim Erstellen, Speichern und Übertragung von Passwörtern zwischen Geräten sowie bei der Eingabe mithilfe der automatischen Vervollständigung.", + "login_info": "Ihr Konto müssen Sie auf der Webseite erstellen." + }, + "modals": { + "unexpected_error": "Unerwarteter Fehler beim Platzieren von Seiten des Anbieters.", + "delete_server_volume": "Server und Speicher löschen?", + "dns_removal_error": "DNS-Einträge konnten nicht entfernt werden.", + "server_deletion_error": "Aktiver Server konnte nicht gelöscht werden.", + "server_validators_error": "Verfügbare Server konnten nicht abgerufen werden.", + "already_exists": "Ein solcher Server existiert bereits.", + "destroy_server": "Den Server zerstören und einen neuen erstellen?", + "try_again": "Nochmal versuchen?", + "are_you_sure": "Sind Sie sicher?", + "purge_all_keys": "Alle Authentifizierungsschlüssel löschen?", + "purge_all_keys_confirm": "Ja, alle meine Token löschen", + "reboot": "Neustarten", + "you_cant_use_this_api": "Sie können diese API nicht für Domains mit einer solchen TLD verwenden.", + "yes": "Ja", + "no": "Nein", + "volume_creation_error": "Volume konnte nicht erstellt werden." + }, + "jobs": { + "create_user": "Benutzer erstellen", + "service_turn_on": "Starten", + "reboot_success": "Server wird neugestartet", + "reboot_failed": "Der Server konnte nicht neugestartet werden. Überprüfen Sie die Logs der Anwendung.", + "create_ssh_key": "SSH-Schlüssel erstellen für {}", + "generic_error": "Es konnte keine Verbindung zum Server hergestellt werden!", + "delete_user": "Benutzer löschen", + "service_turn_off": "Abschalten", + "job_added": "Aufgabe hinzugefügt", + "run_jobs": "Aufgaben ausführen", + "title": "Aufgaben", + "start": "Starten", + "empty": "Keine Aufgaben", + "config_pull_failed": "Konfigurationsaktualisierung konnte nicht ausgeführt werden. Software-Aktualisierung trotzdem gestartet.", + "upgrade_success": "Serveraktualisierung gestartet", + "upgrade_failed": "Serveraktualisierung fehlgeschlagen", + "upgrade_server": "Server aktualisieren", + "reboot_server": "Server neustarten", + "delete_ssh_key": "SSH-Schlüssel löschen für {}", + "server_jobs": "Aufgaben auf dem Server", + "reset_user_password": "Passwort des Benutzers zurücksetzen" + }, + "initializing": { + "locations_not_found": "Oops!", + "backblaze_bad_key_error": "Die Backblaze-Speicherinformationen sind ungültig", + "select_dns": "Lassen Sie uns nun einen DNS-Provider auswählen", + "manage_domain_dns": "Zum Verwalten des DNS Ihrer Domain", + "use_this_domain": "Diese Domäne verwenden?", + "use_this_domain_text": "Das von Ihnen bereitgestellte Token gewährt Zugriff auf die folgende Domäne", + "connect_backblaze_storage": "Backblaze-Speicher verbinden", + "no_connected_domains": "Derzeit keine verbundenen Domains", + "loading_domain_list": "Domänenliste wird geladen", + "found_more_domains": "Mehr als eine Domain gefunden. Zu Ihrer eigenen Sicherheit bitten wir Sie, nicht benötigte Domains zu löschen", + "save_domain": "Domäne speichern", + "final": "Letzter Schritt", + "create_server": "Server erstellen", + "select_provider_countries_text_do": "USA, Niederlande, Singapur, Großbritannien, Deutschland, Kanada, Indien, Australien", + "select_provider_price_text_do": "17 Dollar pro Monat für einen relativ kleinen Server und 50 GB Festplattenspeicher", + "connect_to_server_provider_text": "Mit dem API-Token kann SelfPrivacy eine Maschine mieten und Ihren Server darauf einrichten", + "choose_location_type_text": "Unterschiedliche Standorte bieten unterschiedliche Serverkonfigurationen, Preise und Verbindungsgeschwindigkeiten.", + "choose_server_type_text": "Unterschiedliche Ressourcenfunktionen unterstützen unterschiedliche Dienste. Keine Sorge, Sie können Ihren Server jederzeit erweitern", + "no_server_types_found": "Keine verfügbaren Servertypen gefunden. Stellen Sie sicher, dass auf Ihr Konto zugegriffen werden kann und versuchen Sie Ihren Serverstandort zu ändern.", + "one_more_restart": "Noch ein Neustart, um Ihre Sicherheitszertifikate anzuwenden.", + "checks": "Überprüfungen sind abgeschlossen \n{} aus {}", + "connect_to_server": "Beginnen wir mit einem Server.", + "select_provider": "Wählen Sie einen beliebigen Anbieter aus der folgenden Liste aus, sie alle unterstützen SelfPrivacy", + "select_provider_notice": "Mit „relativ klein“ meinen wir eine Maschine mit 2 CPU-Kernen und 2 Gigabyte RAM.", + "select_provider_countries_title": "verfügbare Länder", + "select_provider_countries_text_hetzner": "Deutschland, Finnland, USA", + "select_provider_price_title": "Durchschnittspreis", + "select_provider_price_text_hetzner": "8 Euro pro Monat für einen relativ kleinen Server und 50 GB Festplattenspeicher", + "select_provider_payment_title": "Zahlungsarten", + "select_provider_payment_text_hetzner": "Kreditkarten, SWIFT, SEPA, PayPal", + "select_provider_payment_text_do": "Kreditkarten, Google Pay, PayPal", + "select_provider_email_notice": "E-Mail-Hosting ist für Neukunden nicht verfügbar. Es wird jedoch freigeschaltet, sobald Sie Ihre erste Zahlung abgeschlossen haben.", + "select_provider_site_button": "Webseite besuchen", + "connect_to_server_provider": "Anmelden bei ", + "how": "So erhalten Sie ein API-Token", + "provider_bad_key_error": "Der API-Schlüssel des Anbieters ist ungültig", + "could_not_connect": "Es konnte keine Verbindung zum Anbieter hergestellt werden.", + "choose_location_type": "Wo möchten Sie Ihren Server mieten?", + "locations_not_found_text": "An diesem Standort stehen keine Server zum Mieten zur Verfügung", + "back_to_locations": "Wählen Sie einen anderen aus", + "no_locations_found": "Keine verfügbaren Standorte gefunden, stellen Sie sicher, dass auf Ihr Konto zugegriffen werden kann", + "choose_server_type": "Welche Art von Server benötigen Sie?", + "choose_server_type_notice": "Die wichtigsten Dinge, die man sich ansehen sollte, sind CPU und RAM. Die Daten Ihrer Dienste werden auf einem leicht erweiterbaren und separat zu zahlenden Datenträger gespeichert.", + "choose_server_type_ram": "{} GB Arbeitsspeicher", + "choose_server_type_storage": "{} GB Systemspeicher", + "choose_server_type_payment_per_month": "{} pro Monat", + "what": "Was bedeutet das?", + "server_rebooted": "Server neugestartet. Warten auf die letzte Bestätigung…", + "server_started": "Server gestartet. Es wird jetzt validiert und neugestartet…", + "server_created": "Server erstellt. DNS-Prüfungen und Server starten…", + "until_the_next_check": "Bis zur nächsten Überprüfung: ", + "check": "Überprüfung", + "create_master_account": "Hauptkonto erstellen", + "enter_username_and_password": "Geben Sie den Benutzernamen und ein gutes Passwort ein", + "finish": "Alles initialisiert", + "steps": { + "hosting": "Hosting", + "server_type": "Server-Typ", + "dns_provider": "DNS-Anbieter", + "backups_provider": "Sicherungskopien", + "domain": "Bereich", + "master_account": "Hauptkonto", + "server": "Server", + "dns_setup": "DNS-Einrichtung", + "nixos_installation": "NixOS-Installation", + "server_reboot": "Server-Neustart", + "final_checks": "Endgültige Kontrollen" + }, + "dns_provider_bad_key_error": "Der API-Schlüssel ist ungültig", + "connect_to_dns": "Verbinden Sie den DNS-Anbieter", + "connect_to_dns_provider_text": "Mithilfe der Token-API konfiguriert die SelfPrivacy-Anwendung DNS-Einträge", + "server_provider_description": "Der Ort, an dem sich Ihre Daten und SelfPrivacy-Dienste befinden:", + "dns_provider_description": "Dadurch wird Ihre Domain mit einer IP-Adresse verknüpft:", + "select_provider_price_free": "Kostenlos", + "select_provider_payment_text_cloudflare": "Bankkarten" + }, + "validations": { + "length_not_equal": "Länge ist [], sollte {} sein", + "required": "Erforderlich", + "already_exist": "Ist bereits vorhanden", + "invalid_format": "Ungültiges Format", + "invalid_format_password": "Das Passwort darf keine Leerzeichen enthalten", + "invalid_format_ssh": "Muss dem SSH-Schlüsselformat entsprechen", + "root_name": "Benutzername darf nicht root sein", + "length_longer": "Länge ist [], sollte kürzer oder gleich {} sein" + }, + "users": { + "could_not_create_user": "Benutzer konnte nicht erstellt werden", + "could_not_delete_user": "Benutzer konnte nicht gelöscht werden", + "could_not_add_ssh_key": "SSH-Schlüssel konnte nicht hinzugefügt werden", + "username_rule": "Der Benutzername darf nur lateinische Kleinbuchstaben, Ziffern und Unterstriche enthalten und darf nicht mit einer Ziffer beginnen", + "no_ssh_notice": "Für diesen Benutzer werden nur E-Mail- und SSH-Konten erstellt. Single Sign On für alle Dienste ist in Kürze verfügbar.", + "add_new_user": "Fügen Sie den ersten Benutzer hinzu", + "new_user": "Neuer Benutzer", + "delete_user": "Benutzer löschen", + "not_ready": "Bitte verbinden Sie Server, Domain und DNS im Providertab, um den ersten Benutzer hinzufügen zu können", + "nobody_here": "Hier werden Benutzer angezeigt", + "login": "Login", + "new_user_info_note": "Neuen Benutzern wird automatisch Zugang zu allen Diensten gewährt", + "delete_confirm_question": "Sind Sie sicher?", + "reset_password": "Passwort zurücksetzen", + "account": "Konto", + "send_registration_data": "Anmeldedaten teilen", + "could_not_fetch_users": "Benutzerliste konnte nicht abgerufen werden", + "could_not_fetch_description": "Bitte überprüfen Sie Ihre Internetverbindung und versuchen Sie es erneut", + "refresh_users": "Benutzerliste aktualisieren", + "email_login": "E-Mail-Anmeldung", + "details_title": "Angaben zum Benutzer" + }, + "devices": { + "main_screen": { + "header": "Geräte", + "description": "Diese Geräte haben über die SelfPrivacy-App vollen Zugriff auf den Server.", + "this_device": "Dieses Gerät", + "other_devices": "Andere Geräte", + "authorize_new_device": "Neues Gerät autorisieren", + "access_granted_on": "Zugriff gewährt auf {}", + "tip": "Tippen Sie auf das Gerät, um den Zugriff zu widerrufen." + }, + "add_new_device_screen": { + "header": "Neues Gerät autorisieren", + "expired": "Der Schlüssel ist nicht mehr gültig.", + "description": "Geben Sie den Schlüssel auf dem Gerät ein, das Sie autorisieren möchten:", + "please_wait": "Bitte warten", + "tip": "Der Schlüssel ist 10 Minuten gültig.", + "get_new_key": "Neuen Schlüssel erhalten" + }, + "revoke_device_alert": { + "header": "Zugangsberechtigung aufheben?", + "description": "Das Gerät {} hat auf den Server keinen Zugriff mehr.", + "yes": "Widerrufen", + "no": "Abbrechen" + } + }, + "recovering": { + "domain_recovery_description": "Geben Sie eine Serverdomäne ein, für die Sie Zugriff erhalten möchten:", + "method_device_description": "Öffnen Sie die Anwendung auf einem anderen Gerät und gehen Sie dann zur Geräteseite. Drücken Sie auf „Gerät hinzufügen“, um Ihren Token zu erhalten.", + "fallback_select_token_copy": "Kopie des Authentifizierungstokens von einer anderen Version der Anwendung.", + "provider_connected_description": "Kommunikation hergestellt. Eingabe Ihres Tokens Token mit Zugriff auf {}:", + "choose_server_description": "Wir konnten nicht herausfinden, mit welchem Server Sie sich verbinden möchten.", + "modal_confirmation_dns_invalid": "Reverse DNS zeigt auf eine andere Domain", + "generic_error": "Vorgang fehlgeschlagen, bitte versuchen Sie es erneut.", + "recovery_main_header": "Verbindung zu einem vorhandenen Server herstellen", + "domain_recover_placeholder": "Ihre Domain", + "domain_recover_error": "Server mit dieser Domain wurde nicht gefunden", + "method_select_description": "Wählen Sie eine Wiederherstellungsmethode aus:", + "method_select_other_device": "Ich habe Zugriff auf einem anderen Gerät", + "method_select_recovery_key": "Ich habe einen Wiederherstellungsschlüssel", + "method_select_nothing": "Ich habe nichts davon", + "method_device_button": "Ich habe mein Token erhalten", + "method_device_input_description": "Geben Sie Ihr Autorisierungstoken ein", + "method_device_input_placeholder": "Token", + "method_recovery_input_description": "Geben Sie Ihren Wiederherstellungsschlüssel ein", + "fallback_select_description": "Was genau haben Sie? Wählen Sie die erste verfügbare Option:", + "fallback_select_root_ssh": "Root-SSH-Zugriff auf den Server.", + "fallback_select_provider_console": "Zugang zur Serverkonsole meines Providers.", + "authorization_failed": "Anmeldung mit diesem Schlüssel nicht möglich", + "fallback_select_provider_console_hint": "Zum Beispiel: Hetzner.", + "provider_connected": "Verbinden Sie sich mit Ihrem {}", + "provider_connected_placeholder": "Token des {}", + "confirm_server": "Server bestätigen", + "confirm_server_description": "Server gefunden! Bestätigen Sie, dass es das Richtige ist:", + "confirm_server_accept": "Ja! Das ist es", + "confirm_server_decline": "Einen anderen Server wählen", + "choose_server": "Wählen Sie Ihren Server", + "no_servers": "Auf Ihrem Konto sind keine Server verfügbar.", + "domain_not_available_on_token": "Die ausgewählte Domäne ist auf diesem Token nicht verfügbar.", + "modal_confirmation_title": "Ist es wirklich Ihr Server?", + "modal_confirmation_description": "Wenn Sie sich mit einem falschen Server verbinden, können Sie alle Ihre Daten verlieren.", + "modal_confirmation_dns_valid": "Reverse DNS ist gültig", + "modal_confirmation_ip_valid": "Die IP ist die gleiche wie im DNS-Eintrag", + "modal_confirmation_ip_invalid": "Die IP ist nicht dieselbe wie im DNS-Eintrag" + }, + "recovery_key": { + "key_connection_error": "Es konnte keine Verbindung zum Server hergestellt werden.", + "key_main_description": "Wird für die SelfPrivacy-Autorisierung benötigt, wenn alle Ihre anderen autorisierten Geräte nicht verfügbar sind.", + "key_synchronizing": "Synchronisieren…", + "key_main_header": "Wiederherstellungsschlüssel", + "key_amount_toggle": "Nutzung einschränken", + "key_amount_field_title": "Maximale Anzahl von Nutzungen", + "key_duedate_toggle": "Zeitlich begrenzen", + "key_duedate_field_title": "Ablaufsdatum", + "key_receive_button": "Schlüssel erhalten", + "key_valid": "Ihr Schlüssel ist gültig", + "key_invalid": "Ihr Schlüssel ist nicht mehr gültig", + "key_valid_until": "Gültig bis {}", + "key_valid_for": "Gültig für {} Nutzungen", + "key_creation_date": "Erstellt am {}", + "key_replace_button": "Neuen Schlüssel generieren", + "key_receiving_info": "Der Schlüssel wird nie wieder angezeigt, aber Sie können ihn durch einen anderen ersetzen.", + "key_receiving_done": "Fertig!", + "generation_error": "Wiederherstellungsschlüssel konnte nicht generiert werden. {}", + "key_receiving_description": "Notieren Sie sich diesen Schlüssel und bewahren Sie ihn an einem sicheren Ort auf. Es wird verwendet, um den vollen Zugriff auf Ihren Server wiederherzustellen:" + }, + "video": { + "title": "Videokonferenz", + "subtitle": "Zoom und Google Meet sind gut, aber Jitsi Meet ist eine wertvolle Alternative, ber der Sie sicher sein können, das Ihre Gespräche nicht abgehört werden.", + "login_info": "Kein Konto erforderlich." + }, + "cloud": { + "title": "Cloud-Speicher", + "subtitle": "Erlauben Sie Cloud-Diensten nicht, Ihre Daten zu lesen, indem Sie NextCloud verwenden.", + "login_info": "Login für den Administrator ist admin, Passwort ist dasselbe wie bei Ihrem Hauptbenutzer. Erstellen Sie neue Konten in der Nextcloud-Oberfläche." + }, + "social_network": { + "title": "Soziales Netzwerk", + "subtitle": "Es ist schwer zu glauben, aber jetzt ist es möglich ein eigenes soziales Netzwerk mit Ihren eigenen Regeln und einer eigenen Zielgruppe zu erstellen.", + "login_info": "Sie müssen ein Konto auf der Website erstellen." + }, + "git": { + "title": "Git-Server", + "subtitle": "Private Alternative zum Github, die Ihnen und nicht Microsoft gehört.", + "login_info": "Sie müssen ein Konto auf der Website erstellen. Der erste Benutzer wird zum Administrator." + }, + "vpn": { + "title": "VPN-Server", + "subtitle": "Privater VPN-Server" + }, + "timer": { + "sec": "{} Sek" + }, + "support": { + "title": "SelfPrivacy-Unterstützung" + }, + "developer_settings": { + "title": "Einstellungen für Entwickler", + "subtitle": "Diese Einstellungen sind nur für Debugging-Zwecke gedacht. Ändern Sie sie nicht, wenn Sie nicht wissen, was Sie tun.", + "server_setup": "Server-Einrichtung", + "use_staging_acme": "Verwendung des ACME-Testservers", + "use_staging_acme_description": "Wird beim Einrichten eines neuen Servers verwendet.", + "routing": "Anwendungsrouting", + "reset_onboarding": "Setzen Sie die Willkommensflagge für den Besuch zurück", + "reset_onboarding_description": "Zurücksetzen des Netzschalters, um den Einschaltbildschirm erneut aufzurufen", + "cubit_statuses": "Aktueller Status der Ladequbits", + "ignore_tls": "Überprüfen Sie keine TLS-Zertifikate", + "ignore_tls_description": "Die Anwendung validiert TLS-Zertifikate nicht, wenn sie eine Verbindung zum Server herstellt." + } +} diff --git a/assets/translations/en.json b/assets/translations/en.json index 46cee9b8..95a22f58 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -1,122 +1,142 @@ { - "test": "en-test", - "locale": "en", - "basis": { - "_comment": "Basic interface elements", - "providers": "Providers", - "services": "Services", - "users": "Users", - "more": "More", - "next": "Next", - "got_it": "Got it", - "settings": "Settings", - "password": "Password", - "create": "Add new", - "confirmation": "Confirmation", - "cancel": "Cancel", - "delete": "Delete", - "close": "Close", - "connect": "Connect", - "domain": "Domain", - "saving": "Saving..", - "nickname": "Nickname", - "loading": "Loading...", - "later": "Skip to setup later", - "connect_to_existing": "Connect to an existing server", - "reset": "Reset", - "details": "Details", - "no_data": "No data", - "wait": "Wait", - "remove": "Remove", - "apply": "Apply", - "done": "Done" - }, - "more": { - "_comment": "'More' tab", - "configuration_wizard": "Setup wizard", - "about_project": "About us", - "about_app": "About application", - "onboarding": "Onboarding", - "create_ssh_key": "Create SSH key", - "generate_key": "Generate key", - "generate_key_text": "You can generate ssh key", - "console": "Console", - "remove": "Remove", - "enable": "Enable", - "ok": "ok", - "continue": "Continue", - "ssh_key_exist_text": "You have generated ssh key", - "yes_delete": "Yes, delete my SSH key", - "share": "Share", - "copy_buffer": "Copy to buffer", - "copied_ssh": "SSH copied to clipboard", - "delete_ssh_text": "Delete SSH key?", - "about_app_page": { - "text": "Application version v.{}" + "test": "en-test", + "locale": "en", + "basis": { + "app_name": "SelfPrivacy", + "providers": "Providers", + "providers_title": "Your Data Center", + "select": "Select", + "services": "Services", + "services_title": "Your personal, private and independent services.", + "users": "Users", + "more": "More", + "next": "Next", + "got_it": "Got it", + "settings": "Settings", + "password": "Password", + "create": "Add new", + "confirmation": "Confirmation", + "cancel": "Cancel", + "delete": "Delete", + "close": "Close", + "connect": "Connect", + "domain": "Domain", + "saving": "Saving…", + "username": "Username", + "loading": "Loading…", + "later": "Skip to setup later", + "connect_to_existing": "Connect to an existing server", + "reset": "Reset", + "details": "Details", + "no_data": "No data", + "wait": "Wait", + "remove": "Remove", + "apply": "Apply", + "done": "Done", + "continue": "Continue", + "alert": "Alert", + "copied_to_clipboard": "Copied to clipboard!", + "please_connect": "Please connect your server, domain and DNS provider to dive in!" }, - "settings": { - "title": "Application settings", - "1": "Dark Theme", - "2": "Change your the app theme", - "3": "Reset app config", - "4": "Reset api keys and root user", - "5": "Delete Server", - "6": "This removes the Server. It will be no longer accessible" - } - }, - "ssh": { - "title": "SSH keys", - "create": "Create SSH key", - "delete": "Delete SSH key", - "delete_confirm_question": "Are you sure you want to delete SSH key?", - "subtitle_with_keys": "{} keys", - "subtitle_without_keys": "No keys", - "no_key_name": "Unnamed key", - "root": { - "title": "These are superuser keys", - "subtitle": "Owners of these keys get full access to the server and can do anything on it. Only add your own keys to the server." + "more_page": { + "configuration_wizard": "Setup wizard", + "about_project": "About us", + "about_application": "About", + "onboarding": "Onboarding", + "create_ssh_key": "Superuser SSH keys", + "console": "Console", + "application_settings": "Application settings" }, - "input_label": "Public ED25519 or RSA key" - }, - "onboarding": { - "_comment": "Onboarding pages", - "page1_title": "Digital independence, available to all of us", - "page1_text": "Mail, VPN, Messenger, social network and much more on your private server, under your control.", - "page2_title": "SelfPrivacy — it's not a cloud, but your personal datacenter", - "page2_text": "SelfPrivacy works only with your provider accounts: Hetzner, Cloudflare, Backblaze. If you do not own those, we'll help you to create them" - }, - "providers": { - "_comment": "'Providers' tab", - "page_title": "Your Data Center", - "server": { - "card_title": "Server", - "status": "Status — Good", - "bottom_sheet": { - "1": "It's a virtual computer, where all your services live.", - "2": "General information", - "3": "Location" - }, - "chart": { + "console_page": { + "title": "Console", + "waiting": "Waiting for initialization…", + "copy": "Copy" + }, + "about_us_page": { + "title": "About us" + }, + "about_application_page": { + "title": "About", + "application_version_text": "Application version {}", + "api_version_text": "Server API version {}", + "privacy_policy": "Privacy policy" + }, + "application_settings": { + "title": "Application settings", + "system_dark_theme_title": "System default theme", + "system_dark_theme_description": "Use light or dark theme depending on system settings", + "dark_theme_title": "Dark theme", + "dark_theme_description": "Switch your application theme", + "dangerous_settings": "Dangerous settings", + "reset_config_title": "Reset application config", + "reset_config_description": "Resets API keys and root user.", + "delete_server_title": "Delete server", + "delete_server_description": "This removes your server. It will be no longer accessible." + }, + "ssh": { + "title": "SSH keys", + "create": "Create SSH key", + "delete": "Delete SSH key", + "delete_confirm_question": "Are you sure you want to delete SSH key?", + "subtitle_with_keys": "{} keys", + "subtitle_without_keys": "No keys", + "no_key_name": "Unnamed key", + "root_title": "These are superuser keys", + "root_subtitle": "Owners of these keys get full access to the server and can do anything on it. Only add your own keys to the server.", + "input_label": "Public ED25519, ECDSA or RSA key" + }, + "onboarding": { + "page1_title": "Digital independence, available to all of us", + "page1_text": "Mail, VPN, Messenger, social network and much more on your private server, under your control.", + "page2_title": "SelfPrivacy is not a cloud, it's Your personal datacenter", + "page2_text": "SelfPrivacy only works with providers that you choose. If you do not have required accounts in those, we'll help you to create them.", + "page2_server_provider_title": "Server provider", + "page2_server_provider_text": "A server provider maintains your server in its own data center. SelfPrivacy will automatically connect to the provider and setup all necessary things.", + "page2_dns_provider_title": "DNS provider", + "page2_dns_provider_text": "You need a domain to have a place in the Internet. And you also need a reliable DNS provider to have the domain pointed to your server. We will suggest you pick a supported DNS provider to automatically setup networking.", + "page2_backup_provider_title": "Backup provider", + "page2_backup_provider_text": "What if something happens to your server? Imagine a hacker attack, an accidental data deletion or denial of service? Your data will be kept safe in your provider of backups. They will be securely encrypted and anytime accessible to restore your server with." + }, + "resource_chart": { "month": "Month", "day": "Day", - "hour": "Hour" - } + "hour": "Hour", + "cpu_title": "CPU Usage", + "network_title": "Network Usage", + "in": "In", + "out": "Out" }, - "domain": { - "card_title": "Domain", - "status": "Status — Good", - "bottom_sheet": { - "1": "It's your personal internet address that will point to the server and other services of yours." - }, - "screen_title": "Domain and DNS", - "states": { - "ok": "Records are OK", - "error": "Problems found", - "error_subtitle": "Tap here to fix them", - "refreshing": "Refreshing status...", - "uninitialized": "Data is not retrieved yet" - }, - "record_description": { + "server": { + "card_title": "Server", + "description": "All your services live here", + "general_information": "General information", + "resource_usage": "Resource usage", + "allow_autoupgrade": "Allow auto-upgrade", + "allow_autoupgrade_hint": "Allow automatic packages upgrades on server", + "reboot_after_upgrade": "Reboot after upgrade", + "reboot_after_upgrade_hint": "Reboot without prompt after applying changes on server", + "server_timezone": "Server timezone", + "select_timezone": "Select timezone", + "timezone_search_bar": "Timezone name or time shift value", + "server_id": "Server ID", + "status": "Status", + "cpu": "CPU", + "ram": "Memory", + "disk": "Disk local", + "monthly_cost": "Monthly cost", + "location": "Location", + "pricing_error": "Couldn't fetch provider prices", + "server_provider": "Server Provider", + "dns_provider": "DNS Provider", + "core_count": { + "one": "{} core", + "two": "{} cores", + "few": "{} cores", + "many": "{} cores", + "other": "{} cores" + } + }, + "record": { "root": "Root domain", "api": "SelfPrivacy API", "cloud": "File cloud", @@ -129,295 +149,483 @@ "dmarc": "DMARC record", "spf": "SPF record", "dkim": "DKIM key" - }, - "cards": { - "services": { - "title": "Services", - "subtitle": "Type “A” records required for each service." - }, - "email": { - "title": "Email", - "subtitle": "Records necessary for secure email exchange." - } - } + }, + "domain": { + "card_title": "Domain", + "screen_title": "Domain and DNS", + "ok": "Records are OK", + "error": "Problems found", + "error_subtitle": "Tap here to fix them. This will also remove custom records.", + "refreshing": "Refreshing status…", + "uninitialized": "Data is not retrieved yet", + "services_title": "Services", + "services_subtitle": "Type “A” records required for each service.", + "email_title": "Email", + "email_subtitle": "Records necessary for secure email exchange.", + "update_list": "Update list" }, "backup": { - "card_title": "Backup", - "status": "Status — Good", - "bottom_sheet": { - "1": "Will save your day in case of incident: hackers attack, server deletion, etc.", - "2": "3Gb/10Gb, last backup was yesterday {}" - }, - "reuploadKey": "Force reupload key", - "reuploadedKey": "Key reuploaded", - "initialize": "Initialize", - "waitingForRebuild": "You will be able to create your first backup in a few minutes.", - "restore": "Restore from backup", - "no_backups": "There are no backups yet", - "create_new": "Create a new backup", - "creating": "Creating a new backup: {}%", - "restoring": "Restoring from backup", - "error_pending": "Server returned error, check it below", - "restore_alert": "You are about to restore from backup created on {}. All current data will be lost. Are you sure?", - "refresh": "Refresh status", - "refetchBackups": "Refetch backup list", - "refetchingList": "In a few minutes list will be updated" - } - }, - "not_ready_card": { - "_comment": "Card shown when user skips initial setup", - "1": "Please finish application setup using ", - "2": "@:more.configuration_wizard", - "3": " for further work", - "in_menu": "Server is not set up yet. Please finish setup using setup wizard for further work." - }, - "services": { - "_comment": "Вкладка сервисы", - "title": "Your personal, private and independent services.", - "mail": { - "title": "E-Mail", - "subtitle": "E-Mail for company and family.", - "login_info": "Use username and password from users tab. IMAP port is 143 with STARTTLS, SMTP port is 587 with STARTTLS.", - "bottom_sheet": { - "1": "To connect to the mailserver, please use {} domain alongside with username and password, that you created. Also feel free to invite", - "2": "new users" - } + "card_title": "Backup", + "card_subtitle": "Manage your backups", + "description": "Will save your day in case of incident: hackers attack, server deletion, etc.", + "reupload_key": "Force reupload key", + "reuploaded_key": "Key reuploaded", + "initialize": "Initialize", + "waiting_for_rebuild": "You will be able to create your first backup in a few minutes.", + "restore": "Restore from backup", + "no_backups": "There are no backups yet", + "create_new": "Create a new backup", + "creating": "Creating a new backup: {}%", + "restoring": "Restoring from backup", + "error_pending": "Server returned error, check it below", + "restore_alert": "You are about to restore from backup created on {}. All current data will be lost. Are you sure?", + "refresh": "Refresh status", + "refetch_backups": "Refetch backup list", + "refetch_backups_subtitle": "Invalidate cache and refetch data from your storage provider. May cause additional charges.", + "reupload_key_subtitle": "Will instruct the server to initialize backup storage again. Use if something is broken.", + "refetching_list": "In a few minutes list will be updated", + "select_all": "Backup everything", + "create_new_select_heading": "Select what to backup", + "start": "Start backup", + "service_busy": "Another backup operation is in progress", + "latest_snapshots": "Latest snapshots", + "latest_snapshots_subtitle": "Showing last 15 snapshots", + "show_more": "Show more", + "autobackup_period_title": "Automatic backups period", + "autobackup_period_subtitle": "Backups created every {period}", + "autobackup_period_never": "Automatic backups are disabled", + "autobackup_period_every": "Every {period}", + "autobackup_period_disable": "Disable automatic backups", + "autobackup_custom": "Custom", + "autobackup_custom_hint": "Enter custom period in minutes", + "autobackup_set_period": "Set period", + "autobackup_period_set": "Period set", + "backups_encryption_key": "Encryption key", + "backups_encryption_key_subtitle": "Keep it in a safe place.", + "backups_encryption_key_copy": "Copy the encryption key", + "backups_encryption_key_show": "Show the encryption key", + "backups_encryption_key_description": "This key is used to encrypt your backups. If you lose it, you will not be able to restore your backups. Keep it in a safe place, as it will be useful if you ever need to restore from backups manually.", + "backups_encryption_key_not_found": "Encryption key not found yet, please try again later.", + "pending_jobs": "Currently running backup jobs", + "snapshots_title": "Snapshot list", + "forget_snapshot": "Forget snapshot", + "forget_snapshot_alert": "You are about to delete this snapshot. Are you sure? This action usually cannot be undone.", + "forget_snapshot_error": "Couldn't forget snapshot", + "snapshot_modal_heading": "Snapshot details", + "snapshot_service_title": "Service", + "snapshot_creation_time_title": "Creation time", + "snapshot_id_title": "Snapshot ID", + "snapshot_modal_select_strategy": "Select the restore strategy", + "snapshot_modal_download_verify_option_title": "Download, verify and then replace", + "snapshot_modal_download_verify_option_description": "Less risk, but more free space needed. Downloads entire snapshot to the temporary storage, verifies it and then replaces the current data.", + "snapshot_modal_inplace_option_title": "Replace in place", + "snapshot_modal_inplace_option_description": "Less free space needed, but more risk. Replaces current data with the snapshot data during the download.", + "snapshot_modal_service_not_found": "This is a snapshot of a service you don't have on your server anymore. Usually this shouldn't happen, and we cannot do the automatic restore. You can still download the snapshot and restore it manually. Contact SelfPrivacy support if you need help.", + "restore_started": "Restore started, check the jobs list for the current status", + "snapshot_reason_title": "Creation reason", + "snapshot_reasons": { + "auto": "Created automatically", + "explicit": "Created by your explicit request", + "pre_restore": "Created as a precaution before risky restore", + "unknown": "Unknown" + }, + "rotation_quotas_title": "Snapshot rotation settings", + "set_rotation_quotas": "Set new rotation quotas", + "quotas_set": "New backup rotation quotas set", + "quotas_only_applied_to_autobackups": "These settings are only applied to automatic backups. Manual backups won't get deleted.", + "quota_titles": { + "last": "How many latest backups to keep", + "daily": "How many daily backups to keep", + "weekly": "How many weekly backups to keep", + "monthly": "How many monthly backups to keep", + "yearly": "How many yearly backups to keep" + }, + "quota_subtitles": { + "no_effect": "This rule has no effect because another rule will keep more backups", + "last": { + "zero": "Rule is disabled", + "one": "Last {} backup will be kept regardless of its age", + "two": "Last {} backups will be kept regardless of their age", + "few": "Last {} backups will be kept regardless of their age", + "many": "Last {} backups will be kept regardless of their age", + "other": "Last {} backups will be kept regardless of their age" + }, + "last_infinite": "All backups will be kept", + "daily": { + "zero": "Rule is disabled", + "one": "Last {} daily backup will be kept", + "two": "Last {} daily backups will be kept", + "few": "Last {} daily backups will be kept", + "many": "Last {} daily backups will be kept", + "other": "Last {} daily backups will be kept" + }, + "daily_infinite": "All daily backups will be kept", + "weekly": { + "zero": "Rule is disabled", + "one": "Last {} weekly backup will be kept", + "two": "Last {} weekly backups will be kept", + "few": "Last {} weekly backups will be kept", + "many": "Last {} weekly backups will be kept", + "other": "Last {} weekly backups will be kept" + }, + "weekly_infinite": "All weekly backups will be kept", + "monthly": { + "zero": "Rule is disabled", + "one": "Last {} monthly backup will be kept", + "two": "Last {} monthly backups will be kept", + "few": "Last {} monthly backups will be kept", + "many": "Last {} monthly backups will be kept", + "other": "Last {} monthly backups will be kept" + }, + "monthly_infinite": "All monthly backups will be kept", + "yearly": { + "zero": "Rule is disabled", + "one": "Last {} yearly backup will be kept", + "two": "Last {} yearly backups will be kept", + "few": "Last {} yearly backups will be kept", + "many": "Last {} yearly backups will be kept", + "other": "Last {} yearly backups will be kept" + }, + "yearly_infinite": "All yearly backups will be kept" + } }, - "messenger": { - "title": "Messenger", - "subtitle": "Telegram or Signal not so private as Delta.Chat that uses your private server.", - "login_info": "Use the same username and password as for e-mail.", - "bottom_sheet": { - "1": "For connection, please use {} domain and credentials that you created." - } + "storage": { + "card_title": "Server Storage", + "status_ok": "Disk usage is OK", + "status_error": "Low disk space", + "disk_usage": "{} used", + "disk_total": "{} total · {}", + "gb": "{} GB", + "mb": "{} MB", + "kb": "{} KB", + "bytes": "Bytes", + "extend_volume_button": "Extend volume", + "extending_volume_title": "Extending volume", + "extending_volume_description": "Resizing volume will allow you to store more data on your server without extending the server itself. Volume can only be extended: shrinking is not possible.", + "extending_volume_price_info": "Price includes VAT and is estimated from pricing data provided by your server provider. Server will be rebooted after resizing.", + "extending_volume_error": "Couldn't initialize volume extending.", + "extending_volume_modal_description": "Upgrade to {} for {} plan per month.", + "size": "Size", + "price": "Price", + "data_migration_title": "Data migration", + "data_migration_notice": "During migration all services will be turned off.", + "start_migration_button": "Start migration", + "migration_process": "Migrating…", + "migration_done": "Finish" + }, + "not_ready_card": { + "in_menu": "Server is not set up yet. Please finish setup using setup wizard for further work." + }, + "service_page": { + "nothing_here": "Nothing here", + "open_in_browser": "Open in browser", + "restart": "Restart service", + "disable": "Disable service", + "enable": "Enable service", + "move": "Move to another volume", + "uses": "Uses {usage} on {volume}", + "snapshots": "Backup snapshots", + "status": { + "active": "Up and running", + "inactive": "Stopped", + "failed": "Failed to start", + "off": "Disabled", + "activating": "Activating", + "deactivating": "Deactivating", + "reloading": "Restarting" + } + }, + "mail": { + "title": "E-Mail", + "subtitle": "E-Mail for company and family.", + "login_info": "Use username and password from users tab. IMAP port is 143 with STARTTLS, SMTP port is 587 with STARTTLS." }, "password_manager": { - "title": "Password Manager", - "subtitle": "Base of your security. Bitwarden will help you to create, store and move passwords between devices, as well as input them, when requested using autocompletion.", - "login_info": "You will have to create an account on the website.", - "bottom_sheet": { - "1": "You can connect to the service and create a user via this link:" - } + "title": "Password Manager", + "subtitle": "Base of your security. Bitwarden will help you to create, store and move passwords between devices, as well as input them, when requested using autocompletion.", + "login_info": "You will have to create an account on the website." }, "video": { - "title": "Videomeet", - "subtitle": "Zoom and Google Meet are good, but Jitsi Meet is a worth alternative that also gives you confidence that you're not being listened.", - "login_info": "No account needed.", - "bottom_sheet": { - "1": "Using Jitsi as simple as just visiting this link:" - } + "title": "Videomeet", + "subtitle": "Zoom and Google Meet are good, but Jitsi Meet is a worth alternative that also gives you confidence that you're not being listened.", + "login_info": "No account needed." }, "cloud": { - "title": "Cloud Storage", - "subtitle": "Do not allow cloud services to read your data by using NextCloud.", - "login_info": "Login is admin, password is the same as with your main user. Create new accounts in Nextcloud interface.", - "bottom_sheet": { - "1": "You can connect and create a new user here:" - } + "title": "Cloud Storage", + "subtitle": "Do not allow cloud services to read your data by using NextCloud.", + "login_info": "Login is admin, password is the same as with your main user. Create new accounts in Nextcloud interface." }, "social_network": { - "title": "Social Network", - "subtitle": "It's hard to believe, but it became possible to create your own social network, with your own rules and target audience.", - "login_info": "You will have to create an account on the website.", - "bottom_sheet": { - "1": "You can connect and create new social user here:" - } + "title": "Social Network", + "subtitle": "It's hard to believe, but it became possible to create your own social network, with your own rules and target audience.", + "login_info": "You will have to create an account on the website." }, "git": { - "title": "Git Server", - "subtitle": "Private alternative to the Github, that belongs to you, but not a Microsoft.", - "login_info": "You will have to create an account on the website. First user will become an admin.", - "bottom_sheet": { - "1": "You can connect and create a new user here:" - } + "title": "Git Server", + "subtitle": "Private alternative to the Github, that belongs to you, but not a Microsoft.", + "login_info": "You will have to create an account on the website. First user will become an admin." }, "vpn": { - "title": "VPN Server", - "subtitle": "Private VPN server", - "bottom_sheet": { - "1": "Openconnect VPN Server. Engine for secure and scalable VPN infrastructure" - } - } - }, - "users": { - "_comment": "'Users' tab", - "add_new_user": "Add a first user", - "new_user": "New user", - "not_ready": "Please connect server, domain and DNS in the Providers tab, to be able to add a first user", - "nobody_here": "Nobody here", - "login": "Login", - "onboarding": "Onboarding", - "console": "Console", - "new_user_info_note": "New user will automatically be granted an access to all of the services", - "delete_confirm_question": "Are you sure?", - "reset_password": "Reset password", - "account": "Account", - "send_registration_data": "Share login credentials" - }, - "initializing": { - "_comment": "initializing page", - "1": "Connect a server", - "2": "A place where your data and SelfPrivacy services will reside:", - "how": "How to obtain API token", - "3": "Connect CloudFlare", - "4": "To manage your domain's DNS", - "5": "CloudFlare API Token", - "6": "Connect Backblaze storage", - "7": "No connected domains at the moment", - "8": "Loading domains list", - "9": "Found more than one domain. For your own security, please be asked to delete unnecessary domains", - "10": "Save domain", - "final": "Final step", - "11": "Create server", - "what": "What does it mean?", - "13": "Server rebooted. Waiting for the last verification...", - "14": "Server started. It will be validated and rebooted now...", - "15": "Server created. DNS checks and server boot in progress...", - "16": "Until the next check: ", - "17": "Check", - "19": "1 Go via this link ", - "20": "\n", - "21": "One more restart to apply your security certificates.", - "22": "Create master account", - "23": "Enter a nickname and strong password", - "finish": "Everything is initialized", - "checks": "Checks have been completed \n{} ouf of {}" - }, - "recovering": { - "recovery_main_header": "Connect to an existing server", - "domain_recovery_description": "Enter a server domain you want to get access for:", - "domain_recover_placeholder": "Your domain", - "domain_recover_error": "Server with such domain was not found", - "method_select_description": "Select a recovery method:", - "method_select_other_device": "I have access on another device", - "method_select_recovery_key": "I have a recovery key", - "method_select_nothing": "I don't have any of that", - "method_device_description": "Open the application on another device, then go to the devices page. Press \"Add device\" to receive your token.", - "method_device_button": "I have received my token", - "method_device_input_description": "Enter your authorization token", - "method_device_input_placeholder": "Token", - "method_recovery_input_description": "Enter your recovery key", - "fallback_select_description": "What exactly do you have? Pick the first available option:", - "fallback_select_token_copy": "Copy of auth token from other version of the application.", - "fallback_select_root_ssh": "Root SSH access to the server.", - "fallback_select_provider_console": "Access to the server console of my prodiver.", - "authorization_failed": "Couldn't log in with this key", - "fallback_select_provider_console_hint": "For example: Hetzner.", - "hetzner_connected": "Connect to Hetzner", - "hetzner_connected_description": "Communication established. Enter Hetzner token with access to {}:", - "hetzner_connected_placeholder": "Hetzner token", - "confirm_server": "Confirm server", - "confirm_server_description": "Found your server! Confirm it is correct.", - "confirm_server_accept": "Yes! That's it", - "confirm_server_decline": "Choose a different server", - "choose_server": "Choose your server", - "choose_server_description": "We couldn't figure out which server your are trying to connect to.", - "no_servers": "There is no available servers on your account.", - "domain_not_available_on_token": "Selected domain is not available on this token.", - "modal_confirmation_title": "Is it really your server?", - "modal_confirmation_description": "If you connect to a wrong server you may lose all your data.", - "modal_confirmation_dns_valid": "Reverse DNS is valid", - "modal_confirmation_dns_invalid": "Reverse DNS points to another domain", - "modal_confirmation_ip_valid": "IP is the same as in DNS record", - "modal_confirmation_ip_invalid": "IP is not the same as in DNS record", - "confirm_cloudflare": "Connect to CloudFlare", - "confirm_cloudflare_description": "Enter a Cloudflare token with access to {}:", - "confirm_backblaze": "Connect to Backblaze", - "confirm_backblaze_description": "Enter a Backblaze token with access to backup storage:" - }, - "devices": { - "main_screen": { - "header": "Devices", - "description": "These devices have full access to the server via SelfPrivacy app.", - "this_device": "This device", - "other_devices": "Other devices", - "authorize_new_device": "Authorize new device", - "access_granted_on" : "Access granted on {}", - "tip": "Press on the device to revoke access." + "title": "VPN Server", + "subtitle": "Private VPN server" }, - "add_new_device_screen": { - "header": "Authorizing new device", - "description": "Enter the key on the device you want to authorize:", - "please_wait": "Please wait", - "tip": "The key is valid for 10 minutes.", - "expired": "The key has expired.", - "get_new_key": "Get new key" + "users": { + "details_title": "User details", + "add_new_user": "Add a first user", + "new_user": "New user", + "delete_user": "Delete user", + "nobody_here": "Nobody here", + "login": "Login", + "new_user_info_note": "New user will automatically be granted an access to all of the services", + "delete_confirm_question": "Are you sure?", + "reset_password": "Reset password", + "account": "Account", + "send_registration_data": "Share login credentials", + "could_not_fetch_users": "Couldn't fetch users list", + "could_not_fetch_description": "Please check your internet connection and try again", + "refresh_users": "Refresh users list", + "could_not_create_user": "Couldn't create user", + "could_not_delete_user": "Couldn't delete user", + "could_not_add_ssh_key": "Couldn't add SSH key", + "username_rule": "Username must contain only lowercase latin letters, digits and underscores, should not start with a digit", + "email_login": "Email login", + "no_ssh_notice": "Only email and SSH accounts are created for this user. Single Sign On for all services is coming soon." }, - "revoke_device_alert": { - "header": "Revoke access?", - "description": "The device {} will no longer have access to the server.", - "yes": "Revoke", - "no": "Cancel" + "initializing": { + "server_provider_description": "A place where your data and SelfPrivacy services will reside:", + "dns_provider_description": "A service which lets your IP point towards domain names:", + "connect_to_server": "Let's start with a server.", + "select_provider": "Pick any provider from the following list, they all support SelfPrivacy", + "select_provider_notice": "By 'Relatively small' we mean a machine with 2 cores of CPU and 2 gigabytes of RAM.", + "select_provider_countries_title": "Available countries", + "select_provider_countries_text_hetzner": "Germany, Finland, USA", + "select_provider_countries_text_do": "USA, Netherlands, Singapore, UK, Germany, Canada, India, Australia", + "select_provider_price_title": "Average price", + "select_provider_price_free": "Free", + "select_provider_price_text_hetzner": "€8 per month for a relatively small server and 50GB of disk storage", + "select_provider_price_text_do": "$17 per month for a relatively small server and 50GB of disk storage", + "select_provider_payment_title": "Payment methods", + "select_provider_payment_text_hetzner": "Credit cards, SWIFT, SEPA, PayPal", + "select_provider_payment_text_do": "Credit cards, Google Pay, PayPal", + "select_provider_payment_text_cloudflare": "Credit cards", + "select_provider_email_notice": "E-mail hosting won't be available for new clients. Nevertheless it will be unlocked as soon as you complete your first payment.", + "select_provider_site_button": "Visit site", + "connect_to_server_provider": "Now log in ", + "connect_to_server_provider_text": "With API token SelfPrivacy will be able to rent a machine and setup your server on it", + "how": "How to obtain API token", + "provider_bad_key_error": "Provider API key is invalid", + "could_not_connect": "Counldn't connect to the provider.", + "choose_location_type": "Where do you want to order your server?", + "choose_location_type_text": "Different locations provide different server configurations, prices and connection speed.", + "locations_not_found": "Oops!", + "locations_not_found_text": "There are no available servers to rent", + "back_to_locations": "Select something else", + "no_locations_found": "No available locations found, make sure your account is accessible", + "choose_server_type": "What type of server do you need?", + "choose_server_type_text": "Different resource capabilities support different services. Don't worry, you can expand your server anytime", + "choose_server_type_notice": "The important things to look at are the CPU and RAM. The data of your services will be stored on a mounted volume which is easily expandable and gets paid for separately.", + "choose_server_type_ram": "{} GB of RAM", + "choose_server_type_storage": "{} GB of system storage", + "choose_server_type_payment_per_month": "{} per month", + "choose_server_type_payment_server": "{} for the server", + "choose_server_type_payment_storage": "{} for additional storage", + "choose_server_type_payment_ip": "{} for the public IPv4 address", + "no_server_types_found": "No available server types found. Make sure your account is accessible and try to change your server location.", + "dns_provider_bad_key_error": "API key is invalid", + "backblaze_bad_key_error": "Backblaze storage information is invalid", + "connect_to_dns": "Connect the DNS provider", + "connect_to_dns_provider_text": "With API token SelfPrivacy will manage all DNS entries", + "select_dns": "Now let's select a DNS provider", + "manage_domain_dns": "To manage your domain's DNS", + "use_this_domain": "Use this domain?", + "use_this_domain_text": "The token you provided gives access to the following domain", + "multiple_domains_found": "Multiple domains found", + "multiple_domains_found_text": "The token you provided gives access to the following domains. Please select the one you want to use. For the security of your other domains, you should restrict this token's access to only the domain you want to use with SelfPrivacy.", + "connect_backblaze_storage": "Connect Backblaze storage", + "no_connected_domains": "No connected domains at the moment", + "loading_domain_list": "Loading domain list", + "found_more_domains": "Found more than one domain. For your own security, please be asked to delete unnecessary domains", + "save_domain": "Save domain", + "final": "Final step", + "create_server": "Create server", + "what": "What does it mean?", + "server_rebooted": "Server rebooted. Waiting for the last verification…", + "server_started": "Server started. It will be validated and rebooted now…", + "server_created": "Server created. DNS checks and server boot in progress…", + "until_the_next_check": "Until the next check: ", + "check": "Check", + "one_more_restart": "One more restart to apply your security certificates.", + "create_master_account": "Create master account", + "enter_username_and_password": "Enter username and strong password", + "finish": "Everything is initialized", + "checks": "Checks have been completed \n{} out of {}", + "steps": { + "hosting": "Hosting", + "server_type": "Server type", + "dns_provider": "DNS provider", + "backups_provider": "Backups", + "domain": "Domain", + "master_account": "Master account", + "server": "Server", + "dns_setup": "DNS setup", + "nixos_installation": "NixOS installation", + "server_reboot": "Server reboot", + "final_checks": "Final checks" + } + }, + "recovering": { + "generic_error": "Operation failed, please try again.", + "recovery_main_header": "Connect to an existing server", + "domain_recovery_description": "Enter a server domain you want to get access for:", + "domain_recover_placeholder": "Your domain", + "domain_recover_error": "Server with such domain was not found", + "method_select_description": "Select a recovery method:", + "method_select_other_device": "I have access on another device", + "method_select_recovery_key": "I have a recovery key", + "method_select_nothing": "I don't have any of that", + "method_device_description": "Open the application on another device, then go to the devices page. Press \"Add device\" to receive your token.", + "method_device_button": "I have received my token", + "method_device_input_description": "Enter your authorization token", + "method_device_input_placeholder": "Token", + "method_recovery_input_description": "Enter your recovery key", + "fallback_select_description": "What exactly do you have? Pick the first available option:", + "fallback_select_token_copy": "Copy of auth token from other version of the application.", + "fallback_select_root_ssh": "Root SSH access to the server.", + "fallback_select_provider_console": "Access to the server console of my provider.", + "authorization_failed": "Couldn't log in with this key", + "fallback_select_provider_console_hint": "For example: Hetzner.", + "provider_connected": "Connect to {}", + "provider_connected_description": "Enter your token with access to {}:", + "provider_connected_placeholder": "{} token", + "confirm_server": "Confirm server", + "confirm_server_description": "Found your server! Confirm it is the right one:", + "confirm_server_accept": "Yes! That's it", + "confirm_server_decline": "Choose a different server", + "choose_server": "Choose your server", + "choose_server_description": "We couldn't figure out which server your are trying to connect to.", + "no_servers": "There is no available servers on your account.", + "domain_not_available_on_token": "Selected domain is not available on this token.", + "modal_confirmation_title": "Is it really your server?", + "modal_confirmation_description": "If you connect to a wrong server you may lose all your data.", + "modal_confirmation_dns_valid": "Reverse DNS is valid", + "modal_confirmation_dns_invalid": "Reverse DNS points to another domain", + "modal_confirmation_ip_valid": "IP is the same as in DNS record", + "modal_confirmation_ip_invalid": "IP is not the same as in DNS record" + }, + "devices": { + "main_screen": { + "header": "Devices", + "description": "These devices have full access to the server via SelfPrivacy app.", + "this_device": "This device", + "other_devices": "Other devices", + "authorize_new_device": "Authorize new device", + "access_granted_on": "Access granted on {}", + "tip": "Press on the device to revoke access." + }, + "add_new_device_screen": { + "header": "Authorizing new device", + "description": "Enter the key on the device you want to authorize:", + "please_wait": "Please wait", + "tip": "The key is valid for 10 minutes.", + "expired": "The key has expired.", + "get_new_key": "Get new key" + }, + "revoke_device_alert": { + "header": "Revoke access?", + "description": "The device {} will no longer have access to the server.", + "yes": "Revoke", + "no": "Cancel" + } + }, + "recovery_key": { + "key_connection_error": "Couldn't connect to the server.", + "key_synchronizing": "Synchronizing…", + "key_main_header": "Recovery key", + "key_main_description": "Is needed for SelfPrivacy authorization when all your other authorized devices aren't available.", + "key_amount_toggle": "Limit by number of uses", + "key_amount_field_title": "Max number of uses", + "key_duedate_toggle": "Limit by time", + "key_duedate_field_title": "Due date of expiration", + "key_receive_button": "Receive key", + "key_valid": "Your key is valid", + "key_invalid": "Your key is no longer valid", + "key_valid_until": "Valid until {}", + "key_valid_for": "Valid for {} uses", + "key_creation_date": "Created on {}", + "key_replace_button": "Generate new key", + "key_receiving_description": "Write down this key and put to a safe place. It is used to restore full access to your server:", + "key_receiving_info": "The key will never ever be shown again, but you will be able to replace it with another one.", + "key_receiving_done": "Done!", + "generation_error": "Couldn't generate a recovery key. {}" + }, + "modals": { + "dns_removal_error": "Couldn't remove DNS records.", + "server_deletion_error": "Couldn't delete active server.", + "volume_creation_error": "Couldn't create volume.", + "server_validators_error": "Couldn't fetch available servers.", + "already_exists": "Such server already exists.", + "unexpected_error": "Unexpected error during placement from the provider side.", + "destroy_server": "Destroy the server and create a new one?", + "try_again": "Try again?", + "are_you_sure": "Are you sure?", + "purge_all_keys": "Purge all authentication keys?", + "purge_all_keys_confirm": "Yes, purge all my tokens", + "delete_server_volume": "Delete the server and volume?", + "reboot": "Reboot", + "you_cant_use_this_api": "You cannot use this API for domains with such TLD.", + "yes": "Yes", + "no": "No" + }, + "timer": { + "sec": "{} sec" + }, + "jobs": { + "title": "Jobs list", + "start": "Start", + "empty": "No jobs", + "create_user": "Create user", + "delete_user": "Delete user", + "service_turn_off": "Turn off", + "service_turn_on": "Turn on", + "job_added": "Job added", + "run_jobs": "Run jobs", + "reboot_success": "Server is rebooting", + "reboot_failed": "Couldn't reboot the server. Check the app logs.", + "config_pull_failed": "Failed to pull configuration upgrade. Started software upgrade anyways.", + "upgrade_success": "Server upgrade started", + "upgrade_failed": "Failed to upgrade server", + "upgrade_server": "Upgrade server", + "reboot_server": "Reboot server", + "create_ssh_key": "Create SSH key for {}", + "delete_ssh_key": "Delete SSH key for {}", + "server_jobs": "Jobs on the server", + "reset_user_password": "Reset password of user", + "generic_error": "Couldn't connect to the server!" + }, + "validations": { + "required": "Required", + "already_exist": "Already exists", + "invalid_format": "Invalid format", + "invalid_format_password": "Password must not contain spaces", + "invalid_format_ssh": "Must follow the SSH key format", + "root_name": "Cannot be 'root'", + "length_not_equal": "Length is [], should be {}", + "length_longer": "Length is [], should be shorter than or equal to {}" + }, + "support": { + "title": "SelfPrivacy Support" + }, + "developer_settings": { + "title": "Developer settings", + "subtitle": "These settings are for debugging only. Don't change them unless you know what you're doing.", + "server_setup": "Server setup", + "use_staging_acme": "Use staging ACME server", + "use_staging_acme_description": "Applies when setting up a new server.", + "ignore_tls": "Do not verify TLS certificates", + "ignore_tls_description": "App will not verify TLS certificates when connecting to the server.", + "routing": "App routing", + "reset_onboarding": "Reset onboarding switch", + "reset_onboarding_description": "Reset onboarding switch to show onboarding screen again", + "cubit_statuses": "Cubit loading statuses" } - }, - "recovery_key": { - "key_connection_error": "Couldn't connect to the server.", - "key_synchronizing": "Synchronizing...", - "key_main_header": "Recovery key", - "key_main_description": "Is needed for SelfPrivacy authorization when all your other authorized devices aren't available.", - "key_amount_toggle": "Limit by number of uses", - "key_amount_field_title": "Max number of uses", - "key_duedate_toggle": "Limit by time", - "key_duedate_field_title": "Due date of expiration", - "key_receive_button": "Receive key", - "key_valid": "Your key is valid", - "key_invalid": "Your key is no longer valid", - "key_valid_until": "Valid until {}", - "key_valid_for": "Valid for {} uses", - "key_creation_date": "Created on {}", - "key_replace_button": "Generate new key", - "key_receiving_description": "Write down this key and put to a safe place. It is used to restore full access to your server:", - "key_receiving_info": "The key will never ever be shown again, but you will be able to replace it with another one.", - "key_receiving_done": "Done!", - "generation_error": "Couldn't generate a recovery key. {}" - }, - "modals": { - "_comment": "messages in modals", - "1": "Server with such name, already exist", - "2": "Destroy server and create a new one?", - "3": "Are you sure?", - "4": "Purge all authentication keys?", - "5": "Yes, purge all my tokens", - "6": "Delete the server and volume?", - "7": "Yes", - "8": "Remove task", - "9": "Reboot", - "10": "You cannot use this API for domains with such TLD.", - "yes": "Yes", - "no": "No" - }, - "timer": { - "sec": "{} sec" - }, - "jobs": { - "_comment": "Jobs list", - "title": "Jobs list", - "start": "Start", - "empty": "No jobs", - "createUser": "Create user", - "deleteUser": "Delete user", - "serviceTurnOff": "Turn off", - "serviceTurnOn": "Turn on", - "jobAdded": "Job added", - "runJobs": "Run jobs", - "rebootSuccess": "Server is rebooting", - "rebootFailed": "Couldn't reboot the server. Check the app logs.", - "configPullFailed": "Failed to pull configuration upgrade. Started software upgrade anyways.", - "upgradeSuccess": "Server upgrade started", - "upgradeFailed": "Failed to upgrade server", - "upgradeServer": "Upgrade server", - "rebootServer": "Reboot server", - "create_ssh_key": "Create SSH key for {}", - "delete_ssh_key": "Delete SSH key for {}" - }, - "validations": { - "required": "Required.", - "invalid_format": "Invalid format.", - "root_name": "User name cannot be 'root'.", - "key_format": "Invalid key format.", - "length_not_equal": "Length is []. Should be {}.", - "length_longer": "Length is []. Should be shorter than or equal to {}.", - "user_already_exist": "This user already exists.", - "key_already_exists": "This key already exists." - } -} +} \ No newline at end of file diff --git a/assets/translations/es.json b/assets/translations/es.json new file mode 100644 index 00000000..2c240edd --- /dev/null +++ b/assets/translations/es.json @@ -0,0 +1,92 @@ +{ + "basis": { + "providers_title": "Su Centro de Datos", + "select": "Seleccione", + "services": "Servicios", + "providers": "Proveedores", + "users": "Usuarios", + "more": "Más", + "next": "Próximo", + "got_it": "Entendido", + "settings": "Ajustes", + "password": "Contraseña", + "create": "Añadir nuevo", + "confirmation": "Confirmación", + "cancel": "Anular", + "delete": "Eliminar", + "services_title": "Tus servicios personales, privados e independientes.", + "connect": "Conecte", + "saving": "Ahorrar…", + "username": "Nombre de usuario", + "loading": "Cargando…", + "later": "Saltar a la configuración posterior", + "reset": "Restablecer", + "details": "Detalles", + "no_data": "Sin datos", + "wait": "Espere", + "alert": "Alerta", + "continue": "Continuar", + "close": "Cerrar", + "domain": "Dominio", + "remove": "Eliminar", + "apply": "Solicitar", + "done": "Hecho", + "connect_to_existing": "Conectarse a un servidor existente" + }, + "test": "es-test", + "locale": "es", + "application_settings": { + "reset_config_title": "Restablecer la configuración de la aplicación", + "dark_theme_description": "Cambia el tema de tu aplicación", + "reset_config_description": "Restablecer claves API y usuario root.", + "delete_server_title": "Eliminar servidor", + "delete_server_description": "Esto elimina su servidor. Ya no será accesible.", + "title": "Ajustes de la aplicación", + "dark_theme_title": "Tema oscuro" + }, + "ssh": { + "delete_confirm_question": "¿Está seguro de que desea eliminar la clave SSH?", + "root_subtitle": "Los propietarios de estas claves tienen acceso total al servidor y pueden hacer cualquier cosa en él. Añade sólo tus propias claves al servidor.", + "title": "SSH Claves", + "create": "Crear clave SSH", + "delete": "Eliminar clave SSH", + "subtitle_with_keys": "{} claves", + "subtitle_without_keys": "Sin llaves", + "no_key_name": "Clave sin nombre", + "root_title": "Estas son las claves de superusuario", + "input_label": "Clave pública ED25519 o RSA" + }, + "about_application_page": { + "application_version_text": "Versión de la aplicación v.{}", + "title": "Sobre", + "api_version_text": "Versión API del servidor v.{}", + "privacy_policy": "Política de privacidad" + }, + "onboarding": { + "page2_text": "SelfPrivacy sólo trabaja con los proveedores que usted elija. Si no tiene cuentas obligatorias en ellos, le ayudaremos a crearlas.", + "page1_title": "La independencia digital, al alcance de todos", + "page1_text": "Correo, VPN, Messenger, red social y mucho más en tu servidor privado, bajo tu control.", + "page2_title": "SelfPrivacy no es una nube, es tu centro de datos personal", + "page2_server_provider_title": "Servidor proveedor", + "page2_server_provider_text": "Un proveedor de servidores mantiene su servidor en su propio centro de datos. SelfPrivacy se conectará automáticamente al proveedor y configurará todo lo necesario.", + "page2_dns_provider_title": "Proveedor de DNS", + "page2_backup_provider_title": "Proveedor de copias de seguridad", + "page2_dns_provider_text": "Necesitas un dominio para tener un lugar en Internet. Y también necesitas un proveedor de DNS fiable para que el dominio apunte a tu servidor. Le sugeriremos que elija un proveedor de DNS compatible para configurar automáticamente la red." + }, + "more_page": { + "configuration_wizard": "Asistente de configuración", + "about_project": "Sobre nosotros", + "about_application": "Sobre", + "onboarding": "Incorporación", + "create_ssh_key": "Claves SSH de superusuario", + "console": "Consola", + "application_settings": "Ajustes de la aplicación" + }, + "console_page": { + "title": "Consola", + "waiting": "Esperando la inicialización…" + }, + "about_us_page": { + "title": "Sobre nosotros" + } +} \ No newline at end of file diff --git a/assets/translations/fr.json b/assets/translations/fr.json new file mode 100644 index 00000000..5fa6d1cc --- /dev/null +++ b/assets/translations/fr.json @@ -0,0 +1,288 @@ +{ + "test": "fr-test", + "locale": "fr", + "basis": { + "providers": "Fournisseurs", + "providers_title": "Ton Centre de Données", + "select": "Sélectionner", + "services": "Services", + "services_title": "Tes services personnels, privés et indépendants.", + "users": "Utilisateurs", + "more": "Plus", + "next": "Suivant", + "got_it": "Compris", + "settings": "Paramètres", + "password": "Mot de passe", + "create": "Ajouter nouveau", + "confirmation": "Confirmation", + "cancel": "Annuler", + "delete": "Supprimer", + "close": "Fermer", + "connect": "Connecter", + "domain": "Domaine", + "saving": "Enregistrement…", + "username": "Nom d'utilisateur", + "loading": "Chargement…", + "later": "Passer à la configuration plus tard", + "connect_to_existing": "Se connecter à un server existant", + "reset": "Réinitialiser", + "details": "Détails", + "no_data": "Pas de données", + "wait": "Attendez", + "remove": "Supprimer", + "alert": "Alerte", + "continue": "Continuer", + "apply": "Appliquer", + "done": "Effectué", + "app_name": "SelfPrivacy" + }, + "more_page": { + "about_application": "À propos", + "create_ssh_key": "Clés SSH du super utilisateur", + "console": "Console", + "application_settings": "Paramètres de l'application", + "configuration_wizard": "Assistant de configuration", + "about_project": "À propos de nous", + "onboarding": "Embarquement" + }, + "console_page": { + "title": "Console", + "waiting": "En attente de l'initialisation…" + }, + "about_us_page": { + "title": "À propos de nous" + }, + "about_application_page": { + "title": "À propos", + "privacy_policy": "Politique de confidentialité", + "application_version_text": "Version de l'application {}" + }, + "application_settings": { + "title": "Paramètres de l'application", + "dark_theme_description": "Changer le thème de l'application", + "reset_config_title": "Réinitialiser la configuration de l'application", + "delete_server_title": "Supprimer le serveur", + "delete_server_description": "Cela va supprimer votre serveur. Celui-ci ne sera plus accessible.", + "dark_theme_title": "Thème sombre", + "reset_config_description": "Réinitialiser les clés API et l'utilisateur root." + }, + "ssh": { + "title": "Clés SSH", + "create": "Créer une clé SSH", + "delete": "Supprimer une clé SSH", + "delete_confirm_question": "Êtes-vous sûr de vouloir supprimer la clé SSH ?", + "subtitle_with_keys": "{} clés", + "subtitle_without_keys": "Pas de clés SSH", + "no_key_name": "Clé sans nom", + "input_label": "Clé RSA ou ED25519 publique", + "root_title": "Ce sont les clés SSH du superutilisateur", + "root_subtitle": "Les possesseurs de ses clés SSH obtiennent le contrôle total sur le serveur et peuvent faire ce qu'ils veulent dessus. Il suffit d'ajouter vos clés SSH sur ce serveur." + }, + "onboarding": { + "page1_title": "Indépendance numérique, accessible à nous tous", + "page1_text": "Mail, VPN, messagerie, réseau social et bien plus sur votre serveur privé, sous votre contrôle.", + "page2_title": "SelfPrivacy n'est pas un cloud, c'est votre centre de données personnel", + "page2_server_provider_title": "Hébergeur", + "page2_server_provider_text": "Un hébergeur maintient votre serveur dans son propre datacenter. SelfPrivacy va automatiquement se connecter à votre fournisseur et installer tout le nécessaire.", + "page2_dns_provider_title": "Fournisseur de DNS", + "page2_backup_provider_title": "Fournisseur de sauvegarde", + "page2_text": "SelfPrivacy fonctionne uniquement avec les fournisseurs que vous choisissez. Si vous n'avez pas de compte sur ceux-ci, nous allons vous aider à en créer.", + "page2_dns_provider_text": "Vous avez besoin d'un domaine pour avoir un espace sur l'Internet. Il est également nécessaire de disposer d'un fournisseur de DNS fiable pour que votre domaine pointe sur votre serveur. Nous allons vous suggérer de choisir des fournisseurs DNS supportés pour automatiquement configurer le réseau." + }, + "resource_chart": { + "month": "Mois", + "day": "Jour", + "hour": "Heure", + "cpu_title": "Utilisation du CPU", + "network_title": "Utilisation du réseau" + }, + "server": { + "card_title": "Serveur", + "general_information": "Informations générales", + "resource_usage": "Utilisation des ressources", + "allow_autoupgrade": "Permettre la mise à jour automatique", + "allow_autoupgrade_hint": "Permettre la mise à jour des paquets automatiquement sur le serveur", + "reboot_after_upgrade": "Redémarrer après mise à jour", + "reboot_after_upgrade_hint": "Redémarrer sans demander après application des changements sur le serveur", + "server_id": "ID du serveur", + "status": "Statut", + "cpu": "Processeur", + "ram": "Mémoire", + "disk": "Disque local", + "monthly_cost": "Coût mensuel", + "location": "Localisation", + "core_count": { + "one": "{} cœur", + "two": "{} cœurs", + "many": "{} cœurs", + "other": "{} cœurs", + "few": "{} cœurs" + }, + "server_timezone": "Fuseau horaire du serveur", + "select_timezone": "Sélectionner le fuseau horaire" + }, + "record": { + "root": "Domaine racine", + "api": "API de SelfPrivacy", + "git": "Serveur Git", + "meet": "Conférence vidéo", + "social": "Réseau social", + "password": "Gestionnaire de mot de passe", + "vpn": "VPN", + "dmarc": "Enregistrement DMARC", + "spf": "Enregistrement SPF", + "dkim": "Clé DKIM", + "mx": "Enregistrement MX" + }, + "domain": { + "card_title": "Domaine", + "screen_title": "Domaines et DNS", + "error": "Problèmes détectés", + "error_subtitle": "Clique ici pour les fixer", + "refreshing": "Rafraîchissement du statut…", + "services_title": "Services", + "services_subtitle": "Enregistrement de type \"A\" requis pour chaque service.", + "email_title": "Email", + "email_subtitle": "Enregistrements nécessaires pour l'échange sécurisé d'email.", + "update_list": "Liste mise à jour", + "ok": "Les enregistrements DNS sont OK", + "uninitialized": "Les données n'ont pas encore été récupérées" + }, + "backup": { + "card_title": "Sauvegarde", + "reuploaded_key": "Clés republiées", + "initialize": "Initialise", + "waiting_for_rebuild": "Vous serez capable de créer votre première sauvegarde dans quelques instants.", + "restore": "Restaurer depuis la sauvegarde", + "create_new": "Créer une nouvelle sauvegarde", + "creating": "Création d'une nouvelle sauvegarde: {}%", + "restoring": "Restauration depuis la sauvegarde", + "restore_alert": "Vous allez restaurer depuis la sauvegarde créée le {}. Toutes les données existantes vont être perdues. Êtes-vous sûr ?", + "refresh": "Statut d'avancement", + "refetching_list": "Dans quelques minutes, la liste sera mise à jour", + "reupload_key": "Forcer la republication de la clé", + "no_backups": "Il n'y a pour l'instant aucune sauvegarde", + "error_pending": "Le serveur a retourné une erreur, vérifier plus bas", + "refetch_backups": "Récupérer la liste des mise à jour" + }, + "storage": { + "card_title": "Stockage du serveur", + "status_ok": "L'utilisation du disque est OK", + "status_error": "Faible espace disque", + "disk_usage": "{} utilisé", + "disk_total": "{} total · {}", + "gb": "{} GB", + "mb": "{} MB", + "kb": "{} KB", + "extending_volume_title": "Extension du volume", + "extending_volume_price_info": "Les prix incluent la TVA et sont estimés par les données commerciales fournies par Hetzner.\nLe serveur va redémarrer après redimensionnement.", + "extending_volume_error": "Pas possible d'initialiser l'extension du volume.", + "size": "Taille", + "data_migration_title": "Migration de données", + "data_migration_notice": "Durant la migration, tous les services ne seront plus accessibles.", + "start_migration_button": "Démarrer la migration", + "migration_process": "Migration en cours…", + "migration_done": "Terminé", + "extend_volume_button": "Étendre le volume", + "extending_volume_description": "Redimensionner le volume va vous permettre de stocker davantage de données sur votre serveur sans étendre le serveur lui-même. Les volumes ne peuvent être qu'étendus, le shrinking n'est pas possible." + }, + "service_page": { + "open_in_browser": "Ouvrir dans le navigateur", + "restart": "Redémarrer le service", + "status": { + "active": "Démarré et opérationnel", + "inactive": "Arrêté", + "failed": "Démarrage en échec", + "off": "Désactivé", + "activating": "Activation", + "deactivating": "Désactivation", + "reloading": "Redémarrage" + }, + "disable": "Désactiver le service", + "enable": "Activer le service", + "move": "Déplacer sur un autre volume", + "uses": "Utilise {usage} du {volume}" + }, + "mail": { + "title": "E-Mail", + "login_info": "Utiliser l'identifiant et le mot de passe de l'onglets utilisateurs. Le port IMAP est 143 avec STARTTLS, le port SMTP est 587 avec STARTTLS.", + "subtitle": "E-Mail pour des entreprises et la famille." + }, + "not_ready_card": { + "in_menu": "Le serveur n'est pas encore configuré. Veuillez finir le paramétrage en utilisant l'assistant d'installation pour aller plus loin." + }, + "validations": { + "invalid_format_password": "Ne doit pas contenir d'espace", + "invalid_format": "Format invalide", + "already_exist": "Existe déjà", + "required": "Requis", + "invalid_format_ssh": "Doit correspondre au format de clé SSH", + "root_name": "Ne peut être 'root'" + }, + "jobs": { + "create_ssh_key": "Créer une clé SSH pour {}", + "reboot_failed": "Impossible de redémarrer le serveur. Veuillez vérifier les logs applicatifs.", + "delete_user": "Supprimer utilisateur", + "generic_error": "Impossible de se connecter au serveur!", + "delete_ssh_key": "Supprimer la clé SSH pour {}", + "reboot_server": "Redémarrer le serveur", + "upgrade_server": "Mise à jour du serveur", + "upgrade_failed": "Impossible de mettre à jour le serveur", + "upgrade_success": "Mise à jour du serveur démarrée", + "reboot_success": "Le serveur redémarre", + "run_jobs": "Lancer des jobs", + "job_added": "Job ajouté", + "service_turn_on": "Lancer", + "reset_user_password": "Réinitialiser le mot de passe de l'utilisateur", + "service_turn_off": "Arrêter", + "create_user": "Créer utilisateur", + "empty": "Pas de jobs", + "server_jobs": "Jobs sur le serveur", + "start": "Démarrer", + "title": "Liste des jobs" + }, + "modals": { + "you_cant_use_this_api": "Vous ne pouvez pas utiliser cette API pour un domaine avec cette extension.", + "destroy_server": "Effacer le serveur et en créer un nouveau?", + "no": "Non", + "yes": "Oui", + "reboot": "Redémarrer", + "delete_server_volume": "Supprimer le serveur et le volume?", + "purge_all_keys_confirm": "Oui, purger tous mes tokens", + "purge_all_keys": "Purger toutes les clés d'authentification?", + "are_you_sure": "Êtes-vous sûr?", + "try_again": "Essayer à nouveau?" + }, + "password_manager": { + "title": "Gestionnaire de mot de passe", + "subtitle": "La base de votre sécurité. Bitwarden vous aidera à créer, stocker et déplacer les mots de passe entre appareils, ainsi qu'à les saisir, lorsque cela est demandé, en utilisant l'autocomplétion.", + "login_info": "Vous devrez créer un compte sur le site web." + }, + "timer": { + "sec": "{} secondes" + }, + "cloud": { + "title": "Stockage cloud", + "subtitle": "Ne permettez pas aux services cloud de lire vos données en utilisant NextCloud.", + "login_info": "Le login est admin, le mot de passe est le même que votre utilisateur principal. Créer de nouveaux comptes via l'interface Nextcloud." + }, + "video": { + "title": "Vidéoconférence", + "subtitle": "Zoom et Google Meet sont biens, mais Jitsi Meet est une alternative valable qui vous donne également l'assurance que vous n'êtes pas écouter.", + "login_info": "Pas de compte nécessaire." + }, + "social_network": { + "title": "Réseaux sociaux", + "subtitle": "C'est difficile à croire mais il devient possible de créer votre propre réseau social, avec vos propres règles et votre propre public cible.", + "login_info": "Vous devrez créer un compte sur le site web." + }, + "git": { + "title": "Serveur Git", + "subtitle": "Une alternative privée à Github, qui vous appartient et qui n'appartient pas à Microsoft.", + "login_info": "Vous devrez créer un compte sur le site web. Le premier utilisateur deviendra admin." + }, + "vpn": { + "title": "Serveur VPN", + "subtitle": "Serveur VPN privé" + } +} diff --git a/assets/translations/id.json b/assets/translations/id.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/assets/translations/id.json @@ -0,0 +1 @@ +{} diff --git a/assets/translations/it.json b/assets/translations/it.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/assets/translations/it.json @@ -0,0 +1 @@ +{} diff --git a/assets/translations/ja.json b/assets/translations/ja.json new file mode 100644 index 00000000..07e84b75 --- /dev/null +++ b/assets/translations/ja.json @@ -0,0 +1,4 @@ +{ + "test": "jp-test", + "locale": "jp" +} diff --git a/assets/translations/ka.json b/assets/translations/ka.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/assets/translations/ka.json @@ -0,0 +1 @@ +{} diff --git a/assets/translations/kk.json b/assets/translations/kk.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/assets/translations/kk.json @@ -0,0 +1 @@ +{} diff --git a/assets/translations/lv.json b/assets/translations/lv.json new file mode 100644 index 00000000..bac9a852 --- /dev/null +++ b/assets/translations/lv.json @@ -0,0 +1,217 @@ +{ + "test": "lv-test", + "basis": { + "app_name": "SelfPrivacy", + "providers_title": "Tavs Datu Centrs", + "select": "Izvēlies", + "services": "Servisi", + "services_title": "Tavi personīgie, privātie un neatkarīgie servisi.", + "users": "Lietotāji", + "more": "Vairāk", + "next": "Nākamais", + "got_it": "Sapratu", + "password": "Parole", + "create": "Pievienot jaunu", + "confirmation": "Apstiprinājums", + "cancel": "Atcelt", + "delete": "Izdzēst", + "close": "Aizvērt", + "connect": "Savienoties", + "domain": "Domēna", + "saving": "Saglabā…", + "username": "Lietotājvārds", + "later": "Izlaist un iestatīt vēlāk", + "connect_to_existing": "Savienoties ar esošu serveri", + "reset": "Atiestatīt", + "details": "Detaļas", + "no_data": "Nav dati", + "wait": "Uzgaidiet", + "remove": "Noņemt", + "apply": "Pieteikties", + "done": "Pabeigts", + "alert": "Brīdinājums", + "providers": "Sniedzēji", + "settings": "Iestatījumi", + "loading": "Lādē…", + "continue": "Turpināt" + }, + "more_page": { + "configuration_wizard": "Iestatīšanas vednis", + "about_project": "Par mums", + "about_application": "Par", + "onboarding": "Pievienošanās", + "console": "Konsole", + "application_settings": "Aplikācijas iestatījumi", + "create_ssh_key": "Superlietotāja SSH atslēgas" + }, + "console_page": { + "title": "Konsole", + "copy": "Kopēt", + "waiting": "Gaida inicializatīnu…" + }, + "about_us_page": { + "title": "Par mums" + }, + "about_application_page": { + "title": "Par", + "application_version_text": "Aplikācijas versija {}", + "api_version_text": "Servera API versija {}", + "privacy_policy": "Privātuma politika" + }, + "application_settings": { + "system_dark_theme_title": "Sistēmas noklusējuma dizains", + "dark_theme_title": "Tumšs dizains", + "title": "Aplikācijas iestatījumi", + "system_dark_theme_description": "Izmantojiet gaišu vai tumšu dizainu atkarībā no sistēmas iestatījumiem", + "dark_theme_description": "Lietojumprogrammas dizaina pārslēgšana", + "dangerous_settings": "Bīstamie iestatījumi", + "reset_config_title": "Atiestatīt lietojumprogrammas konfigurāciju", + "reset_config_description": "Atiestatīt API atslēgas un saknes lietotāju.", + "delete_server_title": "Izdzēst serveri", + "delete_server_description": "Šis izdzēš jūsu serveri. Tas vairs nebūs pieejams." + }, + "locale": "lv", + "ssh": { + "title": "SSH atslēgas", + "create": "Izveidot SSH atslēgu", + "delete": "Izdzēst SSH atslēgu", + "subtitle_with_keys": "{} atslēgas", + "subtitle_without_keys": "Nav atslēgu", + "no_key_name": "Nenosaukta atslēga", + "root_title": "Šīs ir superlietotāja atslēgas", + "input_label": "Publiska ED25519 vai RSA atslēga", + "delete_confirm_question": "Vai jūs esat pārliecināti, ka vēlaties izdzēst SSH atslēgu?", + "root_subtitle": "Šo atslēgu īpašnieki saņem pilnu piekļuvi serverim un var ar to kaut ko darīt. Pievienojiet serverim tikai savas atslēgas." + }, + "onboarding": { + "page1_title": "Digitālā neatkarība, kas pieejama mums visiem", + "page1_text": "Pasts, VPN, Ziņnesis, sociālie tīkli un daudz cits uz tava privātā servera, zem tavas kontroles.", + "page2_title": "SelfPrivacy nav mākonis, tas ir tavs personīgais datu centrs", + "page2_server_provider_title": "Servera sniedzējs", + "page2_server_provider_text": "Servera sniedzējs uztur tavu serveri savā datu centrā. SelfPrivacy automātiksi savienosies ar sniedzēju un uzstādīs visas vajadzīgās lietas.", + "page2_dns_provider_title": "DNS sniedzējs", + "page2_backup_provider_title": "Dublēšanas pakalpojumu sniedzējs", + "page2_backup_provider_text": "Ko darīt, ja kaut kas notiek ar jūsu serveri? Iedomājieties hakeru uzbrukumu, nejaušu datu dzēšanu vai pakalpojuma atteikumu? Jūsu dati tiks glabāti drošībā pie dublējumu nodrošinātāja. Tie būs droši šifrēti un jebkurā laikā pieejami, lai atjaunotu jūsu serveri ar.", + "page2_text": "SelfPrivacy tikai darbojas ar tiem sniedzējiem, ko tu izvēlies. Ja tev nav nepieciešamie konti tajos, mēs tev palīdzēsim tos uztaisīt.", + "page2_dns_provider_text": "Jums ir nepieciešams domēns, lai būtu vieta internetā. Un jums ir nepieciešams arī uzticams DNS nodrošinātājs, lai domēns būtu vērsts uz jūsu serveri. Mēs iesakām izvēlēties atbalstītu DNS nodrošinātāju, lai automātiski iestatītu tīklošanu." + }, + "resource_chart": { + "month": "Mēnesis", + "day": "Diena", + "hour": "Stunda", + "cpu_title": "CPU izmantošana", + "network_title": "Tīkla lietojums", + "in": "Iekšā", + "out": "Ārā" + }, + "server": { + "card_title": "Serveris", + "description": "Visi tavi serveri dzīvo šeit", + "general_information": "Vispārīga informācija", + "resource_usage": "Resursu izmantošana", + "allow_autoupgrade": "Atļaut automātisko jaunināšanu", + "allow_autoupgrade_hint": "Automātisko pakotņu jaunināšanas atļaušana serverī", + "reboot_after_upgrade": "Atsāknēšana pēc jaunināšanas", + "reboot_after_upgrade_hint": "Atsāknēšana bez tūlītējas pēc izmaiņu piemērošanas serverī", + "select_timezone": "Laika joslas atlasīšana", + "timezone_search_bar": "Laika joslas nosaukums vai laika nobīdes vērtība", + "server_id": "Servera ID", + "status": "Status", + "cpu": "CPU", + "disk": "Disks lokāls", + "monthly_cost": "Mēneša maksa", + "location": "Vieta", + "core_count": { + "one": "{} kodols", + "two": "{} kodoli", + "few": "{} kodoli", + "many": "{} kodoli", + "other": "{} kodoli" + }, + "server_timezone": "Servera laika josla", + "ram": "Atmiņa" + }, + "record": { + "root": "Saknes domēns", + "api": "SelfPrivacy API", + "cloud": "Failu mākonis", + "git": "Git serveris", + "meet": "Video konference", + "social": "Sociālais tīkls", + "password": "Paroļu pārvaldnieks", + "vpn": "VPN", + "mx": "MX ieraksts", + "spf": "SPF ieraksts", + "dkim": "DKIM atslēga", + "dmarc": "DMARC ieraksts" + }, + "domain": { + "card_title": "Domēns", + "screen_title": "Domēns un DNS", + "ok": "Ieraksti ir kārtībā", + "error": "Atrastas problēmas", + "refreshing": "Notiek statusa atsvaidzināšana…", + "uninitialized": "Dati vēl nav izgūti", + "services_title": "Pakalpojumi", + "email_title": "E-pasts", + "email_subtitle": "Ieraksti nepieciešami drošai e-pasta apmaiņai.", + "update_list": "Atjaunināt sarakstu", + "error_subtitle": "Pieskarieties šeit, lai tos labotu", + "services_subtitle": "Tips \"A\" ieraksti nepieciešami katram pakalpojumam." + }, + "backup": { + "card_title": "Rezerves", + "description": "Izglābs jūsu dienu incidenta gadījumā: hakeru uzbrukums, servera dzēšana utt.", + "reupload_key": "Piespiedu atkārtotas augšupielādes atslēga", + "initialize": "Inicializēt", + "restore": "Atjaunono dublējuma", + "no_backups": "Vēl nav dublējumu", + "create_new": "Jauna dublējuma izveide", + "creating": "Veido jaunu dublējumu: {}%", + "error_pending": "Servera atgrieztā kļūda, pārbaudiet to zemāk", + "refresh": "Atsvaidzināšanas statuss", + "reuploaded_key": "Atslēga atkārtoti ielādēta", + "waiting_for_rebuild": "Pirmo dublējumu varēsit izveidot dažu minūšu laikā.", + "restore_alert": "Jūs gatavojaties atjaunot no dublējuma, kas izveidots {}. Visi pašreizējie dati tiks zaudēti. Vai esi pārliecināts?", + "restoring": "Atjaunošana no dublējuma", + "refetch_backups": "Atkārtoti ielādēt dublējumkopiju sarakstu", + "refetching_list": "Pēc dažām minūtēm saraksts tiks atjaunināts" + }, + "service_page": { + "uses": "Izmanto {usage} uz {volume}", + "open_in_browser": "Atvērt pārlūkprogrammā", + "restart": "Restartējiet pakalpojumu", + "disable": "Atspējot pakalpojumu", + "enable": "Iespējot pakalpojumu", + "move": "Pāriet uz citu sējumu", + "status": { + "active": "Darbojas", + "inactive": "Apstājies" + } + }, + "storage": { + "start_migration_button": "Sāciet migrāciju", + "card_title": "Servera krātuve", + "status_ok": "Diska lietojums ir kārtībā", + "status_error": "Maz vietas diskā", + "disk_usage": "{} izmantots", + "disk_total": "{} Kopā · {}", + "gb": "{} GB", + "mb": "{} MB", + "kb": "{} KB", + "bytes": "Baiti", + "extend_volume_button": "Pagarināt skaļumu", + "extending_volume_title": "Skaļuma paplašināšana", + "extending_volume_description": "Skaļuma lieluma maiņa ļaus serverī saglabāt vairāk datu, nepagarinot pašu serveri. Skaļumu var tikai palielināt: sarukt nav iespējams.", + "extending_volume_price_info": "Cenā ir iekļauts PVN, un tā ir aprēķināta no Hetzner sniegtajiem cenu datiem. Pēc izmēra maiņas serveris tiks restartēts.", + "extending_volume_error": "Nevarēja inicializēt skaļuma palielināšanu.", + "size": "Lielums", + "data_migration_title": "Datu migrācija", + "data_migration_notice": "Migrācijas laikā visi pakalpojumi tiks izslēgti.", + "migration_process": "Notiek migrēšana…", + "migration_done": "Pabeigt" + }, + "not_ready_card": { + "in_menu": "Serveris vēl nav iestatīts. Lūdzu, pabeidziet iestatīšanu, izmantojot iestatīšanas vedni, lai turpinātu darbu." + } +} \ No newline at end of file diff --git a/assets/translations/mk.json b/assets/translations/mk.json new file mode 100644 index 00000000..0e655d5a --- /dev/null +++ b/assets/translations/mk.json @@ -0,0 +1,9 @@ +{ + "test": "mk-test", + "basis": { + "providers": "Провајдери", + "providers_title": "Вашиот центар за податоци", + "select": "Изберите" + }, + "locale": "mk" +} diff --git a/assets/translations/nl.json b/assets/translations/nl.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/assets/translations/nl.json @@ -0,0 +1 @@ +{} diff --git a/assets/translations/pl.json b/assets/translations/pl.json new file mode 100644 index 00000000..ce1158ed --- /dev/null +++ b/assets/translations/pl.json @@ -0,0 +1,510 @@ +{ + "basis": { + "services_title": "Twoje osobiste, prywatne i niezależne usługi.", + "cancel": "Anuluj", + "providers": "Dostawca usług internetowych", + "providers_title": "Twoje centrum danych", + "select": "Wybierz to", + "services": "Usługi", + "users": "Użytkownicy", + "more": "Jeszcze więcej", + "next": "Dalej", + "got_it": "Zrozumiałem", + "settings": "Ustawienia", + "password": "Hasło", + "create": "Utwórz nowy", + "confirmation": "Potwierdzenie", + "later": "Pomiń i dostosuj później", + "delete": "Usunąć", + "close": "Zamknij to", + "connect": "Podłączyć", + "domain": "Domena", + "saving": "Zapisywanie…", + "username": "Nazwa użytkownika", + "loading": "Ładowanie…", + "connect_to_existing": "Podłącz do istniejącego serwera", + "reset": "Zresetuj to", + "details": "Dokładna informacja", + "no_data": "Brak danych", + "wait": "Ładowanie", + "remove": "Usunąć", + "apply": "Zastosuj to", + "done": "Gotowe", + "continue": "Kontynuować", + "alert": "Powiadomienie", + "app_name": "SelfPrivacy" + }, + "test": "pl-test", + "locale": "pl", + "more_page": { + "about_project": "O projekcie \"SelfPrivacy\"", + "about_application": "O aplikacji", + "create_ssh_key": "SSH klucze administratora", + "console": "Konsola", + "configuration_wizard": "Kreator konfiguracji", + "application_settings": "Ustawienia aplikacji", + "onboarding": "Witamy" + }, + "console_page": { + "title": "Konsola", + "waiting": "Oczekiwanie na inicjalizację…", + "copy": "Kopia" + }, + "about_us_page": { + "title": "O projekcie \"SelfPrivacy\"" + }, + "about_application_page": { + "title": "O aplikacji", + "application_version_text": "Wersja aplikacji {}", + "api_version_text": "Wersja API serwera {}", + "privacy_policy": "Polityka prywatności" + }, + "application_settings": { + "title": "Ustawienia aplikacji", + "dark_theme_title": "Ciemny motyw aplikacji", + "dark_theme_description": "Zmień kolor motywu aplikacji", + "reset_config_title": "Resetowanie", + "reset_config_description": "Zresetuj klucze API i użytkownika root.", + "delete_server_title": "Usuń serwer", + "delete_server_description": "Ta czynność usunie serwer. Po tym będzie niedostępny.", + "system_dark_theme_description": "Użyj jasnego lub ciemnego motywu w zależności od ustawień systemu", + "system_dark_theme_title": "Domyślny motyw systemowy", + "dangerous_settings": "Niebezpieczne ustawienia" + }, + "ssh": { + "title": "klucze SSH", + "delete": "Usuń klucz SSH", + "subtitle_with_keys": "Klucze: {}", + "subtitle_without_keys": "Brak kluczy", + "no_key_name": "Bezimienny klucz", + "root_title": "To są klucze superużytkownika", + "input_label": "Publiczny klucz ED25519 lub RSA", + "create": "Dodaj klucz SSH", + "delete_confirm_question": "Czy na pewno chcesz usunąć następny klucz?", + "root_subtitle": "Właściciele określonych tutaj kluczy uzyskują pełny dostęp do danych i ustawień serwera. Dodaj tylko swoje klucze." + }, + "onboarding": { + "page1_title": "Cyfrowa niezależność jest dostępna dla każdego", + "page1_text": "Poczta, VPN, Messenger, sieć społecznościowa i wiele więcej na Twoim osobistym serwerze, pod Twoją pełną kontrolą.", + "page2_title": "SelfPrivacy — to nie przechowywanie w chmurze, a tylko Twój osobisty centrum danych", + "page2_server_provider_title": "Dostawca-serwer", + "page2_server_provider_text": "Dostawca-serwer będzie obsługiwał Twój serwer w swoim centrum danych. SelfPrivacy automatycznie połączy się z nim i skonfiguruje dla Ciebie serwer.", + "page2_dns_provider_title": "Dostawca DNS", + "page2_backup_provider_title": "Dostawca kopii zapasowych", + "page2_text": "SelfPrivacy współpracuje tylko z wybranymi przez Ciebie dostawcami usług. Jeśli nie masz kont, pomożemy Ci ich założyć.", + "page2_dns_provider_text": "Aby być w Internecie, potrzebujesz domeny. Aby domena wskazywała na Twój serwer, potrzebujesz niezawodnego serwera DNS. Poprosimy Cię o wybranie jednego z obsługiwanych serwerów DNS i automatyczne skonfigurowanie wszystkich rekordów. Chcesz skonfigurować je ręcznie? To też jest możliwe.", + "page2_backup_provider_text": "A co jeśli coś się stanie z serwerem? Atak hakerski, odmowa usługi, a może po prostu przypadkowe usunięcie danych? Twoje dane będą bezpieczne gdzie indziej, u dostawcy magazynu kopii zapasowych. Wszystkie są bezpiecznie zaszyfrowane i możesz przywrócić swój serwer." + }, + "resource_chart": { + "month": "Miesiąc", + "day": "Dzień", + "hour": "Godzina", + "cpu_title": "Wykorzystanie procesora", + "network_title": "Wykorzystanie sieci", + "in": "Odebrane", + "out": "Wysłano" + }, + "server": { + "card_title": "Serwer", + "description": "To wirtualny komputer, na którym działają wszystkie Twoje usługi", + "resource_usage": "Zużycie środków", + "allow_autoupgrade_hint": "Zezwól na automatyczną instalację aktualizacji na serwerze", + "reboot_after_upgrade": "Uruchom ponownie po aktualizacjach", + "server_timezone": "Strefa czasowa serwera", + "server_id": "ID serwera", + "status": "Status", + "cpu": "procesor", + "ram": "Pamięć RAM", + "disk": "Dysk lokalny", + "monthly_cost": "Koszt miesięczny", + "location": "Lokalizacja danych", + "core_count": { + "one": "{} jądro", + "two": "{} jądra", + "many": "{} rdzeni", + "few": "{} jądra", + "other": "{} rdzeni" + }, + "general_information": "Ogólna informacja", + "allow_autoupgrade": "Zezwalaj na automatyczne aktualizacje", + "reboot_after_upgrade_hint": "Automatycznie uruchom ponownie serwer po zastosowaniu aktualizacji", + "select_timezone": "Wybierz swoją strefę czasową", + "timezone_search_bar": "Nazwa strefy czasowej lub znaczenie przesunięcia czasowego" + }, + "record": { + "root": "Domena główna", + "cloud": "Chmura plików", + "git": "Git serwer", + "meet": "Wideokonferencje", + "social": "Serwis społecznościowy", + "password": "Menedżer haseł", + "vpn": "VPN", + "dmarc": "Zapisywanie rekord", + "spf": "Zapisywanie SPF", + "dkim": "DKIM klucz", + "api": "SelfPrivacy API", + "mx": "Zapisywanie MX" + }, + "domain": { + "card_title": "Domena", + "screen_title": "Domena i DNS", + "error": "Znaleziono problemy", + "refreshing": "Aktualizowanie danych…", + "uninitialized": "Dane jeszcze nie otrzymane", + "services_title": "Usługi", + "services_subtitle": "Rekordy „A” są wymagane do działania usług.", + "email_title": "Email", + "update_list": "Zaktualizuj listę", + "ok": "Zapisy są w porządku", + "error_subtitle": "Kliknij tutaj, aby naprawić", + "email_subtitle": "Zapisy wymagane do bezpiecznej wymiany poczty elektronicznej." + }, + "backup": { + "card_title": "Utworzyć kopię zapasową", + "description": "Pomoże Ci w każdej sytuacji: atak hakerski, usunięcie serwera, itp.", + "reupload_key": "Wymuś aktualizację klucza", + "reuploaded_key": "Klucz na serwerze został zaktualizowany", + "initialize": "Nastawić", + "restore": "Przywróć z kopii", + "no_backups": "Nie ma jeszcze żadnych kopii zapasowych", + "create_new": "Utwórz nową kopię", + "creating": "Tworzenie kopii: {}%", + "restoring": "Przywracanie z kopii", + "waiting_for_rebuild": "Po kilku minutach będziesz mógł utworzyć pierwszą kopię.", + "error_pending": "Serwer zwrócił błąd, sprawdź go poniżej", + "refresh": "Odśwież status", + "refetch_backups": "Ponownie pobierz listę kopii zapasowych", + "refetching_list": "Za kilka minut lista zostanie zaktualizowana", + "restore_alert": "Za chwilę przywrócisz z kopii utworzonej przez {}. Wszystkie bieżące dane zostaną utracone. Jesteś pewny?" + }, + "storage": { + "card_title": "Pamięć serwera", + "status_ok": "Użycie dysku jest OK", + "status_error": "Mało miejsca na dysku", + "disk_usage": "{} użyte", + "disk_total": "{} całkowity · {}", + "gb": "{} GB", + "mb": "{} MB", + "kb": "{} KB", + "bytes": "Bajt", + "extend_volume_button": "Rozwiń pamięć", + "extending_volume_title": "Rozszerzenie pamięci", + "extending_volume_price_info": "Cena zawiera podatek VAT i jest oparta na danych cenowych dostarczonych przez firmę Hetzner. Serwer zostanie zrestartowany w trakcie procesu.", + "extending_volume_error": "Nie można rozpocząć rozszerzenia repozytorium.", + "size": "Rozmiar", + "data_migration_title": "Migracja danych", + "data_migration_notice": "Na czas migracji danych wszystkie usługi zostaną wyłączone.", + "start_migration_button": "Rozpocznij migrację", + "migration_process": "Migracja…", + "migration_done": "Zakończyć", + "extending_volume_description": "Zmiana rozmiaru pamięci masowej pozwoli na przechowywanie większej ilości danych na serwerze bez rozszerzenia serwera. Objętość można tylko zwiększyć, nie można jej zmniejszyć." + }, + "service_page": { + "open_in_browser": "Otwórz w przeglądarce", + "status": { + "activating": "Włącza się", + "deactivating": "Wyłącza się", + "reloading": "Uruchamia się ponownie", + "off": "Wyłączone", + "inactive": "Zatrzymany", + "active": "Włączone i działa", + "failed": "Nie udało sie uruchomić" + }, + "move": "Przeniesienie do innego woluminu", + "enable": "Włącz usługę", + "restart": "Uruchom ponownie usługę", + "disable": "Wyłącz usługę", + "uses": "Używa {usage} na {volume}" + }, + "mail": { + "title": "E-mail", + "subtitle": "E-Mail dla firmy i rodziny.", + "login_info": "Użyj nazwy użytkownika i hasła z listy użytkowników. Port IMAP: 143, STARTTLS. Port SMTP: 587, STARTTLS." + }, + "password_manager": { + "title": "Menedżer haseł", + "subtitle": "To podstawa Twojego bezpieczeństwa. Bitwarden pomoże Ci tworzyć, przechowywać, kopiować hasła między urządzeniami i umieszczać je w formularzach.", + "login_info": "Konto należy założyć na stronie internetowej." + }, + "video": { + "title": "Wideokonferencja", + "subtitle": "Zoom i Google Meet są świetne, ale Jitsi Meet to dobra alternatywa, która daje pewność, że nie jesteś słuchany.", + "login_info": "Konto nie jest potrzebne." + }, + "cloud": { + "title": "Przechowywanie w chmurze", + "subtitle": "Nie zezwalaj usługom w chmurze na odczyt twoich danych, dla tego korzystaj z NextCloud.", + "login_info": "Login jest admin, hasło jest takie samo jak w przypadku głównego użytkownika. Utwórz nowe konta w interfejsie Nextcloud." + }, + "social_network": { + "title": "Serwis społecznościowy", + "subtitle": "Trudno w to uwierzyć, ale stało możliwe stworzenie własnego serwisu społecznościowego, z własnymi zasadami i publicznością.", + "login_info": "Konto należy założyć na stronie." + }, + "git": { + "login_info": "Konto należy założyć na stronie. Pierwszy zarejestrowany użytkownik zostaje administratorem.", + "title": "Git Serwer", + "subtitle": "Prywatna alternatywa dla Githuba, która należy do Ciebie, ale nie do Microsoftu." + }, + "vpn": { + "title": "VPN Serwer", + "subtitle": "Prywatny serwer VPN" + }, + "users": { + "add_new_user": "Dodaj pierwszego użytkownika", + "new_user": "Nowy użytkownik", + "delete_user": "Usuń użytkownika", + "not_ready": "Połącz serwer, domenę I DNS w zakładce dostawcy, aby mieć możliwość dodać pierwszego użytkownika", + "nobody_here": "Tu będą pojawią się użytkownicy", + "login": "Login", + "new_user_info_note": "Nowy użytkownik automatycznie otrzyma dostęp do wszystkich serwisów", + "delete_confirm_question": "Czy naprawdę chcesz usunąć konto?", + "reset_password": "Zresetuj hasło", + "account": "Konto", + "send_registration_data": "Udostępnij dane logowania", + "could_not_fetch_users": "Nie udało się uzyskać użytkowników", + "could_not_fetch_description": "Sprawdź połączenie internetowe i spróbuj ponownie", + "refresh_users": "Odśwież listę użytkowników", + "could_not_create_user": "Nie udało się utworzyć użytkownika", + "could_not_delete_user": "Nie udało się usunąć użytkownika", + "could_not_add_ssh_key": "Nie udało się utworzyć SSH klucz", + "username_rule": "Nazwa użytkownika może zawierać tylko małe litery alfabetu łacińskiego, cyfry i podkreślenia, nie może zaczynać się od cyfry", + "email_login": "Logowanie e-mailem", + "no_ssh_notice": "Dla tego użytkownika tworzone są tylko konta e-mail i SSH. Jednokrotne logowanie do wszystkich usług będzie dostępne wkrótce.", + "details_title": "Dane użytkownika" + }, + "validations": { + "length_longer": "Długość ciągu znaków [] musi być mniejsza lub równa {}", + "length_not_equal": "Długość jest [], ależ powinna być {}", + "already_exist": "Już jest", + "invalid_format": "Nieprwidłowy format", + "required": "Potrzebien", + "root_name": "Nie może być 'root'", + "invalid_format_ssh": "Potrzebien format klucza SSH", + "invalid_format_password": "Hasło nie może zawierać spacji" + }, + "not_ready_card": { + "in_menu": "Serwer jeszcze nie jest skonfigurowany, użyj kreatora połączeń." + }, + "initializing": { + "connect_to_server": "Zacznijmy od serwera.", + "select_provider": "Wybierz dowolnego dostawcę z poniższej listy, wszyscy obsługują SelfPrivacy", + "select_provider_notice": "Przez \"stosunkowo małą\" rozumiemy maszynę z 2 rdzeniami procesora i 2 gigabajtami pamięci RAM.", + "select_provider_countries_title": "Dostępne kraje", + "select_provider_countries_text_hetzner": "Niemcy, Finlandia, USA", + "select_provider_countries_text_do": "USA, Holandia, Singapur, Wielka Brytania, Niemcy, Kanada, Indie, Australia", + "select_provider_payment_text_do": "Karty kredytowe, Google Pay, PayPal", + "select_provider_email_notice": "Hosting poczty e-mail nie będzie dostępny dla nowych klientów. Niemniej jednak zostanie odblokowany, gdy tylko dokonasz pierwszej płatności.", + "select_provider_site_button": "Odwiedź storonkę", + "select_provider_price_title": "Średnia cena", + "select_provider_price_text_hetzner": "€8 miesięcznie za stosunkowo mały serwer i 50GB miejsca na dysku", + "select_provider_price_text_do": "$17 miesięcznie za stosunkowo mały serwer i 50GB miejsca na dysku", + "select_provider_payment_title": "Metody płatności", + "select_provider_payment_text_hetzner": "Karty kredytowe, SWIFT, SEPA, PayPal", + "connect_to_server_provider": "Teraz zaloguj się przez ", + "no_locations_found": "Nie znaleziono lokalizacji, upewnij się, że Twoje konto jest dostępne", + "choose_server_type": "Jaki typ serwera powinienem wybrać?", + "choose_server_type_ram": "{} GB pamięci RAM", + "choose_server_type_text": "Od zasobów serwera zależeć będzie, jakie usługi będą mogły być uruchomione. W każdej chwili możliwa będzie rozbudowa serwera", + "choose_server_type_notice": "Główne rzeczy, na które należy zwrócić uwagę, to liczba wątków procesora i ilość pamięci RAM. Dane serwisowe zostaną umieszczone na osobnym dysku, który jest osobno płatny i łatwo rozszerzalny.", + "connect_to_server_provider_text": "Dzięki tokenowi API SelfPrivacy będzie mógł wynająć maszynę i postawić na niej swój serwer", + "how": "Jak uzyskać token API", + "provider_bad_key_error": "Klucz API dostawcy jest nieprawidłowy", + "could_not_connect": "Nie można połączyć się z dostawcą.", + "choose_location_type": "Gdzie chcesz zamówić swój serwer?", + "locations_not_found": "Ups!", + "locations_not_found_text": "W tej lokalizacji nie było dostępnych serwerów do wynajęcia", + "back_to_locations": "Wybierzmy inny", + "choose_location_type_text": "Wybór lokalizacji będzie determinował dostępne konfiguracje, ceny i prędkość połączenia z serwerem.", + "choose_server_type_storage": "{} GB pamięci na diskie", + "choose_server_type_payment_per_month": "{} miesięcznie", + "no_server_types_found": "Nie znaleziono dostępnych typów serwerów! Proszę upewnić się, że masz dostęp do dostawcy serwera...", + "use_this_domain": "Kto używa ten domen?", + "connect_backblaze_storage": "Dodajcie Blackblaze", + "no_connected_domains": "Niema podłączonych domenów", + "what": "Co to znaczy?", + "backblaze_bad_key_error": "Informacja o Blackbaze nieprawidłowa", + "select_dns": "Teraz wybierz provajdera DNS", + "manage_domain_dns": "Aby zarządzać DNS domeny", + "create_master_account": "Dodać konto administratora", + "use_this_domain_text": "Podany token zapewnia kontrolę nad tą domeną", + "loading_domain_list": "Ładowanie listy domen", + "found_more_domains": "Znaleziono więcej niż jedną domenę, dla własnego bezpieczeństwa usuń niepotrzebne domeny", + "save_domain": "Zapisz domenę", + "final": "Ostatni krok", + "create_server": "Utwórz serwer", + "server_rebooted": "Serwer zrestartowany. Czekam na ostatnią weryfikację…", + "server_started": "Serwer działa. Teraz zostanie sprawdzony i zrestartowany…", + "server_created": "Serwer został utworzony. Sprawdzane są adresy DNS i serwer uruchamia się…", + "until_the_next_check": "Do następnej inspekcji: ", + "check": "Sprawdź", + "one_more_restart": "Teraz nastąpi dodatkowe ponowne uruchomienie komputera w celu aktywacji certyfikatów bezpieczeństwa.", + "enter_username_and_password": "Wprowadź nazwę użytkownika i złożone hasło", + "finish": "Wszystko jest inicjalizowane", + "checks": "Kontrole wykonane:\n{} / {}", + "steps": { + "hosting": "Hosting", + "nixos_installation": "Instalacja NixOS", + "server_type": "Typ serwera", + "dns_provider": "Dostawca DNS", + "backups_provider": "Kopie zapasowe", + "domain": "Domena", + "master_account": "Rachunek główny", + "server": "Serwer", + "dns_setup": "Instalacja DNS", + "server_reboot": "Restart serwera", + "final_checks": "Kontrole końcowe" + }, + "dns_provider_bad_key_error": "Klucz API jest nieprawidłowy", + "connect_to_dns_provider_text": "Za pomocą interfejsu API tokena aplikacja SelfPrivacy skonfiguruje rekordy DNS", + "server_provider_description": "Lokalizacja, w której będą znajdować się Twoje dane i usługi SelfPrivacy:", + "dns_provider_description": "Spowoduje to połączenie Twojej domeny z adresem IP:", + "select_provider_price_free": "Za darmo", + "select_provider_payment_text_cloudflare": "Karty bankowe", + "connect_to_dns": "Połącz dostawcę DNS" + }, + "jobs": { + "delete_ssh_key": "Wydalić SSH-klucz dla {}", + "create_user": "Dodać korzystalnika", + "delete_user": "Wydalić korzystalnika", + "reboot_failed": "Nie otrzyma się zrestartować. Przeprowadzicie logi.", + "service_turn_off": "Wyłączyć", + "service_turn_on": "Włączyć", + "create_ssh_key": "Dodać SSH-klucz dla {}", + "generic_error": "Nie otrzyma się podłoczyć k serweru!", + "title": "Zadania", + "start": "Rozpocząć wykonywanie", + "empty": "Nie ma zadań", + "job_added": "Zadanie dodane", + "run_jobs": "Uruchom zadania", + "reboot_success": "Serwer uruchamia się ponownie", + "config_pull_failed": "Aktualizacja konfiguracji serwera nie powiodła się. Rozpoczęto aktualizację oprogramowania.", + "upgrade_success": "Uruchomiono aktualizację serwera", + "upgrade_failed": "Aktualizacja serwera nie działa", + "upgrade_server": "Aktualizacja serwera", + "reboot_server": "Ponowne uruchomienie serwera", + "server_jobs": "Zadania na serwerze", + "reset_user_password": "Zresetuj hasło użytkownika" + }, + "modals": { + "purge_all_keys": "Wydalić wszystkie kluczy autentyfikacji?", + "are_you_sure": "Wpełnieny?", + "dns_removal_error": "Nie otrzyma się wydalić zapis DNS.", + "yes": "Tak", + "no": "Nie", + "reboot": "Restart", + "server_deletion_error": "Nie można usunąć serwera.", + "server_validators_error": "Nie udało się uzyskać listy serwerów.", + "already_exists": "Taki serwer już istnieje.", + "unexpected_error": "Nieprzewidywalny błąd po stronie świadczeniodawcy.", + "destroy_server": "Zniszczyć serwer i stworzyć nowy?", + "try_again": "Mam spróbować jeszcze raz?", + "purge_all_keys_confirm": "Tak, wymazać wszystkie klucze", + "delete_server_volume": "Usunąć serwer i pamięć masową?", + "you_cant_use_this_api": "Nie możesz użyć tego API dla domeny z podobnym TLD.", + "volume_creation_error": "Nie udało się utworzyć woluminu." + }, + "recovery_key": { + "key_main_header": "Klucz odzyskania", + "key_synchronizing": "Synchronizacja…", + "key_receiving_done": "Zrobiono!", + "key_replace_button": "Wytworzyć nowy klucz", + "generation_error": "Nie otrzyma się zrobić klucz odzyskania. {}", + "key_connection_error": "Nie udało się połączyć z serwerem.", + "key_main_description": "Wymagane dla autoryzacji SelfPrivacy, gdy autoryzowane urządzenia są niedostępne.", + "key_amount_toggle": "Ograniczenie stosowania", + "key_amount_field_title": "Maks. liczba zastosowań", + "key_duedate_toggle": "Ograniczenie okresu użytkowania", + "key_duedate_field_title": "Data ważności", + "key_receive_button": "Zdobądź klucz", + "key_valid": "Twój klucz jest ważny", + "key_invalid": "Twój klucz jest już nieważny", + "key_valid_until": "Ważny do {}", + "key_valid_for": "Możesz użyć {} więcej razy", + "key_creation_date": "Utworzony {}", + "key_receiving_description": "Zapisz ten klucz w bezpiecznym miejscu. Dzięki niemu masz pełny dostęp do swojego serwera:", + "key_receiving_info": "Ten klucz nie będzie już pokazywany, ale możesz go zastąpić nowym." + }, + "timer": { + "sec": "{} s" + }, + "recovering": { + "confirm_server_decline": "Wybierz inny serwer", + "domain_not_available_on_token": "Wprowadzony token nie ma dostępu do żądanej domeny.", + "method_recovery_input_description": "Wprowadź swój token odzyskiwania", + "fallback_select_provider_console": "Dostęp do konsoli serwera mojego dostawcy.", + "confirm_server_description": "Znalazłem twój serwer! Potwierdź, że jest to właściwe:", + "confirm_server": "Potwierdzić serwer", + "modal_confirmation_title": "Czy to naprawdę twój serwer?", + "modal_confirmation_description": "Podłączenie się do niewłaściwego serwera może prowadzić do destrukcyjnych konsekwencji.", + "generic_error": "Błąd operacji, spróbuj ponownie.", + "recovery_main_header": "Podłączenie do istniejącego serwera", + "domain_recovery_description": "Wprowadź domenę, pod którą chcesz uzyskać dostęp do serwera:", + "domain_recover_placeholder": "Domena", + "domain_recover_error": "Nie można znaleźć serwera z tą domeną", + "method_device_description": "Otwórz aplikację na innym urządzeniu i otwórz ekran zarządzania urządzeniami. Naciśnij \"Dodaj urządzenie\", aby otrzymać token autoryzacji.", + "method_select_description": "Wybierz metodę wprowadzania danych:", + "method_select_other_device": "Mam dostęp na innym urządzeniu", + "method_select_recovery_key": "Mam klucz odzyskiwania", + "method_select_nothing": "Nie mam żadnego z nich", + "method_device_button": "Otrzymałem symbol", + "method_device_input_description": "Wprowadź swój token autoryzacyjny", + "method_device_input_placeholder": "Token", + "confirm_server_accept": "Tak, to on", + "choose_server": "Wybierz serwer", + "choose_server_description": "Nie można określić, z którym serwerem się komunikujesz.", + "no_servers": "Na Twoim koncie nie ma dostępnych serwerów.", + "modal_confirmation_dns_valid": "Odwrócony DNS jest prawidłowy", + "modal_confirmation_dns_invalid": "Odwrócony DNS wskazuje na inną domenę", + "modal_confirmation_ip_valid": "IP jest takie samo jak w rekordzie DNS", + "modal_confirmation_ip_invalid": "IP nie jest zgodne z tym w rekordzie DNS", + "fallback_select_description": "Które z nich posiadasz? Wybierz pierwszą, która pasuje:", + "fallback_select_token_copy": "Kopia tokena autoryzacyjnego z innej wersji aplikacji.", + "fallback_select_root_ssh": "Dostęp Root do serwera poprzez SSH.", + "authorization_failed": "Nie udało się zalogować za pomocą tego klucza", + "fallback_select_provider_console_hint": "Na przykład: Hetzner.", + "provider_connected": "Połączenie z dostawcą {}", + "provider_connected_description": "Połączenie ustanowione. Podaj swój token z dostępem do {}:", + "provider_connected_placeholder": "{} Token " + }, + "devices": { + "main_screen": { + "description": "Urządzenia te mają pełny dostęp do zarządzania serwerem poprzez aplikację SelfPrivacy.", + "header": "Urządzenia", + "this_device": "To urządzenie", + "other_devices": "Inne urządzenia", + "authorize_new_device": "Autoryzacja nowego urządzenia", + "access_granted_on": "Dostęp przyznany {}", + "tip": "Naciśnij na urządzenie, aby cofnąć dostęp." + }, + "revoke_device_alert": { + "header": "Cofnąć dostęp?", + "description": "Urządzenie {} nie będzie już mogło kontrolować serwera.", + "yes": "Wycofaj się", + "no": "Anulować" + }, + "add_new_device_screen": { + "header": "Autoryzacja nowego urządzenia", + "description": "Wprowadź ten klucz na nowym urządzeniu:", + "please_wait": "Proszę czekać", + "tip": "Klucz jest ważny przez 10 minut.", + "expired": "Klucz stracił ważność.", + "get_new_key": "Uzyskaj nowy klucz" + } + }, + "support": { + "title": "Wsparcie SelfPrivacy" + }, + "developer_settings": { + "subtitle": "Te ustawienia służą wyłącznie do celów debugowania. Nie zmieniaj ich, jeśli nie wiesz, co robisz.", + "title": "Ustawienia dewelopera", + "server_setup": "Kreator instalacji serwera", + "use_staging_acme": "Korzystanie z serwera testów ACME", + "use_staging_acme_description": "Używane podczas konfigurowania nowego serwera.", + "routing": "Trasowanie aplikacji", + "reset_onboarding": "Wyzerowanie flagi powitalnej dla wizyty", + "reset_onboarding_description": "Resetowanie przełącznika zasilania w celu ponownego wyświetlenia ekranu włączania zasilania", + "cubit_statuses": "Aktualny stan qubitów ładujących", + "ignore_tls": "Używane podczas konfigurowania nowego serwera." + } +} diff --git a/assets/translations/ru.json b/assets/translations/ru.json index 68665e26..2371da85 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -1,122 +1,141 @@ { - "test": "ru-test", - "locale": "ru", - "basis": { - "_comment": "базовые элементы интерфейса", - "providers": "Провайдеры", - "services": "Сервисы", - "users": "Пользователи", - "more": "Ещё", - "next": "Далее", - "got_it": "Понял", - "settings": "Настройки", - "password": "Пароль", - "create": "Создать", - "confirmation": "Подтверждение", - "cancel": "Отменить", - "delete": "Удалить", - "close": "Закрыть", - "connect": "Подключить", - "domain": "Домен", - "saving": "Сохранение…", - "nickname": "Никнейм", - "loading": "Загрузка", - "later": "Пропустить и настроить потом", - "connect_to_existing": "Подключиться к существующему серверу", - "reset": "Сбросить", - "details": "Детальная информация", - "no_data": "Нет данных", - "wait": "Загрузка", - "remove": "Удалить", - "apply": "Подать", - "done": "Готово" - }, - "more": { - "_comment": "вкладка ещё", - "configuration_wizard": "Мастер Подключения", - "about_project": "О проекте SelfPrivacy", - "about_app": "О приложении", - "onboarding": "Приветствие", - "console": "Консоль", - "create_ssh_key": "Создать ssh ключ", - "generate_key": "Сгенерировать ключ", - "generate_key_text": "Вы сможете сгенерировать ключ", - "remove": "Отключить", - "enable": "Включить", - "ok": "ok", - "continue": "Продолжить", - "ssh_key_exist_text": "У Вас уже есть сгенерированный ssh ключ", - "yes_delete": "Да, удалить", - "share": "Поделиться", - "copy_buffer": "Копировать в буфер", - "copied_ssh": "SSH ключ cкопирован в буфер", - "delete_ssh_text": "Удалить SSH ключ?", - "about_app_page": { - "text": "Версия приложения: v.{}" + "test": "ru-test", + "locale": "ru", + "basis": { + "providers": "Провайдеры", + "select": "Выбрать", + "providers_title": "Ваш Дата Центр", + "services": "Сервисы", + "services_title": "Ваши личные, приватные и независимые сервисы.", + "users": "Пользователи", + "more": "Ещё", + "next": "Далее", + "got_it": "Понял", + "settings": "Настройки", + "password": "Пароль", + "create": "Создать", + "confirmation": "Подтверждение", + "cancel": "Отменить", + "delete": "Удалить", + "close": "Закрыть", + "connect": "Подключить", + "domain": "Домен", + "saving": "Сохранение…", + "username": "Имя пользователя", + "loading": "Загрузка…", + "later": "Пропустить и настроить потом", + "connect_to_existing": "Подключиться к существующему серверу", + "reset": "Сбросить", + "details": "Детальная информация", + "no_data": "Нет данных", + "wait": "Загрузка", + "remove": "Удалить", + "apply": "Применить", + "done": "Готово", + "continue": "Продолжить", + "alert": "Уведомление", + "copied_to_clipboard": "Скопировано в буфер обмена!", + "app_name": "SelfPrivacy" }, - "settings": { - "title": "Настройки приложения", - "1": "Тёмная тема", - "2": "Сменить цветовую тему.", - "3": "Сброс настроек", - "4": "Сбросить API ключи а также root пользвателя.", - "5": "Удалить сервер", - "6": "Действие приведет к удалению сервера. После этого он будет недоступен." - } - }, - "ssh": { - "title": "SSH ключи", - "create": "Добавить SSH ключ", - "delete": "Удалить SSH ключ", - "delete_confirm_question": "Вы уверены что хотите удалить следующий ключ?", - "subtitle_with_keys": "Ключей: {}", - "subtitle_without_keys": "Ключей нет", - "no_key_name": "Безымянный ключ", - "root": { - "title": "Это ключи суперпользователя", - "subtitle": "Владельцы указанных здесь ключей получают полный доступ к данным и настройкам сервера. Добавляйте исключительно свои ключи." + "more_page": { + "configuration_wizard": "Мастер настройки", + "about_project": "О проекте SelfPrivacy", + "about_application": "О приложении", + "onboarding": "Приветствие", + "console": "Консоль", + "create_ssh_key": "SSH ключи администратора", + "application_settings": "Настройки приложения" }, - "input_label": "Публичный ED25519 или RSA ключ" - }, - "onboarding": { - "_comment": "страницы онбординга", - "page1_title": "Цифровая независимость доступна каждому", - "page1_text": "Почта, VPN, Мессенджер, социальная сеть и многое другое на Вашем личном сервере, под Вашим полным контролем.", - "page2_title": "SelfPrivacy — это не облако, а Ваш личный дата-центр", - "page2_text": "SelfPrivacy работает только с вашими сервис-провайдерами: Hetzner, Cloudflare, Backblaze. Если у Вас нет учётных записей, мы поможем их создать." - }, - "providers": { - "_comment": "вкладка провайдеры", - "page_title": "Ваш Дата-центр", - "server": { - "card_title": "Сервер", - "status": "Статус — в норме", - "bottom_sheet": { - "1": "Это виртуальный компьютер на котором работают все Ваши сервисы.", - "2": "Общая информация", - "3": "Размещение" - }, - "chart": { + "console_page": { + "title": "Консоль", + "waiting": "Ждём инициализации…", + "copy": "Копировать" + }, + "about_us_page": { + "title": "О проекте SelfPrivacy" + }, + "about_application_page": { + "title": "О приложении", + "application_version_text": "Версия приложения {}", + "api_version_text": "Версия API сервера {}", + "privacy_policy": "Политика конфиденциальности" + }, + "application_settings": { + "title": "Настройки приложения", + "dark_theme_title": "Тёмная тема", + "dark_theme_description": "Сменить цветовую тему", + "reset_config_title": "Сброс настроек", + "reset_config_description": "Сбросить API ключи и root пользователя.", + "delete_server_title": "Удалить сервер", + "delete_server_description": "Действие приведёт к удалению сервера. После этого он будет недоступен.", + "system_dark_theme_title": "Системная тема", + "system_dark_theme_description": "Будет использована светлая или тёмная тема в зависимости от системных настроек", + "dangerous_settings": "Опасные настройки" + }, + "ssh": { + "title": "SSH ключи", + "create": "Добавить SSH ключ", + "delete": "Удалить SSH ключ", + "delete_confirm_question": "Вы уверены, что хотите удалить следующий ключ?", + "subtitle_with_keys": "Ключей: {}", + "subtitle_without_keys": "Ключей нет", + "no_key_name": "Безымянный ключ", + "root_title": "Это ключи суперпользователя", + "root_subtitle": "Владельцы указанных здесь ключей получают полный доступ к данным и настройкам сервера. Добавляйте исключительно свои ключи.", + "input_label": "Публичный ED25519 или RSA ключ" + }, + "onboarding": { + "page1_title": "Цифровая независимость доступна каждому", + "page1_text": "Почта, VPN, Мессенджер, социальная сеть и многое другое на Вашем личном сервере, под Вашим полным контролем.", + "page2_title": "SelfPrivacy — это не облако, а ваш личный дата-центр", + "page2_text": "SelfPrivacy работает только с сервис-провайдерами на ваш выбор. Если у Вас нет учётных записей, мы поможем их создать.", + "page2_server_provider_title": "Сервер-провайдер", + "page2_server_provider_text": "Сервер-провайдер будет обслуживать ваш сервер в своём дата-центре. SelfPrivacy автоматически подключится к нему и настроит вам сервер.", + "page2_dns_provider_title": "DNS-провайдер", + "page2_dns_provider_text": "Чтобы быть в интернете, нужен домен. Чтобы домен указывал на ваш сервер, нужен надёжный DNS сервер. Мы предложим вам выбрать один из поддерживаемых DNS серверов автоматически настроим все записи. Хотите настроить их вручную? Так тоже можно.", + "page2_backup_provider_title": "Бэкап-провайдер", + "page2_backup_provider_text": "Что если с сервером что-то случится? Хакерская атака, отказ в обслуживании или просто случайное удаление данных? Ваши данные будут в сохранности в другом месте, у провайдера хранилища ваших резервных копий. Все они надёжно шифруются, и вы сможете восстановить свой сервер." + }, + "resource_chart": { "month": "Месяц", "day": "День", - "hour": "Час" - } + "hour": "Час", + "cpu_title": "Использование процессора", + "network_title": "Использование сети", + "in": "Получено", + "out": "Отправлено" }, - "domain": { - "card_title": "Домен", - "status": "Статус — в норме", - "bottom_sheet": { - "1": "Это ваш личный адрес в интернете, который будет указывать на сервер и другие ваши сервисы." - }, - "screen_title": "Домен и DNS", - "states": { - "ok": "Записи в норме", - "error": "Обнаружены проблемы", - "error_subtitle": "Нажмите здесь, чтобы исправить", - "refreshing": "Обновление данных...", - "uninitialized": "Данные ещё не получены" - }, - "record_description": { + "server": { + "card_title": "Сервер", + "description": "Это виртуальный компьютер на котором работают все ваши сервисы", + "general_information": "Общая информация", + "resource_usage": "Потребление ресурсов", + "allow_autoupgrade": "Разрешить авто-обновления", + "allow_autoupgrade_hint": "Разрешить автоматическую установку обновлений на сервер", + "reboot_after_upgrade": "Перезагружать после обновлений", + "reboot_after_upgrade_hint": "Автоматически перезагружать сервер после применения обновлений", + "server_timezone": "Часовой пояс сервера", + "select_timezone": "Выберите часовой пояс", + "timezone_search_bar": "Имя часового пояса или значение временного сдвига", + "server_id": "ID сервера", + "status": "Статус", + "cpu": "Процессор", + "ram": "Оперативная память", + "disk": "Диск", + "monthly_cost": "Ежемесячная стоимость", + "location": "Размещение", + "pricing_error": "Не удалось получить цены провайдера", + "core_count": { + "one": "{} ядро", + "two": "{} ядра", + "few": "{} ядра", + "many": "{} ядер", + "other": "{} ядер" + }, + "server_provider": "Провайдер сервера", + "dns_provider": "Провайдер DNS" + }, + "record": { "root": "Корневой домен", "api": "SelfPrivacy API", "cloud": "Файловое облако", @@ -129,293 +148,481 @@ "dmarc": "DMARC запись", "spf": "SPF запись", "dkim": "DKIM ключ" - }, - "cards": { - "services": { - "title": "Сервисы", - "subtitle": "Записи типа “A” необходимые для работы сервисов." - }, - "email": { - "title": "Электронная почта", - "subtitle": "Записи необходимые для безопасного обмена электронной почтой." - } - } + }, + "domain": { + "card_title": "Домен", + "screen_title": "Домен и DNS", + "ok": "Записи в норме", + "error": "Обнаружены проблемы", + "error_subtitle": "Нажмите здесь, чтобы исправить. Это также удалит все сторонние записи.", + "refreshing": "Обновление данных…", + "uninitialized": "Данные ещё не получены", + "services_title": "Сервисы", + "services_subtitle": "Записи типа “A” необходимые для работы сервисов.", + "email_title": "Электронная почта", + "email_subtitle": "Записи необходимые для безопасного обмена электронной почтой.", + "update_list": "Обновить список" }, "backup": { - "card_title": "Резервное копирование", - "status": "Статус — в норме", - "bottom_sheet": { - "1": "Выручит Вас в любой ситуации: хакерская атака, удаление сервера и т.д.", - "2": "Использовано 3Gb из бесплатых 10Gb. Последнее копирование была сделано вчера в {}." - }, - "reuploadKey": "Принудительно обновить ключ", - "reuploadedKey": "Ключ на сервере обновлён", - "initialize": "Настроить", - "waitingForRebuild": "Через несколько минут можно будет создать первую копию.", - "restore": "Восстановить из копии", - "no_backups": "Резервных копий пока нет", - "create_new": "Создать новую копию", - "creating": "Создание копии: {}%", - "restoring": "Восстановление из копии", - "error_pending": "Сервер вернул ошибку: проверьте её ниже.", - "restore_alert": "Вы собираетесь восстановить из копии созданной {}. Все текущие данные будут потеряны. Вы уверены?", - "refresh": "Обновить статус", - "refetchBackups": "Обновить список копий", - "refetchingList": "Через несколько минут список будет обновлён" - - } - }, - "not_ready_card": { - "_comment": "Карточка показывающая когда человек скипнул настройку, на карте текст из 3 блоков, средний содержит ссыку на мастер подключения", - "1": "Завершите настройку приложения используя ", - "2": "@:more.configuration_wizard", - "3": " для продолжения работы", - "in_menu": "Сервер ещё не настроен, воспользуйтесь мастером подключения." - }, - "services": { - "_comment": "Вкладка сервисы", - "title": "Ваши личные, приватные и независимые сервисы:", - "mail": { - "title": "Почта", - "subtitle": "Электронная почта для семьи или компании.", - "login_info": "Используйте логин и пароль из вкладки пользователей. IMAP порт: 143, STARTTLS. SMTP порт: 587, STARTTLS.", - "bottom_sheet": { - "1": "Для подключения почтового ящика используйте {} и профиль, который Вы создали. Так же приглашайте", - "2": "новых пользователей." - } + "card_title": "Резервное копирование", + "description": "Выручит Вас в любой ситуации: хакерская атака, удаление сервера и т.д.", + "reupload_key": "Принудительно обновить ключ", + "reuploaded_key": "Ключ на сервере обновлён", + "initialize": "Настроить", + "waiting_for_rebuild": "Через несколько минут можно будет создать первую копию.", + "restore": "Восстановить из копии", + "no_backups": "Резервных копий пока нет", + "create_new": "Создать новую копию", + "creating": "Создание копии: {}%", + "restoring": "Восстановление из копии", + "error_pending": "Сервер вернул ошибку: проверьте её ниже", + "restore_alert": "Вы собираетесь восстановить из копии созданной {}. Все текущие данные будут потеряны. Вы уверены?", + "refresh": "Обновить статус", + "refetch_backups": "Обновить список копий", + "refetching_list": "Через несколько минут список будет обновлён", + "reupload_key_subtitle": "Ещё раз проинициализирует хранилище резервных копий. Используйте, если что-то сломалось.", + "service_busy": "Сейчас создаются другие резервные копии", + "autobackup_period_never": "Автоматическое копирование отключено", + "pending_jobs": "Активные задачи резервного копирования", + "card_subtitle": "Управляйте резервными копиями", + "refetch_backups_subtitle": "Сбросить кэш и запросить данные у провайдера. Может повлечь дополнительные расходы.", + "select_all": "Копировать всё", + "create_new_select_heading": "Выбрать сервисы для копирования", + "start": "Начать создание копий", + "latest_snapshots": "Последние снимки", + "latest_snapshots_subtitle": "Последние 15 снимков", + "show_more": "Показать ещё", + "autobackup_period_title": "Период автоматического копирования", + "autobackup_period_subtitle": "Создание копий раз в {period}", + "autobackup_period_every": "Раз в {period}", + "autobackup_period_disable": "Отключить автоматические копирование", + "autobackup_custom": "Другое", + "autobackup_custom_hint": "Введите период в минутах", + "autobackup_set_period": "Установить период", + "autobackup_period_set": "Период установлен", + "backups_encryption_key": "Ключ шифрования", + "snapshots_title": "Список снимков", + "forget_snapshot_error": "Не удалось забыть снимок", + "backups_encryption_key_not_found": "Ключ шифрования пока не найден, повторите попытку позже.", + "forget_snapshot_alert": "Вы уверены что хотите удалить этот снимок? Это действие обычно нельзя отменить.", + "snapshot_modal_select_strategy": "Выберите стратегию восстановления", + "snapshot_modal_download_verify_option_description": "Меньше риск, но требуется больше свободного места. Загрузка всей резервной копии во временное хранилище, проверка целостности копии, и последующая замена текущих данных.", + "snapshot_modal_service_not_found": "Это снимок сервиса, которого больше нет на вашем сервере. Обычно этого не должно происходить, и мы не сможем выполнить автоматическое восстановление. Вы можете загрузить снимок и восстановить его вручную. Обратитесь в службу поддержки SelfPrivacy, если вам нужна помощь.", + "backups_encryption_key_subtitle": "Храните его в безопасном месте.", + "backups_encryption_key_copy": "Скопируйте ключ шифрования", + "backups_encryption_key_show": "Показать ключ шифрования", + "backups_encryption_key_description": "Этот ключ используется для шифрования резервных копий. Если вы его потеряете, то не сможете восстановить данные из резервной копии. Храните его в надежном месте. Он может пригодиться, если придётся восстанавливать данные вручную.", + "forget_snapshot": "Забудьте о моментальном снимке", + "snapshot_modal_heading": "Сведения о снимке", + "snapshot_service_title": "Сервис", + "snapshot_creation_time_title": "Время создания", + "snapshot_id_title": "ID снимка", + "snapshot_modal_download_verify_option_title": "Загрузить, проверить, и затем заменить", + "snapshot_modal_inplace_option_title": "Заменить на месте", + "snapshot_modal_inplace_option_description": "Требуется меньше свободного места, но выше риск. При загрузке данных из резервной копии заменяют текущие данные сразу.", + "restore_started": "Восстановление началось, проверьте текущий статус в списке заданий", + "quota_subtitles": { + "no_effect": "Это правило не имеет эффекта, так ак перекрыто другим правилом", + "last": { + "two": "Последние {} снимка будут сохраняться вне зависимости от даты создания", + "many": "Последние {} снимков будут сохраняться вне зависимости от даты создания", + "other": "Последние {} снимков будут сохраняться вне зависимости от даты создания", + "zero": "Правило отключено", + "one": "Последний {} снимок будет сохраняться вне зависимости от даты создания", + "few": "Последние {} снимка будут сохраняться вне зависимости от даты создания" + }, + "daily": { + "two": "Последние {} ежедневных снимка будут сохраняться", + "other": "Последние {} ежедневных снимков будут сохраняться", + "zero": "Правило отключено", + "one": "Последний {} ежедневный снимок будет сохраняться", + "few": "Последние {} ежедневных снимка будут сохраняться", + "many": "Последние {} ежедневных снимков будут сохраняться" + }, + "weekly": { + "two": "Последние {} еженедельных снимка будут сохраняться", + "other": "Последние {} еженедельных снимков будут сохраняться", + "zero": "Правило отключено", + "one": "Последний {} еженедельный снимок будет сохраняться", + "few": "Последние {} еженедельных снимка будут сохраняться", + "many": "Последние {} еженедельных снимков будут сохраняться" + }, + "monthly": { + "two": "Последние {} ежемесячных снимка будут сохраняться", + "other": "Последние {} ежемесячных снимков будут сохраняться", + "zero": "Правило отключено", + "one": "Последний {} ежемесячный снимок будет сохраняться", + "few": "Последние {} ежемесячных снимка будут сохраняться", + "many": "Последние {} ежемесячных снимков будут сохраняться" + }, + "yearly": { + "two": "Последние {} ежегодных снимка будут сохраняться", + "many": "Последние {} ежегодных снимков будут сохраняться", + "zero": "Правило отключено", + "one": "Последний {} ежегодный снимок будет сохраняться", + "few": "Последние {} ежегодных снимка будут сохраняться", + "other": "Последние {} ежегодных снимков будут сохраняться" + }, + "last_infinite": "Все снимки будут сохранены", + "daily_infinite": "Все ежедневные снимки будут сохраняться", + "weekly_infinite": "Все еженедельные снимки будут сохраняться", + "monthly_infinite": "Все ежемесячные снимки будут сохраняться", + "yearly_infinite": "Все ежегодные снимки будут сохраняться" + }, + "snapshot_reason_title": "Причина создания", + "snapshot_reasons": { + "auto": "Создано автоматически", + "explicit": "Создано по вашему явному запросу", + "pre_restore": "Создано в качестве меры предосторожности перед рискованным восстановлением", + "unknown": "Неизвестно" + }, + "rotation_quotas_title": "Настройки ротации снимков", + "set_rotation_quotas": "Задать новые квоты ротации", + "quotas_set": "Новые квоты ротации резервных копий заданы", + "quota_titles": { + "last": "Сколько последних снимков сохранять", + "daily": "Сколько ежедневных снимков сохранять", + "weekly": "Сколько еженедельных снимков сохранять", + "monthly": "Сколько ежемесячных снимков сохранять", + "yearly": "Сколько ежегодных снимков сохранять" + }, + "quotas_only_applied_to_autobackups": "Эти настройки применяются только к резервным копиям, созданным автоматически. Созданные вручную резервные копии не будут удалены этими правилами." }, - "messenger": { - "title": "Мессенджер", - "subtitle": "Telegram и Signal не так приватны, как Delta.Chat — он использует Ваш личный сервер.", - "login_info": "Используйте те же логин и пароль, что и для почты.", - "bottom_sheet": { - "1": "Для подключения используйте {} и логин пароль, который Вы создали." - } + "storage": { + "card_title": "Хранилище", + "status_ok": "Проблем на диске не обнаружено", + "status_error": "Заканчивается место на диске", + "disk_usage": "{} использовано", + "disk_total": "{} всего · {}", + "gb": "{} GB", + "mb": "{} MB", + "kb": "{} KB", + "bytes": "Байт", + "extend_volume_button": "Расширить хранилище", + "extending_volume_title": "Расширение хранилища", + "extending_volume_description": "Изменение размера хранилища позволит вам держать больше данных на вашем сервере без расширения самого сервера. Объем можно только увеличить: уменьшить нельзя.", + "extending_volume_price_info": "Цена включает НДС и рассчитана на основе данных о ценах, предоставленных вашим провайдером. Сервер будет перезагружен во время процесса.", + "extending_volume_error": "Не удалось начать расширение хранилища.", + "size": "Размер", + "data_migration_title": "Миграция данных", + "data_migration_notice": "На время миграции данных все сервисы будут выключены.", + "start_migration_button": "Начать миграцию", + "migration_process": "Мигрируем…", + "migration_done": "Завершить" + }, + "not_ready_card": { + "in_menu": "Сервер ещё не настроен, воспользуйтесь мастером подключения." + }, + "service_page": { + "open_in_browser": "Открыть в браузере", + "restart": "Перезапустить сервис", + "disable": "Выключить сервис", + "enable": "Включить сервис", + "move": "Переместить на другой диск", + "uses": "Использует {usage} на {volume}", + "status": { + "active": "Включено и работает", + "inactive": "Остановлено", + "failed": "Не удалось запустить", + "off": "Отключено", + "activating": "Включается", + "deactivating": "Выключается", + "reloading": "Перезапускается" + }, + "snapshots": "Снимки резервных копий" + }, + "mail": { + "title": "Почта", + "subtitle": "Электронная почта для семьи или компании.", + "login_info": "Используйте логин и пароль из вкладки пользователей. IMAP порт: 143, STARTTLS. SMTP порт: 587, STARTTLS." }, "password_manager": { - "title": "Менеджер паролей", - "subtitle": "Это фундамент Вашей безопасности. Создавать, хранить, копировать пароли между устройствами и вбивать их в формы поможет Bitwarden.", - "login_info": "Аккаунт нужно создать на сайте.", - "bottom_sheet": { - "1": "Подключиться к серверу и создать пользователя можно по адресу:." - } + "title": "Менеджер паролей", + "subtitle": "Это фундамент Вашей безопасности. Создавать, хранить, копировать пароли между устройствами и вбивать их в формы поможет Bitwarden.", + "login_info": "Аккаунт нужно создать на сайте." }, "video": { - "title": "Видеоконференция", - "subtitle": "Jitsi meet — отличный аналог Zoom и Google meet который помимо удобства ещё и гарантирует Вам защищённые высококачественные видеоконференции.", - "login_info": "Аккаунт не требуется.", - "bottom_sheet": { - "1": "Для использования просто перейдите по ссылке:." - } + "title": "Видеоконференция", + "subtitle": "Jitsi meet — отличный аналог Zoom и Google meet который помимо удобства ещё и гарантирует Вам защищённые высококачественные видеоконференции.", + "login_info": "Аккаунт не требуется." }, "cloud": { - "title": "Файловое облако", - "subtitle": "Не позволяйте облачным сервисам просматривать ваши данные. Используйте NextCloud — надёжный дом для всех Ваших данных.", - "login_info": "Логин администратора: admin, пароль такой же как у основного пользователя. Создавайте новых пользователей в интерфейсе администратора NextCloud.", - "bottom_sheet": { - "1": "Подключиться к серверу и создать пользователя можно по адресу:." - } + "title": "Файловое облако", + "subtitle": "Не позволяйте облачным сервисам просматривать ваши данные. Используйте NextCloud — надёжный дом для всех Ваших данных.", + "login_info": "Логин администратора: admin, пароль такой же как у основного пользователя. Создавайте новых пользователей в интерфейсе администратора NextCloud." }, "social_network": { - "title": "Социальная сеть", - "subtitle": "Сложно поверить, но стало возможным создать свою собственную социальную сеть, со своими правилами и аудиторией.", - "login_info": "Аккаунт нужно создать на сайте.", - "bottom_sheet": { - "1": "Подключиться к серверу и создать пользователя можно по адресу:." - } + "title": "Социальная сеть", + "subtitle": "Сложно поверить, но стало возможным создать свою собственную социальную сеть, со своими правилами и аудиторией.", + "login_info": "Аккаунт нужно создать на сайте." }, "git": { - "title": "Git-сервер", - "subtitle": "Приватная альтернатива Github, которая принадлежит вам, а не Microsoft.", - "login_info": "Аккаунт нужно создать на сайте. Первый зарегистрированный пользователь становится администратором.", - "bottom_sheet": { - "1": "Подключиться к серверу и создать пользователя можно по адресу:." - } + "title": "Git-сервер", + "subtitle": "Приватная альтернатива Github, которая принадлежит вам, а не Microsoft.", + "login_info": "Аккаунт нужно создать на сайте. Первый зарегистрированный пользователь становится администратором." }, "vpn": { - "title": "VPN сервер", - "subtitle": "Закрытый VPN сервер", - "bottom_sheet": { - "1": "Создать подключиться к VPN-серверу. Движок для безопасной и масштабируемой инфраструктуры VPN" - } - } - }, - "users": { - "_comment": "'Users' tab", - "add_new_user": "Добавьте первого пользователя.", - "new_user": "Новый пользователь", - "not_ready": "Подключите сервер, домен и DNS в разделе Провайдеры чтобы добавить первого пользователя", - "nobody_here": "Здесь будут отображаться пользователи.", - "login": "Логин", - "onboarding": "Приветствие", - "console": "Консоль разработчика", - "new_user_info_note": "Новый пользователь автоматически получит доступ ко всем сервисам.", - "delete_confirm_question": "Вы действительно хотите удалить учетную запись?", - "reset_password": "Сбросить пароль", - "account": "Учетная запись", - "send_registration_data": "Поделиться реквизитами" - }, - "initializing": { - "_comment": "initializing page", - "1": "Подключите сервер", - "2": "Здесь будут жить наши данные и SelfPrivacy-сервисы", - "how": "Как получить API Token", - "3": "Подключите CloudFlare", - "4": "Для управления DNS вашего домена", - "5": "CloudFlare API Token", - "6": "Подключите облачное хранилище Backblaze", - "7": "На данный момент подлюченных доменов нет", - "8": "Загружаем список доменов", - "9": "Найдено больше одного домена, для вашей безопастности, просим Вам удалить не нужные домены", - "10": "Сохранить домен", - "final": "Последний шаг", - "11": "Создать сервер", - "what": "Что это значит?", - "13": "Сервер презагружен, ждем последнюю проверку.", - "14": "Cервер запущен, сейчас он будет проверен и перезагружен.", - "15": "Cервер создан, идет проверка ДНС адресов и запуск сервера.", - "16": "До следующей проверки: ", - "17": "Проверка", - "18": "Как получить Hetzner API Token:'", - "19": "1 Переходим по ссылке ", - "20": "\n2 Заходим в созданный нами проект. Если такового нет - значит создаём.\n3 Наводим мышкой на боковую панель. Она должна раскрыться, показав нам пункты меню. Нас интересует последний — Security (с иконкой ключика).\n4 Далее, в верхней части интерфейса видим примерно такой список: SSH Keys, API Tokens, Certificates, Members. Нам нужен API Tokens. Переходим по нему.\n5 В правой части интерфейса, нас будет ожидать кнопка Generate API token. Если же Вы используете мобильную версию сайта - в нижнем правом углу Вы увидите красный плюсик. Нажимаем на эту кнопку.\n6 В поле Description даём нашему токену название (это может быть любое название, которое Вам нравиться, сути оно не меняет.", - "21": "Сейчас будет дополнительная перезагрузка для активации сертификатов безопастности", - "22": "Создайте главную учетную запись", - "23": "Введите никнейм и сложный пароль", - "finish": "Всё инициализировано.", - "checks": "Проверок выполнено: \n{} / {}" - }, - "recovering": { - "recovery_main_header": "Подключиться к существующему серверу", - "domain_recovery_description": "Введите домен, по которому вы хотите получить доступ к серверу:", - "domain_recover_placeholder": "Домен", - "domain_recover_error": "Не удалось найти сервер с таким доменом", - "method_select_description": "Выберите способ входа:", - "method_select_other_device": "У меня есть доступ на другом устройстве", - "method_select_recovery_key": "У меня есть ключ восстановления", - "method_select_nothing": "У меня ничего из этого нет", - "method_device_description": "Откройте приложение на другом устройстве и откройте экран управления устройствами. Нажмите \"Добавить устройство\" чтобы получить токен для авторизации.", - "method_device_button": "Я получил токен", - "method_device_input_description": "Введите ваш токен авторизации", - "method_device_input_placeholder": "Токен", - "method_recovery_input_description": "Введите ваш токен восстановления", - "fallback_select_description": "Что у вас из этого есть? Выберите первое, что подходит:", - "fallback_select_token_copy": "Копия токена авторизации из другой версии приложения.", - "fallback_select_root_ssh": "Root доступ к серверу по SSH.", - "fallback_select_provider_console": "Доступ к консоли хостинга.", - "authorization_failed": "Не удалось войти с этим ключом", - "fallback_select_provider_console_hint": "Например, Hetzner.", - "hetzner_connected": "Подключение к Hetzner", - "hetzner_connected_description": "Связь с сервером установлена. Введите токен Hetzner с доступом к {}:", - "hetzner_connected_placeholder": "Hetzner токен", - "confirm_server": "Подтвердите сервер", - "confirm_server_description": "Нашли сервер! Подтвердите, что это он:", - "confirm_server_accept": "Да, это он", - "confirm_server_decline": "Выбрать другой сервер", - "choose_server": "Выберите сервер", - "choose_server_description": "Не удалось определить, с каким сервером вы устанавливаете связь.", - "no_servers": "На вашем аккаунте нет доступных серверов.", - "domain_not_available_on_token": "Введённый токен не имеет доступа к нужному домену.", - "modal_confirmation_title": "Это действительно ваш сервер?", - "modal_confirmation_description": "Подключение к неправильному серверу может привести к деструктивным последствиям.", - "confirm_cloudflare": "Подключение к Cloudflare", - "confirm_cloudflare_description": "Введите токен Cloudflare, который имеет права на {}:", - "confirm_backblze": "Подключение к Backblaze", - "confirm_backblaze_description": "Введите токен Backblaze, который имеет права на хранилище резервных копий:" - }, - "devices": { - "main_screen": { - "header": "Устройства", - "description": "Эти устройства имеют полный доступ к управлению сервером через приложение SelfPrivacy.", - "this_device": "Это устройство", - "other_devices": "Другие устройства", - "authorize_new_device": "Авторизовать новое устройство", - "access_granted_on" : "Доступ выдан {}", - "tip": "Нажмите на устройство, чтобы отозвать доступ." + "title": "VPN сервер", + "subtitle": "Закрытый VPN сервер" }, - "add_new_device_screen": { - "header": "Авторизация нового устройства", - "description": "Введите этот ключ на новом устройстве:", - "please_wait": "Пожалуйста, подождите", - "tip": "Ключ действителен 10 минут.", - "expired": "Срок действия ключа истёк.", - "get_new_key": "Получить новый ключ" + "users": { + "add_new_user": "Добавьте первого пользователя", + "new_user": "Новый пользователь", + "delete_user": "Удалить пользователя", + "not_ready": "Подключите сервер, домен и DNS в разделе Провайдеры чтобы добавить первого пользователя", + "nobody_here": "Здесь будут отображаться пользователи", + "login": "Логин", + "new_user_info_note": "Новый пользователь автоматически получит доступ ко всем сервисам", + "delete_confirm_question": "Вы действительно хотите удалить учетную запись?", + "reset_password": "Сбросить пароль", + "account": "Учетная запись", + "send_registration_data": "Поделиться реквизитами", + "could_not_fetch_users": "Не удалось получить пользователей", + "could_not_fetch_description": "Проверьте интернет соединение и попробуйте снова", + "refresh_users": "Обновить список пользователей", + "could_not_create_user": "Не удалось создать пользователя", + "could_not_delete_user": "Не удалось стереть пользователя", + "could_not_add_ssh_key": "Не удалось создать SSH ключ", + "username_rule": "Имя может содержать только маленькие латинские буквы, цифры, подчёркивания, не может начинаться с цифр", + "email_login": "Авторизация по Email", + "no_ssh_notice": "Для этого пользователя созданы только SSH и Email аккаунт. Единая авторизация для всех сервисов ещё не реализована.", + "details_title": "Пользователь" }, - "revoke_device_alert": { - "header": "Отозвать доступ?", - "description": "Устройство {} больше не сможет управлять сервером.", - "yes": "Отозвать", - "no": "Отмена" + "initializing": { + "dns_provider_description": "Это позволит связать ваш домен с IP адресом:", + "connect_to_server": "Начнём с сервера.", + "select_provider": "Ниже подборка провайдеров, которых поддерживает SelfPrivacy", + "select_provider_notice": "Под 'Небольшим сервером' имеется ввиду сервер с двумя потоками процессора и двумя гигабайтами оперативной памяти.", + "select_provider_countries_title": "Доступные страны", + "select_provider_countries_text_hetzner": "Германия, Финляндия, США", + "select_provider_countries_text_do": "США, Нидерланды, Сингапур, Великобритания, Германия, Канада, Индия, Австралия", + "select_provider_price_title": "Средняя цена", + "select_provider_price_free": "Бесплатно", + "select_provider_price_text_hetzner": "€8 в месяц за небольшой сервер и 50GB места на диске", + "select_provider_price_text_do": "$17 в месяц за небольшой сервер и 50GB места на диске", + "select_provider_payment_title": "Методы оплаты", + "select_provider_payment_text_hetzner": "Банковские карты, SWIFT, SEPA, PayPal", + "select_provider_payment_text_cloudflare": "Банковские карты", + "select_provider_payment_text_do": "Банковские карты, Google Pay, PayPal", + "select_provider_email_notice": "Хостинг электронной почты недоступен для новых клиентов. Разблокировать можно будет после первой оплаты.", + "select_provider_site_button": "Посетить сайт", + "connect_to_server_provider": "Авторизоваться в ", + "connect_to_server_provider_text": "С помощью API токена приложение SelfPrivacy сможет от вашего имени заказать и настроить сервер", + "how": "Как получить API Token", + "provider_bad_key_error": "API ключ провайдера неверен", + "could_not_connect": "Не удалось соединиться с провайдером.", + "choose_location_type": "Где заказать сервер?", + "choose_location_type_text": "От выбора локации будут зависеть доступные конфигурации, цены и скорость вашего соединения с сервером.", + "locations_not_found": "Упс!", + "locations_not_found_text": "В этом месте не оказалось доступных серверов для аренды", + "back_to_locations": "Выберем другой", + "no_locations_found": "Не найдено локаций, убедитесь, что ваш аккаунт доступен", + "choose_server_type": "Какой выбрать тип сервера?", + "choose_server_type_text": "От ресурсов сервера зависит, какие сервисы смогут запуститься. Расширить сервер можно будет в любое время", + "choose_server_type_notice": "Главное, на что стоит обратить внимание — количество потоков процессора и объём оперативной памяти. Данные сервисов будут размещены на отдельном диске, который оплачивается отдельно и легко расширяем.", + "choose_server_type_ram": "{} GB у RAM", + "choose_server_type_storage": "{} GB системного хранилища", + "choose_server_type_payment_per_month": "{} в месяц", + "choose_server_type_payment_server": "{} за сам сервер", + "choose_server_type_payment_storage": "{} за расширяемое хранилище", + "choose_server_type_payment_ip": "{} за публичный IPv4", + "no_server_types_found": "Не найдено доступных типов сервера! Пожалуйста, убедитесь, что у вас есть доступ к провайдеру сервера...", + "dns_provider_bad_key_error": "API ключ неверен", + "backblaze_bad_key_error": "Информация о Backblaze хранилище неверна", + "connect_to_dns": "Подключите DNS провайдера", + "connect_to_dns_provider_text": "С помощью API токена приложение SelfPrivacy настроит DNS записи", + "manage_domain_dns": "Для управления DNS вашего домена", + "use_this_domain": "Используем этот домен?", + "use_this_domain_text": "Указанный вами токен даёт контроль над этим доменом", + "connect_backblaze_storage": "Подключите облачное хранилище Backblaze", + "no_connected_domains": "На данный момент подлюченных доменов нет", + "loading_domain_list": "Загружаем список доменов", + "found_more_domains": "Найдено больше одного домена, для вашей безопасности, просим Вам удалить ненужные домены", + "save_domain": "Сохранить домен", + "final": "Последний шаг", + "create_server": "Создать сервер", + "what": "Что это значит?", + "server_rebooted": "Сервер перезагружен, ждём последнюю проверку…", + "server_started": "Сервер запущен. Сейчас он будет проверен и перезагружен…", + "server_created": "Сервер создан. Идёт проверка DNS адресов и запуск сервера…", + "until_the_next_check": "До следующей проверки: ", + "check": "Проверка", + "one_more_restart": "Сейчас будет дополнительная перезагрузка для активации сертификатов безопасности.", + "create_master_account": "Создайте главную учетную запись", + "enter_username_and_password": "Введите имя пользователя и сложный пароль", + "finish": "Всё инициализировано", + "checks": "Проверок выполнено: \n{} / {}", + "select_dns": "Сейчас выберите DNS провайдера", + "steps": { + "hosting": "Хостинг", + "server_type": "Тип сервера", + "nixos_installation": "Установка NixOS", + "dns_provider": "DNS провайдер", + "backups_provider": "Резервное копирование", + "domain": "Домен", + "master_account": "Главная учетная запись", + "server": "Сервер", + "dns_setup": "Установка DNS", + "server_reboot": "Перезагрузка сервера", + "final_checks": "Финальные проверки" + }, + "server_provider_description": "Место, где будут находиться ваши данные и сервисы SelfPrivacy:", + "multiple_domains_found": "Найдено несколько доменов", + "multiple_domains_found_text": "Предоставленный токен дает доступ к следующим доменам. Пожалуйста, выберите тот, который вы хотите использовать. Для обеспечения безопасности других доменов следует ограничить доступ этого токена только тем доменом, который вы хотите использовать с SelfPrivacy." + }, + "recovering": { + "generic_error": "Ошибка проведения операции, попробуйте ещё раз.", + "recovery_main_header": "Подключиться к существующему серверу", + "domain_recovery_description": "Введите домен, по которому вы хотите получить доступ к серверу:", + "domain_recover_placeholder": "Домен", + "domain_recover_error": "Не удалось найти сервер с таким доменом", + "method_select_description": "Выберите способ входа:", + "method_select_other_device": "У меня есть доступ на другом устройстве", + "method_select_recovery_key": "У меня есть ключ восстановления", + "method_select_nothing": "У меня ничего из этого нет", + "method_device_description": "Откройте приложение на другом устройстве и откройте экран управления устройствами. Нажмите \"Добавить устройство\" чтобы получить токен для авторизации.", + "method_device_button": "Я получил токен", + "method_device_input_description": "Введите ваш токен авторизации", + "method_device_input_placeholder": "Токен", + "method_recovery_input_description": "Введите ваш токен восстановления", + "fallback_select_description": "Что у вас из этого есть? Выберите первое, что подходит:", + "fallback_select_token_copy": "Копия токена авторизации из другой версии приложения.", + "fallback_select_root_ssh": "Root доступ к серверу по SSH.", + "fallback_select_provider_console": "Доступ к консоли хостинга.", + "authorization_failed": "Не удалось войти с этим ключом", + "fallback_select_provider_console_hint": "Например, Hetzner.", + "confirm_server": "Подтвердите сервер", + "confirm_server_description": "Нашли сервер! Подтвердите, что это он:", + "confirm_server_accept": "Да, это он", + "confirm_server_decline": "Выбрать другой сервер", + "choose_server": "Выберите сервер", + "choose_server_description": "Не удалось определить, с каким сервером вы устанавливаете связь.", + "no_servers": "На вашем аккаунте нет доступных серверов.", + "domain_not_available_on_token": "Введённый токен не имеет доступа к нужному домену.", + "modal_confirmation_title": "Это действительно ваш сервер?", + "modal_confirmation_description": "Подключение к неправильному серверу может привести к деструктивным последствиям.", + "modal_confirmation_dns_valid": "Обратный DNS корректен", + "modal_confirmation_dns_invalid": "Обратный DNS указывает на другой домен", + "modal_confirmation_ip_valid": "IP совпадает с указанным в DNS записи", + "modal_confirmation_ip_invalid": "IP не совпадает с указанным в DNS записи", + "provider_connected": "Подключение к вашему {}", + "provider_connected_description": "Связь установлена. Введите свой токен с доступом к {}:", + "provider_connected_placeholder": "{} Токен" + }, + "devices": { + "main_screen": { + "header": "Устройства", + "description": "Эти устройства имеют полный доступ к управлению сервером через приложение SelfPrivacy.", + "this_device": "Это устройство", + "other_devices": "Другие устройства", + "authorize_new_device": "Авторизовать новое устройство", + "access_granted_on": "Доступ выдан {}", + "tip": "Нажмите на устройство, чтобы отозвать доступ." + }, + "add_new_device_screen": { + "header": "Авторизация нового устройства", + "description": "Введите этот ключ на новом устройстве:", + "please_wait": "Пожалуйста, подождите", + "tip": "Ключ действителен 10 минут.", + "expired": "Срок действия ключа истёк.", + "get_new_key": "Получить новый ключ" + }, + "revoke_device_alert": { + "header": "Отозвать доступ?", + "description": "Устройство {} больше не сможет управлять сервером.", + "yes": "Отозвать", + "no": "Отмена" + } + }, + "recovery_key": { + "key_connection_error": "Не удалось соединиться с сервером.", + "key_synchronizing": "Синхронизация…", + "key_main_header": "Ключ восстановления", + "key_main_description": "Требуется для авторизации SelfPrivacy, когда авторизованные устройства недоступны.", + "key_amount_toggle": "Ограничить использования", + "key_amount_field_title": "Макс. кол-во использований", + "key_duedate_toggle": "Ограничить срок использования", + "key_duedate_field_title": "Дата окончания срока", + "key_receive_button": "Получить ключ", + "key_valid": "Ваш ключ действителен", + "key_invalid": "Ваш ключ больше не действителен", + "key_valid_until": "Действителен до {}", + "key_valid_for": "Можно использовать ещё {} раз", + "key_creation_date": "Создан {}", + "key_replace_button": "Сгенерировать новый ключ", + "key_receiving_description": "Запишите этот ключ в безопасном месте. Он предоставляет полный доступ к вашему серверу:", + "key_receiving_info": "Этот ключ больше не будет показан, но вы сможете заменить его новым.", + "key_receiving_done": "Готово!", + "generation_error": "Не удалось сгенерировать ключ. {}" + }, + "modals": { + "dns_removal_error": "Невозможно удалить DNS записи.", + "server_deletion_error": "Невозможно удалить сервер.", + "server_validators_error": "Не удалось получить список серверов.", + "already_exists": "Такой сервер уже существует.", + "unexpected_error": "Непредвиденная ошибка со стороны провайдера.", + "destroy_server": "Уничтожить сервер и создать новый?", + "try_again": "Попробовать ещё раз?", + "are_you_sure": "Вы уверены?", + "purge_all_keys": "Стереть все ключи авторизации?", + "purge_all_keys_confirm": "Да, стереть все ключи", + "delete_server_volume": "Удалить сервер и хранилище?", + "reboot": "Перезагрузить", + "you_cant_use_this_api": "Нельзя использовать этот API для доменом с подобным TLD.", + "yes": "Да", + "no": "Нет", + "volume_creation_error": "Не удалось создать хранилище." + }, + "timer": { + "sec": "{} сек" + }, + "jobs": { + "title": "Задачи", + "start": "Начать выполнение", + "empty": "Задач нет", + "create_user": "Создать пользователя", + "delete_user": "Удалить пользователя", + "service_turn_off": "Остановить", + "service_turn_on": "Запустить", + "job_added": "Задача добавлена", + "run_jobs": "Запустите задачи", + "reboot_success": "Сервер перезагружается", + "reboot_failed": "Не удалось перезагрузить сервер, проверьте логи.", + "config_pull_failed": "Не удалось обновить конфигурацию сервера. Обновление ПО запущено.", + "upgrade_success": "Запущено обновление сервера", + "upgrade_failed": "Обновить сервер не вышло", + "upgrade_server": "Обновить сервер", + "reboot_server": "Перезагрузить сервер", + "create_ssh_key": "Создать SSH ключ для {}", + "delete_ssh_key": "Удалить SSH ключ для {}", + "server_jobs": "Задачи на сервере", + "reset_user_password": "Сбросить пароль пользователя", + "generic_error": "Не удалось подключиться к серверу!" + }, + "validations": { + "required": "Обязательное поле", + "already_exist": "Уже существует", + "invalid_format": "Неверный формат", + "invalid_format_password": "Пароль не должен содержать пробелы", + "invalid_format_ssh": "Должен следовать формату SSH ключей", + "root_name": "Имя пользователя не может быть 'root'", + "length_not_equal": "Длина строки [], должна быть равна {}", + "length_longer": "Длина строки [], должна быть меньше либо равна {}" + }, + "support": { + "title": "Поддержка SelfPrivacy" + }, + "developer_settings": { + "title": "Настройки разработчика", + "subtitle": "Эти настройки предназначены только для отладки. Не изменяйте их, если не знаете, что делаете.", + "server_setup": "Мастер установки сервера", + "use_staging_acme": "Использование тестового ACME сервера", + "use_staging_acme_description": "Применяется при настройке нового сервера.", + "routing": "Роутинг приложения", + "reset_onboarding": "Сбросить флаг посещения приветствия", + "cubit_statuses": "Текущий статут кубитов загрузки", + "reset_onboarding_description": "Принудить показ приветственного экрана", + "ignore_tls_description": "Приложение не будет проверять сертификаты TLS при подключении к серверу.", + "ignore_tls": "Не проверять сертификаты TLS" } - }, - "recovery_key": { - "key_connection_error": "Не удалось соединиться с сервером", - "key_synchronizing": "Синхронизация...", - "key_main_header": "Ключ восстановления", - "key_main_description": "Требуется для авторизации SelfPrivacy, когда авторизованные устройства недоступны.", - "key_amount_toggle": "Ограничить использования", - "key_amount_field_title": "Макс. кол-во использований", - "key_duedate_toggle": "Ограничить срок использования", - "key_duedate_field_title": "Дата окончания срока", - "key_receive_button": "Получить ключ", - "key_valid": "Ваш ключ действителен", - "key_invalid": "Ваш ключ больше не действителен", - "key_valid_until": "Действителен до {}", - "key_valid_for": "Можно использовать ещё {} раз", - "key_creation_date": "Создан {}", - "key_replace_button": "Сгенерировать новый ключ", - "key_receiving_description": "Запишите этот ключ в безопасном месте. Он предоставляет полный доступ к вашему серверу:", - "key_receiving_info": "Этот ключ больше не будет показан, но вы сможете заменить его новым.", - "key_receiving_done": "Готово!", - "generation_error": "Не удалось сгенерировать ключ. {}" - }, - "modals": { - "_comment": "messages in modals", - "1": "Сервер с таким именем уже существует", - "2": "Уничтожить сервер и создать новый?", - "3": "Подтвердите", - "4": "Сбросить все ключи?", - "5": "Да, сбросить", - "6": "Удалить сервер и диск?", - "7": "Да, удалить", - "8": "Удалить задачу", - "9": "Перезагрузить", - "10": "API не поддерживает домены с таким TLD.", - "yes": "Да", - "no": "Нет" - }, - "timer": { - "sec": "{} сек" - }, - "jobs": { - "_comment": "Jobs list", - "title": "Задачи", - "start": "Начать выполенение", - "empty": "Пусто.", - "createUser": "Создать пользователя", - "deleteUser": "Удалить пользователя", - "serviceTurnOff": "Остановить", - "serviceTurnOn": "Запустить", - "jobAdded": "Задача добавленна", - "runJobs": "Запустите задачи", - "rebootSuccess": "Сервер перезагружается", - "rebootFailed": "Не удалось перезагрузить сервер, проверьте логи", - "configPullFailed": "Не удалось обновить конфигурацию сервера. Обновление ПО запущено.", - "upgradeSuccess": "Запущено обновление сервера", - "upgradeFailed": "Обновить сервер не вышло", - "upgradeServer": "Обновить сервер", - "rebootServer": "Перезагрузить сервер", - "create_ssh_key": "Создать SSH ключ для {}", - "delete_ssh_key": "Удалить SSH ключ для {}" - }, - "validations": { - "required": "Обязательное поле.", - "invalid_format": "Неверный формат.", - "root_name": "Имя пользователя не может быть 'root'.", - "key_format": "Неверный формат.", - "length_not_equal": "Длина строки []. Должно быть равно {}.", - "length_longer": "Длина строки []. Должно быть меньше либо равно {}.", - "user_already_exist": "Имя уже используется.", - "key_already_exists": "Этот ключ уже добавлен." - } } diff --git a/assets/translations/sk.json b/assets/translations/sk.json new file mode 100644 index 00000000..514b47eb --- /dev/null +++ b/assets/translations/sk.json @@ -0,0 +1,501 @@ +{ + "validations": { + "length_longer": "Dĺžka je [], mala by byť kratšia alebo rovná {}", + "required": "Požadované pole", + "already_exist": "Už existuje", + "invalid_format": "Nesprávny formát", + "invalid_format_ssh": "Musí dodržiavať formát kľúča SSH", + "root_name": "Používateľské meno nemôže byť 'root'", + "length_not_equal": "Dĺžka je [], mala by byť {}", + "invalid_format_password": "Nesmie obsahovať prázdne znaky" + }, + "modals": { + "no": "Nie", + "are_you_sure": "Ste si istý?", + "you_cant_use_this_api": "Toto API nemôžete použiť pre doménu s podobnou TLD.", + "yes": "Áno", + "dns_removal_error": "Nie je možné odstrániť zápisy DNS.", + "server_deletion_error": "Server nie je možné vymazať.", + "server_validators_error": "Nepodarilo sa získať zoznam serverov.", + "already_exists": "Takýto server už existuje.", + "unexpected_error": "Neočakávaná chyba na strane poskytovateľa.", + "destroy_server": "Zničiť server a vytvoriť nový?", + "try_again": "Skúsiť ešte raz?", + "purge_all_keys": "Vymazať všetky autorizačné kľúče?", + "purge_all_keys_confirm": "Áno, vyčistiť všetky moje tokeny", + "delete_server_volume": "Odstrániť server a úložisko?", + "reboot": "Reštartovať" + }, + "jobs": { + "title": "Úlohy", + "start": "Štart", + "empty": "Žiadne úlohy", + "create_user": "Vytvoriť používateľa", + "delete_user": "Vymazať používateľa", + "reboot_success": "Server sa reštartuje", + "config_pull_failed": "Nepodarilo sa stiahnuť aktualizáciu konfigurácie. Aj tak sa začala aktualizácia softvéru.", + "service_turn_off": "Vypnúť", + "service_turn_on": "Zapnúť", + "job_added": "Úloha bola pridaná", + "run_jobs": "Spustiť úlohy", + "reboot_failed": "Server sa nepodarilo reštartovať, skontrolujte protokoly.", + "upgrade_success": "Spustila sa aktualizácia servera", + "upgrade_failed": "Aktualizácia servera zlyhala", + "upgrade_server": "Aktualizovať server", + "reboot_server": "Reštartovať server", + "create_ssh_key": "Vytvoriť kľúč SSH pre {}", + "delete_ssh_key": "Odstrániť kľúč SSH pre {}", + "server_jobs": "Úlohy na serveri", + "reset_user_password": "Obnoviť heslo používateľa", + "generic_error": "Nepodarilo sa pripojiť k serveru!" + }, + "test": "sk-test", + "locale": "sk", + "basis": { + "providers": "Poskytovatelia", + "providers_title": "Vaše dátové centrum", + "select": "Vybrať", + "services": "Služby", + "users": "Užívatelia", + "more": "Viac", + "next": "Ďalší", + "got_it": "Dobre", + "settings": "Nastavenia", + "password": "Heslo", + "create": "Pridať nový", + "confirmation": "Potvrdenie", + "cancel": "Zrušiť", + "delete": "Vymazať", + "close": "Zavrieť", + "connect": "Pripojiť", + "domain": "Doména", + "saving": "Ukladanie…", + "username": "Užívateľské meno", + "later": "Preskočiť a nastaviť neskôr", + "connect_to_existing": "Pripojiť sa k existujúcemu serveru", + "reset": "Resetovať", + "details": "Podrobnosti", + "no_data": "Žiadne dáta", + "wait": "Počkajte", + "remove": "Vymazať", + "done": "Hotovo", + "continue": "Pokračovať", + "alert": "Upozornenie", + "services_title": "Vaše osobné, súkromné a nezávislé služby.", + "loading": "Načítanie…", + "apply": "Uplatniť", + "app_name": "SelfPrivacy" + }, + "more_page": { + "configuration_wizard": "Sprievodca nastavením", + "about_project": "O nás", + "about_application": "O apke", + "console": "Konzola", + "application_settings": "Nastavenia aplikácie", + "onboarding": "Vitajte", + "create_ssh_key": "SSH kľúče superužívateľa" + }, + "console_page": { + "title": "Konzola", + "waiting": "Čakáme na inicializáciu…", + "copy": "Kopírovať" + }, + "about_us_page": { + "title": "O nás" + }, + "about_application_page": { + "title": "O apke", + "application_version_text": "Verzia aplikácie {}", + "api_version_text": "Verzia servera {}", + "privacy_policy": "Zásady ochrany osobných údajov" + }, + "application_settings": { + "title": "Nastavenia aplikácie", + "dark_theme_title": "Temná téma", + "dark_theme_description": "Zmeniť tému aplikácie", + "reset_config_title": "Resetovať nastavenia aplikácie", + "reset_config_description": "Resetovať kľúče API a užívateľa root.", + "delete_server_title": "Zmazať server", + "delete_server_description": "Tým sa odstráni váš server. Už nebude prístupným.", + "system_dark_theme_description": "Použitie svetlej alebo tmavej témy v závislosti od nastavení systému", + "system_dark_theme_title": "Systémová predvolená téma", + "dangerous_settings": "Nebezpečné nastavenia" + }, + "ssh": { + "title": "Kľúče SSH", + "create": "Vytvoriť kľúč SSH", + "delete": "Zmazať kľúč SSH", + "delete_confirm_question": "Ste si istí že chcete vymazať kľúč SSH?", + "subtitle_with_keys": "{} kľúče", + "subtitle_without_keys": "Žiadne kľúče", + "no_key_name": "Kľúč bez mena", + "root_title": "Toto sú kľúče superužívateľa", + "input_label": "Verejný kľúč ED25519 alebo RSA", + "root_subtitle": "Majitelia týchto kľúčov získajú plný prístup na server a môžu na ňom robiť čokoľvek. Pridávajte do servera iba svoje vlastné kľúče." + }, + "onboarding": { + "page1_title": "Digitálna nezávislosť, dostupná každému z nás", + "page1_text": "Email, VPN, Messenger, sociálna sieť a o veľa viac na vašom privátnom servere, pod vašim kontrolom.", + "page2_text": "SelfPrivacy pracuje iba s poskytovateľmi, ktoré si vyberiete. Ak v nich nemáte požadované účty, pomôžeme vám ich vytvoriť.", + "page2_server_provider_title": "Poskytovateľ servera", + "page2_backup_provider_title": "Poskytovateľ zálohovania", + "page2_title": "SelfPrivacy nie je oblak, toto je váš personálny datacentrum", + "page2_server_provider_text": "Poskytovateľ servera udržiava váš server vo svojom vlastnom dátovom centre. SelfPrivacy sa automaticky pripojí k poskytovateľovi a nastaví všetky potrebné veci.", + "page2_dns_provider_title": "Poskytovateľ DNS", + "page2_dns_provider_text": "Potrebujete doménu, aby ste mali miesto na internete. A tiež potrebujete spoľahlivého poskytovateľa DNS, aby bola doména nasmerovaná na váš server. Navrhujeme, aby ste si vybrali podporovaného poskytovateľa DNS na automatické nastavenie sietí.", + "page2_backup_provider_text": "Čo ak sa niečo stane na vašom serveri? Predstavte si hackerský útok, náhodné vymazanie údajov alebo odmietnutie služby? Vaše údaje budú udržiavané v bezpečí vášho poskytovateľa záloh. Budú bezpečne šifrovaní a kedykoľvek sú prístupní na obnovenie servera." + }, + "resource_chart": { + "month": "Mesiac", + "day": "Deň", + "hour": "Hodina", + "cpu_title": "Využitie procesora", + "network_title": "Využitie siete", + "out": "Von", + "in": "Dnu" + }, + "server": { + "card_title": "Server", + "description": "Všetky vaši servery bývajú tu", + "general_information": "Všeobecné informácie", + "resource_usage": "Využitie zdrojov", + "allow_autoupgrade": "Povoliť automatické aktualizácie", + "allow_autoupgrade_hint": "Povoliť automatické aktualizácie balíkov na serveri", + "reboot_after_upgrade": "Reštartovať po aktualizácie", + "reboot_after_upgrade_hint": "Reštartovať server po aktualizácii bez výzvy", + "server_timezone": "Časový pás servera", + "select_timezone": "Vyberte časový pás", + "timezone_search_bar": "Názov časového pásma alebo veľkosť časového posunu", + "server_id": "ID servera", + "status": "Stav", + "cpu": "Procesor", + "ram": "Pamäť", + "disk": "Lokálny disk", + "monthly_cost": "Mesačná cena", + "location": "Lokalita", + "core_count": { + "one": "{} jadro", + "two": "{} jadrá", + "few": "{} jadier", + "many": "{} jadier", + "other": "{} jadier" + } + }, + "record": { + "root": "Koreňová doména", + "api": "SelfPrivacy API", + "cloud": "Súborový cloud", + "git": "Git server", + "meet": "Video konferencie", + "social": "Sociálna sieť", + "password": "Správca hesiel", + "vpn": "VPN", + "mx": "Zápis MX", + "dmarc": "Zápis DMARC", + "spf": "Zápis SPF", + "dkim": "Kľúč DKIM" + }, + "domain": { + "screen_title": "Doména a DNS", + "ok": "Zápise sú v poriadku", + "error": "Nájdené problémy", + "error_subtitle": "Klepnutím sem ich opravíte", + "uninitialized": "Údaje ešte nie sú načítané", + "services_title": "Služby", + "services_subtitle": "Zápisy typu “A” nutne pre každú službu.", + "email_title": "Email", + "update_list": "Obnoviť zoznam", + "card_title": "Doména", + "refreshing": "Obnovovanie stavu…", + "email_subtitle": "Zápisy nutne pre bezpečnú prácu s emailami." + }, + "service_page": { + "open_in_browser": "Otvoriť v prehliadači", + "restart": "Reštartovať službu", + "disable": "Vypnúť službu", + "uses": "Využíva {usage} na {volume}", + "enable": "Zapnúť službu", + "move": "Presunúť na iný disk", + "status": { + "active": "Zapnuté a funguje", + "inactive": "Vypnuté", + "failed": "Chyba pri štarte", + "off": "Vypnuté", + "activating": "Zapína sa", + "deactivating": "Vypína sa", + "reloading": "Reštartuje sa" + } + }, + "backup": { + "card_title": "Záloha", + "description": "Ušetrí vám nervy v prípade incidentu: útok hackerov, vymazanie servera atď.", + "reupload_key": "Násilne aktualizovať kľúč", + "reuploaded_key": "Kľúč je aktualizovaný", + "initialize": "Nastaviť", + "restore": "Obnoviť zo zálohy", + "no_backups": "Zatiaľ nemáte žiadne záložné kópie", + "create_new": "Vytvoriť novú zálohu", + "creating": "Vytvorenie zálohy: {}%", + "restoring": "Obnovenie zo zálohy", + "error_pending": "Server vrátil chybu, pozrite ju nižšie", + "refresh": "Obnoviť stav", + "refetch_backups": "Obnoviť zoznam záloh", + "refetching_list": "O pár minút zoznam bude aktualizovaný", + "waiting_for_rebuild": "O pár minút budete môcť vytvoriť svoju prvú zálohu.", + "restore_alert": "Chystáte sa obnoviť zo zálohy vytvorenej {}. Všetky aktuálne údaje sa stratia. Ste si istý?" + }, + "storage": { + "card_title": "Skladovací priestor", + "status_ok": "Disk je v poriadku", + "status_error": "Málo miesta na disku", + "disk_usage": "Využité {}", + "disk_total": "{} celkove · {}", + "gb": "{} GB", + "mb": "{} MB", + "kb": "{} KB", + "bytes": "Bajtov", + "extend_volume_button": "Rozšíriť úložisko", + "extending_volume_price_info": "Cena je vrátane DPH a vychádza z cenových údajov poskytnutých spoločnosťou Hetzner. Server sa počas procesu reštartuje.", + "extending_volume_error": "Nepodarilo sa spustiť rozšírenie úložiska.", + "size": "Veľkosť", + "data_migration_title": "Migrácia údajov", + "data_migration_notice": "V čase migrácie údajov sa všetky služby vypnú.", + "start_migration_button": "Spustiť migráciu", + "migration_process": "Migruje sa…", + "migration_done": "Dokončiť", + "extending_volume_title": "Rozšírenie úložiska", + "extending_volume_description": "Zmena veľkosti úložiska vám umožní uchovávať viac údajov na serveri bez rozšírenia samotného servera. Objem sa dá len zvýšiť: nemôžete znížiť." + }, + "not_ready_card": { + "in_menu": "Server ešte nie je nakonfigurovaný, použite sprievodcu nastavením." + }, + "initializing": { + "choose_server_type_notice": "Dôležité veci, na ktoré sa treba pozrieť, sú CPU a RAM. Dáta vašich služieb budú uložené na pripojenom disku, ktorý sa dá ľahko rozšíriť a platí sa zaň samostatne.", + "select_provider": "Vyberte si ktoréhokoľvek poskytovateľa z nasledujúceho zoznamu, všetci podporujú SelfPrivacy", + "select_provider_countries_text_do": "USA, Holandsko, Singapur, Spojené kráľovstvo, Nemecko, Kanada, India, Austrália", + "select_provider_email_notice": "E-mailový hosting nie je dostupný pre nových zákazníkov. Odomknutie bude možné po prvej platbe.", + "choose_location_type_text": "Rôzne miesta poskytujú rôzne konfigurácie serverov, ceny a rýchlosť pripojenia.", + "choose_server_type_text": "Rôzne možnosti zdrojov podporujú rôzne služby. Nebojte sa, svoj server môžete kedykoľvek rozšíriť", + "enter_username_and_password": "Zadajte používateľské meno a zložité heslo", + "finish": "Všetko je inicializované", + "use_this_domain_text": "Token, ktorý ste poskytli, poskytuje prístup k nasledujúcej doméne", + "connect_backblaze_storage": "Pripojte svoje cloudové úložisko Backblaze", + "no_connected_domains": "Momentálne nie sú pripojené žiadne domény", + "loading_domain_list": "Načítava sa zoznam domén", + "found_more_domains": "Našlo sa viac ako jedna doména. Pre vašu vlastnú bezpečnosť vás požiadame o odstránenie nepotrebných domén", + "save_domain": "Uložiť doménu", + "final": "Posledný krok", + "create_server": "Vytvoriť server", + "what": "Čo to znamená?", + "server_rebooted": "Server bol reštartovaný. Čaká sa na posledné overenie…", + "server_started": "Server spustený. Teraz bude overený a reštartovaný…", + "server_created": "Server bol vytvorený. Prebieha kontrola DNS a spúšťanie servera…", + "until_the_next_check": "Do nasledujúcej kontroly: ", + "check": "Kontrola", + "one_more_restart": "Teraz dôjde k ďalšiemu reštartu na aktiváciu bezpečnostných certifikátov.", + "connect_to_server": "Začnime so serverom.", + "select_provider_notice": "Pod pojmom „malý server“ rozumieme server s dvoma procesorovými vláknami a dvoma gigabajtmi pamäte RAM.", + "select_provider_countries_title": "Dostupné krajiny", + "select_provider_countries_text_hetzner": "Nemecko, Fínsko, USA", + "select_provider_price_title": "Priemerná cena", + "select_provider_price_text_hetzner": "8€ mesačne za malý server a 50 GB miesta na disku", + "select_provider_price_text_do": "$17 mesačne za malý server a 50 GB miesta na disku", + "select_provider_payment_title": "Spôsoby platby", + "select_provider_payment_text_hetzner": "Bankové karty, SWIFT, SEPA, PayPal", + "select_provider_payment_text_do": "Bankové karty, Google Pay, PayPal", + "select_provider_site_button": "Navštíviť stránku", + "connect_to_server_provider": "Prihlásiť sa v ", + "connect_to_server_provider_text": "S API tokenom si SelfPrivacy bude môcť prenajať stroj a nastaviť na ňom váš server", + "how": "Ako získať token API", + "provider_bad_key_error": "Kľúč API poskytovateľa je neplatný", + "could_not_connect": "Nepodarilo sa pripojiť k poskytovateľovi.", + "choose_location_type": "Kde objednať server?", + "locations_not_found": "Ojoj!", + "locations_not_found_text": "Nie sú k dispozícii žiadne servery na prenájom", + "back_to_locations": "Vybrať niečo iné", + "no_locations_found": "Nenašli sa žiadne dostupné miesta, skontrolujte, či je váš účet prístupný", + "choose_server_type": "Aký typ servera potrebujete?", + "choose_server_type_ram": "{} GB RAM", + "choose_server_type_storage": "{} GB systémového úložiska", + "choose_server_type_payment_per_month": "{} mesačne", + "no_server_types_found": "Nenašli sa žiadne dostupné typy serverov. Uistite sa, že je váš účet prístupný a skúste zmeniť umiestnenie servera.", + "backblaze_bad_key_error": "Informácie o úložisku Backblaze sú neplatné", + "select_dns": "Teraz vyberme poskytovateľa DNS", + "manage_domain_dns": "Na správu DNS vašej domény", + "use_this_domain": "Chcete použiť túto doménu?", + "create_master_account": "Vytvorte hlavný účet", + "checks": "Kontroly boli ukončené\n{} z {}", + "steps": { + "nixos_installation": "Inštalácia NixOS", + "hosting": "Hosting", + "server_type": "Typ servera", + "dns_provider": "Poskytovateľ DNS", + "backups_provider": "Záložné kópie", + "domain": "Doména", + "master_account": "Hlavný účet", + "server": "Server", + "dns_setup": "Nastavenie DNS", + "server_reboot": "Reštartovanie servera", + "final_checks": "Záverečné previerky" + } + }, + "mail": { + "subtitle": "Email pre rodinu alebo spoločnosť.", + "title": "Email", + "login_info": "Užívateľské meno a heslo z tabu používateľov. IMAP port je 143 so STARTTLS, SMTP port je 587 so STARTTLS." + }, + "password_manager": { + "subtitle": "Toto je základ vašej bezpečnosti. Bitwarden vám pomôže vytvárať, ukladať, kopírovať heslá medzi zariadeniami a vkladať ich do formulárov.", + "title": "Správca hesiel", + "login_info": "Musíte vytvoriť účet na webovej stránke." + }, + "cloud": { + "subtitle": "Zabráňte cloudovým službám v prezeraní vašich údajov. Použite NextCloud – bezpečný domov pre všetky vaše dáta.", + "title": "Súborový cloud", + "login_info": "Prihlásenie správcu: admin, heslo je rovnaké ako pre hlavného používateľa. Vytvorte nových používateľov v správcovskom rozhraní NextCloud." + }, + "social_network": { + "subtitle": "Je ťažké tomu uveriť, ale bolo možné vytvoriť si vlastnú sociálnu sieť s vlastnými pravidlami a publikom.", + "title": "Sociálna sieť", + "login_info": "Musíte vytvoriť účet na webovej stránke." + }, + "users": { + "not_ready": "Pripojte server, doménu a DNS v sekcii Poskytovatelia a pridajte prvého používateľa", + "could_not_fetch_users": "Nepodarilo sa načítať používateľov", + "could_not_delete_user": "Nepodarilo sa vymazať účet", + "could_not_add_ssh_key": "Nepodarilo sa pridať kľúč SSH", + "username_rule": "Meno môže obsahovať len malé latinské písmená, čísla, podčiarkovníky, nemôže začínať číslicami", + "add_new_user": "Pridajte prvého používateľa", + "new_user": "Nový používateľ", + "delete_user": "Vymazať používateľa", + "nobody_here": "Tu sa zobrazia používatelia", + "login": "Užívateľské meno", + "new_user_info_note": "Nový používateľ bude mať automaticky prístup ku všetkým službám", + "delete_confirm_question": "Naozaj chcete odstrániť ten účet?", + "reset_password": "Resetovať heslo", + "account": "Účet", + "send_registration_data": "Zdieľať údaje účtu", + "could_not_fetch_description": "Skontrolujte internetové pripojenie a skúste to znova", + "refresh_users": "Aktualizovať zoznam používateľov", + "could_not_create_user": "Nepodarilo sa vytvoriť účet", + "email_login": "Emailová autorizácia", + "no_ssh_notice": "Pre tohto používateľa sú vytvorené iba e-mailové účty a účty SSH. Čoskoro bude k dispozícii jednotné prihlásenie pre všetky služby.", + "details_title": "Detaily používateľa" + }, + "recovering": { + "recovery_main_header": "Pripojiť sa k existujúcemu serveru", + "method_select_other_device": "Mám prístup na inom zariadení", + "method_device_description": "Otvorte aplikáciu na inom zariadení a otvorte obrazovku správy zariadenia. Kliknutím na „Pridať zariadenie“ získate autorizačný token.", + "provider_connected": "Pripojiť sa k poskytovateľovi {}", + "choose_server": "Vyberte si svoj server", + "domain_not_available_on_token": "Vybraná doména nie je na tomto tokene dostupná.", + "modal_confirmation_ip_valid": "IP je rovnaká ako v DNS zázname", + "generic_error": "Operácia zlyhala, skúste to znova.", + "domain_recovery_description": "Zadajte doménu servera, pre ktorú chcete získať prístup:", + "domain_recover_placeholder": "Vaša doména", + "domain_recover_error": "Server s takouto doménou sa nenašiel", + "method_select_description": "Vyberte spôsob obnovenia:", + "method_select_recovery_key": "Mám kľúč na obnovenie", + "method_select_nothing": "Nič z toho nemám", + "method_device_button": "Dostal som token", + "method_device_input_description": "Zadajte svoj autorizačný token", + "method_device_input_placeholder": "Token", + "method_recovery_input_description": "Zadajte svoj token na obnovenie", + "fallback_select_description": "Čo z toho máte? Vyberte prvý, ktorý vyhovuje:", + "fallback_select_token_copy": "Kópia autorizačného tokenu z inej verzie aplikácie.", + "fallback_select_root_ssh": "Rootový prístup k serveru cez SSH.", + "fallback_select_provider_console": "Prístup ku konzole servera môjho poskytovateľa.", + "authorization_failed": "Pomocou tohto kľúča sa nepodarilo prihlásiť", + "fallback_select_provider_console_hint": "Napríklad Hetzner.", + "provider_connected_description": "Spojenie bolo nadviazané. Zadajte svoj token s prístupom k {}:", + "provider_connected_placeholder": "{} Token", + "confirm_server": "Potvrďte server", + "confirm_server_description": "Našiel sa server! Potvrďte, že je to on:", + "confirm_server_accept": "Áno, to je on", + "confirm_server_decline": "Vybrať iný server", + "choose_server_description": "Nepodarilo sa nám zistiť, ku ktorému serveru sa pokúšate pripojiť.", + "no_servers": "Vo vašom účte nie sú dostupné žiadne servery.", + "modal_confirmation_title": "Je to naozaj váš server?", + "modal_confirmation_description": "Ak sa pripojíte k nesprávnemu serveru, môžete stratiť všetky svoje údaje.", + "modal_confirmation_dns_valid": "Reverzný DNS je platný", + "modal_confirmation_dns_invalid": "Reverzné DNS ukazuje na inú doménu", + "modal_confirmation_ip_invalid": "IP nie je rovnaká ako v DNS zázname" + }, + "devices": { + "add_new_device_screen": { + "get_new_key": "Získať nový kľúč", + "header": "Autorizácia nového zariadenia", + "description": "Zadajte kľúč na zariadení, ktoré chcete autorizovať:", + "please_wait": "Prosím počkajte", + "tip": "Platnosť kľúča je 10 minút.", + "expired": "Platnosť kľúča vypršala." + }, + "main_screen": { + "description": "Tieto zariadenia majú plný prístup k serveru prostredníctvom aplikácie SelfPrivacy.", + "tip": "Stlačením na zariadení zrušíte prístup.", + "header": "Zariadenia", + "this_device": "Toto zariadenie", + "other_devices": "Iné zariadenia", + "authorize_new_device": "Autorizovať nové zariadenie", + "access_granted_on": "Prístup poskytnutý {}" + }, + "revoke_device_alert": { + "header": "Odobrať prístup?", + "description": "Zariadenie {} už nebude mať prístup k serveru.", + "yes": "Odobrať", + "no": "Zrušiť" + } + }, + "recovery_key": { + "key_main_description": "Vyžaduje sa pre autorizáciu SelfPrivacy, keď autorizované zariadenia nie sú dostupné.", + "key_valid_for": "Môžete použiť ešte {} krát", + "key_receiving_description": "Zapíšte si tento kľúč na bezpečnom mieste. Poskytuje plný prístup k vášmu serveru:", + "key_connection_error": "Nepodarilo sa pripojiť k serveru.", + "key_synchronizing": "Synchronizácia…", + "key_main_header": "Kľúč na obnovenie", + "key_amount_toggle": "Obmedziť používanie", + "key_amount_field_title": "Maximálny počet použití", + "key_duedate_toggle": "Obmedziť časom", + "key_duedate_field_title": "Dátum splatnosti", + "key_receive_button": "Získať kľúč", + "key_valid": "Váš kľúč je platný", + "key_invalid": "Váš kľúč už nie je platný", + "key_valid_until": "Platný do {}", + "key_creation_date": "Vytvorený {}", + "key_replace_button": "Vygenerovať nový kľúč", + "key_receiving_info": "Tento kľúč sa už nebude zobrazovať, ale môžete ho nahradiť novým.", + "key_receiving_done": "Hotovo!", + "generation_error": "Nepodarilo sa vygenerovať kľúč. {}" + }, + "timer": { + "sec": "{} sek" + }, + "video": { + "title": "Video konferencia", + "subtitle": "Jitsi meet je výborný analog Zoom a Google meet, ktorý vám okrem pohodlia zaručí aj bezpečné videokonferencie vo vysokej kvalite.", + "login_info": "Účet sa nevyžaduje." + }, + "git": { + "title": "Git server", + "subtitle": "Súkromná alternatíva Github, ktorá patrí vám, nie spoločnosti Microsoft.", + "login_info": "Na stránke musí byť vytvorený účet. Prvý registrovaný užívateľ sa stáva administrátorom." + }, + "vpn": { + "title": "Server VPN", + "subtitle": "Súkromný server VPN" + }, + "support": { + "title": "Podpora SelfPrivacy" + }, + "developer_settings": { + "subtitle": "Tieto nastavenia slúžia len na ladenie. Nemeňte ich, ak neviete, čo robíte.", + "reset_onboarding": "Obnovenie uvítacej vlajky pre návštevu", + "title": "Nastavenia vývojára", + "server_setup": "Nastavenie servera", + "use_staging_acme": "Používanie testovacieho servera ACME", + "use_staging_acme_description": "Ak chcete túto hodnotu zmeniť, prekompilujte aplikáciu.", + "routing": "Smerovanie aplikácie", + "reset_onboarding_description": "Resetovanie vypínača na opätovné zobrazenie obrazovky zapnutia", + "cubit_statuses": "Aktuálny stav načítavania qubitov" + } +} diff --git a/assets/translations/sl.json b/assets/translations/sl.json new file mode 100644 index 00000000..f8250745 --- /dev/null +++ b/assets/translations/sl.json @@ -0,0 +1,255 @@ +{ + "test": "sl-test", + "locale": "sl", + "basis": { + "providers_title": "Vaš podatkovni center", + "select": "Izberite", + "services": "Storitve", + "users": "Uporabniki", + "more": "Več", + "next": "Naslednji", + "got_it": "Imam ga", + "settings": "Nastavitve", + "password": "Geslo", + "create": "Dodajte novo", + "confirmation": "Potrditev", + "cancel": "Prekliči", + "providers": "Ponudniki", + "services_title": "Vaše osebne, zasebne in neodvisne storitve.", + "app_name": "SelfPrivacy", + "delete": "Izbriši", + "close": "Zapri", + "connect": "Povežite", + "domain": "Domena", + "saving": "Varčevanje…", + "username": "Uporabniško ime", + "loading": "Nalaganje…", + "later": "Preskočite in prilagodite pozneje", + "connect_to_existing": "Preskočite in prilagodite pozneje", + "reset": "Ponastavitev", + "details": "Podrobne informacije", + "no_data": "Ni podatkov", + "wait": "Prenesi", + "remove": "Izbriši", + "apply": "Uporabi", + "done": "Končano", + "continue": "Nadaljuj", + "alert": "Opozorilo" + }, + "more_page": { + "about_application": "O prilogi", + "about_project": "O storitvi SelfPrivacy", + "onboarding": "Pozdravi", + "console": "Konzola", + "application_settings": "Nastavitve aplikacije", + "create_ssh_key": "Superuporabniški ključi SSH", + "configuration_wizard": "Pomočnik za nastavitev" + }, + "console_page": { + "title": "Konzole", + "waiting": "Čakanje na inicializacijo…", + "copy": "Kopiraj" + }, + "about_us_page": { + "title": "O storitvi SelfPrivacy" + }, + "about_application_page": { + "title": "O dodatku", + "api_version_text": "Različica API strežnika {}", + "privacy_policy": "Pravilnik o zasebnosti", + "application_version_text": "Različica aplikacije {}" + }, + "application_settings": { + "dark_theme_title": "Temna tema", + "title": "Nastavitve aplikacije", + "system_dark_theme_title": "Privzeta tema sistema", + "system_dark_theme_description": "Uporaba svetle ali temne teme glede na sistemske nastavitve", + "dark_theme_description": "Spreminjanje barvne teme", + "dangerous_settings": "Nevarne nastavitve", + "reset_config_title": "Ponastavitev konfiguracije aplikacije", + "delete_server_title": "Brisanje strežnika", + "delete_server_description": "To dejanje povzroči izbris strežnika. Nato bo nedosegljiv." + }, + "onboarding": { + "page1_title": "Digitalna neodvisnost je na voljo vsem", + "page1_text": "Pošta, VPN, messenger, družabna omrežja in še veliko več na lastnem zasebnem strežniku pod vašim popolnim nadzorom.", + "page2_title": "SelfPrivacy ni oblak, temveč vaš osebni podatkovni center", + "page2_text": "Storitev SelfPrivacy sodeluje le s ponudnikom storitev po vaši izbiri. Če nimate računov, vam jih lahko pomagamo ustvariti.", + "page2_server_provider_title": "Ponudnik strežnika", + "page2_server_provider_text": "Ponudnik strežnika bo vaš strežnik vzdrževal v svojem podatkovnem centru. SelfPrivacy se bo samodejno povezal z njim in nastavil vaš strežnik.", + "page2_dns_provider_title": "Ponudnik storitev DNS", + "page2_dns_provider_text": "To be on the Internet, you need a domain. To have a domain point to your server, you need a reliable DNS server. We will offer you to choose one of the supported DNS servers and automatically configure all entries. Want to configure them manually? You can do that too.", + "page2_backup_provider_title": "Ponudnik varnostnih kopij", + "page2_backup_provider_text": "Kaj če se kaj zgodi z vašim strežnikom? Predstavljajte si hekerski napad, nenamerno brisanje podatkov ali zavrnitev storitve? Vaši podatki bodo na varnem pri ponudniku varnostnih kopij. Te bodo varno šifrirane in kadar koli dostopne za obnovitev strežnika." + }, + "resource_chart": { + "month": "Mesec", + "day": "Dan", + "hour": "Ura", + "cpu_title": "Poraba CPU", + "out": "Poslano", + "network_title": "Uporaba omrežja", + "in": "Pridobljeno iz" + }, + "server": { + "card_title": "Server", + "description": "Vse vaše storitve so na voljo tukaj", + "general_information": "Splošne informacije", + "resource_usage": "Uporaba virov", + "allow_autoupgrade": "Dovolite samodejno nadgradnjo", + "allow_autoupgrade_hint": "Omogočite samodejne nadgradnje paketov na strežniku", + "reboot_after_upgrade": "Ponovni zagon po nadgradnji", + "core_count": { + "two": "{} jedra", + "few": "{} jedra", + "one": "{} jedro", + "many": "{} jedra", + "other": "{} jeder" + }, + "reboot_after_upgrade_hint": "Ponovni zagon brez poziva po uporabi sprememb v strežniku", + "server_timezone": "Časovni pas strežnika", + "select_timezone": "Izberite časovni pas", + "timezone_search_bar": "Ime časovnega pasu ali vrednost časovnega zamika", + "server_id": "ID strežnika", + "status": "Status", + "cpu": "CPU", + "ram": "Glavni pomnilnik", + "disk": "Lokalni disk", + "monthly_cost": "Mesečni stroški", + "location": "Lokacija" + }, + "ssh": { + "root_subtitle": "Lastniki tukaj navedenih ključev imajo popoln dostop do podatkov in nastavitev strežnika. Dodajte samo svoje ključe.", + "title": "Ključi SSH", + "create": "Dodajanje ključa SSH", + "delete": "Odstranitev ključa SSH", + "delete_confirm_question": "Ali ste prepričani, da želite izbrisati naslednjo tipko?", + "subtitle_with_keys": "Ključi: {}", + "subtitle_without_keys": "Brez ključev", + "no_key_name": "Ključ brez imena", + "root_title": "To so ključi superuporabnika", + "input_label": "Javni ključ ED25519 ali RSA" + }, + "record": { + "cloud": "Oblak datotek", + "root": "Korenska domena", + "api": "SelfPrivacy API", + "git": "Git Strežnik", + "meet": "Videokonferenca", + "social": "Družabno omrežje", + "password": "Upravitelj gesel", + "vpn": "VPN", + "mx": "Zapis MX", + "dmarc": "DMARC zapis", + "spf": "SPF zapis", + "dkim": "DKIM ključ" + }, + "domain": { + "screen_title": "Domena in DNS", + "card_title": "Domena", + "ok": "Zapisi so v redu", + "error": "Ugotovljene težave", + "error_subtitle": "Kliknite tukaj za popravek", + "refreshing": "Posodobitev podatkov…", + "uninitialized": "Podatki še niso na voljo", + "services_title": "Storitve", + "services_subtitle": "Za vsako storitev so potrebni zapisi tipa \"A\".", + "email_title": "E-pošta", + "email_subtitle": "Zapisi, potrebni za varno izmenjavo e-pošte.", + "update_list": "Seznam posodobitev" + }, + "backup": { + "restore": "Obnovitev iz varnostne kopije", + "no_backups": "Varnostnih kopij še ni", + "card_title": "Varnostna kopija", + "description": "V primeru incidenta: napada hekerjev, izbrisa strežnika itd.", + "reupload_key": "Prisilna posodobitev ključa", + "reuploaded_key": "Ključ v strežniku je bil posodobljen", + "initialize": "Inicializacija", + "waiting_for_rebuild": "Prvo varnostno kopijo boste lahko ustvarili v nekaj minutah.", + "create_new": "Ustvarite novo varnostno kopijo", + "creating": "Ustvarjanje nove varnostne kopije: {}%", + "restoring": "Obnovitev iz varnostne kopije", + "error_pending": "Strežnik je vrnil napako, preverite spodaj", + "restore_alert": "Obnavljate iz varnostne kopije, ustvarjene dne {}. Vsi trenutni podatki bodo izgubljeni. Ste prepričani?", + "refresh": "Osvežitev stanja", + "refetch_backups": "Ponovno pridobivanje seznama varnostnih kopij", + "refetching_list": "V nekaj minutah bo seznam posodobljen" + }, + "mail": { + "login_info": "Uporabite uporabniško ime in geslo iz zavihka uporabniki. Vrata IMAP: 143, STARTTLS. Vrata SMTP: 587, STARTTLS.", + "title": "E-naslov", + "subtitle": "E-pošta za družbo in družino." + }, + "password_manager": { + "title": "Upravitelj gesel", + "subtitle": "Osnova vaše varnosti. Bitwarden vam bo pomagal ustvarjati, shranjevati in prenašati gesla med napravami ter jih na zahtevo vnašati z uporabo samodejnega dopolnjevanja.", + "login_info": "Na spletnem mestu boste morali ustvariti račun." + }, + "video": { + "title": "Videokonferenca", + "subtitle": "Zoom in Google Meet sta dobra, vendar je Jitsi Meet vredna alternativa, ki vam daje tudi zagotovilo, da vam nihče ne prisluškuje.", + "login_info": "Račun ni potreben." + }, + "storage": { + "extending_volume_price_info": "Cena vključuje DDV in je izračunana na podlagi podatkov o cenah, ki jih je posredoval Hetzner. Strežnik se med postopkom ponovno zažene.", + "data_migration_notice": "Med migracijo bodo vse storitve izklopljene.", + "card_title": "Shranjevanje podatkov v strežniku", + "status_ok": "Uporaba diska je v redu", + "status_error": "Malo prostora na disku", + "disk_usage": "{} uporablja se", + "disk_total": "{} skupaj - {}", + "gb": "{} GB", + "mb": "{} MB", + "kb": "{} KB", + "bytes": "Bajti", + "extend_volume_button": "Podaljšanje glasnosti", + "extending_volume_title": "Razširitev prostora za shranjevanje", + "extending_volume_description": "Če spremenite velikost shrambe, lahko v strežniku shranite več podatkov, ne da bi pri tem povečali sam strežnik. Obseg je mogoče samo povečati: ni ga mogoče zmanjšati.", + "extending_volume_error": "Ni uspelo zagnati razširitve shrambe.", + "size": "Velikost", + "data_migration_title": "Migracija podatkov", + "start_migration_button": "Začetek migracije", + "migration_process": "Selitev…", + "migration_done": "Zaključek" + }, + "service_page": { + "uses": "Uporablja {usage} na {volume}", + "status": { + "reloading": "Ponovni zagon spletne strani", + "active": "Vzpostavitev in delovanje", + "inactive": "Ustavljeno", + "failed": "Ni se uspelo zagnati", + "off": "Invalidi", + "activating": "Aktivacija spletne strani", + "deactivating": "Deaktiviranje spletne strani" + }, + "open_in_browser": "Odprite v brskalniku", + "restart": "Ponovni zagon storitve", + "disable": "Onemogočite storitev", + "enable": "Omogočite storitev", + "move": "Premik na drug zvezek" + }, + "social_network": { + "subtitle": "Težko je verjeti, vendar je mogoče ustvariti lastno družbeno omrežje z lastnimi pravili in občinstvom.", + "title": "Družbeno omrežje", + "login_info": "Na spletišču morate ustvariti račun." + }, + "not_ready_card": { + "in_menu": "Strežnik še ni bil nastavljen, uporabite čarovnika za povezavo." + }, + "cloud": { + "title": "Shranjevanje v oblaku", + "subtitle": "Storitvam v oblaku ne dovolite vpogleda v svoje podatke. Uporabite NextCloud - varen dom za vse svoje podatke.", + "login_info": "Prijava skrbnika: admin, geslo je enako geslu glavnega uporabnika. Ustvarite nove uporabnike v skrbniškem vmesniku NextCloud." + }, + "git": { + "title": "Git Strežnik", + "subtitle": "Zasebna alternativa Githubu, ki pripada vam, ne pa Microsoftu.", + "login_info": "Na spletnem mestu morate ustvariti račun. Prvi uporabnik bo postal administrator." + }, + "vpn": { + "title": "VPN Strežnik", + "subtitle": "Zasebni strežnik VPN" + } +} \ No newline at end of file diff --git a/assets/translations/th.json b/assets/translations/th.json new file mode 100644 index 00000000..de7c5082 --- /dev/null +++ b/assets/translations/th.json @@ -0,0 +1,295 @@ +{ + "test": "th-test", + "locale": "th", + "basis": { + "later": "ข้ามไปที่การติดตั้งทีหลัง", + "no_data": "ไม่พบข้อมูล", + "providers_title": "ศูนย์กลางข้อมูล", + "select": "เลือก", + "services": "บริการ", + "users": "ผู้ใช้บริการ", + "more": "มาก", + "next": "ถัดไป", + "got_it": "ได้มา", + "settings": "ตั้งค่า", + "password": "รหัสผ่าน", + "create": "เพิ่มใหม่", + "confirmation": "การยืนยัน", + "cancel": "ยกเลิก", + "delete": "ลบ", + "close": "ปิด", + "domain": "โดเมน", + "saving": "กำลังบันทึก…", + "username": "ชื่อผู้ใช้", + "loading": "กำลังโหลด…", + "connect_to_existing": "เชื่อมต่อไปยังเซริฟเวอร์ที่มีอยู่แล้ว", + "reset": "รีเซ็ต", + "details": "รายละเอียด", + "wait": "รอ", + "remove": "ลบ", + "apply": "นำมาใช้", + "done": "เสร็จสิ้น", + "continue": "ต่อไป", + "alert": "แจ้งเตือน", + "providers": "ผู้ให้บริการ", + "services_title": "ความเป็นส่วนตัวของคุณและอิสระ", + "connect": "เชื่อมต่อ" + }, + "more_page": { + "configuration_wizard": "การติดตั้งอย่างง่าย", + "create_ssh_key": "กุญแจ SSH ของผู้ใช้ระดับสูงสุด", + "about_project": "เกี่ยวกับพวกเรา", + "about_application": "เกี่ยวกับ", + "onboarding": "ออนบอร์ด", + "console": "คอนโซล", + "application_settings": "การตั้งค่าของแอพลิเคชั่น" + }, + "about_us_page": { + "title": "เกี่ยวกับพวกเรา" + }, + "about_application_page": { + "api_version_text": "API เซิฟเวอร์เวอร์ชั้น {}", + "title": "เกี่ยวกับ", + "application_version_text": "แอปพลิเคชั่น เวอร์ชั่น {}", + "privacy_policy": "นโยบายความเป็นส่วนตัว" + }, + "application_settings": { + "dark_theme_description": "สลับธีมแอปพลิเคชั่นของคุณ", + "delete_server_description": "การกระทำนี้จะลบเซิฟเวอร์ของคุณทิ้งและคุณจะไม่สามารถเข้าถึงมันได้อีก", + "title": "การตั้งค่าแอปพลิเคชัน", + "dark_theme_title": "ธีมมืด", + "reset_config_title": "รีเซ็ตค่าดั้งเดิมการตั้งค่าของแอปพลิเคชั่น", + "reset_config_description": "รีเซ็ต API key และผู้ใช้งาน root", + "delete_server_title": "ลบเซิฟเวอร์" + }, + "ssh": { + "create": "สร้างกุญแจ SSH", + "delete_confirm_question": "คุณแน่ใจนะว่าจะลบกุญแจ SSH นี้?", + "root_subtitle": "เจ้าของกุญแจเหล่านี้จะสามารถเข้าถึงเซิฟเวอร์และทำอะไรกับเซิฟเวอร์ก็ได้. โปรดเพิ่มกุญแจนี้ไปที่เซิฟเวอร์ของคุณเท่านั้น", + "title": "กุญแจ SSH", + "delete": "ลบกุญแจ SSH", + "subtitle_with_keys": "{} กุญแจ", + "subtitle_without_keys": "ไม่มีกุญแจ", + "no_key_name": "กุญแจไม่มีชื่อ", + "root_title": "นี่คือกุญแจของผู้ใช้สูงสุด", + "input_label": "กุญแจสาธารณะของ ED25519 หรือ RSA" + }, + "console_page": { + "title": "คอนโซล", + "waiting": "กำลังรอการเริ่มตั้น…" + }, + "domain": { + "services_subtitle": "ระเบียน A จำเป็นสำหรับแต่ละเซิร์ฟเวอร์", + "email_subtitle": "ระเบียนนั้นจำเป็นสำหรับการเปลี่ยนอีเมลอย่างปลอดภัย", + "card_title": "โดเมน", + "screen_title": "โดเมนและDNS", + "ok": "ระเบียนถูกต้อง", + "error": "พบเจอปัญหา", + "refreshing": "สถานะการรีเฟรช…", + "uninitialized": "ยังไม่ได้รับข้อมูล", + "services_title": "บริการ", + "email_title": "อีเมล", + "update_list": "รายการอัปเดต", + "error_subtitle": "กดตรงนี้เพื่อแก้ไขปัญหา" + }, + "backup": { + "reuploaded_key": "กุญแจถูกอัปโหลดอีกครั้งเรียบร้อย", + "waiting_for_rebuild": "คุณจะสามารถสร้างข้อมูลสำรองของคุณได้ในไม่กี่นาที", + "restoring": "การกู้คืนจากการสำรองข้อมูง", + "restore_alert": "คุณกำลังที่จะคืนค่าจากข้อมูลสำรองที่สร้างใน {}. ข้อมูลทั้งหมดตอนนี้จะหายไป คุณแน่ใจไหม?", + "refetching_list": "ในไม่กี่นาทีรายการจะอัปเดต", + "card_title": "สำรอง", + "description": "นี่จะช่วยคุณในวันที่เซิร์ฟเวอร์ของคุณถูกโจมตีด้วยวิธีต่างๆ", + "reupload_key": "บังคับการอัปโหลดอีกครั้งของกุญแจ", + "initialize": "เริ่มตั้น", + "restore": "คืนค่าจากการสำรอง", + "no_backups": "ยังไม่มีการสำรองข้อมูลในตอนนี้", + "create_new": "สร้างข้อมูลสำรองใหม่", + "creating": "กำลังสร้างข้อมูลสำรอง: {}%", + "error_pending": "เซิร์ฟเวอร์ส่งคืนข้อผิดพลาดตรวจสอบด้านล่าง", + "refresh": "สถานะการรีเฟรช", + "refetch_backups": "ดึงข้อมูลรายการสํารองข้อมูลอีกครั้ง" + }, + "onboarding": { + "page2_backup_provider_text": "เกิดอะไรขึ้นถ้ามีอะไรเกิดขึ้นกับเซิร์ฟเวอร์ของคุณ? ลองนึกภาพการโจมตีของแฮ็กเกอร์การลบข้อมูลโดยไม่ตั้งใจหรือการปฏิเสธการให้บริการ? แต่ไม่ต้องห่วง ข้อมูลของคุณจะถูกเก็บไว้อย่างปลอดภัยในผู้ให้บริการสํารองข้อมูลของคุณ พวกเขาจะถูกเข้ารหัสอย่างปลอดภัยและสามารถเข้าถึงได้ตลอดเวลาเพื่อกู้คืนเซิร์ฟเวอร์ของคุณด้วย", + "page1_title": "ความเป็นอิสระทางดิจิทัลมีให้สําหรับเราทุกคน", + "page1_text": "อีเมล,วีพีเอ็น(vpn),การส่งข้อความ,โซเชียลเน็ตเวิร์กและอื่นๆอีกมากมาย ซึ่งอยู่ภายใต้การควบคุมของคุณ", + "page2_title": "SelfPrivacy นั้นไม่ใช่คลาวด์, แต่เป็นศูนย์ข้อมูลส่วนบุคคลของคุณเท่านั้น", + "page2_server_provider_title": "ผู้ให้บริการเซิร์ฟเวอร์", + "page2_dns_provider_title": "ผู้ให้บริการ DNS", + "page2_backup_provider_title": "ผู้ให้บริการการกู้คืนระบบ", + "page2_text": "SelfPrivacy นั้นจะทำงานกับผู้ให้บริการที่คุณเลือกเท่านั้นและหากคุณไม่มีผู้ใช้งานของผู้ให้บริการนั้นๆ, เราจะช่วยคุณสร้างมันขึ้นมาเอง", + "page2_server_provider_text": "ผู้ให้บริการจะดูแลเซิร์ฟเวอร์ของคุณและ SelfPrivacy จะช่วยเชื่อมต่อและติดตั้งสิ่งจำเป็นกับผู้ให้บริการของคุณแบบอัตโนมัติ", + "page2_dns_provider_text": "คุณต้องมีโดเมนเพื่อให้มีสถานที่ของคุณในอินเทอร์เน็ต และคุณต้องมีผู้ให้บริการ DNS ที่เชื่อถือได้เพื่อให้โดเมนชี้ไปที่เซิร์ฟเวอร์ของคุณ เราขอแนะนําให้คุณเลือกผู้ให้บริการ DNS ที่รองรับเพื่อตั้งค่าเครือข่ายโดยอัตโนมัติ" + }, + "resource_chart": { + "month": "เดือน", + "day": "วัน", + "hour": "ชั่วโมง", + "cpu_title": "ค่าการใช้งานของ CPU", + "network_title": "ค่าการใช้งานของระบบเครือข่าย", + "in": "ใน", + "out": "ออก" + }, + "server": { + "card_title": "เซิร์ฟเวอร์", + "description": "บริการของคุณทั้งหมดจะแสดงอยู่ตรงนี้", + "general_information": "ข้อมูลทั่วไป", + "resource_usage": "การใช้ว่าทรัพยากร", + "reboot_after_upgrade": "รีบูตหลังจากอัพเกรด", + "allow_autoupgrade": "ให้อนุญาตการอัพเกรดแบบอัตโนมัติ", + "allow_autoupgrade_hint": "อนุญาตให้มีการอัพเกรดแพคเกจต่างๆบนเซิร์ฟเวอร์", + "reboot_after_upgrade_hint": "รีบูตโดยไม่ต้องขออนุญาตหลังจากเสร็จสิ้นการเปลี่ยนแปลงบนเซิร์ฟเวอร์", + "server_timezone": "เขตเวลาของเซิร์ฟเวอร์", + "select_timezone": "เลือกเขตเวลา", + "timezone_search_bar": "ชื่อเขตเวลาหรือค่ากะเวลา", + "server_id": "ไอดีของเซิร์ฟเวอร์", + "status": "สถานะ", + "cpu": "CPU", + "ram": "หน่วยความจำ", + "monthly_cost": "รายจ่ายต่อเดือน", + "location": "สถานที่", + "core_count": { + "one": "{} core", + "two": "{} จำนวนคอร์", + "many": "{} จำนวนคอร์", + "other": "{} จำนวนคอร์", + "few": "{} จำนวนคอร์" + }, + "disk": "ดิสก์ภายในเครื่อง" + }, + "record": { + "api": "API ของ SelfPrivacy", + "cloud": "ไฟล์คลาวด์", + "git": "Git server", + "meet": "การประชุมแบบใช้วิดีโอ", + "password": "จัดการกับรหัสผ่าน", + "vpn": "VPN", + "mx": "ระเบียน MX", + "dmarc": "ระเบียน DMARC", + "spf": "ระเบียน SPF", + "root": "โดเมนหลัก", + "social": "เครือข่ายสังคมออนไลน์", + "dkim": "กุญแจ DKIM" + }, + "storage": { + "card_title": "พื้นที่ของเซิฟร์เวอร์", + "status_ok": "การใช้งานดิสก์ปกติ", + "status_error": "พื้นที่ว่างดิสก์ต่ำ", + "disk_usage": "{} ใช้แล้ว", + "extending_volume_title": "กำลังขยายความจุ", + "disk_total": "{} ทั้งหมด {}", + "gb": "{} GB", + "mb": "{} MB", + "kb": "{} KB", + "bytes": "ไบต์", + "extend_volume_button": "ขยายความจุ", + "extending_volume_description": "การเปลี่ยนความจุนั้นจะทำให้คุณสามารถเก็บข้อมูลบนเซิร์ฟเวอร์ของคุณได้มากขึ้นโดยที่ไม่ต้องเพิ่มความเร็วเซิร์ฟเวอร์แต่ถว่าความจุนั้นสามารถเพิ่มได้อย่างเดียว ไม่สามารถลดขนาดได้", + "extending_volume_price_info": "ราคารวมภาษีมูลค่าเพิ่มและประมาณการจากข้อมูลราคาที่จัดทำโดย Hetzner เซิร์ฟเวอร์จะรีบูตหลังจากปรับความจุ", + "extending_volume_error": "ไม่สามารถเริ่มต้นการขยายความจุได้", + "size": "ขนาด", + "data_migration_title": "การโยกย้ายข้อมูล", + "data_migration_notice": "ระหว่างการย้ายข้อมูล ทุกเซิร์ฟเวอร์จะถูกปิด", + "start_migration_button": "เริ่มการย้ายข้อมูล", + "migration_process": "กำลังย้ายข้อมูล…", + "migration_done": "เสร็จสิ้น" + }, + "not_ready_card": { + "in_menu": "เซิร์ฟเวอร์ยังไม่ถูกติดตั้งในตอนนี้. โปรดใช้การติดตั้งอย่างง่ายในการติดตั้ง" + }, + "service_page": { + "open_in_browser": "เปิดในบราวเซอร์", + "restart": "รีสตาร์ทบริการ", + "disable": "ปิดบริการ", + "enable": "เปิดใช้บริการ", + "move": "ย้ายไปยังไดรฟ์ข้อมูลอื่น", + "uses": "ใช้ {usage} บน {volume}", + "status": { + "active": "เสร็จสิ้นและกำลังทำงาน", + "inactive": "หยุดแล้ว", + "failed": "การเริ่มต้นผิดพลาด", + "off": "ปิดการใช้แล้ว", + "activating": "กำลังเปิดใช้งาน", + "deactivating": "กำลังปิดใช้งาน", + "reloading": "กำลังรีสตาร์ท" + } + }, + "users": { + "login": "เข้าสู่ระบบ", + "add_new_user": "เพิ่มผู้ใช้แรก", + "not_ready": "โปรดเชื่อมต่อเซิร์ฟเวอร์ โดเมน และ DNS ในแท็บผู้ให้บริการ เพื่อให้สามารถเพิ่มผู้ใช้รายแรกได้", + "delete_confirm_question": "คุณแน่ใจใช่ไหม?", + "could_not_fetch_users": "ไม่สามารถดึงรายชื่อผู้ใช้", + "refresh_users": "รีเฟรชรายชื่อผู้ใช้", + "username_rule": "ชื่อผู้ใช้ต้องประกอบด้วยตัวอักษรละตินตัวพิมพ์เล็กตัวเลขและขีดล่างเท่านั้นไม่ควรขึ้นต้นด้วยตัวเลข", + "no_ssh_notice": "เฉพาะอีเมลและบัญชี SSH เท่านั้นที่ถูกสร้างขึ้นสําหรับผู้ใช้รายนี้ การลงชื่อเข้าระบบครั้งเดียวสําหรับบริการทั้งหมดกําลังจะมาในเร็วๆ นี้", + "new_user": "ผู้ใช้ใหม่", + "delete_user": "ลบผู้ใช้", + "nobody_here": "ไม่มีใครอยู่ในนี้", + "new_user_info_note": "ผู้ใช้ใหม่จะได้รับสิทธิ์เข้าถึงบริการทั้งหมดโดยอัตโนมัติ", + "reset_password": "รีเซ็ตรหัสผ่าน", + "account": "บัญชี", + "send_registration_data": "แบ่งปันข้อมูลรับรองการเข้าสู่ระบบ", + "could_not_fetch_description": "โปรดตรวจสอบการเชื่อมต่ออินเทอร์เน็ตของคุณแล้วลองอีกครั้ง", + "could_not_create_user": "สร้างผู้ใช้ไม่ได้", + "could_not_delete_user": "ลบผู้ใช้ไม่ได้", + "could_not_add_ssh_key": "เพิ่มกุญแจ SSH ไม่ได้", + "email_login": "เข้าสู่ระบบอีเมล" + }, + "social_network": { + "subtitle": "มันยากที่จะเชื่อ แต่มันก็เป็นไปได้ที่จะสร้างโซเชียลเน็ตเวิร์กของคุณเอง โดยมีกฎและกลุ่มเป้าหมายของคุณเอง", + "title": "เครือข่ายสังคม", + "login_info": "คุณจะต้องสร้างบัญชีบนเว็บไซต์" + }, + "git": { + "subtitle": "ทางเลือกส่วนตัวสำหรับ Github ที่เป็นของคุณ แต่ไม่ใช่ของ Microsoft", + "title": "Git Server", + "login_info": "คุณจะต้องสร้างบัญชีบนเว็บไซต์ ผู้ใช้คนแรกจะกลายเป็นผู้ดูแลระบบ" + }, + "initializing": { + "select_provider_notice": "โดย 'ค่อนข้างเล็ก' เราหมายถึงเครื่องที่มีซีพียู 2 คอร์และแรม 2 กิกะไบต์", + "select_provider_countries_text_do": "สหรัฐอเมริกา, เนเธอร์แลนด์, สิงคโปร์, สหราชอาณาจักร, เยอรมนี, แคนาดา, อินเดีย, ออสเตรเลีย", + "select_provider_price_text_do": "$ 17 ต่อเดือนสําหรับเซิร์ฟเวอร์ที่ค่อนข้างเล็กและพื้นที่เก็บข้อมูล 50GB", + "select_provider_email_notice": "การโฮสต์อีเมลจะไม่พร้อมใช้งานสําหรับลูกค้าใหม่ อย่างไรก็ตามมันจะถูกปลดล็อคทันทีที่คุณชําระเงินครั้งแรกเสร็จสิ้น", + "could_not_connect": "ไม่สามารถเชื่อมต่อกับผู้ให้บริการ", + "choose_location_type": "คุณต้องการสั่งซื้อเซิร์ฟเวอร์ของคุณที่ไหน?", + "connect_to_server": "เริ่มจากเซิร์ฟเวอร์กันก่อน", + "select_provider": "เลือกผู้ให้บริการรายใดก็ได้จากรายการต่อไปนี้พวกเขาทั้งหมดสนับสนุน SelfPrivacy", + "select_provider_countries_title": "ประเทศที่ใช้ได้", + "select_provider_countries_text_hetzner": "เยอรมนี, ฟินแลนด์, สหรัฐอเมริกา", + "select_provider_price_title": "ราคาเฉลี่ย", + "select_provider_price_text_hetzner": "€ 8 ต่อเดือนสําหรับเซิร์ฟเวอร์ที่ค่อนข้างเล็กและพื้นที่เก็บข้อมูล 50GB", + "select_provider_payment_title": "ช่องทางการจ่ายเงิน", + "select_provider_payment_text_hetzner": "บัตรเครดิต, SWIFT, SEPA, PayPal", + "select_provider_payment_text_do": "บัตรเครดิต, Google Pay PayPal", + "select_provider_site_button": "เยี่ยมชมเว็บไซต์", + "connect_to_server_provider": "เข้าสู่ระบบใน ", + "connect_to_server_provider_text": "ด้วยโทเค็น API SelfPrivacy จะสามารถเช่าเครื่องและตั้งค่าเซิร์ฟเวอร์ของคุณได้", + "how": "วิธีรับโทเค็น API", + "provider_bad_key_error": "คีย์ API ของผู้ให้บริการไม่ถูกต้อง" + }, + "mail": { + "title": "อีเมล", + "subtitle": "อีเมลสำหรับบริษัทและครอบครัว", + "login_info": "ใช้ชื่อผู้ใช้และรหัสผ่านจากแท็บผู้ใช้ พอร์ต IMAP คือ 143 พร้อม STARTTLS, พอร์ต SMTP คือ 587 พร้อม STARTTLS." + }, + "password_manager": { + "title": "การจัดการรหัสผ่าน", + "subtitle": "ฐานความปลอดภัยของคุณ Bitwarden จะช่วยคุณสร้าง จัดเก็บ และย้ายรหัสผ่านระหว่างอุปกรณ์ ตลอดจนป้อนรหัสผ่าน เมื่อมีการร้องขอโดยใช้การเติมข้อความอัตโนมัติ", + "login_info": "คุณจะต้องสร้างบัญชีบนเว็บไซต์" + }, + "video": { + "title": "การประชุมแบบวิดีโอ", + "subtitle": "Zoom และ Google Meet นั้นดี แต่ Jitsi Meet เป็นทางเลือกที่คุ้มค่าซึ่งยังช่วยให้คุณมั่นใจได้ว่าไม่มีใครฟังคุณ", + "login_info": "ไม่จะเป็นต้องใช้บัญชี" + }, + "cloud": { + "title": "การจัดเก็บบนคลาวด์", + "subtitle": "ไม่อนุญาตให้บริการคลาวด์อ่านข้อมูลของคุณโดยใช้ NextCloud", + "login_info": "การเข้าสู่ระบบคือผู้ดูแลระบบ รหัสผ่านเหมือนกับผู้ใช้หลักของคุณ สร้างบัญชีใหม่ในอินเทอร์เฟซ Nextcloud" + }, + "vpn": { + "title": "เซิฟเวอร์ VPN", + "subtitle": "เซิฟเวอร์ VPN ส่วนตัว" + } +} diff --git a/assets/translations/uk.json b/assets/translations/uk.json new file mode 100644 index 00000000..4ddfe3ff --- /dev/null +++ b/assets/translations/uk.json @@ -0,0 +1,498 @@ +{ + "test": "ua-test", + "basis": { + "services_title": "Ваші особисті, приватні та незалежні послуги.", + "providers_title": "Ваш дата центр", + "select": "Обрати", + "services": "Сервіси", + "users": "Користувачі", + "providers": "Провайдери", + "domain": "Домен", + "saving": "Збереження…", + "username": "І'мя користовача", + "loading": "Завантаження…", + "later": "Перейти до налаштувати пізніше", + "connect_to_existing": "Підключитись до існуючого серверу", + "reset": "Скинути", + "details": "Подробиці", + "no_data": "Немає данних", + "wait": "Зачекайте", + "remove": "Видалити", + "apply": "Застосувати", + "done": "Готово", + "continue": "Продовжити", + "alert": "Оповіщення", + "more": "Більше", + "next": "Наступний", + "got_it": "Зрозумів", + "settings": "Налаштування", + "password": "Пароль", + "create": "Додати новий", + "confirmation": "Підтвердження", + "cancel": "Відмінити", + "delete": "Видалити", + "close": "Закрити", + "connect": "Підключіться", + "app_name": "SelfPrivacy", + "copied_to_clipboard": "Скопійовано в буфер обміну!" + }, + "locale": "ua", + "application_settings": { + "title": "Налаштування додатка", + "reset_config_title": "Скинути налаштування", + "dark_theme_title": "Темна тема", + "dark_theme_description": "Змінити тему додатка", + "reset_config_description": "Скинути API ключі та root користувача.", + "delete_server_title": "Видалити сервер", + "delete_server_description": "Це видалить ваш сервер. Він більше не буде доступний.", + "system_dark_theme_title": "Системна тема за замовчуванням", + "system_dark_theme_description": "Використовуйте світлу або темну теми залежно від системних налаштувань", + "dangerous_settings": "Небезпечні налаштування" + }, + "ssh": { + "delete_confirm_question": "Ви впевнені, що хочете видалити SSH-ключ?", + "root_subtitle": "Власники цих ключів отримують повний доступ до сервера та даних. Додавайте лише свої ключі.", + "title": "SSH-ключі", + "create": "Створити SSH-ключ", + "delete": "Видалити SSH-ключ", + "subtitle_with_keys": "{} ключів", + "subtitle_without_keys": "Ключів немає", + "no_key_name": "Безіменний ключ", + "root_title": "Це ключі суперкористувача", + "input_label": "Публічний ED25519 або RSA ключ" + }, + "more_page": { + "about_project": "Про нас", + "create_ssh_key": "SSH-ключі суперкористовача", + "console": "Консоль", + "application_settings": "Налаштування додатка", + "configuration_wizard": "Майстер установки", + "about_application": "Про нас", + "onboarding": "Адаптація" + }, + "console_page": { + "title": "Консоль", + "waiting": "Очікування ініціалізації…", + "copy": "Copie" + }, + "about_us_page": { + "title": "Про нас" + }, + "about_application_page": { + "application_version_text": "Версія додатку {}", + "api_version_text": "Версія API сервера {}", + "privacy_policy": "Політика конфіденційності", + "title": "Про нас" + }, + "onboarding": { + "page1_title": "Цифрова незалежність, доступна кожному", + "page2_server_provider_title": "Сервер-провайдер", + "page2_title": "Політика конфіденційності-це не хмара, це твій персональний центр обробки даних", + "page1_text": "Пошта, ВПН, Месенджер, соціальна мережа і багато іншого на твоєму приватному сервері, під твоїм контролем.", + "page2_dns_provider_title": "DNS-провайдер", + "page2_dns_provider_text": "Тобі потрібен домен, щоб мати місце в інтернеті. Вам також потрібен надійний DNS-провайдер, щоб вказати домен вашого серверу. Ми запромпонуємо вам вибрати підтримуваного DNS-провайдера для автоматичного налаштування мережі.", + "page2_backup_provider_title": "Backup-провайдер", + "page2_server_provider_text": "Сервер-провайдер обслуговує ваш сервер у власному Data-центрі. Політика конфіденційності буде автоматично зв'язана з провайдером і налаштує все необхідне.", + "page2_text": "Політика конфіденційності працює лише з тим провайдром, якого ви вибрали. Якщо ви не маєте потрібних облікових записів, ми допоможемо їх вам створити.", + "page2_backup_provider_text": "Що якщо з вашим сервером щось трапиться? Уявіть собі атаку хакерів, випадкове видалення даних або відмову в обслуговуванні? Ваші дані будуть у безпеці у резервних копіях вашого провайдера. Вони будуть надійно зашифровані та у будь-який час доступні для відновлення вашого сервера." + }, + "initializing": { + "select_provider_notice": "За 'Відносно невеликим' ми маємо на увазі машину з 2 ядрами CPU і 2 гігабайтами оперативної пам'яті.", + "select_provider_price_text_do": "$ 17 на місяць для порівняно невеликого сервера і 50Гб дискового сховища", + "select_provider_email_notice": "Хостинг електронної пошти не буде доступний для нових клієнтів. Проте, вона буде розблокована, як тільки ви зробите вашу першу оплату.", + "choose_server_type_notice": "Важливими речами, на які слід дивитися, є процесор і оперативна пам'ять. Дані ваших послуг буде збережено.", + "use_this_domain_text": "Токен, який ви надали, надає доступ до наступного домену", + "loading_domain_list": "Завантаження списку доменів", + "found_more_domains": "Знайдено більше одного домену. Для власної безпеки просимо видалити непотрібні домени", + "server_created": "Сервер створений. DNS перевірений і завантаження серверу у прогресі…", + "enter_username_and_password": "Введіть ім’ я користувача і надійний пароль", + "connect_to_server": "Почнемо з сервера.", + "select_provider": "Оберіть будь-якого провайдера з наведеного нижче списку, всі вони підтримують політику конфіденційності", + "select_provider_countries_title": "Доступні країни", + "select_provider_countries_text_hetzner": "Німеччина, Фінляндія, США", + "select_provider_countries_text_do": "США, Нідерланди, Сінгапур, Велика Британія, НІмеччина, Канада, Індія, Австралія", + "select_provider_price_title": "Середня ціна", + "select_provider_price_text_hetzner": "€ 8 на місяць за відносно невеликий сервер і 50 Гб дискового сховища", + "select_provider_payment_title": "Способи оплати", + "select_provider_payment_text_hetzner": "Картка, SWIFT, SEPA, PayPal", + "select_provider_payment_text_do": "Картка, Google Pay, PayPal", + "select_provider_site_button": "Відвідати сайт", + "connect_to_server_provider": "Авторизуйтеся зараз ", + "locations_not_found_text": "Немає доступних серверів для оренди", + "connect_to_server_provider_text": "З токеном API SelfPrivacy зможе орендувати техніку і налаштувати на неї ваш сервер", + "how": "Як отримати токен API", + "provider_bad_key_error": "Некоректний ключ API провайдера", + "could_not_connect": "Не зміг підключитися до провайдера.", + "choose_location_type": "Де ви хочете забронювати свій сервер?", + "choose_location_type_text": "Різні локації забезпечують різні конфігурації серверів, ціни і швидкість з'єднання.", + "locations_not_found": "Упс!", + "back_to_locations": "Оберіть щось інше", + "no_locations_found": "Не знайдено доступних місць, перевірте, чи ваш обліковий запис доступний", + "choose_server_type": "Який тип сервера вам потрібен?", + "choose_server_type_text": "Різні ресурсні можливості підтримують різні сервіси. Не хвилюйся, ти можеш розширити свій сервер коли завгодно", + "choose_server_type_ram": "{} ГБ ОЗУ", + "choose_server_type_storage": "{} ГБ системного сховища", + "choose_server_type_payment_per_month": "{} щомісячно", + "no_server_types_found": "Не знайдено доступних типів серверів. Переконайтеся, що ваш обліковий запис доступний і спробуйте змінити розташування вашого сервера.", + "backblaze_bad_key_error": "Інформація про зберігання Backblaze є недійсною", + "select_dns": "Тепер давайте оберемо DNS-провайдера", + "manage_domain_dns": "Для управління DNS домену", + "use_this_domain": "Скористатися цим доменом?", + "connect_backblaze_storage": "Підключити Backblaze сховище", + "no_connected_domains": "Наразі немає пов'язаних доменів", + "save_domain": "Зберегти домен", + "final": "Останній крок", + "create_server": "Створити сервер", + "what": "Що це значить?", + "server_rebooted": "Сервер перезавантажено. Чекаю останньої перевірки…", + "server_started": "Сервер запущено. Його буде перевірено і перезавантажено…", + "until_the_next_check": "До наступної перевірки: ", + "check": "Перевірка", + "one_more_restart": "Ще один перезапуск, щоб застосувати сертифікати безпеки.", + "create_master_account": "Створити внутрішній рахунок", + "finish": "Усе ініціалізовано", + "checks": "Перевірка була завершена\n{} з {}" + }, + "recovering": { + "recovery_main_header": "З’ єднатися з існуючим сервером", + "generic_error": "Операція зазнала невдачі, будь ласка, спробуйте ще раз.", + "domain_recovery_description": "Введіть домен сервера, до якого ви бажаєте отримати доступ:", + "domain_recover_placeholder": "Ваш домен", + "domain_recover_error": "Сервера з таким доменом не знайдено", + "method_select_description": "Виберіть спосіб відновлення:", + "method_select_other_device": "У мене є доступ до іншого пристрою", + "method_select_recovery_key": "Я маю ключ відновлення", + "method_select_nothing": "Я не маю нічого з цього", + "method_device_description": "Відкрийте програму на іншому пристрої та перейдіть на сторінку пристроїв. Натисніть \"Додати пристрій\", щоб отримати свій токен.", + "method_device_button": "Я отримав свій токен", + "method_device_input_description": "Введіть токен авторизації", + "method_device_input_placeholder": "Токен", + "method_recovery_input_description": "Введіть ключ відновлення", + "fallback_select_description": "Що у вас є? Виберіть перший доступний варіант:", + "fallback_select_token_copy": "Копія токена автентифікації з іншої версії застосунку.", + "fallback_select_root_ssh": "Root SSH доступ до сервера.", + "fallback_select_provider_console": "Доступ до серверної консолі мого продiвера.", + "authorization_failed": "Не можу авторизуватись за цим ключем", + "fallback_select_provider_console_hint": "Наприклад: Hetzner.", + "provider_connected": "Підключіться до {}", + "provider_connected_description": "Зв'язок встановлений. Введіть свій токен з доступом до {}:", + "provider_connected_placeholder": "{} Токен", + "confirm_server": "Підтвердити сервер", + "confirm_server_description": "Знайдено ваш сервер! Підтвердіть, що він правильний:", + "confirm_server_accept": "Так! Це воно", + "confirm_server_decline": "Вибрати інший сервер", + "choose_server": "Виберіть ваш сервер", + "no_servers": "На вашому обліковому записі немає доступних серверів.", + "choose_server_description": "Ми не змогли з'ясувати, до якого сервера ви намагаєтесь підключитися.", + "domain_not_available_on_token": "Вибраний домен недоступний на цьому токені.", + "modal_confirmation_title": "Це насправді ваш сервер?", + "modal_confirmation_description": "Якщо ви підключаєтеся до невірного сервера, ви можете втратити всі ваші дані.", + "modal_confirmation_dns_valid": "Зворотна DNS дійсна", + "modal_confirmation_dns_invalid": "Зворотна DNS вказує на інший домен", + "modal_confirmation_ip_valid": "IP той же, що і в записі DNS", + "modal_confirmation_ip_invalid": "IP не такий, як в DNS-записі" + }, + "resource_chart": { + "month": "Місяць", + "day": "День", + "hour": "Година", + "cpu_title": "Використання CPU", + "network_title": "Використання мережі", + "in": "В", + "out": "Вийти" + }, + "server": { + "card_title": "Сервер", + "description": "Усі твої сервіси тут", + "general_information": "Загальна інформація", + "resource_usage": "Використання ресурсів", + "allow_autoupgrade": "Дозволити авто-оновлення", + "allow_autoupgrade_hint": "Дозволити автоматичне покращення пакетів на сервері", + "select_timezone": "Вибрати часовий пояс", + "status": "Статус", + "server_id": "Сервер ID", + "cpu": "Процессор", + "ram": "Пам'ять", + "core_count": { + "one": "{} ядро", + "few": "{} ядра", + "many": "{} ядер", + "other": "{} ядра", + "two": "{} ядра" + }, + "disk": "Локальний диск", + "reboot_after_upgrade": "Перезавантаження після оновлення", + "reboot_after_upgrade_hint": "Перезавантаження без запиту після застосування змін на сервері", + "server_timezone": "Часовий пояс сервера", + "timezone_search_bar": "Ім'я часового поясу або значення зсуву часу", + "monthly_cost": "Щомісячна вартість", + "location": "Місцезнаходження", + "server_provider": "Провайдер сервера", + "dns_provider": "Провайдер DNS" + }, + "record": { + "api": "SelfPrivacy API", + "cloud": "Файлове сховище", + "git": "Git-сервер", + "social": "Соціальна мережа", + "password": "Менеджер паролів", + "vpn": "VPN", + "mx": "MX-запис", + "dmarc": "DMARC-запис", + "spf": "SPF-запис", + "meet": "Відео конференція", + "dkim": "DKIM ключі", + "root": "Кореневий домен" + }, + "domain": { + "card_title": "Домен", + "screen_title": "Домен та DNS", + "ok": "Записи в порядку", + "error": "Знайдені проблеми", + "refreshing": "Статус оновлення…", + "uninitialized": "Дані ще не відновлено", + "services_title": "Сервіси", + "email_title": "Електронна пошта", + "email_subtitle": "Записи, необхідні для безпечного обміну електронною поштою.", + "update_list": "Лист оновлень", + "error_subtitle": "Натисніть тут, щоб виправити їх. При цьому також буде видалено користувацькі записи.", + "services_subtitle": "Введіть \"А\" записи, необхідні для кожної служби." + }, + "backup": { + "card_title": "Резервне копіювання", + "reupload_key": "Примусове повторне завантаження ключа", + "reuploaded_key": "Ключ повторно завантажений", + "initialize": "Ініціалізація", + "restore": "Відновити з резервної копії", + "no_backups": "Ще немає резервних копій", + "create_new": "Створити нову резервну копію", + "creating": "Створення нової резервної копії: {}%", + "error_pending": "Помилка відновлення сервера, перевірте це нижче", + "refresh": "Оновити статус", + "refetch_backups": "Повторно отримати список резервних копій", + "refetching_list": "За кілька хвилин список буде оновлений", + "description": "Врятує ваш день у разі аварії: хакерська атака, видаленя серверу, тощо.", + "waiting_for_rebuild": "Ви зможете створити свою першу резервну копію через кілька хвилин.", + "restoring": "Відновлення з резервної копії", + "restore_alert": "Ви збираєтеся відновити з резервної копії. створеної на {}. Усі поточні дані будуть втрачені. Ви згодні?", + "refetch_backups_subtitle": "Скинути кеш і запросити дані у провайдера. Може спричинити додаткові витрати.", + "reupload_key_subtitle": "Ще раз проініціалізує сховище резервних копій. Використовуйте, якщо щось зламалося.", + "create_new_select_heading": "Вибрати сервіси для копіювання", + "start": "Почати створення копій", + "service_busy": "Зараз створюються інші резервні копії", + "latest_snapshots": "Останні знімки", + "latest_snapshots_subtitle": "Останні 15 знімків", + "show_more": "Показати ще", + "autobackup_period_title": "Період автоматичного копіювання", + "autobackup_period_subtitle": "Створення копій раз на {period}", + "autobackup_period_never": "Автоматичне копіювання вимкнено", + "autobackup_period_every": "Раз у {period}", + "autobackup_period_disable": "Вимкнути автоматичні копіювання", + "autobackup_custom": "Інше", + "autobackup_custom_hint": "Введіть період у хвилинах", + "autobackup_set_period": "Встановити період", + "autobackup_period_set": "Період встановлено", + "backups_encryption_key": "Ключ шифрування", + "backups_encryption_key_subtitle": "Зберігайте його в безпечному місці.", + "backups_encryption_key_copy": "Скопіюйте ключ шифрування", + "card_subtitle": "Керуйте резервними копіями", + "select_all": "Копіювати все" + }, + "storage": { + "card_title": "Серверне сховище", + "status_error": "Мало місця на диску", + "disk_usage": "{} використано", + "disk_total": "{} всього · {}", + "gb": "{} ГБ", + "mb": "{} МБ", + "kb": "{} КБ", + "bytes": "Байти", + "extend_volume_button": "Збільшити обсяг", + "extending_volume_price_info": "Ціна включає ПДВ і оцінюється з даних ціноутворення, наданих Hetzner. Сервер буде перезавантажений після зміни розміру.", + "extending_volume_error": "Не вдалося ініціалізувати розширення обсягу.", + "size": "Розмір", + "data_migration_title": "Перенесення даних", + "start_migration_button": "Почати перенос", + "migration_process": "Перенос…", + "migration_done": "Закінчити", + "status_ok": "Використання диска нормальне", + "extending_volume_title": "Збільшення обсягу", + "extending_volume_description": "Зміна обсягу дозволить зберігати більше даних на вашому сервері без розширення сервера. Обсяг може бути тільки збільшеним: зменшення неможливе.", + "data_migration_notice": "Під час переносу всі послуги будуть вимкнені." + }, + "not_ready_card": { + "in_menu": "Сервер ще не налаштовано. Будь ласка, завершіть налаштування за допомогою майстра налаштування для подальшої роботи." + }, + "service_page": { + "open_in_browser": "Відкрити у браузері", + "restart": "Перезавантажити сервіс", + "disable": "Викнути сервіс", + "enable": "Увімкнути сервіс", + "move": "Перейти до іншого об'єму", + "status": { + "active": "Створювати і запускати", + "inactive": "Зупинено", + "failed": "Не вдалося запустити", + "off": "Вимкнено", + "activating": "Активація", + "deactivating": "Деактивація", + "reloading": "Перезапуск" + }, + "uses": "Використовує {usage} на {volume}" + }, + "mail": { + "subtitle": "E-Mail для компанії та сім'ї.", + "title": "E-Mail", + "login_info": "Використовувати ім’ я користувача і пароль з вкладки « користувач ». Порт IMAP — 143 з STARTTLS, порт SMTP — 587." + }, + "password_manager": { + "title": "Менеджер паролів", + "login_info": "Вам доведеться створити обліковий запис на веб-сайті.", + "subtitle": "Основа вашої безпеки. Bitwarden допоможе вам створювати, зберігати і переміщати паролі між пристроями, а також вводити їх за запитом за допомогою автозаповнення." + }, + "video": { + "title": "Відеозустріч", + "subtitle": "Zoom і Google Meet хороші, але Jitsi meet є вартою альтернативи, яка також дає вам впевненість, що вас не слухають.", + "login_info": "Обліковий запис не потрібен." + }, + "cloud": { + "title": "Хмарне сховище", + "subtitle": "Не дозволяйте хмарним службам читати ваші дані, за допомогою NextCloud.", + "login_info": "Вхід адміністратора, пароль такий самий, як і у вашого головного користувача. Створити нові аккаунти у Nextcloud." + }, + "social_network": { + "title": "Соціальна мережа", + "login_info": "Вам доведеться створити обліковий запис на веб-сайті.", + "subtitle": "Важко повірити, але стало можливим створити власну соціальну мережу, з твоїми власними правилами та цільовою аудиторією." + }, + "git": { + "title": "Git-сервер", + "subtitle": "Приватна альтернатива Github, яка належить вам, а не Microsoft.", + "login_info": "Вам доведеться створити обліковий запис на веб-сайті. Першим користувачем стане адміністратор." + }, + "vpn": { + "title": "VPN-сервер", + "subtitle": "Приватний VPN-сервер" + }, + "users": { + "add_new_user": "Додати першого користувача", + "new_user": "Новий користувач", + "delete_user": "Видалити користувача", + "nobody_here": "Нікого немає", + "login": "Логін", + "new_user_info_note": "Новий користувач автоматично отримає доступ до всіх послуг", + "delete_confirm_question": "Ви згодні?", + "reset_password": "Скинути пароль", + "account": "Аккаунт", + "send_registration_data": "Поділитися реєстраційними даними", + "could_not_fetch_users": "Не вдалося отримати список користувачів", + "refresh_users": "Оновити список користувачів", + "could_not_create_user": "Не зміг створити користувача", + "could_not_delete_user": "Не зміг видалити користувача", + "could_not_add_ssh_key": "Не зміг додати SSH-ключ", + "email_login": "Увійти через Email", + "not_ready": "Будь ласка, підключіть сервер, домен і DNS на вкладці \"Сервери\", щоб мати можливість додати першого користувача", + "could_not_fetch_description": "Перевірте підключення до Інтернету і спробуйте ще раз", + "username_rule": "Ім'я користувача має містити лише малі латинські літери, цифри і підкреслення, не слід починати з цифри", + "no_ssh_notice": "Для цього користувача створюються тільки поштові та SSH-акаунти. Єдина реєстрація для всіх сервісів незабаром." + }, + "devices": { + "main_screen": { + "header": "Пристрої", + "description": "Ці пристрої мають повний доступ до сервера через додаток SelfPrivacy.", + "this_device": "Цей пристрій", + "other_devices": "Інші пристрої", + "authorize_new_device": "Авторизація нового пристрою", + "access_granted_on": "Доступ надано на {}", + "tip": "Натисніть на пристрої, щоб скасувати доступ." + }, + "add_new_device_screen": { + "header": "Авторизація нового пристрою", + "description": "Введіть ключ пристрою, який ви бажаєте авторизувати:", + "please_wait": "Будь ласка зачекайте", + "tip": "Ключ дійсний протягом 10 хвилин.", + "expired": "Термін дії ключа минув.", + "get_new_key": "Отримати новий ключ" + }, + "revoke_device_alert": { + "header": "Відкликати доступ?", + "description": "Пристрій {} більше не матиме доступу до сервера.", + "yes": "Відкликати", + "no": "Скасувати" + } + }, + "recovery_key": { + "key_connection_error": "Не вдалося підключитися до сервера.", + "key_synchronizing": "Синхронізація…", + "key_main_header": "Ключ відновлення", + "key_main_description": "Необхідно авторизувати SelfPrivacy, коли всі інші авторизовані пристрої недоступні.", + "key_amount_toggle": "Обмеження за кількістю використань", + "key_amount_field_title": "Максимальна кількість використань", + "key_duedate_toggle": "Обмеження за часом", + "key_duedate_field_title": "Дата закінчення терміну дії", + "key_receive_button": "Ключ прийому", + "key_valid": "Ваш ключ дійсний", + "key_invalid": "Ваш ключ більше не дійсний", + "key_valid_until": "Діє до {}", + "key_valid_for": "Дійсний для використання {}", + "key_creation_date": "Створено на {}", + "key_replace_button": "Сгенерувати новий ключ", + "key_receiving_description": "Запишіть цей ключ і сховайте у безпечне місце. Використовується для відновлення повного доступу до вашого сервера:", + "key_receiving_info": "Ключ ніколи більше не відображатиметься, але ви зможете замінити його іншим.", + "key_receiving_done": "Готово!", + "generation_error": "Не вдалося створити ключ відновлення. {}" + }, + "modals": { + "dns_removal_error": "Не вдалося видалити DNS-записи.", + "server_deletion_error": "Не вдалося видалити активний сервер.", + "server_validators_error": "Не вдалося отримати доступні сервери.", + "already_exists": "Такий сервер вже існує.", + "unexpected_error": "Неочікувана помилка під час розміщення з боку провайдера.", + "destroy_server": "Знищити сервер і створити новий?", + "try_again": "Спробувати ще раз?", + "are_you_sure": "Ви впевнені?", + "purge_all_keys": "Очистити всі ключі автентифікації?", + "purge_all_keys_confirm": "Так, очистити всі мої токени", + "delete_server_volume": "Видалити сервер і сховище?", + "reboot": "Перезавантажити", + "you_cant_use_this_api": "Не можна використовувати цей API для доменів з таким TLD.", + "yes": "Так", + "no": "Ні" + }, + "timer": { + "sec": "{} сек" + }, + "jobs": { + "title": "Задачі", + "start": "Початок", + "empty": "Задач немає", + "create_user": "Створити користувача", + "delete_user": "Видалити користувача", + "service_turn_off": "Вимкнути", + "service_turn_on": "Увімкнути", + "job_added": "Завдання додано", + "run_jobs": "Запустіть завдання", + "reboot_success": "Сервер перезавантажується", + "reboot_failed": "Не зміг перезавантажити сервер. Перевірте журнали додатків.", + "config_pull_failed": "Не вдалося виконати оновлення налаштувань. Запустили оновлення програмного забезпечення в будь-якому випадку.", + "upgrade_success": "Почалося оновлення сервера", + "upgrade_failed": "Не вдалося оновити сервер", + "upgrade_server": "Покращити сервер", + "reboot_server": "Перезавантажити сервер", + "create_ssh_key": "Створити SSH-ключ для {}", + "delete_ssh_key": "Видалити SSH-ключ для {}", + "server_jobs": "Задачі на сервері", + "reset_user_password": "Скинути пароль користувача", + "generic_error": "Не вдалося підключитись до сервера!" + }, + "validations": { + "required": "Потрібно", + "already_exist": "Вже існує", + "invalid_format": "Неприпустимий формат", + "invalid_format_password": "Не повинен містити порожніх символів", + "length_longer": "Довжина [] повинна бути коротшою або дорівнює {}", + "invalid_format_ssh": "Повинен відповідати формату ключа SSH", + "root_name": "Не може бути 'root'", + "length_not_equal": "Довжина [], має бути {}" + } +} diff --git a/assets/translations/uz.json b/assets/translations/uz.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/assets/translations/uz.json @@ -0,0 +1 @@ +{} diff --git a/build.yaml b/build.yaml index 709b623e..993e8dd4 100644 --- a/build.yaml +++ b/build.yaml @@ -1,7 +1,17 @@ targets: $default: builders: + graphql_codegen: + options: + scalars: + DateTime: + type: DateTime + fromJsonFunctionName: dateTimeFromJson + toJsonFunctionName: dateTimeToJson + import: package:selfprivacy/utils/scalars.dart + clients: + - graphql json_serializable: options: create_factory: true - create_to_json: false \ No newline at end of file + create_to_json: true diff --git a/ci.py b/ci.py new file mode 100755 index 00000000..b2403bfe --- /dev/null +++ b/ci.py @@ -0,0 +1,249 @@ +#!/usr/bin/env python3 + +import os +import subprocess +import requests +import yaml +import argparse + +CONTAINER_IMAGE = "registry.selfprivacy.org:5001/flutter-build-env" +HOST_HOME = "/var/lib/drone-runner-exec" +CONTAINER_HOME = "/tmp/builder" +HOST_MOUNTED_VOLUME = f"{HOST_HOME}/.local/share/containers/storage/volumes/release/_data" + +APP_NAME = "org.selfprivacy.app" +APP_NAME_NIGHTLY = "org.selfprivacy.app.nightly" +APP_VERSION_FULL = yaml.safe_load(open("pubspec.yaml", "r"))['version'] +APP_SEMVER = APP_VERSION_FULL[:APP_VERSION_FULL.find("+")] +APP_BUILD_ID = APP_VERSION_FULL[APP_VERSION_FULL.find("+"):][1::] +APP_BUILD_ID_NIGHTLY = subprocess.run(["git", "rev-list", "--first-parent", "--count", "HEAD"], check=True, stdout=subprocess.PIPE).stdout.decode().strip() + +GITEA_HOST_URL = "https://git.selfprivacy.org" +GITEA_REPO_FULL = os.environ.get("DRONE_REPO") or "kherel/selfprivacy.org.app" +GITEA_REPO_OWNER = os.environ.get("DRONE_REPO_OWNER") or "kherel" +GITEA_REPO_NAME = os.environ.get("DRONE_REPO_NAME") or "selfprivacy.org.app" + +# Environments + +def podman_offline(dir, *args): + subprocess.run(["podman", "run", "--rm", "--network=none", "--cap-add=CHOWN", f"--workdir={dir}", + "-v", os.getcwd() + f":{CONTAINER_HOME}/src", + "-v", f"{HOST_HOME}/fdroid:{CONTAINER_HOME}/fdroid", + "-v", f"{HOST_HOME}/fdroid-keystore:{CONTAINER_HOME}/fdroid-keystore", + "-v", f"{HOST_HOME}/standalone-keystore:{CONTAINER_HOME}/standalone-keystore", + "-v", f"{HOST_HOME}/google-keystore:{CONTAINER_HOME}/google-keystore", + "--env", "FDROID_KEYSTORE_PASS=" + os.environ.get("FDROID_KEYSTORE_PASS"), + "--env", "STANDALONE_KEYSTORE_PASS=" + os.environ.get("STANDALONE_KEYSTORE_PASS"), + "--env", "GOOGLE_KEYSTORE_PASS=" + os.environ.get("GOOGLE_KEYSTORE_PASS"), + "--user", os.getuid().__str__() + ":" + os.getgid().__str__(), "--userns=keep-id", + "--ulimit", "nofile=102400:102400", CONTAINER_IMAGE, "bash", "-c", ' '.join(args) + ], check=True) + +def podman_online(dir, *args): + subprocess.run(["podman", "run", "--rm", "--cap-add=CHOWN", f"--workdir={dir}", + "-v", os.getcwd() + f":{CONTAINER_HOME}/src", + "--user", os.getuid().__str__() + ":" + os.getgid().__str__(), "--userns=keep-id", + "--ulimit", "nofile=102400:102400",CONTAINER_IMAGE, "bash", "-c", ' '.join(args) + ], check=True) + +# Utilities + +def gitea_create_release(): + url = f"{GITEA_HOST_URL}/api/v1/repos/{GITEA_REPO_FULL}/releases" + token = os.environ.get("GITEA_RELEASE_TOKEN") + params = {"access_token": f"{token}"} + json = {"tag_name": f"{os.environ.get('DRONE_SEMVER') or APP_SEMVER}", "name": f"{os.environ.get('DRONE_SEMVER' or APP_SEMVER)}", "prerelease": True} + + request = requests.post(url, params=params, json=json) + + try: + request.raise_for_status() + except requests.exceptions.HTTPError as error: + print(error) + return error + +def gitea_get_release_id(): + url = f"{GITEA_HOST_URL}/api/v1/repos/{GITEA_REPO_FULL}/releases/tags/{os.environ.get('DRONE_SEMVER') or APP_SEMVER}" + + request = requests.get(url) + + try: + request.raise_for_status() + except requests.exceptions.HTTPError as error: + print(error) + return error + + return request.json()['id'] + +def gitea_upload_attachment(file): + id = gitea_get_release_id() + url = f"{GITEA_HOST_URL}/api/v1/repos/{GITEA_REPO_FULL}/releases/{id}/assets" + token = os.environ.get("GITEA_RELEASE_TOKEN") + params = {"access_token": f"{token}", "name": f"{os.path.basename(file)}"} + files = {"attachment": open(f"{file}", "br")} + + request = requests.post(url, params=params, files=files) + + try: + request.raise_for_status() + except requests.exceptions.HTTPError as error: + print(error) + return error + +# Targets + +def build_linux(): + podman_online(f"{CONTAINER_HOME}/src", "chown -R $(id -u):$(id -g) /tmp/flutter_pub_cache", + "&& flutter pub get --offline", + "&& flutter build linux") + +def build_apk(): + podman_online(f"{CONTAINER_HOME}/src", "chown -R $(id -u):$(id -g) /tmp/gradle /tmp/flutter_pub_cache", + "&& flutter pub get", + "&& flutter build apk --flavor production") +def build_bundle(): + podman_online(f"{CONTAINER_HOME}/src", "chown -R $(id -u):$(id -g) /tmp/gradle /tmp/flutter_pub_cache", + "&& flutter pub get", + "&& flutter build appbundle --flavor production") + +def sign_apk_standalone(): + podman_offline(f"{CONTAINER_HOME}/src", + "zipalign -f -v 4 build/app/outputs/flutter-apk/app-production-release.apk", + f"standalone_{APP_NAME}-{APP_SEMVER}.apk") + podman_offline(f"{CONTAINER_HOME}/src", + f"apksigner sign --ks {CONTAINER_HOME}/standalone-keystore --ks-key-alias standalone --ks-pass", + f"env:STANDALONE_KEYSTORE_PASS standalone_{APP_NAME}-{APP_SEMVER}.apk") + +def sign_apk_fdroid(): + podman_offline(f"{CONTAINER_HOME}/fdroid", f"rm -rf {CONTAINER_HOME}/fdroid/unsigned/*") + podman_offline(f"{CONTAINER_HOME}/fdroid", + f"test ! -f {CONTAINER_HOME}/fdroid/repo/{APP_NAME}_{APP_BUILD_ID}.apk", + "&& cp ../src/build/app/outputs/flutter-apk/app-production-release.apk", + f"unsigned/{APP_NAME}_{APP_BUILD_ID}.apk || echo exist") + podman_offline(f"{CONTAINER_HOME}/fdroid", "fdroid publish") + podman_offline(f"{CONTAINER_HOME}/fdroid", "fdroid update") + +def sign_apk_fdroid_nightly(): + podman_offline(f"{CONTAINER_HOME}/fdroid", f"rm -rf {CONTAINER_HOME}/fdroid/unsigned/*") + podman_offline(f"{CONTAINER_HOME}/fdroid", + f"test ! -f {CONTAINER_HOME}/fdroid/repo/{APP_NAME_NIGHTLY}_{APP_BUILD_ID_NIGHTLY}.apk", + "&& cp ../src/build/app/outputs/flutter-apk/app-nightly-release.apk", + f"unsigned/{APP_NAME_NIGHTLY}_{APP_BUILD_ID_NIGHTLY}.apk || echo exist") + podman_offline(f"{CONTAINER_HOME}/fdroid", "fdroid publish") + podman_offline(f"{CONTAINER_HOME}/fdroid", "fdroid update") + +def sign_bundle(): + podman_offline(f"{CONTAINER_HOME}/src", + f"jarsigner -sigalg SHA256withRSA -digestalg SHA-256 -keystore {CONTAINER_HOME}/google-keystore -signedjar {APP_NAME}-{APP_SEMVER}.aab -storepass:env GOOGLE_KEYSTORE_PASS build/app/outputs/bundle/productionRelease/app-production-release.aab google") + +def package_linux_appimage(): + podman_online(f"{CONTAINER_HOME}/src", "appimage-builder --recipe appimage.yml") + +def package_linux_flatpak(): + subprocess.run(["flatpak-builder", "--force-clean", "--repo=flatpak-repo", "flatpak-build", "flatpak.yml"], check=True) + subprocess.run(["flatpak", "build-bundle", "flatpak-repo", f"{APP_NAME}-{APP_SEMVER}.flatpak", f"{APP_NAME}"], check=True) + +def package_linux_archive(): + podman_online(f"{CONTAINER_HOME}/src", f"tar -C build/linux/x64/release/bundle -vacf {APP_NAME}-{APP_SEMVER}.tar.zstd .") + +def deploy_gitea_release(): + gitea_upload_attachment(f"{HOST_MOUNTED_VOLUME}/standalone_{APP_NAME}-{APP_SEMVER}.apk") + gitea_upload_attachment(f"{HOST_MOUNTED_VOLUME}/standalone_{APP_NAME}-{APP_SEMVER}.apk.idsig") + gitea_upload_attachment(f"{HOST_MOUNTED_VOLUME}/{APP_NAME}-{APP_SEMVER}.aab") + gitea_upload_attachment(f"{HOST_MOUNTED_VOLUME}/SelfPrivacy-{APP_SEMVER}-x86_64.AppImage") + gitea_upload_attachment(f"{HOST_MOUNTED_VOLUME}/SelfPrivacy-{APP_SEMVER}-x86_64.AppImage.zsync") + gitea_upload_attachment(f"{HOST_MOUNTED_VOLUME}/{APP_NAME}-{APP_SEMVER}.flatpak") + gitea_upload_attachment(f"{HOST_MOUNTED_VOLUME}/{APP_NAME}-{APP_SEMVER}.tar.zstd") + +def package_windows_archive(): + import shutil + shutil.make_archive(f"selfprivacy-{APP_SEMVER}-win32", 'zip', "build/windows/runner/Release") + +def deploy_windows_archive(): + gitea_upload_attachment(f"selfprivacy-{APP_SEMVER}-win32.zip") + +def deploy_fdroid_repo(): + subprocess.run([f"""eval $(ssh-agent -s) && + echo \"$SSH_PRIVATE_KEY\" | tr -d '\r' | ssh-add - && + scp -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null -r {HOST_HOME}/fdroid/repo/* deployer@selfprivacy.org:/var/www/fdroid.selfprivacy.org + """], shell=True, check=True) + +def ci_build_linux(): + podman_online(f"{CONTAINER_HOME}/src", "chown -R $(id -u):$(id -g) /tmp/flutter_pub_cache", + "&& flutter pub get", + "&& flutter build linux --debug") + +def ci_build_apk(): + podman_online(f"{CONTAINER_HOME}/src", "chown -R $(id -u):$(id -g) /tmp/gradle /tmp/flutter_pub_cache", + "&& flutter pub get", + "&& flutter build apk --flavor nightly") + +def ci_run_tests(): + podman_online(f"{CONTAINER_HOME}/src", "flutter test", + "&& flutter test --machine --coverage > tests.output") + +# Arguments + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + group = parser.add_mutually_exclusive_group() + group.add_argument("--build-linux", action="store_true") + group.add_argument("--build-apk", action="store_true") + group.add_argument("--build-bundle", action="store_true") + group.add_argument("--sign-apk-standalone", action="store_true", help="depends on $STANDALONE_KEYSTORE_PASS") + group.add_argument("--sign-apk-fdroid", action="store_true", help="depends on $FDROID_KEYSTORE_PASS") + group.add_argument("--sign-apk-fdroid-nightly", action="store_true", help="depends on $FDROID_KEYSTORE_PASS") + group.add_argument("--sign-bundle", action="store_true", help="depends on $GOOGLE_KEYSTORE_PASS") + group.add_argument("--package-linux-appimage", action="store_true") + group.add_argument("--package-linux-flatpak", action="store_true") + group.add_argument("--package-linux-archive", action="store_true") + group.add_argument("--package-windows-archive", action="store_true") + group.add_argument("--deploy-gitea-release", action="store_true", help="depends on $GITEA_RELEASE_TOKEN") + group.add_argument("--deploy-fdroid-repo", action="store_true", help="depends on $SSH_PRIVATE_KEY") + group.add_argument("--deploy-windows-archive", action="store_true") + group.add_argument("--ci-build-linux", action="store_true") + group.add_argument("--ci-build-apk", action="store_true") + group.add_argument("--ci-run-tests", action="store_true") + group.add_argument("--gitea-create-release", action="store_true") + group.add_argument("--gitea-upload-attachment", action="store") + args = parser.parse_args() + +if args.build_linux: + build_linux() +elif args.build_apk: + build_apk() +elif args.build_bundle: + build_bundle() +elif args.sign_apk_standalone: + sign_apk_standalone() +elif args.sign_apk_fdroid: + sign_apk_fdroid() +elif args.sign_apk_fdroid_nightly: + sign_apk_fdroid_nightly() +elif args.sign_bundle: + sign_bundle() +elif args.package_linux_appimage: + package_linux_appimage() +elif args.package_linux_flatpak: + package_linux_flatpak() +elif args.package_linux_archive: + package_linux_archive() +elif args.package_windows_archive: + package_windows_archive() +elif args.deploy_gitea_release: + deploy_gitea_release() +elif args.deploy_fdroid_repo: + deploy_fdroid_repo() +elif args.deploy_windows_archive: + deploy_windows_archive() +elif args.ci_build_linux: + ci_build_linux() +elif args.ci_build_apk: + ci_build_apk() +elif args.ci_run_tests: + ci_run_tests() +elif args.gitea_create_release: + gitea_create_release() +elif args.gitea_upload_attachment: + gitea_upload_attachment(args.gitea_upload_attachment) + diff --git a/fastlane/metadata/android/en-US/changelogs/0.6.1.txt b/fastlane/metadata/android/en-US/changelogs/0.6.1.txt new file mode 100644 index 00000000..0f5c3964 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/0.6.1.txt @@ -0,0 +1,3 @@ +- Fixed routing errors and broken "back" buttons on recovery stages +- Fixed broken validation on api token fields +- Minor improvements \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/0.7.0.txt b/fastlane/metadata/android/en-US/changelogs/0.7.0.txt new file mode 100644 index 00000000..60316c0b --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/0.7.0.txt @@ -0,0 +1,9 @@ +- Server storage management +- New screen for service management +- New users management screen +- User passwords can be changed +- Server auto upgrade settings can be changed now +- Server timezone can be changed +- Fixed bugs related to users list synchronization +- UI fixes +- Minor bug fixes diff --git a/fastlane/metadata/android/en-US/changelogs/0.8.0.txt b/fastlane/metadata/android/en-US/changelogs/0.8.0.txt new file mode 100644 index 00000000..b03a63d2 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/0.8.0.txt @@ -0,0 +1,37 @@ +Server setup: +- Added support for Digital Ocean as server provider +- You can now choose server region +- You can now choose server tier +- Server installation UI has been refreshed +- Fields now have more specific error messages + +Common UI: +- New app bar used in most of the screens + +Services: +- Services are now sorted by their status + +Server settings: +- Timezone search screen now has a search bar +- Fixed job creation when switching the setting multiple times +- Server destruction now works + +Jobs: +- Jobs panel now should take slightly less space + +Auth: +- Recovery key page can now be reloaded by dragging down + +Logging: +- Log console now has a limit of 500 lines +- GraphQL API requests are now logged in the console +- Networks errors are better handled + +For developers: +- App now only uses GraphQL API to communicate with the server. All REST API calls have been removed. +- Server can now be deployed with staging ACME certificates +- Language assets have been reorganized + +Translations: +- Added translation for Ukrainian +- Also activated unfinished translations for German, French, Spanish, Czech, Polish, Thai diff --git a/fastlane/metadata/android/en-US/changelogs/0.9.0.txt b/fastlane/metadata/android/en-US/changelogs/0.9.0.txt new file mode 100644 index 00000000..29ac8043 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/0.9.0.txt @@ -0,0 +1,145 @@ +### Features + +- New backups implementation ([#228](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/228), [#274](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/274), [#324](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/324), [#325](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/325), [#326](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/326), [#331](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/331), [#332](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/332)) +- DigitalOcean as a DNS provider ([#213](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/213)) +- DeSEC as a DNS provider ([#211](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/211)) +- Support drawer and basic support documentation logic unit ([#203](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/203)) +- Automatic day/night theme ([#203](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/203)) +- New router and adaptive layouts ([#203](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/203)) +- New Material 3 animation curves ([#203](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/203)) +- Jobs button to the app bar of more screens ([#203](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/203)) +- Refreshed UI of modal sheets ([#228](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/228)) +- Support for `XDG_DATA_HOME` storage path on Linux for app data ([#240](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/240)) +- Accept-Language header for the server API ([#243](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/243), resolves [#205](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/205)) +- Visible providers names during server recovery ([#264](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/264), resolves [#249](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/249)) +- Visible volume and IPv4 cost added to overall monthly cost of the server ([#270](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/270), resolves [#115](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/115)) +- Support for autofocus on text fields for keyboard displaying ([#294](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/294), resolves [#292](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/292)) +- New dialogue to choose a domain if user DNS token provides access to several ([#330](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/330), resolves [#328](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/328)) + +### Bug Fixes + +- Fix opening URLs from the app ([#213](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/213)) +- Fix parsing of RAM size with DigitalOcean ([#200](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/200), resolves [#199](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/199)) +- Devices and Recovery Key cubits couldn't initialize right after server installation ([#203](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/203)) +- Fix BottomBar showing incorrect animation when navigating from sibling routes ([#203](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/203)) +- PopUpDialogs couldn't find the context. ([#203](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/203)) +- Update recovery flow to use new support drawer ([#203](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/203)) +- New app log console ([#203](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/203)) +- Improve installation failure dialogues ([#213](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/213)) +- Privacy policy link pointed at wrong domain ([#207](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/207)) +- Remove price lists for DNS ([#211](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/211)) +- Implement better domain id check on DNS restoration ([#211](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/211)) +- Add forced JSON content type to REST APIs ([#212](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/212)) +- Remove unneded DNS check depending on CLOUDFLARE ([#212](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/212)) +- Add background for dialogue pop ups and move them to root navigator ([#233](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/233), resolves [#231](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/231)) +- Make currency be properly shown again via shortcode ([#234](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/234), related to [#223](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/223)) +- Add proper server type value loading ([#236](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/236), resolves [#215](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/215)) +- Implement proper load functions for DNS and Server providers ([#237](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/237), resolves [#220](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/220)) +- Prevent moving a service if volume is null for some reason ([#245](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/245)) +- Replace hard reset from server provider with direct server reboot ([#269](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/269), resolves [#266](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/266)) +- Normalize Hetzner CPU usage percentage by cached amount of cores ([#272](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/272), resolves [#156](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/156)) +- Change broken validations string for superuser SSH ([#276](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/27)) +- Don't let service migration to start bif the same volume was picked ([#297](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/297), resolves [#289](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/289)) +- Wrap DNS check in catch to avoid runtime crash ([#322](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/322)) +- Implement Backblaze bucket restoration on server recovery ([#324](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/324)) + +### Refactor + +- Migrate to Flutter 3.10 and Dart 3.0 +- Migrate to AutoRouter v6 ([#203](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/203)) +- Get rid of BrandText and restructure the buttons ([#203](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/203)) +- Remove brand alert dialogs and bottom sheet ([#203](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/203)) +- Remove unused UI components ([#203](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/203)) +- Remove BrandCards ([#203](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/203)) +- Allow changing values for TLS settings +- Replace String shortcode with Currency class ([#226](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/226)) +- Rearrange Server Provider interface ([#227](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/227)) +- Remove unused service state getters ([#228](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/228)) +- Remove unused utils, add duration formatter ([#228](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/228)) +- Move rest api methods according to their business logic files positions ([#235](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/235), partially resolves [#217](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/217) and [#219](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/219)) +- Make flag getter a part of server provider location object ([#238](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/238), resolves [#222](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/222)) + + +### Translation contributions + +* Ukrainian + + * FoxMeste (3) + * Mithras (31) + +* Latvian + + * Not Telling Lol (183) + + +* German + + * Mithras (41) + * FoxMeste (213) + + +* Thai + + * FoxMeste (77) + + +* Polish + + * Mithras (41) + * Thary (43) + * FoxMeste (163) + + +* Slovenian + + * Mithras (212) + + +* Czech + + * NaiJi ✨ (2) + * Mithras (109) + * FoxMeste (308) + + +* Russian + + * FoxMeste (4) + * Revertron (8) + * NaiJi ✨ (23) + * Mithras (54) + * Inex Code (59) + + +* Slovak + + * Mithras (29) + * Revertron (396) + + +* Macedonian + + * FoxMeste (7) + + +* Belarusian + + * Thary (1) + * FoxMeste (3) + * Mithras (47) + + +* French + + * Côme (211) + + +* Spanish + + * FoxMeste (7) + + +* Azerbaijani + + * Mithras (28) + * Ortibexon (403) diff --git a/fastlane/metadata/android/en-US/changelogs/0.9.1.txt b/fastlane/metadata/android/en-US/changelogs/0.9.1.txt new file mode 100644 index 00000000..59b28862 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/0.9.1.txt @@ -0,0 +1,20 @@ +### Bug Fixes + +- Fix volume resizing on Digital Ocean ([#368](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/368), resolves [#367](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/367)) +- Disable the storage card while volume information is being fetched ([#369](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/369), resolves [#317](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/317)) + + +### Features + +- Add copy-to-clipboard for email on user page ([#329](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/329), resolves [#287](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/287)) +- Add support for ECDSA SSH keys ([#362](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/362), resolves [#319](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/319)) +- Implement confirmation modal for the volume resize ([#372](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/372), resolves [#308](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/308)) + +### Other changes + +- Move service descriptions above login info for service cards ([#342](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/342), resolves [#341](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/341)) +- Add measure units to 'Extending volume' page ([#344](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/344), resolves [#301](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/301)) +- Make users to be ordered properly on users page ([#343](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/343), resolves [#340](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/340)) +- Move service card name to its icon row ([#352](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/352), resolves [#350](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/350)) +- Reorganize placeholders for empty pages ([#359](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/359), resolves [#348](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/348)) +- Remove redundant zone id cache for Cloudflare ([#371](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/371)) diff --git a/fastlane/metadata/android/en-US/full_description.txt b/fastlane/metadata/android/en-US/full_description.txt index 32c7f449..f5ba9142 100644 --- a/fastlane/metadata/android/en-US/full_description.txt +++ b/fastlane/metadata/android/en-US/full_description.txt @@ -1,15 +1,23 @@ -SelfPrivacy - is a platform on your cloud hosting, that allows to deploy your own private services and control them using mobile application. -To use this application, you'll be required to create accounts of different service providers. Please reffer to this manual: https://selfprivacy.org/en/second.html -Application will do the following things for you: -1. Create your personal server -2. Setup NixOS -3. Bring all services to the ready-to-use state. Services include: -* E-mail, ready to use with DeltaChat -* NextCloud - your personal cloud storage -* Bitwarden - secure and private password manager -* Pleroma - your private fediverse space for blogging -* Jitsi — awesome Zoom alternative -* Gitea - your own Git server -* OpenConnect - Personal VPN server +

SelfPrivacy — is a platform on your cloud hosting, that allows to deploy your own private services and control them using mobile application.

-!!! Project is currently in open beta state. Feel free to try it. It would be much appreciated if you would provide us with some feedback. \ No newline at end of file +

To use this application, you'll be required to create accounts of different service providers. Please reffer to this manual: https://selfprivacy.org/en/second

+ +

Application will do the following things for you:

+ +
    +
  1. Create your personal server
  2. +
  3. Setup NixOS
  4. +
  5. Bring all services to the ready-to-use state. Services include:
  6. +
+ +
    +
  • E-mail, ready to use with DeltaChat
  • +
  • NextCloud — your personal cloud storage
  • +
  • Bitwarden — secure and private password manager
  • +
  • Pleroma — your private fediverse space for blogging
  • +
  • Jitsi — awesome Zoom alternative
  • +
  • Gitea — your own Git server
  • +
  • OpenConnect — Personal VPN server
  • +
+ +

Project is currently in open beta state. Feel free to try it. It would be much appreciated if you would provide us with some feedback.

diff --git a/fastlane/metadata/android/en-US/images/icon.png b/fastlane/metadata/android/en-US/images/icon.png new file mode 100644 index 00000000..fee217d6 Binary files /dev/null and b/fastlane/metadata/android/en-US/images/icon.png differ diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..14cdcc98 --- /dev/null +++ b/flake.lock @@ -0,0 +1,92 @@ +{ + "nodes": { + "flake-utils": { + "locked": { + "lastModified": 1659877975, + "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "locked": { + "lastModified": 1659877975, + "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixgl": { + "inputs": { + "flake-utils": "flake-utils_2", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1661367362, + "narHash": "sha256-Qc8MXcV+YCPREu8kk6oggk23ZBKLqeQRAIsLbHEviPE=", + "owner": "guibou", + "repo": "nixGL", + "rev": "7165ffbccbd2cf4379b6cd6d2edd1620a427e5ae", + "type": "github" + }, + "original": { + "owner": "guibou", + "repo": "nixGL", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1660551188, + "narHash": "sha256-a1LARMMYQ8DPx1BgoI/UN4bXe12hhZkCNqdxNi6uS0g=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "441dc5d512153039f19ef198e662e4f3dbb9fd65", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1693250523, + "narHash": "sha256-y3up5gXMTbnCsXrNEB5j+7TVantDLUYyQLu/ueiXuyg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "3efb0f6f404ec8dae31bdb1a9b17705ce0d6986e", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "ref": "nixos-unstable", + "type": "indirect" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixgl": "nixgl", + "nixpkgs": "nixpkgs_2" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..a124dc2c --- /dev/null +++ b/flake.nix @@ -0,0 +1,151 @@ +{ + nixConfig.bash-prompt = "\[selfprivacy\]$ "; + + inputs.nixpkgs.url = "nixpkgs/nixos-unstable"; + inputs.flake-utils.url = "github:numtide/flake-utils"; + inputs.nixgl.url = "github:guibou/nixGL"; + + outputs = { self, nixpkgs, flake-utils, nixgl }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { + inherit system; + config.allowUnfree = true; + config.android_sdk.accept_license = true; + overlays = [ nixgl.overlay ]; + }; + + androidComposition = pkgs.androidenv.composeAndroidPackages { + platformToolsVersion = "34.0.4"; + buildToolsVersions = [ "34.0.0" ]; + platformVersions = [ "34" "33" "32" "31" "30" ]; + }; + + spAndroidStudio = pkgs.symlinkJoin { + name = "spAndroidStudio"; + paths = with pkgs; [ + android-studio + flutter.unwrapped + # dart + gnumake + check + pkg-config + glibc + android-tools + jdk + git + ]; + + nativeBuildInputs = [ pkgs.makeWrapper ]; + postBuild = '' + wrapProgram $out/bin/flutter \ + --prefix ANDROID_SDK_ROOT=${androidComposition.androidsdk}/libexec/android-sdk \ + --prefix ANDROID_HOME=${androidComposition.androidsdk}/libexec/android-sdk \ + --prefix ANDROID_JAVA_HOME=${pkgs.jdk.home} + + wrapProgram $out/bin/android-studio \ + --prefix FLUTTER_SDK=${pkgs.flutter.unwrapped} \ + --prefix ANDROID_SDKz_ROOT=${androidComposition.androidsdk}/libexec/android-sdk \ + --prefix ANDROID_HOME=${androidComposition.androidsdk}/libexec/android-sdk \ + --prefix ANDROID_JAVA_HOME=${pkgs.jdk.home} + ''; + }; + + buildDeps = with pkgs; [ + gtk3 + glib + pcre + util-linux + libselinux + libsepol + libthai + libdatrie + xorg.libXdmcp + xorg.libXtst + libxkbcommon + dbus + at-spi2-core + libsecret + jsoncpp + xorg.libX11 + libepoxy + libgcrypt + libgpg-error + ]; + + nativeBuildDeps = with pkgs; [ + flutter.unwrapped + bash + curl + flutter.dart + git + unzip + which + xz + cmake + ninja + pkg-config + wrapGAppsHook + autoPatchelfHook + androidComposition.androidsdk + openjdk11_headless + clang + ]; + + releaseDerivation = pkgs.flutter.mkFlutterApp rec { + pname = "selfprivacy"; + version = "0.6.0"; + + vendorHash = "sha256-7cbiAyIlaz3HqEsZN/nZxaLZjseJv5CmiIHqsoGa4ZI="; + + nativeBuildInputs = [ pkgs.nixgl.auto.nixGLDefault ]; + + src = ./.; + + desktopItem = pkgs.makeDesktopItem { + name = "${pname}"; + exec = "@out@/bin/${pname}"; + desktopName = "SelfPrivacy"; + }; + + postInstall = '' + rm $out/bin/$pname + + printf "#!/bin/sh\n${pkgs.nixgl.auto.nixGLDefault}/bin/nixGL $out/app/${pname}" > $out/bin/$pname + patchShebangs $out/bin/$pname + chmod +x $out/bin/$pname + wrapProgram $out/bin/$pname --set PATH ${pkgs.lib.makeBinPath [ pkgs.xdg-user-dirs ]} + + mkdir -p $out/share/applications + cp $desktopItem/share/applications/*.desktop $out/share/applications + substituteInPlace $out/share/applications/*.desktop --subst-var out + ''; + }; + in + { + packages = { + release = releaseDerivation; + }; + defaultPackage = releaseDerivation; + + devShell = pkgs.mkShell { + buildInputs = buildDeps; + nativeBuildInputs = nativeBuildDeps; + + JAVA_HOME = "${pkgs.openjdk11_headless.home}"; + ANDROID_HOME = "${androidComposition.androidsdk}/libexec/android-sdk"; + ANDROID_SDK_ROOT = "${androidComposition.androidsdk}/libexec/android-sdk"; + + NIX_LDFLAGS = "-rpath ${pkgs.lib.makeLibraryPath buildDeps}"; + NIX_CFLAGS_COMPILE = "-I${pkgs.xorg.libX11}/include"; + LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildDeps; + + shellHook = '' + export TMP=$(mktemp -d) + export HOME="$TMP" + export PUB_CACHE=''${PUB_CACHE:-"$HOME/.pub-cache"} + export ANDROID_EMULATOR_USE_SYSTEM_LIBS=1 + ''; + }; + }); +} diff --git a/flatpak.yml b/flatpak.yml new file mode 100644 index 00000000..6ab28b3d --- /dev/null +++ b/flatpak.yml @@ -0,0 +1,47 @@ +app-id: org.selfprivacy.app +runtime: org.freedesktop.Platform +runtime-version: '22.08' +sdk: org.freedesktop.Sdk +command: selfprivacy +finish-args: + - "--share=ipc" + - "--socket=x11" + - "--socket=fallback-x11" + - "--socket=wayland" + - "--share=network" + - "--own-name=org.selfprivacy.app" + - "--device=dri" +modules: + - name: selfprivacy + buildsystem: simple + build-commands: + - cp -r * /app + - mkdir /app/bin + - ln -s /app/selfprivacy /app/bin/selfprivacy + - install -Dm644 logo.svg /app/share/icons/hicolor/scalable/apps/org.selfprivacy.app.svg + - install -Dm644 org.selfprivacy.app.desktop /app/share/applications/org.selfprivacy.app.desktop + sources: + - type: dir + path: build/linux/x64/release/bundle + - type: file + path: assets/images/icon/logo.svg + - type: file + path: org.selfprivacy.app.desktop + - name: libsecret + buildsystem: meson + config-opts: + - "-Dvapi=false" + - "-Dgtk_doc=false" + sources: + - type: git + url: https://gitlab.gnome.org/GNOME/libsecret.git + tag: 0.20.5 + - name: libjsoncpp + buildsystem: meson + config-opts: + - "--buildtype=release" + - "--default-library=shared" + sources: + - type: git + url: https://github.com/open-source-parsers/jsoncpp.git + tag: 1.9.5 diff --git a/ios/.gitignore b/ios/.gitignore index e96ef602..7a7f9873 100644 --- a/ios/.gitignore +++ b/ios/.gitignore @@ -1,3 +1,4 @@ +**/dgph *.mode1v3 *.mode2v3 *.moved-aside @@ -18,6 +19,7 @@ Flutter/App.framework Flutter/Flutter.framework Flutter/Flutter.podspec Flutter/Generated.xcconfig +Flutter/ephemeral/ Flutter/app.flx Flutter/app.zip Flutter/flutter_assets/ diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist index f2872cf4..9625e105 100644 --- a/ios/Flutter/AppFrameworkInfo.plist +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -3,7 +3,7 @@ CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) + en CFBundleExecutable App CFBundleIdentifier @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 9.0 + 11.0 diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig index e8efba11..ec97fc6f 100644 --- a/ios/Flutter/Debug.xcconfig +++ b/ios/Flutter/Debug.xcconfig @@ -1,2 +1,2 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig index 399e9340..c4855bfe 100644 --- a/ios/Flutter/Release.xcconfig +++ b/ios/Flutter/Release.xcconfig @@ -1,2 +1,2 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/ios/Podfile b/ios/Podfile index 1e8c3c90..88359b22 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '9.0' +# platform :ios, '11.0' # 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 5db2ddac..ac765d6c 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,64 +1,83 @@ PODS: - - Flutter (1.0.0) - - flutter_secure_storage (3.3.1): + - connectivity_plus (0.0.1): - Flutter - - local_auth (0.0.1): + - ReachabilitySwift + - device_info_plus (0.0.1): + - Flutter + - Flutter (1.0.0) + - flutter_secure_storage (6.0.0): + - Flutter + - local_auth_ios (0.0.1): - Flutter - package_info (0.0.1): - Flutter - - path_provider (0.0.1): + - path_provider_ios (0.0.1): - Flutter + - ReachabilitySwift (5.0.0) - share_plus (0.0.1): - Flutter - - shared_preferences (0.0.1): + - shared_preferences_ios (0.0.1): - Flutter - - url_launcher (0.0.1): + - url_launcher_ios (0.0.1): - Flutter - wakelock (0.0.1): - Flutter DEPENDENCIES: + - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) + - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - Flutter (from `Flutter`) - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) - - local_auth (from `.symlinks/plugins/local_auth/ios`) + - local_auth_ios (from `.symlinks/plugins/local_auth_ios/ios`) - package_info (from `.symlinks/plugins/package_info/ios`) - - path_provider (from `.symlinks/plugins/path_provider/ios`) + - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`) - - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) - - url_launcher (from `.symlinks/plugins/url_launcher/ios`) + - shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`) + - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - wakelock (from `.symlinks/plugins/wakelock/ios`) +SPEC REPOS: + trunk: + - ReachabilitySwift + EXTERNAL SOURCES: + connectivity_plus: + :path: ".symlinks/plugins/connectivity_plus/ios" + device_info_plus: + :path: ".symlinks/plugins/device_info_plus/ios" Flutter: :path: Flutter flutter_secure_storage: :path: ".symlinks/plugins/flutter_secure_storage/ios" - local_auth: - :path: ".symlinks/plugins/local_auth/ios" + local_auth_ios: + :path: ".symlinks/plugins/local_auth_ios/ios" package_info: :path: ".symlinks/plugins/package_info/ios" - path_provider: - :path: ".symlinks/plugins/path_provider/ios" + path_provider_ios: + :path: ".symlinks/plugins/path_provider_ios/ios" share_plus: :path: ".symlinks/plugins/share_plus/ios" - shared_preferences: - :path: ".symlinks/plugins/shared_preferences/ios" - url_launcher: - :path: ".symlinks/plugins/url_launcher/ios" + shared_preferences_ios: + :path: ".symlinks/plugins/shared_preferences_ios/ios" + url_launcher_ios: + :path: ".symlinks/plugins/url_launcher_ios/ios" wakelock: :path: ".symlinks/plugins/wakelock/ios" SPEC CHECKSUMS: - Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a - flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec - local_auth: 25938960984c3a7f6e3253e3f8d962fdd16852bd + connectivity_plus: 413a8857dd5d9f1c399a39130850d02fe0feaf7e + device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed + Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be + local_auth_ios: 0d333dde7780f669e66f19d2ff6005f3ea84008d package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62 - path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c + path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02 + ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68 - shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d - url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef + shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad + url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f -PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c +PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 -COCOAPODS: 1.11.2 +COCOAPODS: 1.11.3 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index b22b290f..ad268014 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -8,12 +8,12 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 157C97814E2B6EE2FC34CDB1 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 257F298CD626562B3A0BE8BC /* Pods_Runner.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - B724A8991C5C577148A932D7 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DAE4FF490544D49BC2DD9A3 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -30,17 +30,17 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 02BB87F67990BDBF46B33A99 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 247FA89325766918005A3DE3 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Main.strings; sourceTree = ""; }; - 247FA89425766918005A3DE3 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/LaunchScreen.strings; sourceTree = ""; }; + 257F298CD626562B3A0BE8BC /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 32ED047D2988556F00CEC6F8 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; + 32ED047E298857FA00CEC6F8 /* lib */ = {isa = PBXFileReference; lastKnownFileType = folder; name = lib; path = ../lib; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 4F50D46E1FAD96D4DE911FF3 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - 5DAE4FF490544D49BC2DD9A3 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 76C01250B18E06E028B2A21D /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 88863573AD2094F1791C05BE /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -48,7 +48,7 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - F3A7AA4BF6A26F2649361520 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + FAE3312046B1506D10D287F2 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -56,17 +56,17 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - B724A8991C5C577148A932D7 /* Pods_Runner.framework in Frameworks */, + 157C97814E2B6EE2FC34CDB1 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 89F487B6EA8992414428CAB5 /* Frameworks */ = { + 1725124A7D564E6014199E7A /* Frameworks */ = { isa = PBXGroup; children = ( - 5DAE4FF490544D49BC2DD9A3 /* Pods_Runner.framework */, + 257F298CD626562B3A0BE8BC /* Pods_Runner.framework */, ); name = Frameworks; sourceTree = ""; @@ -74,6 +74,7 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( + 32ED047E298857FA00CEC6F8 /* lib */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, @@ -88,8 +89,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, - A0CB544917625008B1ABA634 /* Pods */, - 89F487B6EA8992414428CAB5 /* Frameworks */, + BAF4554E70AEB98F4DF5B768 /* Pods */, + 1725124A7D564E6014199E7A /* Frameworks */, ); sourceTree = ""; }; @@ -104,6 +105,7 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( + 32ED047D2988556F00CEC6F8 /* Runner.entitlements */, 97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, @@ -116,12 +118,12 @@ path = Runner; sourceTree = ""; }; - A0CB544917625008B1ABA634 /* Pods */ = { + BAF4554E70AEB98F4DF5B768 /* Pods */ = { isa = PBXGroup; children = ( - 02BB87F67990BDBF46B33A99 /* Pods-Runner.debug.xcconfig */, - F3A7AA4BF6A26F2649361520 /* Pods-Runner.release.xcconfig */, - 4F50D46E1FAD96D4DE911FF3 /* Pods-Runner.profile.xcconfig */, + FAE3312046B1506D10D287F2 /* Pods-Runner.debug.xcconfig */, + 88863573AD2094F1791C05BE /* Pods-Runner.release.xcconfig */, + 76C01250B18E06E028B2A21D /* Pods-Runner.profile.xcconfig */, ); path = Pods; sourceTree = ""; @@ -133,14 +135,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - EE51AAD2B783790EFCD497B1 /* [CP] Check Pods Manifest.lock */, + 70552AB84A03C7D8459451CE /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 3687B4F187EC56464925D9AB /* [CP] Embed Pods Frameworks */, + C3CADDF8A29BE6F206C19D86 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -173,7 +175,6 @@ knownRegions = ( en, Base, - ru, ); mainGroup = 97C146E51CF9000F007C117D; productRefGroup = 97C146EF1CF9000F007C117D /* Products */; @@ -200,23 +201,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 3687B4F187EC56464925D9AB /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -231,21 +215,7 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 9740EEB61CF901F6004384FC /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; - }; - EE51AAD2B783790EFCD497B1 /* [CP] Check Pods Manifest.lock */ = { + 70552AB84A03C7D8459451CE /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -267,6 +237,37 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + C3CADDF8A29BE6F206C19D86 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -286,7 +287,6 @@ isa = PBXVariantGroup; children = ( 97C146FB1CF9000F007C117D /* Base */, - 247FA89325766918005A3DE3 /* ru */, ); name = Main.storyboard; sourceTree = ""; @@ -295,7 +295,6 @@ isa = PBXVariantGroup; children = ( 97C147001CF9000F007C117D /* Base */, - 247FA89425766918005A3DE3 /* ru */, ); name = LaunchScreen.storyboard; sourceTree = ""; @@ -307,7 +306,6 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -345,7 +343,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -360,27 +358,19 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 7; - DEVELOPMENT_TEAM = UVNTKR53DD; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 46723VZHWZ; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = pro.kherel.selfprivacy; + PRODUCT_BUNDLE_IDENTIFIER = org.selfprivacy.selfprivacy; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; }; name = Profile; @@ -389,7 +379,6 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -433,7 +422,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -445,7 +434,6 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -483,7 +471,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -500,28 +488,20 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 7; - DEVELOPMENT_TEAM = UVNTKR53DD; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 46723VZHWZ; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = pro.kherel.selfprivacy; + PRODUCT_BUNDLE_IDENTIFIER = org.selfprivacy.selfprivacy; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; @@ -532,27 +512,19 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 7; - DEVELOPMENT_TEAM = UVNTKR53DD; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 46723VZHWZ; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = pro.kherel.selfprivacy; + PRODUCT_BUNDLE_IDENTIFIER = org.selfprivacy.selfprivacy; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 3db53b6e..c87d15a3 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -27,8 +27,6 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> - - - - + + - - CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Selfprivacy CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 - CFBundleLocalizations - - ru - en - CFBundleName selfprivacy CFBundlePackageType @@ -24,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - $(CURRENT_PROJECT_VERSION) + $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS UILaunchStoryboardName @@ -34,6 +31,8 @@ UISupportedInterfaceOrientations UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad @@ -44,5 +43,9 @@ UIViewControllerBasedStatusBarAppearance + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + diff --git a/ios/Runner/Runner.entitlements b/ios/Runner/Runner.entitlements new file mode 100644 index 00000000..ccfa3da0 --- /dev/null +++ b/ios/Runner/Runner.entitlements @@ -0,0 +1,10 @@ + + + + + keychain-access-groups + + $(AppIdentifierPrefix)* + + + diff --git a/ios/Runner/ru.lproj/LaunchScreen.strings b/ios/Runner/ru.lproj/LaunchScreen.strings deleted file mode 100644 index 8b137891..00000000 --- a/ios/Runner/ru.lproj/LaunchScreen.strings +++ /dev/null @@ -1 +0,0 @@ - diff --git a/ios/Runner/ru.lproj/Main.strings b/ios/Runner/ru.lproj/Main.strings deleted file mode 100644 index 8b137891..00000000 --- a/ios/Runner/ru.lproj/Main.strings +++ /dev/null @@ -1 +0,0 @@ - diff --git a/lib/config/bloc_config.dart b/lib/config/bloc_config.dart index 3946d3b9..381261fe 100644 --- a/lib/config/bloc_config.dart +++ b/lib/config/bloc_config.dart @@ -2,43 +2,62 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:selfprivacy/logic/cubit/devices/devices_cubit.dart'; import 'package:selfprivacy/logic/cubit/recovery_key/recovery_key_cubit.dart'; +import 'package:selfprivacy/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart'; import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart'; import 'package:selfprivacy/logic/cubit/dns_records/dns_records_cubit.dart'; -import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart'; +import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart'; import 'package:selfprivacy/logic/cubit/providers/providers_cubit.dart'; +import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_cubit.dart'; +import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart'; import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; +import 'package:selfprivacy/logic/cubit/support_system/support_system_cubit.dart'; import 'package:selfprivacy/logic/cubit/users/users_cubit.dart'; +import 'package:selfprivacy/logic/cubit/provider_volumes/provider_volume_cubit.dart'; class BlocAndProviderConfig extends StatelessWidget { - const BlocAndProviderConfig({final super.key, this.child}); + const BlocAndProviderConfig({super.key, this.child}); final Widget? child; @override Widget build(final BuildContext context) { const isDark = false; + const isAutoDark = true; final serverInstallationCubit = ServerInstallationCubit()..load(); + final supportSystemCubit = SupportSystemCubit(); final usersCubit = UsersCubit(serverInstallationCubit); final servicesCubit = ServicesCubit(serverInstallationCubit); final backupsCubit = BackupsCubit(serverInstallationCubit); final dnsRecordsCubit = DnsRecordsCubit(serverInstallationCubit); final recoveryKeyCubit = RecoveryKeyCubit(serverInstallationCubit); final apiDevicesCubit = ApiDevicesCubit(serverInstallationCubit); + final apiVolumesCubit = ApiProviderVolumeCubit(serverInstallationCubit); + final apiServerVolumesCubit = + ApiServerVolumeCubit(serverInstallationCubit, apiVolumesCubit); + final serverJobsCubit = ServerJobsCubit(serverInstallationCubit); + final serverDetailsCubit = ServerDetailsCubit(serverInstallationCubit); + return MultiProvider( providers: [ BlocProvider( create: (final _) => AppSettingsCubit( isDarkModeOn: isDark, + isAutoDarkModeOn: isAutoDark, isOnboardingShowing: true, )..load(), ), + BlocProvider( + create: (final _) => supportSystemCubit, + ), BlocProvider( create: (final _) => serverInstallationCubit, lazy: false, ), - BlocProvider(create: (final _) => ProvidersCubit()), + BlocProvider( + create: (final _) => ProvidersCubit(), + ), BlocProvider( create: (final _) => usersCubit..load(), lazy: false, @@ -61,8 +80,22 @@ class BlocAndProviderConfig extends StatelessWidget { create: (final _) => apiDevicesCubit..load(), ), BlocProvider( - create: (final _) => - JobsCubit(usersCubit: usersCubit, servicesCubit: servicesCubit), + create: (final _) => apiVolumesCubit..load(), + ), + BlocProvider( + create: (final _) => apiServerVolumesCubit..load(), + ), + BlocProvider( + create: (final _) => serverJobsCubit..load(), + ), + BlocProvider( + create: (final _) => serverDetailsCubit..load(), + ), + BlocProvider( + create: (final _) => JobsCubit( + usersCubit: usersCubit, + servicesCubit: servicesCubit, + ), ), ], child: child, diff --git a/lib/config/bloc_observer.dart b/lib/config/bloc_observer.dart index e68923c9..3cb9e1c0 100644 --- a/lib/config/bloc_observer.dart +++ b/lib/config/bloc_observer.dart @@ -1,29 +1,7 @@ -import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:selfprivacy/ui/components/error/error.dart'; -import 'package:selfprivacy/utils/route_transitions/basic.dart'; -import 'package:selfprivacy/config/get_it_config.dart'; +// import 'package:selfprivacy/config/get_it_config.dart'; class SimpleBlocObserver extends BlocObserver { SimpleBlocObserver(); - - @override - void onError( - final BlocBase bloc, - final Object error, - final StackTrace stackTrace, - ) { - final NavigatorState navigator = getIt.get().navigator!; - - navigator.push( - materialRoute( - BrandError( - error: error, - stackTrace: stackTrace, - ), - ), - ); - super.onError(bloc, error, stackTrace); - } } diff --git a/lib/config/brand_colors.dart b/lib/config/brand_colors.dart index 15d1433a..335e652e 100644 --- a/lib/config/brand_colors.dart +++ b/lib/config/brand_colors.dart @@ -2,53 +2,16 @@ import 'package:flutter/material.dart'; class BrandColors { static const Color blue = Color(0xFF093CEF); - static const Color white = Colors.white; - static const Color black = Colors.black; - - static const Color gray1 = Color(0xFF555555); - static const Color gray2 = Color(0xFF7C7C7C); - static const Color gray3 = Color(0xFFFAFAFA); - static const Color gray4 = Color(0xFFDDDDDD); - static const Color gray5 = Color(0xFFEDEEF1); - static Color gray6 = const Color(0xFF181818).withOpacity(0.7); - static const Color grey7 = Color(0xFFABABAB); - - static const Color red1 = Color(0xFFFA0E0E); - static const Color red2 = Color(0xFFE65527); - - static const Color green1 = Color(0xFF00AF54); - - static const Color green2 = Color(0xFF0F8849); - - static Color get navBackgroundLight => white.withOpacity(0.8); - static Color get navBackgroundDark => black.withOpacity(0.8); static const List uninitializedGradientColors = [ Color(0xFF555555), Color(0xFFABABAB), ]; - static const List stableGradientColors = [ - Color(0xFF093CEF), - Color(0xFF14A1CB), - ]; - static const List progressGradientColors = [ - Color(0xFF093CEF), - Color(0xFF14A1CB), - ]; static const List warningGradientColors = [ Color(0xFFEF4E09), Color(0xFFEFD135), ]; static const Color primary = blue; - static const Color headlineColor = black; - static const Color inactive = gray2; - static const Color scaffoldBackground = gray3; - static const Color inputInactive = gray4; - - static const Color textColor1 = black; - static const Color textColor2 = gray1; - static const Color dividerColor = gray5; - static const Color warning = red1; } diff --git a/lib/config/brand_theme.dart b/lib/config/brand_theme.dart index 3ad0623c..aa94a8ec 100644 --- a/lib/config/brand_theme.dart +++ b/lib/config/brand_theme.dart @@ -1,88 +1,3 @@ import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/text_themes.dart'; - -import 'package:selfprivacy/config/brand_colors.dart'; - -final ThemeData lightTheme = ThemeData( - useMaterial3: true, - primaryColor: BrandColors.primary, - fontFamily: 'Inter', - brightness: Brightness.light, - scaffoldBackgroundColor: BrandColors.scaffoldBackground, - inputDecorationTheme: const InputDecorationTheme( - border: InputBorder.none, - contentPadding: EdgeInsets.all(16), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(4)), - borderSide: BorderSide(color: BrandColors.inputInactive), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(4)), - borderSide: BorderSide(color: BrandColors.blue), - ), - errorBorder: OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(4)), - borderSide: BorderSide( - width: 1, - color: BrandColors.red1, - ), - ), - focusedErrorBorder: OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(4)), - borderSide: BorderSide( - width: 1, - color: BrandColors.red1, - ), - ), - errorStyle: TextStyle( - fontSize: 12, - color: BrandColors.red1, - ), - ), - listTileTheme: const ListTileThemeData( - minLeadingWidth: 24.0, - ), - textTheme: TextTheme( - headline1: headline1Style, - headline2: headline2Style, - headline3: headline3Style, - headline4: headline4Style, - bodyText1: body1Style, - subtitle1: const TextStyle(fontSize: 15, height: 1.6), // text input style - ), -); - -ThemeData darkTheme = lightTheme.copyWith( - brightness: Brightness.dark, - scaffoldBackgroundColor: const Color(0xFF202120), - iconTheme: const IconThemeData(color: BrandColors.gray3), - cardColor: BrandColors.gray1, - dialogBackgroundColor: const Color(0xFF202120), - textTheme: TextTheme( - headline1: headline1Style.copyWith(color: BrandColors.white), - headline2: headline2Style.copyWith(color: BrandColors.white), - headline3: headline3Style.copyWith(color: BrandColors.white), - headline4: headline4Style.copyWith(color: BrandColors.white), - bodyText1: body1Style.copyWith(color: BrandColors.white), - subtitle1: const TextStyle(fontSize: 15, height: 1.6), // text input style - ), - inputDecorationTheme: const InputDecorationTheme( - labelStyle: TextStyle(color: BrandColors.white), - hintStyle: TextStyle(color: BrandColors.white), - border: OutlineInputBorder( - borderSide: BorderSide( - color: BrandColors.white, - ), - ), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: BrandColors.white, - ), - ), - ), -); - -const EdgeInsets paddingH15V30 = - EdgeInsets.symmetric(horizontal: 15, vertical: 30); const EdgeInsets paddingH15V0 = EdgeInsets.symmetric(horizontal: 15); diff --git a/lib/config/hive_config.dart b/lib/config/hive_config.dart index 03355311..01118bba 100644 --- a/lib/config/hive_config.dart +++ b/lib/config/hive_config.dart @@ -4,23 +4,27 @@ import 'dart:typed_data'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart'; -import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart'; +import 'package:selfprivacy/logic/models/hive/backups_credential.dart'; import 'package:selfprivacy/logic/models/hive/server_details.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/logic/models/hive/user.dart'; +import 'package:selfprivacy/utils/platform_adapter.dart'; class HiveConfig { static Future init() async { - await Hive.initFlutter(); + final String? storagePath = PlatformAdapter.storagePath; + print('HiveConfig: Custom storage path: $storagePath'); + await Hive.initFlutter(storagePath); Hive.registerAdapter(UserAdapter()); Hive.registerAdapter(ServerHostingDetailsAdapter()); Hive.registerAdapter(ServerDomainAdapter()); - Hive.registerAdapter(BackblazeCredentialAdapter()); + Hive.registerAdapter(BackupsCredentialAdapter()); Hive.registerAdapter(BackblazeBucketAdapter()); Hive.registerAdapter(ServerVolumeAdapter()); - - Hive.registerAdapter(DnsProviderAdapter()); - Hive.registerAdapter(ServerProviderAdapter()); + Hive.registerAdapter(UserTypeAdapter()); + Hive.registerAdapter(DnsProviderTypeAdapter()); + Hive.registerAdapter(ServerProviderTypeAdapter()); + Hive.registerAdapter(BackupsProviderTypeAdapter()); await Hive.openBox(BNames.appSettingsBox); @@ -34,8 +38,8 @@ class HiveConfig { final Box deprecatedUsers = Hive.box(BNames.usersDeprecated); if (deprecatedUsers.isNotEmpty) { final Box users = Hive.box(BNames.usersBox); - users.addAll(deprecatedUsers.values.toList()); - deprecatedUsers.clear(); + await users.addAll(deprecatedUsers.values.toList()); + await deprecatedUsers.clear(); } await Hive.openBox(BNames.serverInstallationBox, encryptionCipher: cipher); @@ -62,6 +66,9 @@ class BNames { /// A boolean field of [appSettingsBox] box. static String isDarkModeOn = 'isDarkModeOn'; + /// A boolean field of [appSettingsBox] box. + static String isAutoDarkModeOn = 'isAutoDarkModeOn'; + /// A boolean field of [appSettingsBox] box. static String isOnboardingShowing = 'isOnboardingShowing'; @@ -86,16 +93,28 @@ class BNames { /// A String field of [serverInstallationBox] box. static String hetznerKey = 'hetznerKey'; + /// A String field of [serverInstallationBox] box. + static String serverProvider = 'serverProvider'; + + /// A String field of [serverInstallationBox] box. + static String dnsProvider = 'dnsProvider'; + + /// A String field of [serverLocation] box. + static String serverLocation = 'serverLocation'; + /// A String field of [serverInstallationBox] box. static String cloudFlareKey = 'cloudFlareKey'; + /// A String field of [serverTypeIdentifier] box. + static String serverTypeIdentifier = 'serverTypeIdentifier'; + /// A [User] field of [serverInstallationBox] box. static String rootUser = 'rootUser'; /// A [ServerHostingDetails] field of [serverInstallationBox] box. static String serverDetails = 'hetznerServer'; - /// A [BackblazeCredential] field of [serverInstallationBox] box. + /// A [BackupsCredential] field of [serverInstallationBox] box. static String backblazeCredential = 'backblazeKey'; /// A [BackblazeBucket] field of [serverInstallationBox] box. diff --git a/lib/config/localization.dart b/lib/config/localization.dart index b8356950..fb50b58e 100644 --- a/lib/config/localization.dart +++ b/lib/config/localization.dart @@ -3,16 +3,27 @@ import 'package:flutter/material.dart'; class Localization extends StatelessWidget { const Localization({ - final super.key, + super.key, this.child, }); final Widget? child; @override Widget build(final BuildContext context) => EasyLocalization( - supportedLocales: const [Locale('ru'), Locale('en')], + supportedLocales: const [ + Locale('ru'), + Locale('en'), + Locale('uk'), + Locale('de'), + Locale('fr'), + Locale('es'), + Locale('cs'), + Locale('pl'), + Locale('th'), + ], path: 'assets/translations', fallbackLocale: const Locale('en'), + useFallbackTranslations: true, saveLocale: false, useOnlyLangCode: true, child: child!, diff --git a/lib/config/text_themes.dart b/lib/config/text_themes.dart deleted file mode 100644 index 63b4b99c..00000000 --- a/lib/config/text_themes.dart +++ /dev/null @@ -1,80 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:selfprivacy/utils/named_font_weight.dart'; - -import 'package:selfprivacy/config/brand_colors.dart'; - -const TextStyle defaultTextStyle = TextStyle( - fontSize: 15, - color: BrandColors.textColor1, -); - -final TextStyle headline1Style = defaultTextStyle.copyWith( - fontSize: 40, - fontWeight: NamedFontWeight.extraBold, - color: BrandColors.headlineColor, -); - -final TextStyle headline2Style = defaultTextStyle.copyWith( - fontSize: 24, - fontWeight: NamedFontWeight.extraBold, - color: BrandColors.headlineColor, -); - -final TextStyle onboardingTitle = defaultTextStyle.copyWith( - fontSize: 30, - fontWeight: NamedFontWeight.extraBold, - color: BrandColors.headlineColor, -); - -final TextStyle headline3Style = defaultTextStyle.copyWith( - fontSize: 20, - fontWeight: NamedFontWeight.extraBold, - color: BrandColors.headlineColor, -); - -final TextStyle headline4Style = defaultTextStyle.copyWith( - fontSize: 18, - fontWeight: NamedFontWeight.medium, - color: BrandColors.headlineColor, -); - -final TextStyle headline4UnderlinedStyle = defaultTextStyle.copyWith( - fontSize: 18, - fontWeight: NamedFontWeight.medium, - color: BrandColors.headlineColor, - decoration: TextDecoration.underline, -); - -final TextStyle headline5Style = defaultTextStyle.copyWith( - fontSize: 15, - fontWeight: NamedFontWeight.medium, - color: BrandColors.headlineColor.withOpacity(0.8), -); - -const TextStyle body1Style = defaultTextStyle; -final TextStyle body2Style = defaultTextStyle.copyWith( - color: BrandColors.textColor2, -); - -final TextStyle buttonTitleText = defaultTextStyle.copyWith( - color: BrandColors.white, - fontSize: 16, - fontWeight: FontWeight.bold, - height: 1, -); - -final TextStyle mediumStyle = - defaultTextStyle.copyWith(fontSize: 13, height: 1.53); - -final TextStyle smallStyle = - defaultTextStyle.copyWith(fontSize: 11, height: 1.45); - -const TextStyle progressTextStyleLight = TextStyle( - fontSize: 11, - color: BrandColors.textColor1, - height: 1.7, -); - -final TextStyle progressTextStyleDark = progressTextStyleLight.copyWith( - color: BrandColors.white, -); diff --git a/lib/illustrations/stray_deer.dart b/lib/illustrations/stray_deer.dart new file mode 100644 index 00000000..88fd55c3 --- /dev/null +++ b/lib/illustrations/stray_deer.dart @@ -0,0 +1,2895 @@ +import 'package:dynamic_color/dynamic_color.dart'; +import 'package:flutter/material.dart'; +import 'package:material_color_utilities/palettes/core_palette.dart'; + +// max_width coefficient is 1 +class StrayDeerPainter extends CustomPainter { + StrayDeerPainter({required this.colorPalette, required this.colorScheme}); + + final CorePalette colorPalette; + final ColorScheme colorScheme; + + @override + void paint(final Canvas canvas, final Size size) { + final Color deerTracks = Color(colorPalette.tertiary.get(70)); + final Color mailBag = Color(colorPalette.tertiary.get(80)); + final Color contourColor = Color(colorPalette.tertiary.get(10)); + final Color deerSkin = + const Color(0xffe0ac9c).harmonizeWith(colorScheme.primary); + + print('deerSkin: $deerSkin'); + print('colorScheme.primary: ${colorScheme.primary}'); + print('colorPalette.tertiary.get(10): ${colorPalette.tertiary.get(50)}'); + + final Path path0 = Path(); + path0.moveTo(size.width * 0.6099773, size.height * 0.6719577); + path0.lineTo(size.width * 0.6088435, size.height * 0.6719577); + path0.lineTo(size.width * 0.6148904, size.height * 0.6727135); + path0.lineTo(size.width * 0.6099773, size.height * 0.6719577); + path0.close(); + path0.moveTo(size.width * 0.5593348, size.height * 0.6723356); + path0.cubicTo( + size.width * 0.5525321, + size.height * 0.6723356, + size.width * 0.5461073, + size.height * 0.6738473, + size.width * 0.5400605, + size.height * 0.6761149, + ); + path0.lineTo(size.width * 0.5476190, size.height * 0.6806500); + path0.cubicTo( + size.width * 0.5529101, + size.height * 0.6798942, + size.width * 0.5585790, + size.height * 0.6806500, + size.width * 0.5634921, + size.height * 0.6787604, + ); + path0.cubicTo( + size.width * 0.5653817, + size.height * 0.6780045, + size.width * 0.5680272, + size.height * 0.6764928, + size.width * 0.5680272, + size.height * 0.6746032, + ); + path0.cubicTo( + size.width * 0.5680272, + size.height * 0.6727135, + size.width * 0.5653817, + size.height * 0.6727135, + size.width * 0.5642479, + size.height * 0.6727135, + ); + path0.lineTo(size.width * 0.5593348, size.height * 0.6727135); + path0.close(); + path0.moveTo(size.width * 0.6757370, size.height * 0.6727135); + path0.lineTo(size.width * 0.6700680, size.height * 0.6730915); + path0.cubicTo( + size.width * 0.6681784, + size.height * 0.6734694, + size.width * 0.6655329, + size.height * 0.6730915, + size.width * 0.6636432, + size.height * 0.6746032, + ); + path0.lineTo(size.width * 0.6636432, size.height * 0.6753590); + path0.lineTo(size.width * 0.6723356, size.height * 0.6810280); + path0.lineTo(size.width * 0.6874528, size.height * 0.6825397); + path0.cubicTo( + size.width * 0.6897203, + size.height * 0.6825397, + size.width * 0.6923658, + size.height * 0.6832955, + size.width * 0.6938776, + size.height * 0.6814059, + ); + path0.cubicTo( + size.width * 0.6953893, + size.height * 0.6798942, + size.width * 0.6931217, + size.height * 0.6776266, + size.width * 0.6919879, + size.height * 0.6772487, + ); + path0.arcToPoint( + Offset(size.width * 0.6757370, size.height * 0.6727135), + radius: Radius.elliptical( + size.width * 0.03325775, + size.height * 0.03325775, + ), + rotation: 0, + largeArc: false, + clockwise: false, + ); + path0.close(); + path0.moveTo(size.width * 0.5971277, size.height * 0.6768707); + path0.cubicTo( + size.width * 0.5997732, + size.height * 0.6780045, + size.width * 0.6027967, + size.height * 0.6780045, + size.width * 0.6054422, + size.height * 0.6768707, + ); + path0.close(); + path0.moveTo(size.width * 0.4950869, size.height * 0.6814059); + path0.lineTo(size.width * 0.4924414, size.height * 0.6814059); + path0.cubicTo( + size.width * 0.4909297, + size.height * 0.6814059, + size.width * 0.4886621, + size.height * 0.6814059, + size.width * 0.4871504, + size.height * 0.6825397, + ); + path0.lineTo(size.width * 0.4833711, size.height * 0.6912320); + path0.cubicTo( + size.width * 0.4894180, + size.height * 0.6904762, + size.width * 0.4954649, + size.height * 0.6904762, + size.width * 0.5011338, + size.height * 0.6882086, + ); + path0.cubicTo( + size.width * 0.5030234, + size.height * 0.6874528, + size.width * 0.5049131, + size.height * 0.6866969, + size.width * 0.5064248, + size.height * 0.6851852, + ); + path0.cubicTo( + size.width * 0.5071807, + size.height * 0.6840514, + size.width * 0.5056689, + size.height * 0.6829176, + size.width * 0.5045351, + size.height * 0.6825397, + ); + path0.cubicTo( + size.width * 0.5015117, + size.height * 0.6817838, + size.width * 0.4984883, + size.height * 0.6814059, + size.width * 0.4950869, + size.height * 0.6814059, + ); + path0.close(); + path0.moveTo(size.width * 0.6303855, size.height * 0.6832955); + path0.arcToPoint( + Offset(size.width * 0.6179138, size.height * 0.6863190), + radius: Radius.elliptical( + size.width * 0.03401361, + size.height * 0.03401361, + ), + rotation: 0, + largeArc: false, + clockwise: false, + ); + path0.cubicTo( + size.width * 0.6167800, + size.height * 0.6866969, + size.width * 0.6148904, + size.height * 0.6874528, + size.width * 0.6148904, + size.height * 0.6889645, + ); + path0.cubicTo( + size.width * 0.6148904, + size.height * 0.6904762, + size.width * 0.6167800, + size.height * 0.6904762, + size.width * 0.6175359, + size.height * 0.6900983, + ); + path0.lineTo(size.width * 0.6179138, size.height * 0.6938776); + path0.cubicTo( + size.width * 0.6250945, + size.height * 0.6934996, + size.width * 0.6322751, + size.height * 0.6934996, + size.width * 0.6386999, + size.height * 0.6904762, + ); + path0.cubicTo( + size.width * 0.6398337, + size.height * 0.6900983, + size.width * 0.6417234, + size.height * 0.6889645, + size.width * 0.6413454, + size.height * 0.6874528, + ); + path0.cubicTo( + size.width * 0.6405896, + size.height * 0.6851852, + size.width * 0.6379441, + size.height * 0.6848073, + size.width * 0.6360544, + size.height * 0.6840514, + ); + path0.lineTo(size.width * 0.6303855, size.height * 0.6832955); + path0.close(); + path0.moveTo(size.width * 0.7959184, size.height * 0.6885865); + path0.lineTo(size.width * 0.7932729, size.height * 0.6889645); + path0.lineTo(size.width * 0.7932729, size.height * 0.6893424); + path0.lineTo(size.width * 0.7932729, size.height * 0.6893424); + path0.lineTo(size.width * 0.7951625, size.height * 0.6980348); + path0.arcToPoint( + Offset(size.width * 0.8148148, size.height * 0.7018141), + radius: Radius.elliptical( + size.width * 0.02116402, + size.height * 0.02116402, + ), + rotation: 0, + largeArc: false, + clockwise: false, + ); + path0.cubicTo( + size.width * 0.8163265, + size.height * 0.7010582, + size.width * 0.8178382, + size.height * 0.6999244, + size.width * 0.8170824, + size.height * 0.6980348, + ); + path0.cubicTo( + size.width * 0.8163265, + size.height * 0.6961451, + size.width * 0.8140590, + size.height * 0.6946334, + size.width * 0.8121693, + size.height * 0.6938776, + ); + path0.arcToPoint( + Offset(size.width * 0.7981859, size.height * 0.6885865), + radius: Radius.elliptical( + size.width * 0.03665911, + size.height * 0.03665911, + ), + rotation: 0, + largeArc: false, + clockwise: false, + ); + path0.lineTo(size.width * 0.7959184, size.height * 0.6885865); + path0.close(); + path0.moveTo(size.width * 0.7448980, size.height * 0.6900983); + path0.lineTo(size.width * 0.7430083, size.height * 0.6900983); + path0.cubicTo( + size.width * 0.7407407, + size.height * 0.6904762, + size.width * 0.7384732, + size.height * 0.6916100, + size.width * 0.7369615, + size.height * 0.6938776, + ); + path0.cubicTo( + size.width * 0.7365835, + size.height * 0.6938776, + size.width * 0.7369615, + size.height * 0.6946334, + size.width * 0.7369615, + size.height * 0.6946334, + ); + path0.lineTo(size.width * 0.7369615, size.height * 0.6999244); + path0.cubicTo( + size.width * 0.7418745, + size.height * 0.7006803, + size.width * 0.7467876, + size.height * 0.7014361, + size.width * 0.7520786, + size.height * 0.7006803, + ); + path0.cubicTo( + size.width * 0.7543462, + size.height * 0.7006803, + size.width * 0.7566138, + size.height * 0.6999244, + size.width * 0.7585034, + size.height * 0.6980348, + ); + path0.cubicTo( + size.width * 0.7600151, + size.height * 0.6969010, + size.width * 0.7585034, + size.height * 0.6953893, + size.width * 0.7573696, + size.height * 0.6942555, + ); + path0.cubicTo( + size.width * 0.7535903, + size.height * 0.6919879, + size.width * 0.7494331, + size.height * 0.6897203, + size.width * 0.7448980, + size.height * 0.6900983, + ); + path0.close(); + path0.moveTo(size.width * 0.4357521, size.height * 0.6961451); + path0.lineTo(size.width * 0.4349962, size.height * 0.7052154); + path0.cubicTo( + size.width * 0.4399093, + size.height * 0.7055933, + size.width * 0.4448224, + size.height * 0.7040816, + size.width * 0.4489796, + size.height * 0.7014361, + ); + path0.cubicTo( + size.width * 0.4504913, + size.height * 0.7010582, + size.width * 0.4523810, + size.height * 0.7003023, + size.width * 0.4531368, + size.height * 0.6991686, + ); + path0.cubicTo( + size.width * 0.4538927, + size.height * 0.6980348, + size.width * 0.4527589, + size.height * 0.6972789, + size.width * 0.4520030, + size.height * 0.6972789, + ); + path0.cubicTo( + size.width * 0.4489796, + size.height * 0.6961451, + size.width * 0.4459562, + size.height * 0.6972789, + size.width * 0.4433107, + size.height * 0.6965231, + ); + path0.lineTo(size.width * 0.4357521, size.height * 0.6961451); + path0.close(); + path0.moveTo(size.width * 0.3408919, size.height * 0.6999244); + path0.arcToPoint( + Offset(size.width * 0.3250189, size.height * 0.7052154), + radius: Radius.elliptical( + size.width * 0.03401361, + size.height * 0.03401361, + ), + rotation: 0, + largeArc: false, + clockwise: false, + ); + path0.lineTo(size.width * 0.3208617, size.height * 0.7108844); + path0.cubicTo( + size.width * 0.3465608, + size.height * 0.7146636, + size.width * 0.3522298, + size.height * 0.6999244, + size.width * 0.3408919, + size.height * 0.6999244, + ); + path0.close(); + path0.moveTo(size.width * 0.8435374, size.height * 0.7195767); + path0.cubicTo( + size.width * 0.8420257, + size.height * 0.7195767, + size.width * 0.8405140, + size.height * 0.7199546, + size.width * 0.8397581, + size.height * 0.7210884, + ); + path0.lineTo(size.width * 0.8344671, size.height * 0.7248677); + path0.cubicTo( + size.width * 0.8359788, + size.height * 0.7271353, + size.width * 0.8374906, + size.height * 0.7297808, + size.width * 0.8397581, + size.height * 0.7312925, + ); + path0.cubicTo( + size.width * 0.8416478, + size.height * 0.7328042, + size.width * 0.8446712, + size.height * 0.7324263, + size.width * 0.8473167, + size.height * 0.7324263, + ); + path0.cubicTo( + size.width * 0.8488284, + size.height * 0.7324263, + size.width * 0.8510960, + size.height * 0.7324263, + size.width * 0.8526077, + size.height * 0.7309146, + ); + path0.cubicTo( + size.width * 0.8541194, + size.height * 0.7286470, + size.width * 0.8533636, + size.height * 0.7260015, + size.width * 0.8522298, + size.height * 0.7241119, + ); + path0.arcToPoint( + Offset(size.width * 0.8431595, size.height * 0.7195767), + radius: Radius.elliptical( + size.width * 0.01020408, + size.height * 0.01020408, + ), + rotation: 0, + largeArc: false, + clockwise: false, + ); + path0.close(); + path0.moveTo(size.width * 0.2883598, size.height * 0.7237339); + path0.cubicTo( + size.width * 0.2853364, + size.height * 0.7237339, + size.width * 0.2808012, + size.height * 0.7244898, + size.width * 0.2743764, + size.height * 0.7256236, + ); + path0.lineTo(size.width * 0.2660620, size.height * 0.7343159); + path0.cubicTo( + size.width * 0.2834467, + size.height * 0.7331822, + size.width * 0.3012094, + size.height * 0.7237339, + size.width * 0.2883598, + size.height * 0.7237339, + ); + path0.close(); + path0.moveTo(size.width * 0.8907785, size.height * 0.7241119); + path0.lineTo(size.width * 0.8892668, size.height * 0.7241119); + path0.lineTo(size.width * 0.8869992, size.height * 0.7278912); + path0.cubicTo( + size.width * 0.8881330, + size.height * 0.7312925, + size.width * 0.8904006, + size.height * 0.7331822, + size.width * 0.8926682, + size.height * 0.7350718, + ); + path0.cubicTo( + size.width * 0.8945578, + size.height * 0.7369615, + size.width * 0.8964475, + size.height * 0.7388511, + size.width * 0.8990930, + size.height * 0.7403628, + ); + path0.cubicTo( + size.width * 0.9006047, + size.height * 0.7411187, + size.width * 0.9028723, + size.height * 0.7422525, + size.width * 0.9043840, + size.height * 0.7418745, + ); + path0.cubicTo( + size.width * 0.9058957, + size.height * 0.7411187, + size.width * 0.9058957, + size.height * 0.7392290, + size.width * 0.9062736, + size.height * 0.7380952, + ); + path0.cubicTo( + size.width * 0.9062736, + size.height * 0.7339380, + size.width * 0.9047619, + size.height * 0.7294029, + size.width * 0.9009826, + size.height * 0.7271353, + ); + path0.cubicTo( + size.width * 0.8987150, + size.height * 0.7256236, + size.width * 0.8956916, + size.height * 0.7248677, + size.width * 0.8926682, + size.height * 0.7244898, + ); + path0.lineTo(size.width * 0.8907785, size.height * 0.7244898); + path0.close(); + path0.moveTo(size.width * 0.2078609, size.height * 0.7373394); + path0.cubicTo( + size.width * 0.2029478, + size.height * 0.7388511, + size.width * 0.1976568, + size.height * 0.7388511, + size.width * 0.1931217, + size.height * 0.7414966, + ); + path0.cubicTo( + size.width * 0.1908541, + size.height * 0.7430083, + size.width * 0.1893424, + size.height * 0.7464097, + size.width * 0.1912320, + size.height * 0.7486772, + ); + path0.cubicTo( + size.width * 0.1931217, + size.height * 0.7509448, + size.width * 0.1965231, + size.height * 0.7513228, + size.width * 0.1991686, + size.height * 0.7517007, + ); + path0.cubicTo( + size.width * 0.2048375, + size.height * 0.7517007, + size.width * 0.2105064, + size.height * 0.7517007, + size.width * 0.2154195, + size.height * 0.7490552, + ); + path0.arcToPoint( + Offset(size.width * 0.2214664, size.height * 0.7452759), + radius: Radius.elliptical( + size.width * 0.02645503, + size.height * 0.02645503, + ), + rotation: 0, + largeArc: false, + clockwise: false, + ); + path0.cubicTo( + size.width * 0.2226002, + size.height * 0.7445200, + size.width * 0.2237339, + size.height * 0.7422525, + size.width * 0.2222222, + size.height * 0.7407407, + ); + path0.cubicTo( + size.width * 0.2203326, + size.height * 0.7388511, + size.width * 0.2173091, + size.height * 0.7380952, + size.width * 0.2146636, + size.height * 0.7384732, + ); + path0.cubicTo( + size.width * 0.2135299, + size.height * 0.7384732, + size.width * 0.2123961, + size.height * 0.7392290, + size.width * 0.2116402, + size.height * 0.7399849, + ); + path0.close(); + path0.moveTo(size.width * 0.1583522, size.height * 0.7392290); + path0.cubicTo( + size.width * 0.1572184, + size.height * 0.7392290, + size.width * 0.1564626, + size.height * 0.7392290, + size.width * 0.1553288, + size.height * 0.7399849, + ); + path0.cubicTo( + size.width * 0.1545729, + size.height * 0.7403628, + size.width * 0.1541950, + size.height * 0.7411187, + size.width * 0.1541950, + size.height * 0.7422525, + ); + path0.lineTo(size.width * 0.1466364, size.height * 0.7422525); + path0.cubicTo( + size.width * 0.1436130, + size.height * 0.7441421, + size.width * 0.1405896, + size.height * 0.7460317, + size.width * 0.1379441, + size.height * 0.7490552, + ); + path0.cubicTo( + size.width * 0.1364324, + size.height * 0.7505669, + size.width * 0.1341648, + size.height * 0.7528345, + size.width * 0.1341648, + size.height * 0.7551020, + ); + path0.cubicTo( + size.width * 0.1349206, + size.height * 0.7573696, + size.width * 0.1371882, + size.height * 0.7577475, + size.width * 0.1390779, + size.height * 0.7577475, + ); + path0.cubicTo( + size.width * 0.1451247, + size.height * 0.7577475, + size.width * 0.1504157, + size.height * 0.7554800, + size.width * 0.1557067, + size.height * 0.7520786, + ); + path0.cubicTo( + size.width * 0.1579743, + size.height * 0.7505669, + size.width * 0.1594860, + size.height * 0.7482993, + size.width * 0.1617536, + size.height * 0.7464097, + ); + path0.cubicTo( + size.width * 0.1628874, + size.height * 0.7448980, + size.width * 0.1647770, + size.height * 0.7426304, + size.width * 0.1628874, + size.height * 0.7407407, + ); + path0.arcToPoint( + Offset(size.width * 0.1583522, size.height * 0.7392290), + radius: Radius.elliptical( + size.width * 0.007558579, + size.height * 0.007558579, + ), + rotation: 0, + largeArc: false, + clockwise: false, + ); + path0.close(); + path0.moveTo(size.width * 0.8764172, size.height * 0.7505669); + path0.lineTo(size.width * 0.8726379, size.height * 0.7532124); + path0.cubicTo( + size.width * 0.8737717, + size.height * 0.7569917, + size.width * 0.8733938, + size.height * 0.7615268, + size.width * 0.8756614, + size.height * 0.7645503, + ); + path0.lineTo(size.width * 0.8794407, size.height * 0.7694633); + path0.cubicTo( + size.width * 0.8809524, + size.height * 0.7705971, + size.width * 0.8832200, + size.height * 0.7717309, + size.width * 0.8851096, + size.height * 0.7705971, + ); + path0.cubicTo( + size.width * 0.8862434, + size.height * 0.7694633, + size.width * 0.8866213, + size.height * 0.7675737, + size.width * 0.8862434, + size.height * 0.7660620, + ); + path0.cubicTo( + size.width * 0.8862434, + size.height * 0.7630385, + size.width * 0.8847317, + size.height * 0.7600151, + size.width * 0.8832200, + size.height * 0.7573696, + ); + path0.cubicTo( + size.width * 0.8824641, + size.height * 0.7547241, + size.width * 0.8809524, + size.height * 0.7520786, + size.width * 0.8783069, + size.height * 0.7513228, + ); + path0.arcToPoint( + Offset(size.width * 0.8764172, size.height * 0.7509448), + radius: Radius.elliptical( + size.width * 0.007558579, + size.height * 0.007558579, + ), + rotation: 0, + largeArc: false, + clockwise: false, + ); + path0.close(); + path0.moveTo(size.width * 0.1190476, size.height * 0.7709751); + path0.cubicTo( + size.width * 0.1190476, + size.height * 0.7709751, + size.width * 0.1190476, + size.height * 0.7709751, + size.width * 0.1190476, + size.height * 0.7709751, + ); + path0.cubicTo( + size.width * 0.1171580, + size.height * 0.7709751, + size.width * 0.1156463, + size.height * 0.7713530, + size.width * 0.1145125, + size.height * 0.7721088, + ); + path0.lineTo(size.width * 0.1141345, size.height * 0.7728647); + path0.lineTo(size.width * 0.1111111, size.height * 0.7739985); + path0.cubicTo( + size.width * 0.1092215, + size.height * 0.7762661, + size.width * 0.1073318, + size.height * 0.7785336, + size.width * 0.1065760, + size.height * 0.7815571, + ); + path0.cubicTo( + size.width * 0.1061980, + size.height * 0.7834467, + size.width * 0.1065760, + size.height * 0.7853364, + size.width * 0.1080877, + size.height * 0.7860922, + ); + path0.cubicTo( + size.width * 0.1095994, + size.height * 0.7868481, + size.width * 0.1114890, + size.height * 0.7860922, + size.width * 0.1130008, + size.height * 0.7853364, + ); + path0.cubicTo( + size.width * 0.1156463, + size.height * 0.7834467, + size.width * 0.1182918, + size.height * 0.7815571, + size.width * 0.1205593, + size.height * 0.7785336, + ); + path0.cubicTo( + size.width * 0.1213152, + size.height * 0.7766440, + size.width * 0.1220711, + size.height * 0.7747543, + size.width * 0.1216931, + size.height * 0.7728647, + ); + path0.cubicTo( + size.width * 0.1216931, + size.height * 0.7713530, + size.width * 0.1201814, + size.height * 0.7709751, + size.width * 0.1190476, + size.height * 0.7709751, + ); + path0.close(); + path0.moveTo(size.width * 0.9213908, size.height * 0.7739985); + path0.lineTo(size.width * 0.9172336, size.height * 0.7758881); + path0.cubicTo( + size.width * 0.9142101, + size.height * 0.7808012, + size.width * 0.9126984, + size.height * 0.7868481, + size.width * 0.9123205, + size.height * 0.7925170, + ); + path0.cubicTo( + size.width * 0.9123205, + size.height * 0.7940287, + size.width * 0.9111867, + size.height * 0.7962963, + size.width * 0.9123205, + size.height * 0.7974301, + ); + path0.cubicTo( + size.width * 0.9138322, + size.height * 0.7981859, + size.width * 0.9157218, + size.height * 0.7974301, + size.width * 0.9168556, + size.height * 0.7970522, + ); + path0.arcToPoint( + Offset(size.width * 0.9263039, size.height * 0.7845805), + radius: Radius.elliptical( + size.width * 0.01511716, + size.height * 0.01511716, + ), + rotation: 0, + largeArc: false, + clockwise: false, + ); + path0.cubicTo( + size.width * 0.9263039, + size.height * 0.7808012, + size.width * 0.9259259, + size.height * 0.7770219, + size.width * 0.9229025, + size.height * 0.7743764, + ); + path0.arcToPoint( + Offset(size.width * 0.9213908, size.height * 0.7743764), + radius: Radius.elliptical( + size.width * 0.002267574, + size.height * 0.002267574, + ), + rotation: 0, + largeArc: false, + clockwise: false, + ); + path0.close(); + path0.moveTo(size.width * 0.1511716, size.height * 0.7902494); + path0.lineTo(size.width * 0.1451247, size.height * 0.8027211); + path0.cubicTo( + size.width * 0.1466364, + size.height * 0.8061224, + size.width * 0.1485261, + size.height * 0.8099017, + size.width * 0.1519274, + size.height * 0.8117914, + ); + path0.cubicTo( + size.width * 0.1553288, + size.height * 0.8133031, + size.width * 0.1594860, + size.height * 0.8140590, + size.width * 0.1625094, + size.height * 0.8121693, + ); + path0.cubicTo( + size.width * 0.1643991, + size.height * 0.8114135, + size.width * 0.1651550, + size.height * 0.8091459, + size.width * 0.1651550, + size.height * 0.8072562, + ); + path0.cubicTo( + size.width * 0.1643991, + size.height * 0.8015873, + size.width * 0.1625094, + size.height * 0.7959184, + size.width * 0.1575964, + size.height * 0.7928949, + ); + path0.arcToPoint( + Offset(size.width * 0.1511716, size.height * 0.7902494), + radius: Radius.elliptical( + size.width * 0.01511716, + size.height * 0.01511716, + ), + rotation: 0, + largeArc: false, + clockwise: false, + ); + path0.close(); + path0.moveTo(size.width * 0.8805745, size.height * 0.8008314); + path0.lineTo(size.width * 0.8779289, size.height * 0.8012094); + path0.arcToPoint( + Offset(size.width * 0.8643235, size.height * 0.8087680), + radius: Radius.elliptical( + size.width * 0.03665911, + size.height * 0.03665911, + ), + rotation: 0, + largeArc: false, + clockwise: false, + ); + path0.cubicTo( + size.width * 0.8635676, + size.height * 0.8099017, + size.width * 0.8643235, + size.height * 0.8110355, + size.width * 0.8650794, + size.height * 0.8110355, + ); + path0.cubicTo( + size.width * 0.8673469, + size.height * 0.8114135, + size.width * 0.8699924, + size.height * 0.8117914, + size.width * 0.8722600, + size.height * 0.8110355, + ); + path0.cubicTo( + size.width * 0.8752834, + size.height * 0.8102797, + size.width * 0.8779289, + size.height * 0.8087680, + size.width * 0.8798186, + size.height * 0.8065004, + ); + path0.cubicTo( + size.width * 0.8809524, + size.height * 0.8053666, + size.width * 0.8817082, + size.height * 0.8042328, + size.width * 0.8813303, + size.height * 0.8027211, + ); + path0.cubicTo( + size.width * 0.8813303, + size.height * 0.8019652, + size.width * 0.8805745, + size.height * 0.8012094, + size.width * 0.8809524, + size.height * 0.8008314, + ); + path0.close(); + path0.moveTo(size.width * 0.8030990, size.height * 0.8216175); + path0.lineTo(size.width * 0.7985639, size.height * 0.8227513); + path0.arcToPoint( + Offset(size.width * 0.7910053, size.height * 0.8276644), + radius: Radius.elliptical( + size.width * 0.02267574, + size.height * 0.02267574, + ), + rotation: 0, + largeArc: false, + clockwise: false, + ); + path0.cubicTo( + size.width * 0.7902494, + size.height * 0.8287982, + size.width * 0.7910053, + size.height * 0.8303099, + size.width * 0.7921391, + size.height * 0.8306878, + ); + path0.cubicTo( + size.width * 0.7947846, + size.height * 0.8321995, + size.width * 0.7974301, + size.height * 0.8321995, + size.width * 0.8004535, + size.height * 0.8318216, + ); + path0.cubicTo( + size.width * 0.8046107, + size.height * 0.8318216, + size.width * 0.8087680, + size.height * 0.8303099, + size.width * 0.8129252, + size.height * 0.8295540, + ); + path0.cubicTo( + size.width * 0.8140590, + size.height * 0.8291761, + size.width * 0.8155707, + size.height * 0.8280423, + size.width * 0.8136810, + size.height * 0.8272865, + ); + path0.lineTo(size.width * 0.8046107, size.height * 0.8216175); + path0.lineTo(size.width * 0.8030990, size.height * 0.8216175); + path0.close(); + path0.moveTo(size.width * 0.8499622, size.height * 0.8280423); + path0.cubicTo( + size.width * 0.8473167, + size.height * 0.8284203, + size.width * 0.8450491, + size.height * 0.8295540, + size.width * 0.8427816, + size.height * 0.8310658, + ); + path0.cubicTo( + size.width * 0.8405140, + size.height * 0.8321995, + size.width * 0.8382464, + size.height * 0.8340892, + size.width * 0.8374906, + size.height * 0.8363568, + ); + path0.cubicTo( + size.width * 0.8367347, + size.height * 0.8374906, + size.width * 0.8374906, + size.height * 0.8393802, + size.width * 0.8386243, + size.height * 0.8397581, + ); + path0.cubicTo( + size.width * 0.8408919, + size.height * 0.8401361, + size.width * 0.8435374, + size.height * 0.8397581, + size.width * 0.8458050, + size.height * 0.8393802, + ); + path0.cubicTo( + size.width * 0.8480726, + size.height * 0.8386243, + size.width * 0.8507181, + size.height * 0.8382464, + size.width * 0.8526077, + size.height * 0.8367347, + ); + path0.cubicTo( + size.width * 0.8544974, + size.height * 0.8356009, + size.width * 0.8563870, + size.height * 0.8340892, + size.width * 0.8575208, + size.height * 0.8321995, + ); + path0.cubicTo( + size.width * 0.8582766, + size.height * 0.8306878, + size.width * 0.8563870, + size.height * 0.8303099, + size.width * 0.8552532, + size.height * 0.8299320, + ); + path0.lineTo(size.width * 0.8533636, size.height * 0.8299320); + path0.close(); + path0.moveTo(size.width * 0.1375661, size.height * 0.8291761); + path0.cubicTo( + size.width * 0.1364324, + size.height * 0.8291761, + size.width * 0.1356765, + size.height * 0.8291761, + size.width * 0.1345427, + size.height * 0.8299320, + ); + path0.cubicTo( + size.width * 0.1341648, + size.height * 0.8299320, + size.width * 0.1334089, + size.height * 0.8306878, + size.width * 0.1334089, + size.height * 0.8314437, + ); + path0.lineTo(size.width * 0.1303855, size.height * 0.8352230); + path0.cubicTo( + size.width * 0.1296296, + size.height * 0.8374906, + size.width * 0.1315193, + size.height * 0.8397581, + size.width * 0.1334089, + size.height * 0.8412698, + ); + path0.lineTo(size.width * 0.1379441, size.height * 0.8450491); + path0.arcToPoint( + Offset(size.width * 0.1455026, size.height * 0.8480726), + radius: Radius.elliptical( + size.width * 0.01133787, + size.height * 0.01133787, + ), + rotation: 0, + largeArc: false, + clockwise: false, + ); + path0.cubicTo( + size.width * 0.1481481, + size.height * 0.8480726, + size.width * 0.1511716, + size.height * 0.8469388, + size.width * 0.1530612, + size.height * 0.8450491, + ); + path0.cubicTo( + size.width * 0.1541950, + size.height * 0.8431595, + size.width * 0.1530612, + size.height * 0.8412698, + size.width * 0.1523054, + size.height * 0.8393802, + ); + path0.cubicTo( + size.width * 0.1496599, + size.height * 0.8356009, + size.width * 0.1458806, + size.height * 0.8329554, + size.width * 0.1421013, + size.height * 0.8303099, + ); + path0.cubicTo( + size.width * 0.1409675, + size.height * 0.8295540, + size.width * 0.1390779, + size.height * 0.8291761, + size.width * 0.1375661, + size.height * 0.8291761, + ); + path0.close(); + path0.moveTo(size.width * 0.6749811, size.height * 0.8363568); + path0.lineTo(size.width * 0.6742252, size.height * 0.8363568); + path0.cubicTo( + size.width * 0.6708239, + size.height * 0.8363568, + size.width * 0.6674225, + size.height * 0.8363568, + size.width * 0.6643991, + size.height * 0.8378685, + ); + path0.lineTo(size.width * 0.6598639, size.height * 0.8405140); + path0.cubicTo( + size.width * 0.6606198, + size.height * 0.8416478, + size.width * 0.6621315, + size.height * 0.8424036, + size.width * 0.6636432, + size.height * 0.8427816, + ); + path0.lineTo(size.width * 0.6712018, size.height * 0.8446712); + path0.cubicTo( + size.width * 0.6749811, + size.height * 0.8458050, + size.width * 0.6798942, + size.height * 0.8461829, + size.width * 0.6836735, + size.height * 0.8442933, + ); + path0.cubicTo( + size.width * 0.6851852, + size.height * 0.8431595, + size.width * 0.6870748, + size.height * 0.8420257, + size.width * 0.6866969, + size.height * 0.8405140, + ); + path0.cubicTo( + size.width * 0.6866969, + size.height * 0.8386243, + size.width * 0.6848073, + size.height * 0.8378685, + size.width * 0.6829176, + size.height * 0.8374906, + ); + path0.cubicTo( + size.width * 0.6806500, + size.height * 0.8367347, + size.width * 0.6776266, + size.height * 0.8363568, + size.width * 0.6749811, + size.height * 0.8363568, + ); + path0.close(); + path0.moveTo(size.width * 0.2003023, size.height * 0.8401361); + path0.cubicTo( + size.width * 0.1984127, + size.height * 0.8401361, + size.width * 0.1965231, + size.height * 0.8401361, + size.width * 0.1946334, + size.height * 0.8412698, + ); + path0.cubicTo( + size.width * 0.1938776, + size.height * 0.8416478, + size.width * 0.1916100, + size.height * 0.8424036, + size.width * 0.1931217, + size.height * 0.8431595, + ); + path0.lineTo(size.width * 0.1931217, size.height * 0.8442933); + path0.cubicTo( + size.width * 0.1931217, + size.height * 0.8465608, + size.width * 0.1953893, + size.height * 0.8480726, + size.width * 0.1969010, + size.height * 0.8488284, + ); + path0.cubicTo( + size.width * 0.2006803, + size.height * 0.8503401, + size.width * 0.2044596, + size.height * 0.8510960, + size.width * 0.2082389, + size.height * 0.8507181, + ); + path0.cubicTo( + size.width * 0.2101285, + size.height * 0.8503401, + size.width * 0.2120181, + size.height * 0.8495843, + size.width * 0.2127740, + size.height * 0.8480726, + ); + path0.cubicTo( + size.width * 0.2135299, + size.height * 0.8458050, + size.width * 0.2123961, + size.height * 0.8439153, + size.width * 0.2105064, + size.height * 0.8427816, + ); + path0.cubicTo( + size.width * 0.2074830, + size.height * 0.8408919, + size.width * 0.2040816, + size.height * 0.8401361, + size.width * 0.2003023, + size.height * 0.8401361, + ); + path0.close(); + path0.moveTo(size.width * 0.5816327, size.height * 0.8439153); + path0.cubicTo( + size.width * 0.5782313, + size.height * 0.8439153, + size.width * 0.5752079, + size.height * 0.8446712, + size.width * 0.5721844, + size.height * 0.8450491, + ); + path0.cubicTo( + size.width * 0.5691610, + size.height * 0.8458050, + size.width * 0.5661376, + size.height * 0.8465608, + size.width * 0.5646259, + size.height * 0.8495843, + ); + path0.lineTo(size.width * 0.5661376, size.height * 0.8507181); + path0.lineTo(size.width * 0.5714286, size.height * 0.8522298); + path0.cubicTo( + size.width * 0.5752079, + size.height * 0.8533636, + size.width * 0.5789872, + size.height * 0.8537415, + size.width * 0.5827664, + size.height * 0.8533636, + ); + path0.arcToPoint( + Offset(size.width * 0.5903250, size.height * 0.8514739), + radius: Radius.elliptical( + size.width * 0.02267574, + size.height * 0.02267574, + ), + rotation: 0, + largeArc: false, + clockwise: false, + ); + path0.cubicTo( + size.width * 0.5918367, + size.height * 0.8507181, + size.width * 0.5933485, + size.height * 0.8492063, + size.width * 0.5922147, + size.height * 0.8476946, + ); + path0.cubicTo( + size.width * 0.5910809, + size.height * 0.8461829, + size.width * 0.5891912, + size.height * 0.8454271, + size.width * 0.5873016, + size.height * 0.8446712, + ); + path0.cubicTo( + size.width * 0.5854119, + size.height * 0.8439153, + size.width * 0.5835223, + size.height * 0.8439153, + size.width * 0.5816327, + size.height * 0.8439153, + ); + path0.close(); + path0.moveTo(size.width * 0.7403628, size.height * 0.8492063); + path0.cubicTo( + size.width * 0.7384732, + size.height * 0.8492063, + size.width * 0.7365835, + size.height * 0.8499622, + size.width * 0.7346939, + size.height * 0.8507181, + ); + path0.cubicTo( + size.width * 0.7324263, + size.height * 0.8510960, + size.width * 0.7309146, + size.height * 0.8544974, + size.width * 0.7328042, + size.height * 0.8567649, + ); + path0.cubicTo( + size.width * 0.7339380, + size.height * 0.8578987, + size.width * 0.7358277, + size.height * 0.8578987, + size.width * 0.7373394, + size.height * 0.8582766, + ); + path0.cubicTo( + size.width * 0.7426304, + size.height * 0.8590325, + size.width * 0.7482993, + size.height * 0.8594104, + size.width * 0.7524565, + size.height * 0.8571429, + ); + path0.cubicTo( + size.width * 0.7547241, + size.height * 0.8563870, + size.width * 0.7562358, + size.height * 0.8552532, + size.width * 0.7577475, + size.height * 0.8533636, + ); + path0.cubicTo( + size.width * 0.7585034, + size.height * 0.8518519, + size.width * 0.7566138, + size.height * 0.8510960, + size.width * 0.7554800, + size.height * 0.8507181, + ); + path0.lineTo(size.width * 0.7543462, size.height * 0.8507181); + path0.lineTo(size.width * 0.7418745, size.height * 0.8492063); + path0.lineTo(size.width * 0.7403628, size.height * 0.8492063); + path0.close(); + path0.moveTo(size.width * 0.3287982, size.height * 0.8552532); + path0.lineTo(size.width * 0.3208617, size.height * 0.8560091); + path0.cubicTo( + size.width * 0.3159486, + size.height * 0.8567649, + size.width * 0.3117914, + size.height * 0.8586546, + size.width * 0.3087680, + size.height * 0.8620559, + ); + path0.lineTo(size.width * 0.3087680, size.height * 0.8665911); + path0.lineTo(size.width * 0.3140590, size.height * 0.8665911); + path0.cubicTo( + size.width * 0.3208617, + size.height * 0.8650794, + size.width * 0.3276644, + size.height * 0.8647014, + size.width * 0.3340892, + size.height * 0.8624339, + ); + path0.cubicTo( + size.width * 0.3356009, + size.height * 0.8616780, + size.width * 0.3378685, + size.height * 0.8609221, + size.width * 0.3378685, + size.height * 0.8590325, + ); + path0.cubicTo( + size.width * 0.3382464, + size.height * 0.8571429, + size.width * 0.3359788, + size.height * 0.8560091, + size.width * 0.3340892, + size.height * 0.8556311, + ); + path0.lineTo(size.width * 0.3287982, size.height * 0.8556311); + path0.close(); + path0.moveTo(size.width * 0.3083900, size.height * 0.8665911); + path0.lineTo(size.width * 0.3083900, size.height * 0.8665911); + path0.close(); + path0.moveTo(size.width * 0.4546485, size.height * 0.8601663); + path0.cubicTo( + size.width * 0.4527589, + size.height * 0.8601663, + size.width * 0.4508692, + size.height * 0.8601663, + size.width * 0.4489796, + size.height * 0.8609221, + ); + path0.cubicTo( + size.width * 0.4463341, + size.height * 0.8616780, + size.width * 0.4436886, + size.height * 0.8631897, + size.width * 0.4433107, + size.height * 0.8658352, + ); + path0.lineTo(size.width * 0.4444444, size.height * 0.8684807); + path0.lineTo(size.width * 0.4493575, size.height * 0.8688587); + path0.cubicTo( + size.width * 0.4542706, + size.height * 0.8696145, + size.width * 0.4591837, + size.height * 0.8696145, + size.width * 0.4640967, + size.height * 0.8688587, + ); + path0.cubicTo( + size.width * 0.4659864, + size.height * 0.8688587, + size.width * 0.4678760, + size.height * 0.8681028, + size.width * 0.4693878, + size.height * 0.8662132, + ); + path0.cubicTo( + size.width * 0.4701436, + size.height * 0.8650794, + size.width * 0.4708995, + size.height * 0.8631897, + size.width * 0.4693878, + size.height * 0.8624339, + ); + path0.cubicTo( + size.width * 0.4671202, + size.height * 0.8613001, + size.width * 0.4644747, + size.height * 0.8613001, + size.width * 0.4618292, + size.height * 0.8609221, + ); + path0.lineTo(size.width * 0.4542706, size.height * 0.8601663); + path0.close(); + path0.moveTo(size.width * 0.2301587, size.height * 0.8654573); + path0.lineTo(size.width * 0.2290249, size.height * 0.8654573); + path0.cubicTo( + size.width * 0.2260015, + size.height * 0.8654573, + size.width * 0.2233560, + size.height * 0.8665911, + size.width * 0.2207105, + size.height * 0.8677249, + ); + path0.cubicTo( + size.width * 0.2199546, + size.height * 0.8677249, + size.width * 0.2191988, + size.height * 0.8681028, + size.width * 0.2207105, + size.height * 0.8677249, + ); + path0.lineTo(size.width * 0.2169312, size.height * 0.8722600); + path0.cubicTo( + size.width * 0.2184429, + size.height * 0.8749055, + size.width * 0.2214664, + size.height * 0.8760393, + size.width * 0.2241119, + size.height * 0.8760393, + ); + path0.cubicTo( + size.width * 0.2297808, + size.height * 0.8779289, + size.width * 0.2358277, + size.height * 0.8786848, + size.width * 0.2414966, + size.height * 0.8771731, + ); + path0.cubicTo( + size.width * 0.2437642, + size.height * 0.8767952, + size.width * 0.2460317, + size.height * 0.8764172, + size.width * 0.2467876, + size.height * 0.8745276, + ); + path0.cubicTo( + size.width * 0.2479214, + size.height * 0.8726379, + size.width * 0.2460317, + size.height * 0.8711262, + size.width * 0.2448980, + size.height * 0.8699924, + ); + path0.cubicTo( + size.width * 0.2411187, + size.height * 0.8662132, + size.width * 0.2354497, + size.height * 0.8654573, + size.width * 0.2297808, + size.height * 0.8654573, + ); + path0.close(); + path0.moveTo(size.width * 0.6326531, size.height * 0.8730159); + path0.lineTo(size.width * 0.6239607, size.height * 0.8741497); + path0.cubicTo( + size.width * 0.6201814, + size.height * 0.8749055, + size.width * 0.6160242, + size.height * 0.8760393, + size.width * 0.6126228, + size.height * 0.8790627, + ); + path0.lineTo(size.width * 0.6137566, size.height * 0.8828420); + path0.lineTo(size.width * 0.6190476, size.height * 0.8839758); + path0.lineTo(size.width * 0.6292517, size.height * 0.8843537); + path0.cubicTo( + size.width * 0.6337868, + size.height * 0.8843537, + size.width * 0.6386999, + size.height * 0.8839758, + size.width * 0.6417234, + size.height * 0.8805745, + ); + path0.cubicTo( + size.width * 0.6428571, + size.height * 0.8798186, + size.width * 0.6439909, + size.height * 0.8783069, + size.width * 0.6436130, + size.height * 0.8767952, + ); + path0.cubicTo( + size.width * 0.6428571, + size.height * 0.8749055, + size.width * 0.6402116, + size.height * 0.8749055, + size.width * 0.6386999, + size.height * 0.8741497, + ); + path0.cubicTo( + size.width * 0.6368103, + size.height * 0.8733938, + size.width * 0.6349206, + size.height * 0.8733938, + size.width * 0.6326531, + size.height * 0.8733938, + ); + path0.close(); + path0.moveTo(size.width * 0.6137566, size.height * 0.8832200); + path0.cubicTo( + size.width * 0.6137566, + size.height * 0.8832200, + size.width * 0.6137566, + size.height * 0.8832200, + size.width * 0.6137566, + size.height * 0.8832200, + ); + path0.lineTo(size.width * 0.6137566, size.height * 0.8832200); + path0.close(); + path0.moveTo(size.width * 0.6791383, size.height * 0.8783069); + path0.lineTo(size.width * 0.6798942, size.height * 0.8794407); + path0.lineTo(size.width * 0.6791383, size.height * 0.8783069); + path0.close(); + path0.moveTo(size.width * 0.6798942, size.height * 0.8794407); + path0.cubicTo( + size.width * 0.6814059, + size.height * 0.8813303, + size.width * 0.6825397, + size.height * 0.8835979, + size.width * 0.6844293, + size.height * 0.8851096, + ); + path0.lineTo(size.width * 0.6851852, size.height * 0.8858655); + path0.close(); + path0.moveTo(size.width * 0.6689342, size.height * 0.8801965); + path0.lineTo(size.width * 0.6746032, size.height * 0.8858655); + path0.lineTo(size.width * 0.6689342, size.height * 0.8801965); + path0.close(); + path0.moveTo(size.width * 0.3556311, size.height * 0.8801965); + path0.cubicTo( + size.width * 0.3529856, + size.height * 0.8801965, + size.width * 0.3503401, + size.height * 0.8805745, + size.width * 0.3480726, + size.height * 0.8820862, + ); + path0.cubicTo( + size.width * 0.3473167, + size.height * 0.8828420, + size.width * 0.3461829, + size.height * 0.8835979, + size.width * 0.3458050, + size.height * 0.8847317, + ); + path0.lineTo(size.width * 0.3427816, size.height * 0.8862434); + path0.arcToPoint( + Offset(size.width * 0.3616780, size.height * 0.8938020), + radius: Radius.elliptical( + size.width * 0.03590325, + size.height * 0.03590325, + ), + rotation: 0, + largeArc: false, + clockwise: false, + ); + path0.cubicTo( + size.width * 0.3654573, + size.height * 0.8945578, + size.width * 0.3692366, + size.height * 0.8956916, + size.width * 0.3730159, + size.height * 0.8938020, + ); + path0.cubicTo( + size.width * 0.3745276, + size.height * 0.8926682, + size.width * 0.3775510, + size.height * 0.8915344, + size.width * 0.3771731, + size.height * 0.8892668, + ); + path0.cubicTo( + size.width * 0.3771731, + size.height * 0.8869992, + size.width * 0.3749055, + size.height * 0.8854875, + size.width * 0.3733938, + size.height * 0.8847317, + ); + path0.arcToPoint( + Offset(size.width * 0.3556311, size.height * 0.8801965), + radius: Radius.elliptical( + size.width * 0.03476946, + size.height * 0.03476946, + ), + rotation: 0, + largeArc: false, + clockwise: false, + ); + path0.close(); + path0.moveTo(size.width * 0.4962207, size.height * 0.8832200); + path0.lineTo(size.width * 0.4913076, size.height * 0.8835979); + path0.cubicTo( + size.width * 0.4894180, + size.height * 0.8839758, + size.width * 0.4871504, + size.height * 0.8854875, + size.width * 0.4867725, + size.height * 0.8877551, + ); + path0.lineTo(size.width * 0.4875283, size.height * 0.8896447); + path0.lineTo(size.width * 0.4871504, size.height * 0.8900227); + path0.cubicTo( + size.width * 0.4867725, + size.height * 0.8915344, + size.width * 0.4894180, + size.height * 0.8919123, + size.width * 0.4909297, + size.height * 0.8926682, + ); + path0.arcToPoint( + Offset(size.width * 0.5045351, size.height * 0.8964475), + radius: Radius.elliptical( + size.width * 0.03401361, + size.height * 0.03401361, + ), + rotation: 0, + largeArc: false, + clockwise: false, + ); + path0.cubicTo( + size.width * 0.5079365, + size.height * 0.8964475, + size.width * 0.5117158, + size.height * 0.8964475, + size.width * 0.5143613, + size.height * 0.8945578, + ); + path0.cubicTo( + size.width * 0.5158730, + size.height * 0.8934240, + size.width * 0.5173847, + size.height * 0.8907785, + size.width * 0.5154951, + size.height * 0.8892668, + ); + path0.cubicTo( + size.width * 0.5128496, + size.height * 0.8873772, + size.width * 0.5098262, + size.height * 0.8862434, + size.width * 0.5064248, + size.height * 0.8851096, + ); + path0.cubicTo( + size.width * 0.5034014, + size.height * 0.8839758, + size.width * 0.4996221, + size.height * 0.8832200, + size.width * 0.4962207, + size.height * 0.8832200, + ); + path0.close(); + path0.moveTo(size.width * 0.6746032, size.height * 0.8858655); + path0.lineTo(size.width * 0.6749811, size.height * 0.8866213); + path0.close(); + + final Paint paint0Stroke = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = size.width * 0.007558579; + paint0Stroke.color = deerTracks.withOpacity(1); + paint0Stroke.strokeCap = StrokeCap.round; + paint0Stroke.strokeJoin = StrokeJoin.round; + canvas.drawPath(path0, paint0Stroke); + + final Path path_1 = Path(); + path_1.moveTo(size.width * 0.3605442, size.height * 0.4489796); + path_1.cubicTo( + size.width * 0.3594104, + size.height * 0.4667423, + size.width * 0.3439153, + size.height * 0.4690098, + size.width * 0.3367347, + size.height * 0.4708995, + ); + path_1.cubicTo( + size.width * 0.3227513, + size.height * 0.4232804, + size.width * 0.3053666, + size.height * 0.4395314, + size.width * 0.3193500, + size.height * 0.4765684, + ); + path_1.cubicTo( + size.width * 0.3072562, + size.height * 0.4811036, + size.width * 0.2800454, + size.height * 0.4882842, + size.width * 0.2702192, + size.height * 0.4916856, + ); + path_1.cubicTo( + size.width * 0.2653061, + size.height * 0.4848828, + size.width * 0.2426304, + size.height * 0.4622071, + size.width * 0.2195767, + size.height * 0.4656085, + ); + path_1.cubicTo( + size.width * 0.2044596, + size.height * 0.4640967, + size.width * 0.1836735, + size.height * 0.4648526, + size.width * 0.1855631, + size.height * 0.4754346, + ); + path_1.cubicTo( + size.width * 0.1965231, + size.height * 0.4863946, + size.width * 0.1825397, + size.height * 0.4962207, + size.width * 0.1885865, + size.height * 0.4965986, + ); + path_1.cubicTo( + size.width * 0.1613757, + size.height * 0.5086924, + size.width * 0.1545729, + size.height * 0.4523810, + size.width * 0.1405896, + size.height * 0.4523810, + ); + path_1.cubicTo( + size.width * 0.1315193, + size.height * 0.4572940, + size.width * 0.1462585, + size.height * 0.4973545, + size.width * 0.1311413, + size.height * 0.4807256, + ); + path_1.cubicTo( + size.width * 0.1164021, + size.height * 0.4769463, + size.width * 0.1148904, + size.height * 0.4331066, + size.width * 0.09977324, + size.height * 0.4452003, + ); + path_1.cubicTo( + size.width * 0.09599395, + size.height * 0.4656085, + size.width * 0.1095994, + size.height * 0.4867725, + size.width * 0.1239607, + size.height * 0.5003779, + ); + path_1.cubicTo( + size.width * 0.1409675, + size.height * 0.5162509, + size.width * 0.1678005, + size.height * 0.5060469, + size.width * 0.1844293, + size.height * 0.5192744, + ); + path_1.cubicTo( + size.width * 0.1825397, + size.height * 0.5347695, + size.width * 0.2014361, + size.height * 0.5495087, + size.width * 0.1908541, + size.height * 0.5650038, + ); + path_1.cubicTo( + size.width * 0.1870748, + size.height * 0.5740741, + size.width * 0.1825397, + size.height * 0.5831444, + size.width * 0.1961451, + size.height * 0.5831444, + ); + path_1.cubicTo( + size.width * 0.2082389, + size.height * 0.5914588, + size.width * 0.2278912, + size.height * 0.5922147, + size.width * 0.2377173, + size.height * 0.5967498, + ); + path_1.cubicTo( + size.width * 0.2460317, + size.height * 0.6107332, + size.width * 0.2728647, + size.height * 0.6148904, + size.width * 0.2626606, + size.height * 0.6360544, + ); + path_1.lineTo(size.width * 0.2539683, size.height * 0.7324263); + path_1.cubicTo( + size.width * 0.2743764, + size.height * 0.7384732, + size.width * 0.2811791, + size.height * 0.7150416, + size.width * 0.2834467, + size.height * 0.6999244, + ); + path_1.cubicTo( + size.width * 0.2860922, + size.height * 0.6900983, + size.width * 0.2879819, + size.height * 0.6651550, + size.width * 0.2966742, + size.height * 0.6662887, + ); + path_1.cubicTo( + size.width * 0.3053666, + size.height * 0.6787604, + size.width * 0.3042328, + size.height * 0.7154195, + size.width * 0.3257748, + size.height * 0.7067271, + ); + path_1.cubicTo( + size.width * 0.3446712, + size.height * 0.6972789, + size.width * 0.3253968, + size.height * 0.6757370, + size.width * 0.3257748, + size.height * 0.6602419, + ); + path_1.lineTo(size.width * 0.3125472, size.height * 0.6024187); + path_1.lineTo(size.width * 0.4070295, size.height * 0.6024187); + path_1.cubicTo( + size.width * 0.4085412, + size.height * 0.6364324, + size.width * 0.4089191, + size.height * 0.6734694, + size.width * 0.4104308, + size.height * 0.7071051, + ); + path_1.cubicTo( + size.width * 0.4323507, + size.height * 0.7142857, + size.width * 0.4436886, + size.height * 0.6874528, + size.width * 0.4410431, + size.height * 0.6693122, + ); + path_1.lineTo(size.width * 0.4470899, size.height * 0.6277400); + path_1.cubicTo( + size.width * 0.4569161, + size.height * 0.6466364, + size.width * 0.4557823, + size.height * 0.6712018, + size.width * 0.4720333, + size.height * 0.6863190, + ); + path_1.cubicTo( + size.width * 0.4939531, + size.height * 0.6832955, + size.width * 0.4890401, + size.height * 0.6693122, + size.width * 0.4833711, + size.height * 0.6428571, + ); + path_1.cubicTo( + size.width * 0.4803477, + size.height * 0.6050642, + size.width * 0.4708995, + size.height * 0.5684051, + size.width * 0.4618292, + size.height * 0.5321240, + ); + path_1.cubicTo( + size.width * 0.4792139, + size.height * 0.5192744, + size.width * 0.4811036, + size.height * 0.4943311, + size.width * 0.4693878, + size.height * 0.4773243, + ); + path_1.cubicTo( + size.width * 0.4576720, + size.height * 0.4546485, + size.width * 0.4455782, + size.height * 0.4848828, + size.width * 0.4467120, + size.height * 0.4977324, + ); + path_1.cubicTo( + size.width * 0.4489796, + size.height * 0.5211640, + size.width * 0.4202570, + size.height * 0.5000000, + size.width * 0.4070295, + size.height * 0.5011338, + ); + path_1.cubicTo( + size.width * 0.3941799, + size.height * 0.4973545, + size.width * 0.3556311, + size.height * 0.4890401, + size.width * 0.3537415, + size.height * 0.4848828, + ); + path_1.cubicTo( + size.width * 0.3813303, + size.height * 0.4716553, + size.width * 0.3854875, + size.height * 0.4648526, + size.width * 0.3771731, + size.height * 0.4402872, + ); + path_1.cubicTo( + size.width * 0.3654573, + size.height * 0.3873772, + size.width * 0.3439153, + size.height * 0.4021164, + size.width * 0.3605442, + size.height * 0.4489796, + ); + path_1.close(); + + final Paint paint1Fill = Paint()..style = PaintingStyle.fill; + paint1Fill.color = deerSkin.withOpacity(1); + canvas.drawPath(path_1, paint1Fill); + + final Path path_2 = Path(); + path_2.moveTo(size.width * 0.3624339, size.height * 0.4818594); + path_2.lineTo(size.width * 0.3492063, size.height * 0.4882842); + path_2.cubicTo( + size.width * 0.3616780, + size.height * 0.4886621, + size.width * 0.3775510, + size.height * 0.5090703, + size.width * 0.3760393, + size.height * 0.5192744, + ); + path_2.cubicTo( + size.width * 0.3548753, + size.height * 0.5222978, + size.width * 0.3325775, + size.height * 0.5283447, + size.width * 0.3178382, + size.height * 0.5241875, + ); + path_2.lineTo(size.width * 0.3114135, size.height * 0.5003779); + path_2.cubicTo( + size.width * 0.3076342, + size.height * 0.5011338, + size.width * 0.2944067, + size.height * 0.4992441, + size.width * 0.2981859, + size.height * 0.5064248, + ); + path_2.lineTo(size.width * 0.3057445, size.height * 0.5253212); + path_2.arcToPoint( + Offset(size.width * 0.2970522, size.height * 0.5423280), + radius: Radius.elliptical( + size.width * 0.01360544, + size.height * 0.01360544, + ), + rotation: 0, + largeArc: false, + clockwise: false, + ); + path_2.cubicTo( + size.width * 0.3004535, + size.height * 0.5748299, + size.width * 0.2898715, + size.height * 0.6345427, + size.width * 0.3393802, + size.height * 0.6194255, + ); + path_2.lineTo(size.width * 0.4168556, size.height * 0.6054422); + path_2.lineTo(size.width * 0.4259259, size.height * 0.5952381); + path_2.cubicTo( + size.width * 0.4229025, + size.height * 0.5718065, + size.width * 0.4236584, + size.height * 0.5623583, + size.width * 0.4202570, + size.height * 0.5385488, + ); + path_2.cubicTo( + size.width * 0.4179894, + size.height * 0.5306122, + size.width * 0.4115646, + size.height * 0.5166289, + size.width * 0.4036281, + size.height * 0.5185185, + ); + path_2.cubicTo( + size.width * 0.3998488, + size.height * 0.5177627, + size.width * 0.3956916, + size.height * 0.5177627, + size.width * 0.3915344, + size.height * 0.5177627, + ); + path_2.cubicTo( + size.width * 0.3885110, + size.height * 0.5068027, + size.width * 0.3862434, + size.height * 0.4920635, + size.width * 0.3794407, + size.height * 0.4882842, + ); + path_2.cubicTo( + size.width * 0.3771731, + size.height * 0.4867725, + size.width * 0.3643235, + size.height * 0.4837491, + size.width * 0.3624339, + size.height * 0.4818594, + ); + path_2.close(); + + final Paint paint2Fill = Paint()..style = PaintingStyle.fill; + paint2Fill.color = mailBag.withOpacity(1); + canvas.drawPath(path_2, paint2Fill); + + final Path path3 = Path(); + path3.moveTo(size.width * 0.2872260, size.height * 0.8212396); + path3.cubicTo( + size.width * 0.2917611, + size.height * 0.8193500, + size.width * 0.2966742, + size.height * 0.8189720, + size.width * 0.3015873, + size.height * 0.8201058, + ); + path3.lineTo(size.width * 0.3091459, size.height * 0.8219955); + path3.moveTo(size.width * 0.3212396, size.height * 0.8072562); + path3.arcToPoint( + Offset(size.width * 0.3696145, size.height * 0.8174603), + radius: Radius.elliptical( + size.width * 0.09070295, + size.height * 0.09070295, + ), + rotation: 0, + largeArc: false, + clockwise: true, + ); + path3.moveTo(size.width * 0.3624339, size.height * 0.8034769); + path3.cubicTo( + size.width * 0.3673469, + size.height * 0.8049887, + size.width * 0.3722600, + size.height * 0.8065004, + size.width * 0.3764172, + size.height * 0.8099017, + ); + path3.lineTo(size.width * 0.3813303, size.height * 0.8136810); + + final Paint paint3Stroke = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = size.width * 0.007558579; + paint3Stroke.color = deerTracks.withOpacity(1); + paint3Stroke.strokeCap = StrokeCap.round; + paint3Stroke.strokeJoin = StrokeJoin.round; + canvas.drawPath(path3, paint3Stroke); + + final Path path4 = Path(); + path4.moveTo(size.width * 0.2437642, size.height * 0.6077098); + path4.arcToPoint( + Offset(size.width * 0.2634165, size.height * 0.6167800), + radius: Radius.elliptical( + size.width * 0.02947846, + size.height * 0.02947846, + ), + rotation: 0, + largeArc: false, + clockwise: false, + ); + + final Paint paint4Stroke = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = size.width * 0.007558579; + paint4Stroke.color = contourColor.withOpacity(1); + paint4Stroke.strokeCap = StrokeCap.round; + paint4Stroke.strokeJoin = StrokeJoin.round; + canvas.drawPath(path4, paint4Stroke); + + final Path path5 = Path(); + path5.moveTo(size.width * 0.2653061, size.height * 0.6046863); + path5.cubicTo( + size.width * 0.2603930, + size.height * 0.6455026, + size.width * 0.2551020, + size.height * 0.6878307, + size.width * 0.2524565, + size.height * 0.7282691, + ); + path5.moveTo(size.width * 0.2464097, size.height * 0.7312925); + path5.cubicTo( + size.width * 0.2569917, + size.height * 0.7369615, + size.width * 0.2671958, + size.height * 0.7339380, + size.width * 0.2773998, + size.height * 0.7305367, + ); + path5.moveTo(size.width * 0.3053666, size.height * 0.6043084); + path5.cubicTo( + size.width * 0.2978080, + size.height * 0.6356765, + size.width * 0.2928949, + size.height * 0.6972789, + size.width * 0.2751323, + size.height * 0.7244898, + ); + path5.moveTo(size.width * 0.2974301, size.height * 0.6621315); + path5.cubicTo( + size.width * 0.3004535, + size.height * 0.6734694, + size.width * 0.3034769, + size.height * 0.6851852, + size.width * 0.3087680, + size.height * 0.6961451, + ); + path5.moveTo(size.width * 0.3117914, size.height * 0.7040816); + path5.lineTo(size.width * 0.3136810, size.height * 0.7040816); + path5.moveTo(size.width * 0.3121693, size.height * 0.7112623); + path5.cubicTo( + size.width * 0.3182162, + size.height * 0.7120181, + size.width * 0.3219955, + size.height * 0.7093726, + size.width * 0.3280423, + size.height * 0.7082389, + ); + path5.moveTo(size.width * 0.3185941, size.height * 0.6224490); + path5.cubicTo( + size.width * 0.3201058, + size.height * 0.6269841, + size.width * 0.3204837, + size.height * 0.6318972, + size.width * 0.3216175, + size.height * 0.6364324, + ); + path5.cubicTo( + size.width * 0.3253968, + size.height * 0.6523054, + size.width * 0.3284203, + size.height * 0.6678005, + size.width * 0.3306878, + size.height * 0.6836735, + ); + path5.lineTo(size.width * 0.3321995, size.height * 0.7014361); + + final Paint paint5Stroke = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = size.width * 0.007558579; + paint5Stroke.color = contourColor.withOpacity(1); + paint5Stroke.strokeCap = StrokeCap.round; + paint5Stroke.strokeJoin = StrokeJoin.miter; + canvas.drawPath(path5, paint5Stroke); + + final Path path6 = Path(); + path6.moveTo(size.width * 0.2362056, size.height * 0.5933485); + path6.cubicTo( + size.width * 0.2467876, + size.height * 0.6050642, + size.width * 0.2675737, + size.height * 0.6073318, + size.width * 0.2815571, + size.height * 0.6009070, + ); + path6.moveTo(size.width * 0.2165533, size.height * 0.5899471); + path6.cubicTo( + size.width * 0.2392290, + size.height * 0.5967498, + size.width * 0.2543462, + size.height * 0.5793651, + size.width * 0.2641723, + size.height * 0.5612245, + ); + path6.moveTo(size.width * 0.2033258, size.height * 0.5835223); + path6.cubicTo( + size.width * 0.2071051, + size.height * 0.5884354, + size.width * 0.2120181, + size.height * 0.5891912, + size.width * 0.2169312, + size.height * 0.5861678, + ); + path6.moveTo(size.width * 0.1965231, size.height * 0.5529101); + path6.cubicTo( + size.width * 0.1965231, + size.height * 0.5551776, + size.width * 0.1840514, + size.height * 0.5680272, + size.width * 0.1870748, + size.height * 0.5774754, + ); + path6.moveTo(size.width * 0.1987906, size.height * 0.5234316); + path6.cubicTo( + size.width * 0.1987906, + size.height * 0.5287226, + size.width * 0.2037037, + size.height * 0.5415722, + size.width * 0.2089947, + size.height * 0.5479970, + ); + path6.moveTo(size.width * 0.2256236, size.height * 0.5245654); + path6.arcToPoint( + Offset(size.width * 0.2354497, size.height * 0.5483749), + radius: Radius.elliptical( + size.width * 0.05668934, + size.height * 0.05668934, + ), + rotation: 0, + largeArc: false, + clockwise: false, + ); + path6.moveTo(size.width * 0.1904762, size.height * 0.4947090); + path6.cubicTo( + size.width * 0.1764928, + size.height * 0.5136054, + size.width * 0.1836735, + size.height * 0.5393046, + size.width * 0.1965231, + size.height * 0.5521542, + ); + + final Paint paint6Stroke = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = size.width * 0.007558579; + paint6Stroke.color = contourColor.withOpacity(1); + paint6Stroke.strokeCap = StrokeCap.round; + paint6Stroke.strokeJoin = StrokeJoin.miter; + canvas.drawPath(path6, paint6Stroke); + + final Path path7 = Path(); + path7.moveTo(size.width * 0.09826153, size.height * 0.4595616); + path7.cubicTo( + size.width * 0.1046863, + size.height * 0.4935752, + size.width * 0.1337868, + size.height * 0.5117158, + size.width * 0.1825397, + size.height * 0.5124717, + ); + + final Paint paint7Stroke = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = size.width * 0.007558579; + paint7Stroke.color = contourColor.withOpacity(1); + paint7Stroke.strokeCap = StrokeCap.round; + paint7Stroke.strokeJoin = StrokeJoin.miter; + canvas.drawPath(path7, paint7Stroke); + + final Path path8 = Path(); + path8.moveTo(size.width * 0.09788360, size.height * 0.4546485); + path8.cubicTo( + size.width * 0.09297052, + size.height * 0.4421769, + size.width * 0.1092215, + size.height * 0.4334845, + size.width * 0.1152683, + size.height * 0.4527589, + ); + path8.cubicTo( + size.width * 0.1182918, + size.height * 0.4625850, + size.width * 0.1228269, + size.height * 0.4724112, + size.width * 0.1269841, + size.height * 0.4788360, + ); + path8.moveTo(size.width * 0.1292517, size.height * 0.4811036); + path8.cubicTo( + size.width * 0.1341648, + size.height * 0.4845049, + size.width * 0.1398337, + size.height * 0.4863946, + size.width * 0.1447468, + size.height * 0.4886621, + ); + path8.moveTo(size.width * 0.1462585, size.height * 0.4894180); + path8.cubicTo( + size.width * 0.1424792, + size.height * 0.4818594, + size.width * 0.1371882, + size.height * 0.4701436, + size.width * 0.1352986, + size.height * 0.4622071, + ); + path8.cubicTo( + size.width * 0.1334089, + size.height * 0.4531368, + size.width * 0.1451247, + size.height * 0.4459562, + size.width * 0.1504157, + size.height * 0.4584278, + ); + path8.moveTo(size.width * 0.1670446, size.height * 0.4935752); + path8.cubicTo( + size.width * 0.1632653, + size.height * 0.4826153, + size.width * 0.1557067, + size.height * 0.4678760, + size.width * 0.1507937, + size.height * 0.4580499, + ); + + final Paint paint8Stroke = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = size.width * 0.007558579; + paint8Stroke.color = contourColor.withOpacity(1); + paint8Stroke.strokeCap = StrokeCap.round; + paint8Stroke.strokeJoin = StrokeJoin.miter; + canvas.drawPath(path8, paint8Stroke); + + final Path path9 = Path(); + path9.moveTo(size.width * 0.1957672, size.height * 0.4950869); + path9.cubicTo( + size.width * 0.1719577, + size.height * 0.4977324, + size.width * 0.1938776, + size.height * 0.4818594, + size.width * 0.2071051, + size.height * 0.4829932, + ); + path9.cubicTo( + size.width * 0.2021920, + size.height * 0.4818594, + size.width * 0.1882086, + size.height * 0.4829932, + size.width * 0.1851852, + size.height * 0.4773243, + ); + path9.cubicTo( + size.width * 0.1814059, + size.height * 0.4697657, + size.width * 0.2116402, + size.height * 0.4671202, + size.width * 0.2161754, + size.height * 0.4663643, + ); + path9.cubicTo( + size.width * 0.2422525, + size.height * 0.4629630, + size.width * 0.2630385, + size.height * 0.4754346, + size.width * 0.2690854, + size.height * 0.4905518, + ); + + final Paint paint9Stroke = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = size.width * 0.007558579; + paint9Stroke.color = contourColor.withOpacity(1); + paint9Stroke.strokeCap = StrokeCap.round; + paint9Stroke.strokeJoin = StrokeJoin.round; + canvas.drawPath(path9, paint9Stroke); + + final Path path10 = Path(); + path10.moveTo(size.width * 0.2694633, size.height * 0.4935752); + path10.cubicTo( + size.width * 0.2872260, + size.height * 0.4897959, + size.width * 0.2989418, + size.height * 0.4860166, + size.width * 0.3163265, + size.height * 0.4799698, + ); + path10.moveTo(size.width * 0.2751323, size.height * 0.5105820); + path10.cubicTo( + size.width * 0.2970522, + size.height * 0.5041572, + size.width * 0.3817082, + size.height * 0.4863946, + size.width * 0.3809524, + size.height * 0.4538927, + ); + path10.moveTo(size.width * 0.3790627, size.height * 0.4459562); + path10.cubicTo( + size.width * 0.3809524, + size.height * 0.4444444, + size.width * 0.3805745, + size.height * 0.4452003, + size.width * 0.3820862, + size.height * 0.4433107, + ); + path10.moveTo(size.width * 0.3193500, size.height * 0.4773243); + path10.cubicTo( + size.width * 0.3163265, + size.height * 0.4705215, + size.width * 0.3163265, + size.height * 0.4629630, + size.width * 0.3129252, + size.height * 0.4561602, + ); + path10.moveTo(size.width * 0.3401361, size.height * 0.4705215); + path10.cubicTo( + size.width * 0.3469388, + size.height * 0.4671202, + size.width * 0.3590325, + size.height * 0.4606954, + size.width * 0.3578987, + size.height * 0.4489796, + ); + path10.moveTo(size.width * 0.3371126, size.height * 0.4693878); + path10.cubicTo( + size.width * 0.3333333, + size.height * 0.4603175, + size.width * 0.3276644, + size.height * 0.4327286, + size.width * 0.3163265, + size.height * 0.4399093, + ); + path10.cubicTo( + size.width * 0.3106576, + size.height * 0.4433107, + size.width * 0.3110355, + size.height * 0.4478458, + size.width * 0.3117914, + size.height * 0.4531368, + ); + path10.moveTo(size.width * 0.4100529, size.height * 0.7093726); + path10.cubicTo( + size.width * 0.4149660, + size.height * 0.7105064, + size.width * 0.4229025, + size.height * 0.7093726, + size.width * 0.4244142, + size.height * 0.7082389, + ); + path10.moveTo(size.width * 0.4327286, size.height * 0.7048375); + path10.lineTo(size.width * 0.4289494, size.height * 0.7010582); + path10.moveTo(size.width * 0.4108088, size.height * 0.7010582); + path10.cubicTo( + size.width * 0.4092971, + size.height * 0.6708239, + size.width * 0.4115646, + size.height * 0.6405896, + size.width * 0.4108088, + size.height * 0.6103553, + ); + path10.moveTo(size.width * 0.4357521, size.height * 0.6965231); + path10.cubicTo( + size.width * 0.4410431, + size.height * 0.6560847, + size.width * 0.4508692, + size.height * 0.6141345, + size.width * 0.4527589, + size.height * 0.5729403, + ); + path10.moveTo(size.width * 0.4508692, size.height * 0.5672714); + path10.cubicTo( + size.width * 0.4546485, + size.height * 0.5702948, + size.width * 0.4523810, + size.height * 0.5680272, + size.width * 0.4569161, + size.height * 0.5748299, + ); + path10.moveTo(size.width * 0.4656085, size.height * 0.6855631); + path10.lineTo(size.width * 0.4693878, size.height * 0.6817838); + + final Paint paint10Stroke = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = size.width * 0.007558579; + paint10Stroke.color = contourColor.withOpacity(1); + paint10Stroke.strokeCap = StrokeCap.round; + paint10Stroke.strokeJoin = StrokeJoin.miter; + canvas.drawPath(path10, paint10Stroke); + + final Path path11 = Path(); + path11.moveTo(size.width * 0.4648526, size.height * 0.6753590); + path11.cubicTo( + size.width * 0.4603175, + size.height * 0.6579743, + size.width * 0.4520030, + size.height * 0.6417234, + size.width * 0.4452003, + size.height * 0.6254724, + ); + path11.moveTo(size.width * 0.4739229, size.height * 0.6878307); + path11.arcToPoint( + Offset(size.width * 0.4863946, size.height * 0.6832955), + radius: Radius.elliptical( + size.width * 0.04913076, + size.height * 0.04913076, + ), + rotation: 0, + largeArc: false, + clockwise: false, + ); + path11.moveTo(size.width * 0.4882842, size.height * 0.6746032); + path11.arcToPoint( + Offset(size.width * 0.4606954, size.height * 0.5362812), + radius: + Radius.elliptical(size.width * 0.8752834, size.height * 0.8752834), + rotation: 0, + largeArc: false, + clockwise: false, + ); + path11.moveTo(size.width * 0.4622071, size.height * 0.5328798); + path11.cubicTo( + size.width * 0.4863946, + size.height * 0.5166289, + size.width * 0.4735450, + size.height * 0.4822373, + size.width * 0.4546485, + size.height * 0.4686319, + ); + + final Paint paint11Stroke = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = size.width * 0.007558579; + paint11Stroke.color = contourColor.withOpacity(1); + paint11Stroke.strokeCap = StrokeCap.round; + paint11Stroke.strokeJoin = StrokeJoin.miter; + canvas.drawPath(path11, paint11Stroke); + + final Path path12 = Path(); + path12.moveTo(size.width * 0.4512472, size.height * 0.4761905); + path12.cubicTo( + size.width * 0.4531368, + size.height * 0.4803477, + size.width * 0.4572940, + size.height * 0.4863946, + size.width * 0.4588057, + size.height * 0.4894180, + ); + path12.moveTo(size.width * 0.4501134, size.height * 0.4829932); + path12.cubicTo( + size.width * 0.4425548, + size.height * 0.4913076, + size.width * 0.4399093, + size.height * 0.5113379, + size.width * 0.4512472, + size.height * 0.5173847, + ); + path12.moveTo(size.width * 0.3900227, size.height * 0.5000000); + path12.cubicTo( + size.width * 0.4070295, + size.height * 0.5011338, + size.width * 0.4270597, + size.height * 0.5022676, + size.width * 0.4433107, + size.height * 0.5083144, + ); + + final Paint paint12Stroke = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = size.width * 0.007558579; + paint12Stroke.color = contourColor.withOpacity(1); + paint12Stroke.strokeCap = StrokeCap.round; + paint12Stroke.strokeJoin = StrokeJoin.miter; + canvas.drawPath(path12, paint12Stroke); + + final Path path13 = Path(); + path13.moveTo(size.width * 0.3065004, size.height * 0.6084656); + path13.cubicTo( + size.width * 0.3072562, + size.height * 0.6133787, + size.width * 0.3091459, + size.height * 0.6179138, + size.width * 0.3140590, + size.height * 0.6194255, + ); + path13.cubicTo( + size.width * 0.3201058, + size.height * 0.6224490, + size.width * 0.3941799, + size.height * 0.6130008, + size.width * 0.4160998, + size.height * 0.6069539, + ); + path13.moveTo(size.width * 0.4225246, size.height * 0.5990174); + path13.cubicTo( + size.width * 0.4213908, + size.height * 0.5975057, + size.width * 0.4202570, + size.height * 0.5959940, + size.width * 0.4187453, + size.height * 0.5952381, + ); + path13.moveTo(size.width * 0.4270597, size.height * 0.5910809); + path13.cubicTo( + size.width * 0.4270597, + size.height * 0.5850340, + size.width * 0.4198791, + size.height * 0.5393046, + size.width * 0.4195011, + size.height * 0.5328798, + ); + path13.cubicTo( + size.width * 0.4183673, + size.height * 0.5234316, + size.width * 0.4157218, + size.height * 0.5158730, + size.width * 0.4032502, + size.height * 0.5177627, + ); + path13.moveTo(size.width * 0.3926682, size.height * 0.5721844); + path13.cubicTo( + size.width * 0.3990930, + size.height * 0.5767196, + size.width * 0.4092971, + size.height * 0.5831444, + size.width * 0.4138322, + size.height * 0.5884354, + ); + + final Paint paint13Stroke = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = size.width * 0.007558579; + paint13Stroke.color = contourColor.withOpacity(1); + paint13Stroke.strokeCap = StrokeCap.round; + paint13Stroke.strokeJoin = StrokeJoin.round; + canvas.drawPath(path13, paint13Stroke); + + final Path path14 = Path(); + path14.moveTo(size.width * 0.4104308, size.height * 0.5226757); + path14.cubicTo( + size.width * 0.3998488, + size.height * 0.5377929, + size.width * 0.3847317, + size.height * 0.5740741, + size.width * 0.3707483, + size.height * 0.5755858, + ); + path14.cubicTo( + size.width * 0.3556311, + size.height * 0.5767196, + size.width * 0.3163265, + size.height * 0.5468632, + size.width * 0.3038549, + size.height * 0.5389267, + ); + path14.moveTo(size.width * 0.3182162, size.height * 0.6111111); + path14.cubicTo( + size.width * 0.3235072, + size.height * 0.6027967, + size.width * 0.3306878, + size.height * 0.5963719, + size.width * 0.3356009, + size.height * 0.5876795, + ); + path14.moveTo(size.width * 0.3408919, size.height * 0.5801209); + path14.cubicTo( + size.width * 0.3431595, + size.height * 0.5767196, + size.width * 0.3454271, + size.height * 0.5729403, + size.width * 0.3469388, + size.height * 0.5687831, + ); + + final Paint paint14Stroke = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = size.width * 0.007558579; + paint14Stroke.color = contourColor.withOpacity(1); + paint14Stroke.strokeCap = StrokeCap.round; + paint14Stroke.strokeJoin = StrokeJoin.round; + canvas.drawPath(path14, paint14Stroke); + + final Path path15 = Path(); + path15.moveTo(size.width * 0.3046107, size.height * 0.6009070); + path15.cubicTo( + size.width * 0.3034769, + size.height * 0.5903250, + size.width * 0.2955404, + size.height * 0.5529101, + size.width * 0.2955404, + size.height * 0.5427060, + ); + path15.cubicTo( + size.width * 0.2955404, + size.height * 0.5328798, + size.width * 0.2978080, + size.height * 0.5275888, + size.width * 0.3219955, + size.height * 0.5264550, + ); + path15.moveTo(size.width * 0.3303099, size.height * 0.5253212); + path15.cubicTo( + size.width * 0.3514739, + size.height * 0.5215420, + size.width * 0.3733938, + size.height * 0.5196523, + size.width * 0.3934240, + size.height * 0.5188964, + ); + path15.moveTo(size.width * 0.3518519, size.height * 0.4935752); + path15.cubicTo( + size.width * 0.3662132, + size.height * 0.4860166, + size.width * 0.3749055, + size.height * 0.5079365, + size.width * 0.3756614, + size.height * 0.5166289, + ); + path15.moveTo(size.width * 0.3631897, size.height * 0.4818594); + path15.cubicTo( + size.width * 0.3813303, + size.height * 0.4829932, + size.width * 0.3907785, + size.height * 0.5034014, + size.width * 0.3896447, + size.height * 0.5170068, + ); + + final Paint paint15Stroke = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = size.width * 0.007558579; + paint15Stroke.color = contourColor.withOpacity(1); + paint15Stroke.strokeCap = StrokeCap.round; + paint15Stroke.strokeJoin = StrokeJoin.round; + canvas.drawPath(path15, paint15Stroke); + + final Path path16 = Path(); + path16.moveTo(size.width * 0.3049887, size.height * 0.5291005); + path16.cubicTo( + size.width * 0.3034769, + size.height * 0.5222978, + size.width * 0.3004535, + size.height * 0.5154951, + size.width * 0.2981859, + size.height * 0.5086924, + ); + path16.moveTo(size.width * 0.3212396, size.height * 0.5268330); + path16.cubicTo( + size.width * 0.3185941, + size.height * 0.5192744, + size.width * 0.3167045, + size.height * 0.5117158, + size.width * 0.3129252, + size.height * 0.5049131, + ); + path16.moveTo(size.width * 0.1678005, size.height * 0.4950869); + path16.lineTo(size.width * 0.1749811, size.height * 0.4965986); + path16.moveTo(size.width * 0.2683296, size.height * 0.5226757); + path16.cubicTo( + size.width * 0.2910053, + size.height * 0.5464853, + size.width * 0.2944067, + size.height * 0.5162509, + size.width * 0.2728647, + size.height * 0.5102041, + ); + + final Paint paint16Stroke = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = size.width * 0.007558579; + paint16Stroke.color = contourColor.withOpacity(1); + paint16Stroke.strokeCap = StrokeCap.round; + paint16Stroke.strokeJoin = StrokeJoin.round; + canvas.drawPath(path16, paint16Stroke); + + final Path path17 = Path(); + path17.moveTo(size.width * 0.1927438, size.height * 0.5820106); + path17.cubicTo( + size.width * 0.1980348, + size.height * 0.5839002, + size.width * 0.2033258, + size.height * 0.5823885, + size.width * 0.2101285, + size.height * 0.5782313, + ); + + final Paint paint17Stroke = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = size.width * 0.007558579; + paint17Stroke.color = contourColor.withOpacity(1); + paint17Stroke.strokeCap = StrokeCap.round; + paint17Stroke.strokeJoin = StrokeJoin.round; + canvas.drawPath(path17, paint17Stroke); + + final Paint paint17Fill = Paint()..style = PaintingStyle.fill; + paint17Fill.color = contourColor.withOpacity(1); + canvas.drawPath(path17, paint17Fill); + + final Path path18 = Path(); + path18.moveTo(size.width * 0.3764172, size.height * 0.4365079); + path18.cubicTo( + size.width * 0.3730159, + size.height * 0.4270597, + size.width * 0.3718821, + size.height * 0.4040060, + size.width * 0.3571429, + size.height * 0.4085412, + ); + path18.cubicTo( + size.width * 0.3469388, + size.height * 0.4119426, + size.width * 0.3563870, + size.height * 0.4349962, + size.width * 0.3571429, + size.height * 0.4402872, + ); + + final Paint paint18Stroke = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = size.width * 0.007558579; + paint18Stroke.color = contourColor.withOpacity(1); + paint18Stroke.strokeCap = StrokeCap.round; + paint18Stroke.strokeJoin = StrokeJoin.miter; + canvas.drawPath(path18, paint18Stroke); + + final Path path19 = Path(); + path19.moveTo(size.width * 0.1530612, size.height * 0.4104308); + path19.cubicTo( + size.width * 0.1477702, + size.height * 0.4085412, + size.width * 0.1447468, + size.height * 0.4028723, + size.width * 0.1455026, + size.height * 0.3972033, + ); + path19.cubicTo( + size.width * 0.1458806, + size.height * 0.3919123, + size.width * 0.1485261, + size.height * 0.3866213, + size.width * 0.1530612, + size.height * 0.3839758, + ); + path19.cubicTo( + size.width * 0.1568405, + size.height * 0.3817082, + size.width * 0.1617536, + size.height * 0.3809524, + size.width * 0.1655329, + size.height * 0.3828420, + ); + path19.cubicTo( + size.width * 0.1700680, + size.height * 0.3847317, + size.width * 0.1742252, + size.height * 0.3881330, + size.width * 0.1757370, + size.height * 0.3926682, + ); + path19.arcToPoint( + Offset(size.width * 0.1734694, size.height * 0.4096750), + radius: Radius.elliptical( + size.width * 0.01889645, + size.height * 0.01889645, + ), + rotation: 0, + largeArc: false, + clockwise: true, + ); + path19.cubicTo( + size.width * 0.1712018, + size.height * 0.4134543, + size.width * 0.1659108, + size.height * 0.4142101, + size.width * 0.1651550, + size.height * 0.4187453, + ); + path19.cubicTo( + size.width * 0.1643991, + size.height * 0.4232804, + size.width * 0.1689342, + size.height * 0.4270597, + size.width * 0.1708239, + size.height * 0.4308390, + ); + path19.moveTo(size.width * 0.1757370, size.height * 0.4383976); + path19.cubicTo( + size.width * 0.1757370, + size.height * 0.4387755, + size.width * 0.1761149, + size.height * 0.4383976, + size.width * 0.1761149, + size.height * 0.4383976, + ); + path19.lineTo(size.width * 0.1761149, size.height * 0.4387755); + + final Paint paint19Stroke = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = size.width * 0.007558579; + paint19Stroke.color = contourColor.withOpacity(1); + paint19Stroke.strokeCap = StrokeCap.round; + paint19Stroke.strokeJoin = StrokeJoin.round; + canvas.drawPath(path19, paint19Stroke); + } + + @override + bool shouldRepaint(covariant final StrayDeerPainter oldDelegate) => + colorPalette != oldDelegate.colorPalette; +} diff --git a/lib/logic/api_maps/cloudflare.dart b/lib/logic/api_maps/cloudflare.dart deleted file mode 100644 index 9141d5fe..00000000 --- a/lib/logic/api_maps/cloudflare.dart +++ /dev/null @@ -1,259 +0,0 @@ -import 'dart:io'; - -import 'package:dio/dio.dart'; -import 'package:selfprivacy/config/get_it_config.dart'; -import 'package:selfprivacy/logic/api_maps/api_map.dart'; -import 'package:selfprivacy/logic/models/hive/server_domain.dart'; -import 'package:selfprivacy/logic/models/json/dns_records.dart'; - -class DomainNotFoundException implements Exception { - DomainNotFoundException(this.message); - final String message; -} - -class CloudflareApi extends ApiMap { - CloudflareApi({ - this.hasLogger = false, - this.isWithToken = true, - this.customToken, - }); - @override - final bool hasLogger; - @override - final bool isWithToken; - - final String? customToken; - - @override - BaseOptions get options { - final BaseOptions options = BaseOptions(baseUrl: rootAddress); - if (isWithToken) { - final String? token = getIt().cloudFlareKey; - assert(token != null); - options.headers = {'Authorization': 'Bearer $token'}; - } - - if (customToken != null) { - options.headers = {'Authorization': 'Bearer $customToken'}; - } - - if (validateStatus != null) { - options.validateStatus = validateStatus!; - } - return options; - } - - @override - String rootAddress = 'https://api.cloudflare.com/client/v4'; - - Future isValid(final String token) async { - validateStatus = (final status) => - status == HttpStatus.ok || status == HttpStatus.unauthorized; - - final Dio client = await getClient(); - final Response response = await client.get( - '/user/tokens/verify', - options: Options(headers: {'Authorization': 'Bearer $token'}), - ); - - close(client); - - if (response.statusCode == HttpStatus.ok) { - return true; - } else if (response.statusCode == HttpStatus.unauthorized) { - return false; - } else { - throw Exception('code: ${response.statusCode}'); - } - } - - Future getZoneId(final String domain) async { - validateStatus = (final status) => - status == HttpStatus.ok || status == HttpStatus.forbidden; - final Dio client = await getClient(); - final Response response = await client.get( - '/zones', - queryParameters: {'name': domain}, - ); - - close(client); - - if (response.data['result'].isEmpty) { - throw DomainNotFoundException('No domains found'); - } else { - return response.data['result'][0]['id']; - } - } - - Future removeSimilarRecords({ - required final ServerDomain cloudFlareDomain, - final String? ip4, - }) async { - final String domainName = cloudFlareDomain.domainName; - final String domainZoneId = cloudFlareDomain.zoneId; - - final String url = '/zones/$domainZoneId/dns_records'; - - final Dio client = await getClient(); - final Response response = await client.get(url); - - final List records = response.data['result'] ?? []; - final List allDeleteFutures = []; - - for (final record in records) { - if (record['zone_name'] == domainName) { - allDeleteFutures.add( - client.delete('$url/${record["id"]}'), - ); - } - } - - await Future.wait(allDeleteFutures); - close(client); - } - - Future> getDnsRecords({ - required final ServerDomain cloudFlareDomain, - }) async { - final String domainName = cloudFlareDomain.domainName; - final String domainZoneId = cloudFlareDomain.zoneId; - - final String url = '/zones/$domainZoneId/dns_records'; - - final Dio client = await getClient(); - final Response response = await client.get(url); - - final List records = response.data['result'] ?? []; - final List allRecords = []; - - for (final record in records) { - if (record['zone_name'] == domainName) { - allRecords.add( - DnsRecord( - name: record['name'], - type: record['type'], - content: record['content'], - ttl: record['ttl'], - proxied: record['proxied'], - ), - ); - } - } - - close(client); - return allRecords; - } - - Future createMultipleDnsRecords({ - required final ServerDomain cloudFlareDomain, - final String? ip4, - }) async { - final String domainName = cloudFlareDomain.domainName; - final String domainZoneId = cloudFlareDomain.zoneId; - final List listDnsRecords = projectDnsRecords(domainName, ip4); - final List allCreateFutures = []; - - final Dio client = await getClient(); - try { - for (final DnsRecord record in listDnsRecords) { - allCreateFutures.add( - client.post( - '/zones/$domainZoneId/dns_records', - data: record.toJson(), - ), - ); - } - await Future.wait(allCreateFutures); - } on DioError catch (e) { - print(e.message); - rethrow; - } finally { - close(client); - } - } - - List projectDnsRecords( - final String? domainName, - final String? ip4, - ) { - final DnsRecord domainA = - DnsRecord(type: 'A', name: domainName, content: ip4); - - final DnsRecord mx = DnsRecord(type: 'MX', name: '@', content: domainName); - final DnsRecord apiA = DnsRecord(type: 'A', name: 'api', content: ip4); - final DnsRecord cloudA = DnsRecord(type: 'A', name: 'cloud', content: ip4); - final DnsRecord gitA = DnsRecord(type: 'A', name: 'git', content: ip4); - final DnsRecord meetA = DnsRecord(type: 'A', name: 'meet', content: ip4); - final DnsRecord passwordA = - DnsRecord(type: 'A', name: 'password', content: ip4); - final DnsRecord socialA = - DnsRecord(type: 'A', name: 'social', content: ip4); - final DnsRecord vpn = DnsRecord(type: 'A', name: 'vpn', content: ip4); - - final DnsRecord txt1 = DnsRecord( - type: 'TXT', - name: '_dmarc', - content: 'v=DMARC1; p=none', - ttl: 18000, - ); - - final DnsRecord txt2 = DnsRecord( - type: 'TXT', - name: domainName, - content: 'v=spf1 a mx ip4:$ip4 -all', - ttl: 18000, - ); - - return [ - domainA, - apiA, - cloudA, - gitA, - meetA, - passwordA, - socialA, - mx, - txt1, - txt2, - vpn - ]; - } - - Future setDkim( - final String dkimRecordString, - final ServerDomain cloudFlareDomain, - ) async { - final String domainZoneId = cloudFlareDomain.zoneId; - final String url = '$rootAddress/zones/$domainZoneId/dns_records'; - - final DnsRecord dkimRecord = DnsRecord( - type: 'TXT', - name: 'selector._domainkey', - content: dkimRecordString, - ttl: 18000, - ); - - final Dio client = await getClient(); - await client.post( - url, - data: dkimRecord.toJson(), - ); - - client.close(); - } - - Future> domainList() async { - final String url = '$rootAddress/zones'; - final Dio client = await getClient(); - - final Response response = await client.get( - url, - queryParameters: {'per_page': 50}, - ); - - close(client); - return response.data['result'] - .map((final el) => el['name'] as String) - .toList(); - } -} diff --git a/lib/logic/api_maps/generic_result.dart b/lib/logic/api_maps/generic_result.dart new file mode 100644 index 00000000..5ce31561 --- /dev/null +++ b/lib/logic/api_maps/generic_result.dart @@ -0,0 +1,15 @@ +class GenericResult { + GenericResult({ + required this.success, + required this.data, + this.message, + this.code, + }); + + /// Whether was a response successfully received, + /// doesn't represent success of the request if `data` is `bool` + final bool success; + final String? message; + final T data; + final int? code; +} diff --git a/lib/logic/api_maps/graphql_maps/graphql_api_map.dart b/lib/logic/api_maps/graphql_maps/graphql_api_map.dart new file mode 100644 index 00000000..6a00f5b6 --- /dev/null +++ b/lib/logic/api_maps/graphql_maps/graphql_api_map.dart @@ -0,0 +1,131 @@ +import 'dart:io'; + +import 'package:graphql_flutter/graphql_flutter.dart'; +import 'package:http/io_client.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/api_maps/tls_options.dart'; +import 'package:selfprivacy/logic/models/message.dart'; + +void _logToAppConsole(final T objectToLog) { + getIt.get().addMessage( + Message( + text: objectToLog.toString(), + ), + ); +} + +class RequestLoggingLink extends Link { + @override + Stream request( + final Request request, [ + final NextLink? forward, + ]) async* { + getIt.get().addMessage( + GraphQlRequestMessage( + operation: request.operation, + variables: request.variables, + context: request.context, + ), + ); + yield* forward!(request); + } +} + +class ResponseLoggingParser extends ResponseParser { + @override + Response parseResponse(final Map body) { + final response = super.parseResponse(body); + getIt.get().addMessage( + GraphQlResponseMessage( + data: response.data, + errors: response.errors, + context: response.context, + ), + ); + return response; + } + + @override + GraphQLError parseError(final Map error) { + final graphQlError = super.parseError(error); + _logToAppConsole(graphQlError); + return graphQlError; + } +} + +abstract class GraphQLApiMap { + Future getClient() async { + IOClient? ioClient; + if (TlsOptions.stagingAcme || !TlsOptions.verifyCertificate) { + final HttpClient httpClient = HttpClient(); + httpClient.badCertificateCallback = ( + final cert, + final host, + final port, + ) => + true; + ioClient = IOClient(httpClient); + } + + final httpLink = HttpLink( + 'https://api.$rootAddress/graphql', + httpClient: ioClient, + parser: ResponseLoggingParser(), + defaultHeaders: {'Accept-Language': _locale}, + ); + + final Link graphQLLink = RequestLoggingLink().concat( + isWithToken + ? AuthLink( + getToken: () async => + customToken == '' ? 'Bearer $_token' : customToken, + ).concat(httpLink) + : httpLink, + ); + + // Every request goes through either chain: + // 1. RequestLoggingLink -> AuthLink -> HttpLink + // 2. RequestLoggingLink -> HttpLink + + return GraphQLClient( + cache: GraphQLCache(), + link: graphQLLink, + ); + } + + Future getSubscriptionClient() async { + final WebSocketLink webSocketLink = WebSocketLink( + 'ws://api.$rootAddress/graphql', + config: SocketClientConfig( + autoReconnect: true, + headers: _token.isEmpty + ? null + : { + 'Authorization': 'Bearer $_token', + 'Accept-Language': _locale, + }, + ), + ); + + return GraphQLClient( + cache: GraphQLCache(), + link: webSocketLink, + ); + } + + String get _locale => getIt.get().localeCode ?? 'en'; + + String get _token { + String token = ''; + final serverDetails = getIt().serverDetails; + if (serverDetails != null) { + token = getIt().serverDetails!.apiToken; + } + return token; + } + + abstract final String? rootAddress; + abstract final bool hasLogger; + abstract final bool isWithToken; + abstract final String customToken; +} diff --git a/lib/logic/api_maps/graphql_maps/schema/backups.graphql b/lib/logic/api_maps/graphql_maps/schema/backups.graphql new file mode 100644 index 00000000..16930940 --- /dev/null +++ b/lib/logic/api_maps/graphql_maps/schema/backups.graphql @@ -0,0 +1,124 @@ +query BackupConfiguration { + backup { + configuration { + autobackupPeriod + encryptionKey + isInitialized + locationId + locationName + provider + autobackupQuotas { + last + daily + weekly + monthly + yearly + } + } + } +} + +query AllBackupSnapshots { + backup { + allSnapshots { + id + createdAt + service { + displayName + id + } + reason + } + } +} + +fragment genericBackupConfigReturn on GenericBackupConfigReturn { + code + message + success + configuration { + provider + encryptionKey + isInitialized + autobackupPeriod + locationName + locationId + autobackupQuotas { + last + daily + weekly + monthly + yearly + } + } +} + +mutation ForceSnapshotsReload { + backup { + forceSnapshotsReload { + ...basicMutationReturnFields + } + } +} + +mutation StartBackup($serviceId: String!) { + backup { + startBackup(serviceId: $serviceId) { + ...basicMutationReturnFields + job { + ...basicApiJobsFields + } + } + } +} + +mutation SetAutobackupPeriod($period: Int = null) { + backup { + setAutobackupPeriod(period: $period) { + ...genericBackupConfigReturn + } + } +} + +mutation setAutobackupQuotas($quotas: AutobackupQuotasInput!) { + backup { + setAutobackupQuotas(quotas: $quotas) { + ...genericBackupConfigReturn + } + } +} + +mutation RemoveRepository { + backup { + removeRepository { + ...genericBackupConfigReturn + } + } +} + +mutation InitializeRepository($repository: InitializeRepositoryInput!) { + backup { + initializeRepository(repository: $repository) { + ...genericBackupConfigReturn + } + } +} + +mutation RestoreBackup($snapshotId: String!, $strategy: RestoreStrategy! = DOWNLOAD_VERIFY_OVERWRITE) { + backup { + restoreBackup(snapshotId: $snapshotId, strategy: $strategy) { + ...basicMutationReturnFields + job { + ...basicApiJobsFields + } + } + } +} + +mutation ForgetSnapshot($snapshotId: String!) { + backup { + forgetSnapshot(snapshotId: $snapshotId) { + ...basicMutationReturnFields + } + } +} diff --git a/lib/logic/api_maps/graphql_maps/schema/backups.graphql.dart b/lib/logic/api_maps/graphql_maps/schema/backups.graphql.dart new file mode 100644 index 00000000..1df1e8ac --- /dev/null +++ b/lib/logic/api_maps/graphql_maps/schema/backups.graphql.dart @@ -0,0 +1,7663 @@ +import 'dart:async'; +import 'package:gql/ast.dart'; +import 'package:graphql/client.dart' as graphql; +import 'package:selfprivacy/utils/scalars.dart'; +import 'schema.graphql.dart'; +import 'server_api.graphql.dart'; + +class Fragment$genericBackupConfigReturn { + Fragment$genericBackupConfigReturn({ + required this.code, + required this.message, + required this.success, + this.configuration, + this.$__typename = 'GenericBackupConfigReturn', + }); + + factory Fragment$genericBackupConfigReturn.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$configuration = json['configuration']; + final l$$__typename = json['__typename']; + return Fragment$genericBackupConfigReturn( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + configuration: l$configuration == null + ? null + : Fragment$genericBackupConfigReturn$configuration.fromJson( + (l$configuration as Map)), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final Fragment$genericBackupConfigReturn$configuration? configuration; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$configuration = configuration; + _resultData['configuration'] = l$configuration?.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$configuration = configuration; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$configuration, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Fragment$genericBackupConfigReturn) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$configuration = configuration; + final lOther$configuration = other.configuration; + if (l$configuration != lOther$configuration) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$genericBackupConfigReturn + on Fragment$genericBackupConfigReturn { + CopyWith$Fragment$genericBackupConfigReturn< + Fragment$genericBackupConfigReturn> + get copyWith => CopyWith$Fragment$genericBackupConfigReturn( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$genericBackupConfigReturn { + factory CopyWith$Fragment$genericBackupConfigReturn( + Fragment$genericBackupConfigReturn instance, + TRes Function(Fragment$genericBackupConfigReturn) then, + ) = _CopyWithImpl$Fragment$genericBackupConfigReturn; + + factory CopyWith$Fragment$genericBackupConfigReturn.stub(TRes res) = + _CopyWithStubImpl$Fragment$genericBackupConfigReturn; + + TRes call({ + int? code, + String? message, + bool? success, + Fragment$genericBackupConfigReturn$configuration? configuration, + String? $__typename, + }); + CopyWith$Fragment$genericBackupConfigReturn$configuration + get configuration; +} + +class _CopyWithImpl$Fragment$genericBackupConfigReturn + implements CopyWith$Fragment$genericBackupConfigReturn { + _CopyWithImpl$Fragment$genericBackupConfigReturn( + this._instance, + this._then, + ); + + final Fragment$genericBackupConfigReturn _instance; + + final TRes Function(Fragment$genericBackupConfigReturn) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? configuration = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$genericBackupConfigReturn( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + configuration: configuration == _undefined + ? _instance.configuration + : (configuration + as Fragment$genericBackupConfigReturn$configuration?), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Fragment$genericBackupConfigReturn$configuration + get configuration { + final local$configuration = _instance.configuration; + return local$configuration == null + ? CopyWith$Fragment$genericBackupConfigReturn$configuration.stub( + _then(_instance)) + : CopyWith$Fragment$genericBackupConfigReturn$configuration( + local$configuration, (e) => call(configuration: e)); + } +} + +class _CopyWithStubImpl$Fragment$genericBackupConfigReturn + implements CopyWith$Fragment$genericBackupConfigReturn { + _CopyWithStubImpl$Fragment$genericBackupConfigReturn(this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + Fragment$genericBackupConfigReturn$configuration? configuration, + String? $__typename, + }) => + _res; + CopyWith$Fragment$genericBackupConfigReturn$configuration + get configuration => + CopyWith$Fragment$genericBackupConfigReturn$configuration.stub(_res); +} + +const fragmentDefinitiongenericBackupConfigReturn = FragmentDefinitionNode( + name: NameNode(value: 'genericBackupConfigReturn'), + typeCondition: TypeConditionNode( + on: NamedTypeNode( + name: NameNode(value: 'GenericBackupConfigReturn'), + isNonNull: false, + )), + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'code'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'message'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'success'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'configuration'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'provider'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'encryptionKey'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'isInitialized'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'autobackupPeriod'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'locationName'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'locationId'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'autobackupQuotas'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'last'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'daily'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'weekly'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'monthly'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'yearly'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), +); +const documentNodeFragmentgenericBackupConfigReturn = + DocumentNode(definitions: [ + fragmentDefinitiongenericBackupConfigReturn, +]); + +extension ClientExtension$Fragment$genericBackupConfigReturn + on graphql.GraphQLClient { + void writeFragment$genericBackupConfigReturn({ + required Fragment$genericBackupConfigReturn data, + required Map idFields, + bool broadcast = true, + }) => + this.writeFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'genericBackupConfigReturn', + document: documentNodeFragmentgenericBackupConfigReturn, + ), + ), + data: data.toJson(), + broadcast: broadcast, + ); + Fragment$genericBackupConfigReturn? readFragment$genericBackupConfigReturn({ + required Map idFields, + bool optimistic = true, + }) { + final result = this.readFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'genericBackupConfigReturn', + document: documentNodeFragmentgenericBackupConfigReturn, + ), + ), + optimistic: optimistic, + ); + return result == null + ? null + : Fragment$genericBackupConfigReturn.fromJson(result); + } +} + +class Fragment$genericBackupConfigReturn$configuration { + Fragment$genericBackupConfigReturn$configuration({ + required this.provider, + required this.encryptionKey, + required this.isInitialized, + this.autobackupPeriod, + this.locationName, + this.locationId, + required this.autobackupQuotas, + this.$__typename = 'BackupConfiguration', + }); + + factory Fragment$genericBackupConfigReturn$configuration.fromJson( + Map json) { + final l$provider = json['provider']; + final l$encryptionKey = json['encryptionKey']; + final l$isInitialized = json['isInitialized']; + final l$autobackupPeriod = json['autobackupPeriod']; + final l$locationName = json['locationName']; + final l$locationId = json['locationId']; + final l$autobackupQuotas = json['autobackupQuotas']; + final l$$__typename = json['__typename']; + return Fragment$genericBackupConfigReturn$configuration( + provider: fromJson$Enum$BackupProvider((l$provider as String)), + encryptionKey: (l$encryptionKey as String), + isInitialized: (l$isInitialized as bool), + autobackupPeriod: (l$autobackupPeriod as int?), + locationName: (l$locationName as String?), + locationId: (l$locationId as String?), + autobackupQuotas: + Fragment$genericBackupConfigReturn$configuration$autobackupQuotas + .fromJson((l$autobackupQuotas as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Enum$BackupProvider provider; + + final String encryptionKey; + + final bool isInitialized; + + final int? autobackupPeriod; + + final String? locationName; + + final String? locationId; + + final Fragment$genericBackupConfigReturn$configuration$autobackupQuotas + autobackupQuotas; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$provider = provider; + _resultData['provider'] = toJson$Enum$BackupProvider(l$provider); + final l$encryptionKey = encryptionKey; + _resultData['encryptionKey'] = l$encryptionKey; + final l$isInitialized = isInitialized; + _resultData['isInitialized'] = l$isInitialized; + final l$autobackupPeriod = autobackupPeriod; + _resultData['autobackupPeriod'] = l$autobackupPeriod; + final l$locationName = locationName; + _resultData['locationName'] = l$locationName; + final l$locationId = locationId; + _resultData['locationId'] = l$locationId; + final l$autobackupQuotas = autobackupQuotas; + _resultData['autobackupQuotas'] = l$autobackupQuotas.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$provider = provider; + final l$encryptionKey = encryptionKey; + final l$isInitialized = isInitialized; + final l$autobackupPeriod = autobackupPeriod; + final l$locationName = locationName; + final l$locationId = locationId; + final l$autobackupQuotas = autobackupQuotas; + final l$$__typename = $__typename; + return Object.hashAll([ + l$provider, + l$encryptionKey, + l$isInitialized, + l$autobackupPeriod, + l$locationName, + l$locationId, + l$autobackupQuotas, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Fragment$genericBackupConfigReturn$configuration) || + runtimeType != other.runtimeType) { + return false; + } + final l$provider = provider; + final lOther$provider = other.provider; + if (l$provider != lOther$provider) { + return false; + } + final l$encryptionKey = encryptionKey; + final lOther$encryptionKey = other.encryptionKey; + if (l$encryptionKey != lOther$encryptionKey) { + return false; + } + final l$isInitialized = isInitialized; + final lOther$isInitialized = other.isInitialized; + if (l$isInitialized != lOther$isInitialized) { + return false; + } + final l$autobackupPeriod = autobackupPeriod; + final lOther$autobackupPeriod = other.autobackupPeriod; + if (l$autobackupPeriod != lOther$autobackupPeriod) { + return false; + } + final l$locationName = locationName; + final lOther$locationName = other.locationName; + if (l$locationName != lOther$locationName) { + return false; + } + final l$locationId = locationId; + final lOther$locationId = other.locationId; + if (l$locationId != lOther$locationId) { + return false; + } + final l$autobackupQuotas = autobackupQuotas; + final lOther$autobackupQuotas = other.autobackupQuotas; + if (l$autobackupQuotas != lOther$autobackupQuotas) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$genericBackupConfigReturn$configuration + on Fragment$genericBackupConfigReturn$configuration { + CopyWith$Fragment$genericBackupConfigReturn$configuration< + Fragment$genericBackupConfigReturn$configuration> + get copyWith => CopyWith$Fragment$genericBackupConfigReturn$configuration( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$genericBackupConfigReturn$configuration { + factory CopyWith$Fragment$genericBackupConfigReturn$configuration( + Fragment$genericBackupConfigReturn$configuration instance, + TRes Function(Fragment$genericBackupConfigReturn$configuration) then, + ) = _CopyWithImpl$Fragment$genericBackupConfigReturn$configuration; + + factory CopyWith$Fragment$genericBackupConfigReturn$configuration.stub( + TRes res) = + _CopyWithStubImpl$Fragment$genericBackupConfigReturn$configuration; + + TRes call({ + Enum$BackupProvider? provider, + String? encryptionKey, + bool? isInitialized, + int? autobackupPeriod, + String? locationName, + String? locationId, + Fragment$genericBackupConfigReturn$configuration$autobackupQuotas? + autobackupQuotas, + String? $__typename, + }); + CopyWith$Fragment$genericBackupConfigReturn$configuration$autobackupQuotas< + TRes> get autobackupQuotas; +} + +class _CopyWithImpl$Fragment$genericBackupConfigReturn$configuration + implements CopyWith$Fragment$genericBackupConfigReturn$configuration { + _CopyWithImpl$Fragment$genericBackupConfigReturn$configuration( + this._instance, + this._then, + ); + + final Fragment$genericBackupConfigReturn$configuration _instance; + + final TRes Function(Fragment$genericBackupConfigReturn$configuration) _then; + + static const _undefined = {}; + + TRes call({ + Object? provider = _undefined, + Object? encryptionKey = _undefined, + Object? isInitialized = _undefined, + Object? autobackupPeriod = _undefined, + Object? locationName = _undefined, + Object? locationId = _undefined, + Object? autobackupQuotas = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$genericBackupConfigReturn$configuration( + provider: provider == _undefined || provider == null + ? _instance.provider + : (provider as Enum$BackupProvider), + encryptionKey: encryptionKey == _undefined || encryptionKey == null + ? _instance.encryptionKey + : (encryptionKey as String), + isInitialized: isInitialized == _undefined || isInitialized == null + ? _instance.isInitialized + : (isInitialized as bool), + autobackupPeriod: autobackupPeriod == _undefined + ? _instance.autobackupPeriod + : (autobackupPeriod as int?), + locationName: locationName == _undefined + ? _instance.locationName + : (locationName as String?), + locationId: locationId == _undefined + ? _instance.locationId + : (locationId as String?), + autobackupQuotas: autobackupQuotas == _undefined || + autobackupQuotas == null + ? _instance.autobackupQuotas + : (autobackupQuotas + as Fragment$genericBackupConfigReturn$configuration$autobackupQuotas), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Fragment$genericBackupConfigReturn$configuration$autobackupQuotas< + TRes> get autobackupQuotas { + final local$autobackupQuotas = _instance.autobackupQuotas; + return CopyWith$Fragment$genericBackupConfigReturn$configuration$autobackupQuotas( + local$autobackupQuotas, (e) => call(autobackupQuotas: e)); + } +} + +class _CopyWithStubImpl$Fragment$genericBackupConfigReturn$configuration + implements CopyWith$Fragment$genericBackupConfigReturn$configuration { + _CopyWithStubImpl$Fragment$genericBackupConfigReturn$configuration(this._res); + + TRes _res; + + call({ + Enum$BackupProvider? provider, + String? encryptionKey, + bool? isInitialized, + int? autobackupPeriod, + String? locationName, + String? locationId, + Fragment$genericBackupConfigReturn$configuration$autobackupQuotas? + autobackupQuotas, + String? $__typename, + }) => + _res; + CopyWith$Fragment$genericBackupConfigReturn$configuration$autobackupQuotas< + TRes> + get autobackupQuotas => + CopyWith$Fragment$genericBackupConfigReturn$configuration$autobackupQuotas + .stub(_res); +} + +class Fragment$genericBackupConfigReturn$configuration$autobackupQuotas { + Fragment$genericBackupConfigReturn$configuration$autobackupQuotas({ + required this.last, + required this.daily, + required this.weekly, + required this.monthly, + required this.yearly, + this.$__typename = 'AutobackupQuotas', + }); + + factory Fragment$genericBackupConfigReturn$configuration$autobackupQuotas.fromJson( + Map json) { + final l$last = json['last']; + final l$daily = json['daily']; + final l$weekly = json['weekly']; + final l$monthly = json['monthly']; + final l$yearly = json['yearly']; + final l$$__typename = json['__typename']; + return Fragment$genericBackupConfigReturn$configuration$autobackupQuotas( + last: (l$last as int), + daily: (l$daily as int), + weekly: (l$weekly as int), + monthly: (l$monthly as int), + yearly: (l$yearly as int), + $__typename: (l$$__typename as String), + ); + } + + final int last; + + final int daily; + + final int weekly; + + final int monthly; + + final int yearly; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$last = last; + _resultData['last'] = l$last; + final l$daily = daily; + _resultData['daily'] = l$daily; + final l$weekly = weekly; + _resultData['weekly'] = l$weekly; + final l$monthly = monthly; + _resultData['monthly'] = l$monthly; + final l$yearly = yearly; + _resultData['yearly'] = l$yearly; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$last = last; + final l$daily = daily; + final l$weekly = weekly; + final l$monthly = monthly; + final l$yearly = yearly; + final l$$__typename = $__typename; + return Object.hashAll([ + l$last, + l$daily, + l$weekly, + l$monthly, + l$yearly, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other + is Fragment$genericBackupConfigReturn$configuration$autobackupQuotas) || + runtimeType != other.runtimeType) { + return false; + } + final l$last = last; + final lOther$last = other.last; + if (l$last != lOther$last) { + return false; + } + final l$daily = daily; + final lOther$daily = other.daily; + if (l$daily != lOther$daily) { + return false; + } + final l$weekly = weekly; + final lOther$weekly = other.weekly; + if (l$weekly != lOther$weekly) { + return false; + } + final l$monthly = monthly; + final lOther$monthly = other.monthly; + if (l$monthly != lOther$monthly) { + return false; + } + final l$yearly = yearly; + final lOther$yearly = other.yearly; + if (l$yearly != lOther$yearly) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$genericBackupConfigReturn$configuration$autobackupQuotas + on Fragment$genericBackupConfigReturn$configuration$autobackupQuotas { + CopyWith$Fragment$genericBackupConfigReturn$configuration$autobackupQuotas< + Fragment$genericBackupConfigReturn$configuration$autobackupQuotas> + get copyWith => + CopyWith$Fragment$genericBackupConfigReturn$configuration$autobackupQuotas( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$genericBackupConfigReturn$configuration$autobackupQuotas< + TRes> { + factory CopyWith$Fragment$genericBackupConfigReturn$configuration$autobackupQuotas( + Fragment$genericBackupConfigReturn$configuration$autobackupQuotas instance, + TRes Function( + Fragment$genericBackupConfigReturn$configuration$autobackupQuotas) + then, + ) = _CopyWithImpl$Fragment$genericBackupConfigReturn$configuration$autobackupQuotas; + + factory CopyWith$Fragment$genericBackupConfigReturn$configuration$autobackupQuotas.stub( + TRes res) = + _CopyWithStubImpl$Fragment$genericBackupConfigReturn$configuration$autobackupQuotas; + + TRes call({ + int? last, + int? daily, + int? weekly, + int? monthly, + int? yearly, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$genericBackupConfigReturn$configuration$autobackupQuotas< + TRes> + implements + CopyWith$Fragment$genericBackupConfigReturn$configuration$autobackupQuotas< + TRes> { + _CopyWithImpl$Fragment$genericBackupConfigReturn$configuration$autobackupQuotas( + this._instance, + this._then, + ); + + final Fragment$genericBackupConfigReturn$configuration$autobackupQuotas + _instance; + + final TRes Function( + Fragment$genericBackupConfigReturn$configuration$autobackupQuotas) _then; + + static const _undefined = {}; + + TRes call({ + Object? last = _undefined, + Object? daily = _undefined, + Object? weekly = _undefined, + Object? monthly = _undefined, + Object? yearly = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$genericBackupConfigReturn$configuration$autobackupQuotas( + last: + last == _undefined || last == null ? _instance.last : (last as int), + daily: daily == _undefined || daily == null + ? _instance.daily + : (daily as int), + weekly: weekly == _undefined || weekly == null + ? _instance.weekly + : (weekly as int), + monthly: monthly == _undefined || monthly == null + ? _instance.monthly + : (monthly as int), + yearly: yearly == _undefined || yearly == null + ? _instance.yearly + : (yearly as int), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$genericBackupConfigReturn$configuration$autobackupQuotas< + TRes> + implements + CopyWith$Fragment$genericBackupConfigReturn$configuration$autobackupQuotas< + TRes> { + _CopyWithStubImpl$Fragment$genericBackupConfigReturn$configuration$autobackupQuotas( + this._res); + + TRes _res; + + call({ + int? last, + int? daily, + int? weekly, + int? monthly, + int? yearly, + String? $__typename, + }) => + _res; +} + +class Query$BackupConfiguration { + Query$BackupConfiguration({ + required this.backup, + this.$__typename = 'Query', + }); + + factory Query$BackupConfiguration.fromJson(Map json) { + final l$backup = json['backup']; + final l$$__typename = json['__typename']; + return Query$BackupConfiguration( + backup: Query$BackupConfiguration$backup.fromJson( + (l$backup as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Query$BackupConfiguration$backup backup; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$backup = backup; + _resultData['backup'] = l$backup.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$backup = backup; + final l$$__typename = $__typename; + return Object.hashAll([ + l$backup, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$BackupConfiguration) || + runtimeType != other.runtimeType) { + return false; + } + final l$backup = backup; + final lOther$backup = other.backup; + if (l$backup != lOther$backup) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$BackupConfiguration + on Query$BackupConfiguration { + CopyWith$Query$BackupConfiguration get copyWith => + CopyWith$Query$BackupConfiguration( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$BackupConfiguration { + factory CopyWith$Query$BackupConfiguration( + Query$BackupConfiguration instance, + TRes Function(Query$BackupConfiguration) then, + ) = _CopyWithImpl$Query$BackupConfiguration; + + factory CopyWith$Query$BackupConfiguration.stub(TRes res) = + _CopyWithStubImpl$Query$BackupConfiguration; + + TRes call({ + Query$BackupConfiguration$backup? backup, + String? $__typename, + }); + CopyWith$Query$BackupConfiguration$backup get backup; +} + +class _CopyWithImpl$Query$BackupConfiguration + implements CopyWith$Query$BackupConfiguration { + _CopyWithImpl$Query$BackupConfiguration( + this._instance, + this._then, + ); + + final Query$BackupConfiguration _instance; + + final TRes Function(Query$BackupConfiguration) _then; + + static const _undefined = {}; + + TRes call({ + Object? backup = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$BackupConfiguration( + backup: backup == _undefined || backup == null + ? _instance.backup + : (backup as Query$BackupConfiguration$backup), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$BackupConfiguration$backup get backup { + final local$backup = _instance.backup; + return CopyWith$Query$BackupConfiguration$backup( + local$backup, (e) => call(backup: e)); + } +} + +class _CopyWithStubImpl$Query$BackupConfiguration + implements CopyWith$Query$BackupConfiguration { + _CopyWithStubImpl$Query$BackupConfiguration(this._res); + + TRes _res; + + call({ + Query$BackupConfiguration$backup? backup, + String? $__typename, + }) => + _res; + CopyWith$Query$BackupConfiguration$backup get backup => + CopyWith$Query$BackupConfiguration$backup.stub(_res); +} + +const documentNodeQueryBackupConfiguration = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.query, + name: NameNode(value: 'BackupConfiguration'), + variableDefinitions: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'backup'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'configuration'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'autobackupPeriod'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'encryptionKey'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'isInitialized'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'locationId'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'locationName'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'provider'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'autobackupQuotas'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'last'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'daily'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'weekly'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'monthly'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'yearly'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), +]); +Query$BackupConfiguration _parserFn$Query$BackupConfiguration( + Map data) => + Query$BackupConfiguration.fromJson(data); +typedef OnQueryComplete$Query$BackupConfiguration = FutureOr Function( + Map?, + Query$BackupConfiguration?, +); + +class Options$Query$BackupConfiguration + extends graphql.QueryOptions { + Options$Query$BackupConfiguration({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$BackupConfiguration? typedOptimisticResult, + Duration? pollInterval, + graphql.Context? context, + OnQueryComplete$Query$BackupConfiguration? onComplete, + graphql.OnQueryError? onError, + }) : onCompleteWithParsed = onComplete, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + pollInterval: pollInterval, + context: context, + onComplete: onComplete == null + ? null + : (data) => onComplete( + data, + data == null + ? null + : _parserFn$Query$BackupConfiguration(data), + ), + onError: onError, + document: documentNodeQueryBackupConfiguration, + parserFn: _parserFn$Query$BackupConfiguration, + ); + + final OnQueryComplete$Query$BackupConfiguration? onCompleteWithParsed; + + @override + List get properties => [ + ...super.onComplete == null + ? super.properties + : super.properties.where((property) => property != onComplete), + onCompleteWithParsed, + ]; +} + +class WatchOptions$Query$BackupConfiguration + extends graphql.WatchQueryOptions { + WatchOptions$Query$BackupConfiguration({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$BackupConfiguration? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeQueryBackupConfiguration, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Query$BackupConfiguration, + ); +} + +class FetchMoreOptions$Query$BackupConfiguration + extends graphql.FetchMoreOptions { + FetchMoreOptions$Query$BackupConfiguration( + {required graphql.UpdateQuery updateQuery}) + : super( + updateQuery: updateQuery, + document: documentNodeQueryBackupConfiguration, + ); +} + +extension ClientExtension$Query$BackupConfiguration on graphql.GraphQLClient { + Future> + query$BackupConfiguration( + [Options$Query$BackupConfiguration? options]) async => + await this.query(options ?? Options$Query$BackupConfiguration()); + graphql.ObservableQuery + watchQuery$BackupConfiguration( + [WatchOptions$Query$BackupConfiguration? options]) => + this.watchQuery(options ?? WatchOptions$Query$BackupConfiguration()); + void writeQuery$BackupConfiguration({ + required Query$BackupConfiguration data, + bool broadcast = true, + }) => + this.writeQuery( + graphql.Request( + operation: graphql.Operation( + document: documentNodeQueryBackupConfiguration)), + data: data.toJson(), + broadcast: broadcast, + ); + Query$BackupConfiguration? readQuery$BackupConfiguration( + {bool optimistic = true}) { + final result = this.readQuery( + graphql.Request( + operation: graphql.Operation( + document: documentNodeQueryBackupConfiguration)), + optimistic: optimistic, + ); + return result == null ? null : Query$BackupConfiguration.fromJson(result); + } +} + +class Query$BackupConfiguration$backup { + Query$BackupConfiguration$backup({ + required this.configuration, + this.$__typename = 'Backup', + }); + + factory Query$BackupConfiguration$backup.fromJson(Map json) { + final l$configuration = json['configuration']; + final l$$__typename = json['__typename']; + return Query$BackupConfiguration$backup( + configuration: Query$BackupConfiguration$backup$configuration.fromJson( + (l$configuration as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Query$BackupConfiguration$backup$configuration configuration; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$configuration = configuration; + _resultData['configuration'] = l$configuration.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$configuration = configuration; + final l$$__typename = $__typename; + return Object.hashAll([ + l$configuration, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$BackupConfiguration$backup) || + runtimeType != other.runtimeType) { + return false; + } + final l$configuration = configuration; + final lOther$configuration = other.configuration; + if (l$configuration != lOther$configuration) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$BackupConfiguration$backup + on Query$BackupConfiguration$backup { + CopyWith$Query$BackupConfiguration$backup + get copyWith => CopyWith$Query$BackupConfiguration$backup( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$BackupConfiguration$backup { + factory CopyWith$Query$BackupConfiguration$backup( + Query$BackupConfiguration$backup instance, + TRes Function(Query$BackupConfiguration$backup) then, + ) = _CopyWithImpl$Query$BackupConfiguration$backup; + + factory CopyWith$Query$BackupConfiguration$backup.stub(TRes res) = + _CopyWithStubImpl$Query$BackupConfiguration$backup; + + TRes call({ + Query$BackupConfiguration$backup$configuration? configuration, + String? $__typename, + }); + CopyWith$Query$BackupConfiguration$backup$configuration + get configuration; +} + +class _CopyWithImpl$Query$BackupConfiguration$backup + implements CopyWith$Query$BackupConfiguration$backup { + _CopyWithImpl$Query$BackupConfiguration$backup( + this._instance, + this._then, + ); + + final Query$BackupConfiguration$backup _instance; + + final TRes Function(Query$BackupConfiguration$backup) _then; + + static const _undefined = {}; + + TRes call({ + Object? configuration = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$BackupConfiguration$backup( + configuration: configuration == _undefined || configuration == null + ? _instance.configuration + : (configuration as Query$BackupConfiguration$backup$configuration), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$BackupConfiguration$backup$configuration + get configuration { + final local$configuration = _instance.configuration; + return CopyWith$Query$BackupConfiguration$backup$configuration( + local$configuration, (e) => call(configuration: e)); + } +} + +class _CopyWithStubImpl$Query$BackupConfiguration$backup + implements CopyWith$Query$BackupConfiguration$backup { + _CopyWithStubImpl$Query$BackupConfiguration$backup(this._res); + + TRes _res; + + call({ + Query$BackupConfiguration$backup$configuration? configuration, + String? $__typename, + }) => + _res; + CopyWith$Query$BackupConfiguration$backup$configuration + get configuration => + CopyWith$Query$BackupConfiguration$backup$configuration.stub(_res); +} + +class Query$BackupConfiguration$backup$configuration { + Query$BackupConfiguration$backup$configuration({ + this.autobackupPeriod, + required this.encryptionKey, + required this.isInitialized, + this.locationId, + this.locationName, + required this.provider, + required this.autobackupQuotas, + this.$__typename = 'BackupConfiguration', + }); + + factory Query$BackupConfiguration$backup$configuration.fromJson( + Map json) { + final l$autobackupPeriod = json['autobackupPeriod']; + final l$encryptionKey = json['encryptionKey']; + final l$isInitialized = json['isInitialized']; + final l$locationId = json['locationId']; + final l$locationName = json['locationName']; + final l$provider = json['provider']; + final l$autobackupQuotas = json['autobackupQuotas']; + final l$$__typename = json['__typename']; + return Query$BackupConfiguration$backup$configuration( + autobackupPeriod: (l$autobackupPeriod as int?), + encryptionKey: (l$encryptionKey as String), + isInitialized: (l$isInitialized as bool), + locationId: (l$locationId as String?), + locationName: (l$locationName as String?), + provider: fromJson$Enum$BackupProvider((l$provider as String)), + autobackupQuotas: + Query$BackupConfiguration$backup$configuration$autobackupQuotas + .fromJson((l$autobackupQuotas as Map)), + $__typename: (l$$__typename as String), + ); + } + + final int? autobackupPeriod; + + final String encryptionKey; + + final bool isInitialized; + + final String? locationId; + + final String? locationName; + + final Enum$BackupProvider provider; + + final Query$BackupConfiguration$backup$configuration$autobackupQuotas + autobackupQuotas; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$autobackupPeriod = autobackupPeriod; + _resultData['autobackupPeriod'] = l$autobackupPeriod; + final l$encryptionKey = encryptionKey; + _resultData['encryptionKey'] = l$encryptionKey; + final l$isInitialized = isInitialized; + _resultData['isInitialized'] = l$isInitialized; + final l$locationId = locationId; + _resultData['locationId'] = l$locationId; + final l$locationName = locationName; + _resultData['locationName'] = l$locationName; + final l$provider = provider; + _resultData['provider'] = toJson$Enum$BackupProvider(l$provider); + final l$autobackupQuotas = autobackupQuotas; + _resultData['autobackupQuotas'] = l$autobackupQuotas.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$autobackupPeriod = autobackupPeriod; + final l$encryptionKey = encryptionKey; + final l$isInitialized = isInitialized; + final l$locationId = locationId; + final l$locationName = locationName; + final l$provider = provider; + final l$autobackupQuotas = autobackupQuotas; + final l$$__typename = $__typename; + return Object.hashAll([ + l$autobackupPeriod, + l$encryptionKey, + l$isInitialized, + l$locationId, + l$locationName, + l$provider, + l$autobackupQuotas, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$BackupConfiguration$backup$configuration) || + runtimeType != other.runtimeType) { + return false; + } + final l$autobackupPeriod = autobackupPeriod; + final lOther$autobackupPeriod = other.autobackupPeriod; + if (l$autobackupPeriod != lOther$autobackupPeriod) { + return false; + } + final l$encryptionKey = encryptionKey; + final lOther$encryptionKey = other.encryptionKey; + if (l$encryptionKey != lOther$encryptionKey) { + return false; + } + final l$isInitialized = isInitialized; + final lOther$isInitialized = other.isInitialized; + if (l$isInitialized != lOther$isInitialized) { + return false; + } + final l$locationId = locationId; + final lOther$locationId = other.locationId; + if (l$locationId != lOther$locationId) { + return false; + } + final l$locationName = locationName; + final lOther$locationName = other.locationName; + if (l$locationName != lOther$locationName) { + return false; + } + final l$provider = provider; + final lOther$provider = other.provider; + if (l$provider != lOther$provider) { + return false; + } + final l$autobackupQuotas = autobackupQuotas; + final lOther$autobackupQuotas = other.autobackupQuotas; + if (l$autobackupQuotas != lOther$autobackupQuotas) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$BackupConfiguration$backup$configuration + on Query$BackupConfiguration$backup$configuration { + CopyWith$Query$BackupConfiguration$backup$configuration< + Query$BackupConfiguration$backup$configuration> + get copyWith => CopyWith$Query$BackupConfiguration$backup$configuration( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$BackupConfiguration$backup$configuration { + factory CopyWith$Query$BackupConfiguration$backup$configuration( + Query$BackupConfiguration$backup$configuration instance, + TRes Function(Query$BackupConfiguration$backup$configuration) then, + ) = _CopyWithImpl$Query$BackupConfiguration$backup$configuration; + + factory CopyWith$Query$BackupConfiguration$backup$configuration.stub( + TRes res) = + _CopyWithStubImpl$Query$BackupConfiguration$backup$configuration; + + TRes call({ + int? autobackupPeriod, + String? encryptionKey, + bool? isInitialized, + String? locationId, + String? locationName, + Enum$BackupProvider? provider, + Query$BackupConfiguration$backup$configuration$autobackupQuotas? + autobackupQuotas, + String? $__typename, + }); + CopyWith$Query$BackupConfiguration$backup$configuration$autobackupQuotas + get autobackupQuotas; +} + +class _CopyWithImpl$Query$BackupConfiguration$backup$configuration + implements CopyWith$Query$BackupConfiguration$backup$configuration { + _CopyWithImpl$Query$BackupConfiguration$backup$configuration( + this._instance, + this._then, + ); + + final Query$BackupConfiguration$backup$configuration _instance; + + final TRes Function(Query$BackupConfiguration$backup$configuration) _then; + + static const _undefined = {}; + + TRes call({ + Object? autobackupPeriod = _undefined, + Object? encryptionKey = _undefined, + Object? isInitialized = _undefined, + Object? locationId = _undefined, + Object? locationName = _undefined, + Object? provider = _undefined, + Object? autobackupQuotas = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$BackupConfiguration$backup$configuration( + autobackupPeriod: autobackupPeriod == _undefined + ? _instance.autobackupPeriod + : (autobackupPeriod as int?), + encryptionKey: encryptionKey == _undefined || encryptionKey == null + ? _instance.encryptionKey + : (encryptionKey as String), + isInitialized: isInitialized == _undefined || isInitialized == null + ? _instance.isInitialized + : (isInitialized as bool), + locationId: locationId == _undefined + ? _instance.locationId + : (locationId as String?), + locationName: locationName == _undefined + ? _instance.locationName + : (locationName as String?), + provider: provider == _undefined || provider == null + ? _instance.provider + : (provider as Enum$BackupProvider), + autobackupQuotas: autobackupQuotas == _undefined || + autobackupQuotas == null + ? _instance.autobackupQuotas + : (autobackupQuotas + as Query$BackupConfiguration$backup$configuration$autobackupQuotas), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$BackupConfiguration$backup$configuration$autobackupQuotas + get autobackupQuotas { + final local$autobackupQuotas = _instance.autobackupQuotas; + return CopyWith$Query$BackupConfiguration$backup$configuration$autobackupQuotas( + local$autobackupQuotas, (e) => call(autobackupQuotas: e)); + } +} + +class _CopyWithStubImpl$Query$BackupConfiguration$backup$configuration + implements CopyWith$Query$BackupConfiguration$backup$configuration { + _CopyWithStubImpl$Query$BackupConfiguration$backup$configuration(this._res); + + TRes _res; + + call({ + int? autobackupPeriod, + String? encryptionKey, + bool? isInitialized, + String? locationId, + String? locationName, + Enum$BackupProvider? provider, + Query$BackupConfiguration$backup$configuration$autobackupQuotas? + autobackupQuotas, + String? $__typename, + }) => + _res; + CopyWith$Query$BackupConfiguration$backup$configuration$autobackupQuotas + get autobackupQuotas => + CopyWith$Query$BackupConfiguration$backup$configuration$autobackupQuotas + .stub(_res); +} + +class Query$BackupConfiguration$backup$configuration$autobackupQuotas { + Query$BackupConfiguration$backup$configuration$autobackupQuotas({ + required this.last, + required this.daily, + required this.weekly, + required this.monthly, + required this.yearly, + this.$__typename = 'AutobackupQuotas', + }); + + factory Query$BackupConfiguration$backup$configuration$autobackupQuotas.fromJson( + Map json) { + final l$last = json['last']; + final l$daily = json['daily']; + final l$weekly = json['weekly']; + final l$monthly = json['monthly']; + final l$yearly = json['yearly']; + final l$$__typename = json['__typename']; + return Query$BackupConfiguration$backup$configuration$autobackupQuotas( + last: (l$last as int), + daily: (l$daily as int), + weekly: (l$weekly as int), + monthly: (l$monthly as int), + yearly: (l$yearly as int), + $__typename: (l$$__typename as String), + ); + } + + final int last; + + final int daily; + + final int weekly; + + final int monthly; + + final int yearly; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$last = last; + _resultData['last'] = l$last; + final l$daily = daily; + _resultData['daily'] = l$daily; + final l$weekly = weekly; + _resultData['weekly'] = l$weekly; + final l$monthly = monthly; + _resultData['monthly'] = l$monthly; + final l$yearly = yearly; + _resultData['yearly'] = l$yearly; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$last = last; + final l$daily = daily; + final l$weekly = weekly; + final l$monthly = monthly; + final l$yearly = yearly; + final l$$__typename = $__typename; + return Object.hashAll([ + l$last, + l$daily, + l$weekly, + l$monthly, + l$yearly, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other + is Query$BackupConfiguration$backup$configuration$autobackupQuotas) || + runtimeType != other.runtimeType) { + return false; + } + final l$last = last; + final lOther$last = other.last; + if (l$last != lOther$last) { + return false; + } + final l$daily = daily; + final lOther$daily = other.daily; + if (l$daily != lOther$daily) { + return false; + } + final l$weekly = weekly; + final lOther$weekly = other.weekly; + if (l$weekly != lOther$weekly) { + return false; + } + final l$monthly = monthly; + final lOther$monthly = other.monthly; + if (l$monthly != lOther$monthly) { + return false; + } + final l$yearly = yearly; + final lOther$yearly = other.yearly; + if (l$yearly != lOther$yearly) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$BackupConfiguration$backup$configuration$autobackupQuotas + on Query$BackupConfiguration$backup$configuration$autobackupQuotas { + CopyWith$Query$BackupConfiguration$backup$configuration$autobackupQuotas< + Query$BackupConfiguration$backup$configuration$autobackupQuotas> + get copyWith => + CopyWith$Query$BackupConfiguration$backup$configuration$autobackupQuotas( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$BackupConfiguration$backup$configuration$autobackupQuotas< + TRes> { + factory CopyWith$Query$BackupConfiguration$backup$configuration$autobackupQuotas( + Query$BackupConfiguration$backup$configuration$autobackupQuotas instance, + TRes Function( + Query$BackupConfiguration$backup$configuration$autobackupQuotas) + then, + ) = _CopyWithImpl$Query$BackupConfiguration$backup$configuration$autobackupQuotas; + + factory CopyWith$Query$BackupConfiguration$backup$configuration$autobackupQuotas.stub( + TRes res) = + _CopyWithStubImpl$Query$BackupConfiguration$backup$configuration$autobackupQuotas; + + TRes call({ + int? last, + int? daily, + int? weekly, + int? monthly, + int? yearly, + String? $__typename, + }); +} + +class _CopyWithImpl$Query$BackupConfiguration$backup$configuration$autobackupQuotas< + TRes> + implements + CopyWith$Query$BackupConfiguration$backup$configuration$autobackupQuotas< + TRes> { + _CopyWithImpl$Query$BackupConfiguration$backup$configuration$autobackupQuotas( + this._instance, + this._then, + ); + + final Query$BackupConfiguration$backup$configuration$autobackupQuotas + _instance; + + final TRes Function( + Query$BackupConfiguration$backup$configuration$autobackupQuotas) _then; + + static const _undefined = {}; + + TRes call({ + Object? last = _undefined, + Object? daily = _undefined, + Object? weekly = _undefined, + Object? monthly = _undefined, + Object? yearly = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$BackupConfiguration$backup$configuration$autobackupQuotas( + last: + last == _undefined || last == null ? _instance.last : (last as int), + daily: daily == _undefined || daily == null + ? _instance.daily + : (daily as int), + weekly: weekly == _undefined || weekly == null + ? _instance.weekly + : (weekly as int), + monthly: monthly == _undefined || monthly == null + ? _instance.monthly + : (monthly as int), + yearly: yearly == _undefined || yearly == null + ? _instance.yearly + : (yearly as int), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Query$BackupConfiguration$backup$configuration$autobackupQuotas< + TRes> + implements + CopyWith$Query$BackupConfiguration$backup$configuration$autobackupQuotas< + TRes> { + _CopyWithStubImpl$Query$BackupConfiguration$backup$configuration$autobackupQuotas( + this._res); + + TRes _res; + + call({ + int? last, + int? daily, + int? weekly, + int? monthly, + int? yearly, + String? $__typename, + }) => + _res; +} + +class Query$AllBackupSnapshots { + Query$AllBackupSnapshots({ + required this.backup, + this.$__typename = 'Query', + }); + + factory Query$AllBackupSnapshots.fromJson(Map json) { + final l$backup = json['backup']; + final l$$__typename = json['__typename']; + return Query$AllBackupSnapshots( + backup: Query$AllBackupSnapshots$backup.fromJson( + (l$backup as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Query$AllBackupSnapshots$backup backup; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$backup = backup; + _resultData['backup'] = l$backup.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$backup = backup; + final l$$__typename = $__typename; + return Object.hashAll([ + l$backup, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$AllBackupSnapshots) || + runtimeType != other.runtimeType) { + return false; + } + final l$backup = backup; + final lOther$backup = other.backup; + if (l$backup != lOther$backup) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$AllBackupSnapshots + on Query$AllBackupSnapshots { + CopyWith$Query$AllBackupSnapshots get copyWith => + CopyWith$Query$AllBackupSnapshots( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$AllBackupSnapshots { + factory CopyWith$Query$AllBackupSnapshots( + Query$AllBackupSnapshots instance, + TRes Function(Query$AllBackupSnapshots) then, + ) = _CopyWithImpl$Query$AllBackupSnapshots; + + factory CopyWith$Query$AllBackupSnapshots.stub(TRes res) = + _CopyWithStubImpl$Query$AllBackupSnapshots; + + TRes call({ + Query$AllBackupSnapshots$backup? backup, + String? $__typename, + }); + CopyWith$Query$AllBackupSnapshots$backup get backup; +} + +class _CopyWithImpl$Query$AllBackupSnapshots + implements CopyWith$Query$AllBackupSnapshots { + _CopyWithImpl$Query$AllBackupSnapshots( + this._instance, + this._then, + ); + + final Query$AllBackupSnapshots _instance; + + final TRes Function(Query$AllBackupSnapshots) _then; + + static const _undefined = {}; + + TRes call({ + Object? backup = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$AllBackupSnapshots( + backup: backup == _undefined || backup == null + ? _instance.backup + : (backup as Query$AllBackupSnapshots$backup), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$AllBackupSnapshots$backup get backup { + final local$backup = _instance.backup; + return CopyWith$Query$AllBackupSnapshots$backup( + local$backup, (e) => call(backup: e)); + } +} + +class _CopyWithStubImpl$Query$AllBackupSnapshots + implements CopyWith$Query$AllBackupSnapshots { + _CopyWithStubImpl$Query$AllBackupSnapshots(this._res); + + TRes _res; + + call({ + Query$AllBackupSnapshots$backup? backup, + String? $__typename, + }) => + _res; + CopyWith$Query$AllBackupSnapshots$backup get backup => + CopyWith$Query$AllBackupSnapshots$backup.stub(_res); +} + +const documentNodeQueryAllBackupSnapshots = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.query, + name: NameNode(value: 'AllBackupSnapshots'), + variableDefinitions: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'backup'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'allSnapshots'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'id'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'createdAt'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'service'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'displayName'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'id'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: 'reason'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), +]); +Query$AllBackupSnapshots _parserFn$Query$AllBackupSnapshots( + Map data) => + Query$AllBackupSnapshots.fromJson(data); +typedef OnQueryComplete$Query$AllBackupSnapshots = FutureOr Function( + Map?, + Query$AllBackupSnapshots?, +); + +class Options$Query$AllBackupSnapshots + extends graphql.QueryOptions { + Options$Query$AllBackupSnapshots({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$AllBackupSnapshots? typedOptimisticResult, + Duration? pollInterval, + graphql.Context? context, + OnQueryComplete$Query$AllBackupSnapshots? onComplete, + graphql.OnQueryError? onError, + }) : onCompleteWithParsed = onComplete, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + pollInterval: pollInterval, + context: context, + onComplete: onComplete == null + ? null + : (data) => onComplete( + data, + data == null + ? null + : _parserFn$Query$AllBackupSnapshots(data), + ), + onError: onError, + document: documentNodeQueryAllBackupSnapshots, + parserFn: _parserFn$Query$AllBackupSnapshots, + ); + + final OnQueryComplete$Query$AllBackupSnapshots? onCompleteWithParsed; + + @override + List get properties => [ + ...super.onComplete == null + ? super.properties + : super.properties.where((property) => property != onComplete), + onCompleteWithParsed, + ]; +} + +class WatchOptions$Query$AllBackupSnapshots + extends graphql.WatchQueryOptions { + WatchOptions$Query$AllBackupSnapshots({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$AllBackupSnapshots? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeQueryAllBackupSnapshots, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Query$AllBackupSnapshots, + ); +} + +class FetchMoreOptions$Query$AllBackupSnapshots + extends graphql.FetchMoreOptions { + FetchMoreOptions$Query$AllBackupSnapshots( + {required graphql.UpdateQuery updateQuery}) + : super( + updateQuery: updateQuery, + document: documentNodeQueryAllBackupSnapshots, + ); +} + +extension ClientExtension$Query$AllBackupSnapshots on graphql.GraphQLClient { + Future> + query$AllBackupSnapshots( + [Options$Query$AllBackupSnapshots? options]) async => + await this.query(options ?? Options$Query$AllBackupSnapshots()); + graphql.ObservableQuery + watchQuery$AllBackupSnapshots( + [WatchOptions$Query$AllBackupSnapshots? options]) => + this.watchQuery(options ?? WatchOptions$Query$AllBackupSnapshots()); + void writeQuery$AllBackupSnapshots({ + required Query$AllBackupSnapshots data, + bool broadcast = true, + }) => + this.writeQuery( + graphql.Request( + operation: graphql.Operation( + document: documentNodeQueryAllBackupSnapshots)), + data: data.toJson(), + broadcast: broadcast, + ); + Query$AllBackupSnapshots? readQuery$AllBackupSnapshots( + {bool optimistic = true}) { + final result = this.readQuery( + graphql.Request( + operation: + graphql.Operation(document: documentNodeQueryAllBackupSnapshots)), + optimistic: optimistic, + ); + return result == null ? null : Query$AllBackupSnapshots.fromJson(result); + } +} + +class Query$AllBackupSnapshots$backup { + Query$AllBackupSnapshots$backup({ + required this.allSnapshots, + this.$__typename = 'Backup', + }); + + factory Query$AllBackupSnapshots$backup.fromJson(Map json) { + final l$allSnapshots = json['allSnapshots']; + final l$$__typename = json['__typename']; + return Query$AllBackupSnapshots$backup( + allSnapshots: (l$allSnapshots as List) + .map((e) => Query$AllBackupSnapshots$backup$allSnapshots.fromJson( + (e as Map))) + .toList(), + $__typename: (l$$__typename as String), + ); + } + + final List allSnapshots; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$allSnapshots = allSnapshots; + _resultData['allSnapshots'] = + l$allSnapshots.map((e) => e.toJson()).toList(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$allSnapshots = allSnapshots; + final l$$__typename = $__typename; + return Object.hashAll([ + Object.hashAll(l$allSnapshots.map((v) => v)), + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$AllBackupSnapshots$backup) || + runtimeType != other.runtimeType) { + return false; + } + final l$allSnapshots = allSnapshots; + final lOther$allSnapshots = other.allSnapshots; + if (l$allSnapshots.length != lOther$allSnapshots.length) { + return false; + } + for (int i = 0; i < l$allSnapshots.length; i++) { + final l$allSnapshots$entry = l$allSnapshots[i]; + final lOther$allSnapshots$entry = lOther$allSnapshots[i]; + if (l$allSnapshots$entry != lOther$allSnapshots$entry) { + return false; + } + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$AllBackupSnapshots$backup + on Query$AllBackupSnapshots$backup { + CopyWith$Query$AllBackupSnapshots$backup + get copyWith => CopyWith$Query$AllBackupSnapshots$backup( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$AllBackupSnapshots$backup { + factory CopyWith$Query$AllBackupSnapshots$backup( + Query$AllBackupSnapshots$backup instance, + TRes Function(Query$AllBackupSnapshots$backup) then, + ) = _CopyWithImpl$Query$AllBackupSnapshots$backup; + + factory CopyWith$Query$AllBackupSnapshots$backup.stub(TRes res) = + _CopyWithStubImpl$Query$AllBackupSnapshots$backup; + + TRes call({ + List? allSnapshots, + String? $__typename, + }); + TRes allSnapshots( + Iterable Function( + Iterable< + CopyWith$Query$AllBackupSnapshots$backup$allSnapshots< + Query$AllBackupSnapshots$backup$allSnapshots>>) + _fn); +} + +class _CopyWithImpl$Query$AllBackupSnapshots$backup + implements CopyWith$Query$AllBackupSnapshots$backup { + _CopyWithImpl$Query$AllBackupSnapshots$backup( + this._instance, + this._then, + ); + + final Query$AllBackupSnapshots$backup _instance; + + final TRes Function(Query$AllBackupSnapshots$backup) _then; + + static const _undefined = {}; + + TRes call({ + Object? allSnapshots = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$AllBackupSnapshots$backup( + allSnapshots: allSnapshots == _undefined || allSnapshots == null + ? _instance.allSnapshots + : (allSnapshots + as List), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + TRes allSnapshots( + Iterable Function( + Iterable< + CopyWith$Query$AllBackupSnapshots$backup$allSnapshots< + Query$AllBackupSnapshots$backup$allSnapshots>>) + _fn) => + call( + allSnapshots: _fn(_instance.allSnapshots + .map((e) => CopyWith$Query$AllBackupSnapshots$backup$allSnapshots( + e, + (i) => i, + ))).toList()); +} + +class _CopyWithStubImpl$Query$AllBackupSnapshots$backup + implements CopyWith$Query$AllBackupSnapshots$backup { + _CopyWithStubImpl$Query$AllBackupSnapshots$backup(this._res); + + TRes _res; + + call({ + List? allSnapshots, + String? $__typename, + }) => + _res; + allSnapshots(_fn) => _res; +} + +class Query$AllBackupSnapshots$backup$allSnapshots { + Query$AllBackupSnapshots$backup$allSnapshots({ + required this.id, + required this.createdAt, + required this.service, + required this.reason, + this.$__typename = 'SnapshotInfo', + }); + + factory Query$AllBackupSnapshots$backup$allSnapshots.fromJson( + Map json) { + final l$id = json['id']; + final l$createdAt = json['createdAt']; + final l$service = json['service']; + final l$reason = json['reason']; + final l$$__typename = json['__typename']; + return Query$AllBackupSnapshots$backup$allSnapshots( + id: (l$id as String), + createdAt: dateTimeFromJson(l$createdAt), + service: Query$AllBackupSnapshots$backup$allSnapshots$service.fromJson( + (l$service as Map)), + reason: fromJson$Enum$BackupReason((l$reason as String)), + $__typename: (l$$__typename as String), + ); + } + + final String id; + + final DateTime createdAt; + + final Query$AllBackupSnapshots$backup$allSnapshots$service service; + + final Enum$BackupReason reason; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$id = id; + _resultData['id'] = l$id; + final l$createdAt = createdAt; + _resultData['createdAt'] = dateTimeToJson(l$createdAt); + final l$service = service; + _resultData['service'] = l$service.toJson(); + final l$reason = reason; + _resultData['reason'] = toJson$Enum$BackupReason(l$reason); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$id = id; + final l$createdAt = createdAt; + final l$service = service; + final l$reason = reason; + final l$$__typename = $__typename; + return Object.hashAll([ + l$id, + l$createdAt, + l$service, + l$reason, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$AllBackupSnapshots$backup$allSnapshots) || + runtimeType != other.runtimeType) { + return false; + } + final l$id = id; + final lOther$id = other.id; + if (l$id != lOther$id) { + return false; + } + final l$createdAt = createdAt; + final lOther$createdAt = other.createdAt; + if (l$createdAt != lOther$createdAt) { + return false; + } + final l$service = service; + final lOther$service = other.service; + if (l$service != lOther$service) { + return false; + } + final l$reason = reason; + final lOther$reason = other.reason; + if (l$reason != lOther$reason) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$AllBackupSnapshots$backup$allSnapshots + on Query$AllBackupSnapshots$backup$allSnapshots { + CopyWith$Query$AllBackupSnapshots$backup$allSnapshots< + Query$AllBackupSnapshots$backup$allSnapshots> + get copyWith => CopyWith$Query$AllBackupSnapshots$backup$allSnapshots( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$AllBackupSnapshots$backup$allSnapshots { + factory CopyWith$Query$AllBackupSnapshots$backup$allSnapshots( + Query$AllBackupSnapshots$backup$allSnapshots instance, + TRes Function(Query$AllBackupSnapshots$backup$allSnapshots) then, + ) = _CopyWithImpl$Query$AllBackupSnapshots$backup$allSnapshots; + + factory CopyWith$Query$AllBackupSnapshots$backup$allSnapshots.stub(TRes res) = + _CopyWithStubImpl$Query$AllBackupSnapshots$backup$allSnapshots; + + TRes call({ + String? id, + DateTime? createdAt, + Query$AllBackupSnapshots$backup$allSnapshots$service? service, + Enum$BackupReason? reason, + String? $__typename, + }); + CopyWith$Query$AllBackupSnapshots$backup$allSnapshots$service + get service; +} + +class _CopyWithImpl$Query$AllBackupSnapshots$backup$allSnapshots + implements CopyWith$Query$AllBackupSnapshots$backup$allSnapshots { + _CopyWithImpl$Query$AllBackupSnapshots$backup$allSnapshots( + this._instance, + this._then, + ); + + final Query$AllBackupSnapshots$backup$allSnapshots _instance; + + final TRes Function(Query$AllBackupSnapshots$backup$allSnapshots) _then; + + static const _undefined = {}; + + TRes call({ + Object? id = _undefined, + Object? createdAt = _undefined, + Object? service = _undefined, + Object? reason = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$AllBackupSnapshots$backup$allSnapshots( + id: id == _undefined || id == null ? _instance.id : (id as String), + createdAt: createdAt == _undefined || createdAt == null + ? _instance.createdAt + : (createdAt as DateTime), + service: service == _undefined || service == null + ? _instance.service + : (service as Query$AllBackupSnapshots$backup$allSnapshots$service), + reason: reason == _undefined || reason == null + ? _instance.reason + : (reason as Enum$BackupReason), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$AllBackupSnapshots$backup$allSnapshots$service + get service { + final local$service = _instance.service; + return CopyWith$Query$AllBackupSnapshots$backup$allSnapshots$service( + local$service, (e) => call(service: e)); + } +} + +class _CopyWithStubImpl$Query$AllBackupSnapshots$backup$allSnapshots + implements CopyWith$Query$AllBackupSnapshots$backup$allSnapshots { + _CopyWithStubImpl$Query$AllBackupSnapshots$backup$allSnapshots(this._res); + + TRes _res; + + call({ + String? id, + DateTime? createdAt, + Query$AllBackupSnapshots$backup$allSnapshots$service? service, + Enum$BackupReason? reason, + String? $__typename, + }) => + _res; + CopyWith$Query$AllBackupSnapshots$backup$allSnapshots$service + get service => + CopyWith$Query$AllBackupSnapshots$backup$allSnapshots$service.stub( + _res); +} + +class Query$AllBackupSnapshots$backup$allSnapshots$service { + Query$AllBackupSnapshots$backup$allSnapshots$service({ + required this.displayName, + required this.id, + this.$__typename = 'Service', + }); + + factory Query$AllBackupSnapshots$backup$allSnapshots$service.fromJson( + Map json) { + final l$displayName = json['displayName']; + final l$id = json['id']; + final l$$__typename = json['__typename']; + return Query$AllBackupSnapshots$backup$allSnapshots$service( + displayName: (l$displayName as String), + id: (l$id as String), + $__typename: (l$$__typename as String), + ); + } + + final String displayName; + + final String id; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$displayName = displayName; + _resultData['displayName'] = l$displayName; + final l$id = id; + _resultData['id'] = l$id; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$displayName = displayName; + final l$id = id; + final l$$__typename = $__typename; + return Object.hashAll([ + l$displayName, + l$id, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$AllBackupSnapshots$backup$allSnapshots$service) || + runtimeType != other.runtimeType) { + return false; + } + final l$displayName = displayName; + final lOther$displayName = other.displayName; + if (l$displayName != lOther$displayName) { + return false; + } + final l$id = id; + final lOther$id = other.id; + if (l$id != lOther$id) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$AllBackupSnapshots$backup$allSnapshots$service + on Query$AllBackupSnapshots$backup$allSnapshots$service { + CopyWith$Query$AllBackupSnapshots$backup$allSnapshots$service< + Query$AllBackupSnapshots$backup$allSnapshots$service> + get copyWith => + CopyWith$Query$AllBackupSnapshots$backup$allSnapshots$service( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$AllBackupSnapshots$backup$allSnapshots$service< + TRes> { + factory CopyWith$Query$AllBackupSnapshots$backup$allSnapshots$service( + Query$AllBackupSnapshots$backup$allSnapshots$service instance, + TRes Function(Query$AllBackupSnapshots$backup$allSnapshots$service) then, + ) = _CopyWithImpl$Query$AllBackupSnapshots$backup$allSnapshots$service; + + factory CopyWith$Query$AllBackupSnapshots$backup$allSnapshots$service.stub( + TRes res) = + _CopyWithStubImpl$Query$AllBackupSnapshots$backup$allSnapshots$service; + + TRes call({ + String? displayName, + String? id, + String? $__typename, + }); +} + +class _CopyWithImpl$Query$AllBackupSnapshots$backup$allSnapshots$service + implements + CopyWith$Query$AllBackupSnapshots$backup$allSnapshots$service { + _CopyWithImpl$Query$AllBackupSnapshots$backup$allSnapshots$service( + this._instance, + this._then, + ); + + final Query$AllBackupSnapshots$backup$allSnapshots$service _instance; + + final TRes Function(Query$AllBackupSnapshots$backup$allSnapshots$service) + _then; + + static const _undefined = {}; + + TRes call({ + Object? displayName = _undefined, + Object? id = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$AllBackupSnapshots$backup$allSnapshots$service( + displayName: displayName == _undefined || displayName == null + ? _instance.displayName + : (displayName as String), + id: id == _undefined || id == null ? _instance.id : (id as String), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Query$AllBackupSnapshots$backup$allSnapshots$service< + TRes> + implements + CopyWith$Query$AllBackupSnapshots$backup$allSnapshots$service { + _CopyWithStubImpl$Query$AllBackupSnapshots$backup$allSnapshots$service( + this._res); + + TRes _res; + + call({ + String? displayName, + String? id, + String? $__typename, + }) => + _res; +} + +class Mutation$ForceSnapshotsReload { + Mutation$ForceSnapshotsReload({ + required this.backup, + this.$__typename = 'Mutation', + }); + + factory Mutation$ForceSnapshotsReload.fromJson(Map json) { + final l$backup = json['backup']; + final l$$__typename = json['__typename']; + return Mutation$ForceSnapshotsReload( + backup: Mutation$ForceSnapshotsReload$backup.fromJson( + (l$backup as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$ForceSnapshotsReload$backup backup; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$backup = backup; + _resultData['backup'] = l$backup.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$backup = backup; + final l$$__typename = $__typename; + return Object.hashAll([ + l$backup, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$ForceSnapshotsReload) || + runtimeType != other.runtimeType) { + return false; + } + final l$backup = backup; + final lOther$backup = other.backup; + if (l$backup != lOther$backup) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$ForceSnapshotsReload + on Mutation$ForceSnapshotsReload { + CopyWith$Mutation$ForceSnapshotsReload + get copyWith => CopyWith$Mutation$ForceSnapshotsReload( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$ForceSnapshotsReload { + factory CopyWith$Mutation$ForceSnapshotsReload( + Mutation$ForceSnapshotsReload instance, + TRes Function(Mutation$ForceSnapshotsReload) then, + ) = _CopyWithImpl$Mutation$ForceSnapshotsReload; + + factory CopyWith$Mutation$ForceSnapshotsReload.stub(TRes res) = + _CopyWithStubImpl$Mutation$ForceSnapshotsReload; + + TRes call({ + Mutation$ForceSnapshotsReload$backup? backup, + String? $__typename, + }); + CopyWith$Mutation$ForceSnapshotsReload$backup get backup; +} + +class _CopyWithImpl$Mutation$ForceSnapshotsReload + implements CopyWith$Mutation$ForceSnapshotsReload { + _CopyWithImpl$Mutation$ForceSnapshotsReload( + this._instance, + this._then, + ); + + final Mutation$ForceSnapshotsReload _instance; + + final TRes Function(Mutation$ForceSnapshotsReload) _then; + + static const _undefined = {}; + + TRes call({ + Object? backup = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$ForceSnapshotsReload( + backup: backup == _undefined || backup == null + ? _instance.backup + : (backup as Mutation$ForceSnapshotsReload$backup), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$ForceSnapshotsReload$backup get backup { + final local$backup = _instance.backup; + return CopyWith$Mutation$ForceSnapshotsReload$backup( + local$backup, (e) => call(backup: e)); + } +} + +class _CopyWithStubImpl$Mutation$ForceSnapshotsReload + implements CopyWith$Mutation$ForceSnapshotsReload { + _CopyWithStubImpl$Mutation$ForceSnapshotsReload(this._res); + + TRes _res; + + call({ + Mutation$ForceSnapshotsReload$backup? backup, + String? $__typename, + }) => + _res; + CopyWith$Mutation$ForceSnapshotsReload$backup get backup => + CopyWith$Mutation$ForceSnapshotsReload$backup.stub(_res); +} + +const documentNodeMutationForceSnapshotsReload = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'ForceSnapshotsReload'), + variableDefinitions: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'backup'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'forceSnapshotsReload'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, +]); +Mutation$ForceSnapshotsReload _parserFn$Mutation$ForceSnapshotsReload( + Map data) => + Mutation$ForceSnapshotsReload.fromJson(data); +typedef OnMutationCompleted$Mutation$ForceSnapshotsReload = FutureOr + Function( + Map?, + Mutation$ForceSnapshotsReload?, +); + +class Options$Mutation$ForceSnapshotsReload + extends graphql.MutationOptions { + Options$Mutation$ForceSnapshotsReload({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$ForceSnapshotsReload? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$ForceSnapshotsReload? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$ForceSnapshotsReload(data), + ), + update: update, + onError: onError, + document: documentNodeMutationForceSnapshotsReload, + parserFn: _parserFn$Mutation$ForceSnapshotsReload, + ); + + final OnMutationCompleted$Mutation$ForceSnapshotsReload? + onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$ForceSnapshotsReload + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$ForceSnapshotsReload({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$ForceSnapshotsReload? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationForceSnapshotsReload, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$ForceSnapshotsReload, + ); +} + +extension ClientExtension$Mutation$ForceSnapshotsReload + on graphql.GraphQLClient { + Future> + mutate$ForceSnapshotsReload( + [Options$Mutation$ForceSnapshotsReload? options]) async => + await this.mutate(options ?? Options$Mutation$ForceSnapshotsReload()); + graphql.ObservableQuery + watchMutation$ForceSnapshotsReload( + [WatchOptions$Mutation$ForceSnapshotsReload? options]) => + this.watchMutation( + options ?? WatchOptions$Mutation$ForceSnapshotsReload()); +} + +class Mutation$ForceSnapshotsReload$backup { + Mutation$ForceSnapshotsReload$backup({ + required this.forceSnapshotsReload, + this.$__typename = 'BackupMutations', + }); + + factory Mutation$ForceSnapshotsReload$backup.fromJson( + Map json) { + final l$forceSnapshotsReload = json['forceSnapshotsReload']; + final l$$__typename = json['__typename']; + return Mutation$ForceSnapshotsReload$backup( + forceSnapshotsReload: + Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload.fromJson( + (l$forceSnapshotsReload as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload + forceSnapshotsReload; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$forceSnapshotsReload = forceSnapshotsReload; + _resultData['forceSnapshotsReload'] = l$forceSnapshotsReload.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$forceSnapshotsReload = forceSnapshotsReload; + final l$$__typename = $__typename; + return Object.hashAll([ + l$forceSnapshotsReload, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$ForceSnapshotsReload$backup) || + runtimeType != other.runtimeType) { + return false; + } + final l$forceSnapshotsReload = forceSnapshotsReload; + final lOther$forceSnapshotsReload = other.forceSnapshotsReload; + if (l$forceSnapshotsReload != lOther$forceSnapshotsReload) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$ForceSnapshotsReload$backup + on Mutation$ForceSnapshotsReload$backup { + CopyWith$Mutation$ForceSnapshotsReload$backup< + Mutation$ForceSnapshotsReload$backup> + get copyWith => CopyWith$Mutation$ForceSnapshotsReload$backup( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$ForceSnapshotsReload$backup { + factory CopyWith$Mutation$ForceSnapshotsReload$backup( + Mutation$ForceSnapshotsReload$backup instance, + TRes Function(Mutation$ForceSnapshotsReload$backup) then, + ) = _CopyWithImpl$Mutation$ForceSnapshotsReload$backup; + + factory CopyWith$Mutation$ForceSnapshotsReload$backup.stub(TRes res) = + _CopyWithStubImpl$Mutation$ForceSnapshotsReload$backup; + + TRes call({ + Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload? + forceSnapshotsReload, + String? $__typename, + }); + CopyWith$Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload + get forceSnapshotsReload; +} + +class _CopyWithImpl$Mutation$ForceSnapshotsReload$backup + implements CopyWith$Mutation$ForceSnapshotsReload$backup { + _CopyWithImpl$Mutation$ForceSnapshotsReload$backup( + this._instance, + this._then, + ); + + final Mutation$ForceSnapshotsReload$backup _instance; + + final TRes Function(Mutation$ForceSnapshotsReload$backup) _then; + + static const _undefined = {}; + + TRes call({ + Object? forceSnapshotsReload = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$ForceSnapshotsReload$backup( + forceSnapshotsReload: forceSnapshotsReload == _undefined || + forceSnapshotsReload == null + ? _instance.forceSnapshotsReload + : (forceSnapshotsReload + as Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload + get forceSnapshotsReload { + final local$forceSnapshotsReload = _instance.forceSnapshotsReload; + return CopyWith$Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload( + local$forceSnapshotsReload, (e) => call(forceSnapshotsReload: e)); + } +} + +class _CopyWithStubImpl$Mutation$ForceSnapshotsReload$backup + implements CopyWith$Mutation$ForceSnapshotsReload$backup { + _CopyWithStubImpl$Mutation$ForceSnapshotsReload$backup(this._res); + + TRes _res; + + call({ + Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload? + forceSnapshotsReload, + String? $__typename, + }) => + _res; + CopyWith$Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload + get forceSnapshotsReload => + CopyWith$Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload + .stub(_res); +} + +class Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload + implements Fragment$basicMutationReturnFields$$GenericMutationReturn { + Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'GenericMutationReturn', + }); + + factory Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload + on Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload { + CopyWith$Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload< + Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload> + get copyWith => + CopyWith$Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload< + TRes> { + factory CopyWith$Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload( + Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload instance, + TRes Function(Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload) + then, + ) = _CopyWithImpl$Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload; + + factory CopyWith$Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload.stub( + TRes res) = + _CopyWithStubImpl$Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload< + TRes> + implements + CopyWith$Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload< + TRes> { + _CopyWithImpl$Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload( + this._instance, + this._then, + ); + + final Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload _instance; + + final TRes Function(Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload) + _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload< + TRes> + implements + CopyWith$Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload< + TRes> { + _CopyWithStubImpl$Mutation$ForceSnapshotsReload$backup$forceSnapshotsReload( + this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +class Variables$Mutation$StartBackup { + factory Variables$Mutation$StartBackup({required String serviceId}) => + Variables$Mutation$StartBackup._({ + r'serviceId': serviceId, + }); + + Variables$Mutation$StartBackup._(this._$data); + + factory Variables$Mutation$StartBackup.fromJson(Map data) { + final result$data = {}; + final l$serviceId = data['serviceId']; + result$data['serviceId'] = (l$serviceId as String); + return Variables$Mutation$StartBackup._(result$data); + } + + Map _$data; + + String get serviceId => (_$data['serviceId'] as String); + Map toJson() { + final result$data = {}; + final l$serviceId = serviceId; + result$data['serviceId'] = l$serviceId; + return result$data; + } + + CopyWith$Variables$Mutation$StartBackup + get copyWith => CopyWith$Variables$Mutation$StartBackup( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$StartBackup) || + runtimeType != other.runtimeType) { + return false; + } + final l$serviceId = serviceId; + final lOther$serviceId = other.serviceId; + if (l$serviceId != lOther$serviceId) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$serviceId = serviceId; + return Object.hashAll([l$serviceId]); + } +} + +abstract class CopyWith$Variables$Mutation$StartBackup { + factory CopyWith$Variables$Mutation$StartBackup( + Variables$Mutation$StartBackup instance, + TRes Function(Variables$Mutation$StartBackup) then, + ) = _CopyWithImpl$Variables$Mutation$StartBackup; + + factory CopyWith$Variables$Mutation$StartBackup.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$StartBackup; + + TRes call({String? serviceId}); +} + +class _CopyWithImpl$Variables$Mutation$StartBackup + implements CopyWith$Variables$Mutation$StartBackup { + _CopyWithImpl$Variables$Mutation$StartBackup( + this._instance, + this._then, + ); + + final Variables$Mutation$StartBackup _instance; + + final TRes Function(Variables$Mutation$StartBackup) _then; + + static const _undefined = {}; + + TRes call({Object? serviceId = _undefined}) => + _then(Variables$Mutation$StartBackup._({ + ..._instance._$data, + if (serviceId != _undefined && serviceId != null) + 'serviceId': (serviceId as String), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$StartBackup + implements CopyWith$Variables$Mutation$StartBackup { + _CopyWithStubImpl$Variables$Mutation$StartBackup(this._res); + + TRes _res; + + call({String? serviceId}) => _res; +} + +class Mutation$StartBackup { + Mutation$StartBackup({ + required this.backup, + this.$__typename = 'Mutation', + }); + + factory Mutation$StartBackup.fromJson(Map json) { + final l$backup = json['backup']; + final l$$__typename = json['__typename']; + return Mutation$StartBackup( + backup: Mutation$StartBackup$backup.fromJson( + (l$backup as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$StartBackup$backup backup; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$backup = backup; + _resultData['backup'] = l$backup.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$backup = backup; + final l$$__typename = $__typename; + return Object.hashAll([ + l$backup, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$StartBackup) || runtimeType != other.runtimeType) { + return false; + } + final l$backup = backup; + final lOther$backup = other.backup; + if (l$backup != lOther$backup) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$StartBackup on Mutation$StartBackup { + CopyWith$Mutation$StartBackup get copyWith => + CopyWith$Mutation$StartBackup( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$StartBackup { + factory CopyWith$Mutation$StartBackup( + Mutation$StartBackup instance, + TRes Function(Mutation$StartBackup) then, + ) = _CopyWithImpl$Mutation$StartBackup; + + factory CopyWith$Mutation$StartBackup.stub(TRes res) = + _CopyWithStubImpl$Mutation$StartBackup; + + TRes call({ + Mutation$StartBackup$backup? backup, + String? $__typename, + }); + CopyWith$Mutation$StartBackup$backup get backup; +} + +class _CopyWithImpl$Mutation$StartBackup + implements CopyWith$Mutation$StartBackup { + _CopyWithImpl$Mutation$StartBackup( + this._instance, + this._then, + ); + + final Mutation$StartBackup _instance; + + final TRes Function(Mutation$StartBackup) _then; + + static const _undefined = {}; + + TRes call({ + Object? backup = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$StartBackup( + backup: backup == _undefined || backup == null + ? _instance.backup + : (backup as Mutation$StartBackup$backup), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$StartBackup$backup get backup { + final local$backup = _instance.backup; + return CopyWith$Mutation$StartBackup$backup( + local$backup, (e) => call(backup: e)); + } +} + +class _CopyWithStubImpl$Mutation$StartBackup + implements CopyWith$Mutation$StartBackup { + _CopyWithStubImpl$Mutation$StartBackup(this._res); + + TRes _res; + + call({ + Mutation$StartBackup$backup? backup, + String? $__typename, + }) => + _res; + CopyWith$Mutation$StartBackup$backup get backup => + CopyWith$Mutation$StartBackup$backup.stub(_res); +} + +const documentNodeMutationStartBackup = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'StartBackup'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'serviceId')), + type: NamedTypeNode( + name: NameNode(value: 'String'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'backup'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'startBackup'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'serviceId'), + value: VariableNode(name: NameNode(value: 'serviceId')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: 'job'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicApiJobsFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, + fragmentDefinitionbasicApiJobsFields, +]); +Mutation$StartBackup _parserFn$Mutation$StartBackup( + Map data) => + Mutation$StartBackup.fromJson(data); +typedef OnMutationCompleted$Mutation$StartBackup = FutureOr Function( + Map?, + Mutation$StartBackup?, +); + +class Options$Mutation$StartBackup + extends graphql.MutationOptions { + Options$Mutation$StartBackup({ + String? operationName, + required Variables$Mutation$StartBackup variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$StartBackup? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$StartBackup? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null ? null : _parserFn$Mutation$StartBackup(data), + ), + update: update, + onError: onError, + document: documentNodeMutationStartBackup, + parserFn: _parserFn$Mutation$StartBackup, + ); + + final OnMutationCompleted$Mutation$StartBackup? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$StartBackup + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$StartBackup({ + String? operationName, + required Variables$Mutation$StartBackup variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$StartBackup? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationStartBackup, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$StartBackup, + ); +} + +extension ClientExtension$Mutation$StartBackup on graphql.GraphQLClient { + Future> mutate$StartBackup( + Options$Mutation$StartBackup options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$StartBackup( + WatchOptions$Mutation$StartBackup options) => + this.watchMutation(options); +} + +class Mutation$StartBackup$backup { + Mutation$StartBackup$backup({ + required this.startBackup, + this.$__typename = 'BackupMutations', + }); + + factory Mutation$StartBackup$backup.fromJson(Map json) { + final l$startBackup = json['startBackup']; + final l$$__typename = json['__typename']; + return Mutation$StartBackup$backup( + startBackup: Mutation$StartBackup$backup$startBackup.fromJson( + (l$startBackup as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$StartBackup$backup$startBackup startBackup; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$startBackup = startBackup; + _resultData['startBackup'] = l$startBackup.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$startBackup = startBackup; + final l$$__typename = $__typename; + return Object.hashAll([ + l$startBackup, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$StartBackup$backup) || + runtimeType != other.runtimeType) { + return false; + } + final l$startBackup = startBackup; + final lOther$startBackup = other.startBackup; + if (l$startBackup != lOther$startBackup) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$StartBackup$backup + on Mutation$StartBackup$backup { + CopyWith$Mutation$StartBackup$backup + get copyWith => CopyWith$Mutation$StartBackup$backup( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$StartBackup$backup { + factory CopyWith$Mutation$StartBackup$backup( + Mutation$StartBackup$backup instance, + TRes Function(Mutation$StartBackup$backup) then, + ) = _CopyWithImpl$Mutation$StartBackup$backup; + + factory CopyWith$Mutation$StartBackup$backup.stub(TRes res) = + _CopyWithStubImpl$Mutation$StartBackup$backup; + + TRes call({ + Mutation$StartBackup$backup$startBackup? startBackup, + String? $__typename, + }); + CopyWith$Mutation$StartBackup$backup$startBackup get startBackup; +} + +class _CopyWithImpl$Mutation$StartBackup$backup + implements CopyWith$Mutation$StartBackup$backup { + _CopyWithImpl$Mutation$StartBackup$backup( + this._instance, + this._then, + ); + + final Mutation$StartBackup$backup _instance; + + final TRes Function(Mutation$StartBackup$backup) _then; + + static const _undefined = {}; + + TRes call({ + Object? startBackup = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$StartBackup$backup( + startBackup: startBackup == _undefined || startBackup == null + ? _instance.startBackup + : (startBackup as Mutation$StartBackup$backup$startBackup), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$StartBackup$backup$startBackup get startBackup { + final local$startBackup = _instance.startBackup; + return CopyWith$Mutation$StartBackup$backup$startBackup( + local$startBackup, (e) => call(startBackup: e)); + } +} + +class _CopyWithStubImpl$Mutation$StartBackup$backup + implements CopyWith$Mutation$StartBackup$backup { + _CopyWithStubImpl$Mutation$StartBackup$backup(this._res); + + TRes _res; + + call({ + Mutation$StartBackup$backup$startBackup? startBackup, + String? $__typename, + }) => + _res; + CopyWith$Mutation$StartBackup$backup$startBackup get startBackup => + CopyWith$Mutation$StartBackup$backup$startBackup.stub(_res); +} + +class Mutation$StartBackup$backup$startBackup + implements Fragment$basicMutationReturnFields$$GenericJobMutationReturn { + Mutation$StartBackup$backup$startBackup({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'GenericJobMutationReturn', + this.job, + }); + + factory Mutation$StartBackup$backup$startBackup.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + final l$job = json['job']; + return Mutation$StartBackup$backup$startBackup( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + job: l$job == null + ? null + : Fragment$basicApiJobsFields.fromJson( + (l$job as Map)), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + final Fragment$basicApiJobsFields? job; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + final l$job = job; + _resultData['job'] = l$job?.toJson(); + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + final l$job = job; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + l$job, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$StartBackup$backup$startBackup) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + final l$job = job; + final lOther$job = other.job; + if (l$job != lOther$job) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$StartBackup$backup$startBackup + on Mutation$StartBackup$backup$startBackup { + CopyWith$Mutation$StartBackup$backup$startBackup< + Mutation$StartBackup$backup$startBackup> + get copyWith => CopyWith$Mutation$StartBackup$backup$startBackup( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$StartBackup$backup$startBackup { + factory CopyWith$Mutation$StartBackup$backup$startBackup( + Mutation$StartBackup$backup$startBackup instance, + TRes Function(Mutation$StartBackup$backup$startBackup) then, + ) = _CopyWithImpl$Mutation$StartBackup$backup$startBackup; + + factory CopyWith$Mutation$StartBackup$backup$startBackup.stub(TRes res) = + _CopyWithStubImpl$Mutation$StartBackup$backup$startBackup; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + Fragment$basicApiJobsFields? job, + }); + CopyWith$Fragment$basicApiJobsFields get job; +} + +class _CopyWithImpl$Mutation$StartBackup$backup$startBackup + implements CopyWith$Mutation$StartBackup$backup$startBackup { + _CopyWithImpl$Mutation$StartBackup$backup$startBackup( + this._instance, + this._then, + ); + + final Mutation$StartBackup$backup$startBackup _instance; + + final TRes Function(Mutation$StartBackup$backup$startBackup) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + Object? job = _undefined, + }) => + _then(Mutation$StartBackup$backup$startBackup( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + job: job == _undefined + ? _instance.job + : (job as Fragment$basicApiJobsFields?), + )); + CopyWith$Fragment$basicApiJobsFields get job { + final local$job = _instance.job; + return local$job == null + ? CopyWith$Fragment$basicApiJobsFields.stub(_then(_instance)) + : CopyWith$Fragment$basicApiJobsFields(local$job, (e) => call(job: e)); + } +} + +class _CopyWithStubImpl$Mutation$StartBackup$backup$startBackup + implements CopyWith$Mutation$StartBackup$backup$startBackup { + _CopyWithStubImpl$Mutation$StartBackup$backup$startBackup(this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + Fragment$basicApiJobsFields? job, + }) => + _res; + CopyWith$Fragment$basicApiJobsFields get job => + CopyWith$Fragment$basicApiJobsFields.stub(_res); +} + +class Variables$Mutation$SetAutobackupPeriod { + factory Variables$Mutation$SetAutobackupPeriod({int? period}) => + Variables$Mutation$SetAutobackupPeriod._({ + if (period != null) r'period': period, + }); + + Variables$Mutation$SetAutobackupPeriod._(this._$data); + + factory Variables$Mutation$SetAutobackupPeriod.fromJson( + Map data) { + final result$data = {}; + if (data.containsKey('period')) { + final l$period = data['period']; + result$data['period'] = (l$period as int?); + } + return Variables$Mutation$SetAutobackupPeriod._(result$data); + } + + Map _$data; + + int? get period => (_$data['period'] as int?); + Map toJson() { + final result$data = {}; + if (_$data.containsKey('period')) { + final l$period = period; + result$data['period'] = l$period; + } + return result$data; + } + + CopyWith$Variables$Mutation$SetAutobackupPeriod< + Variables$Mutation$SetAutobackupPeriod> + get copyWith => CopyWith$Variables$Mutation$SetAutobackupPeriod( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$SetAutobackupPeriod) || + runtimeType != other.runtimeType) { + return false; + } + final l$period = period; + final lOther$period = other.period; + if (_$data.containsKey('period') != other._$data.containsKey('period')) { + return false; + } + if (l$period != lOther$period) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$period = period; + return Object.hashAll([_$data.containsKey('period') ? l$period : const {}]); + } +} + +abstract class CopyWith$Variables$Mutation$SetAutobackupPeriod { + factory CopyWith$Variables$Mutation$SetAutobackupPeriod( + Variables$Mutation$SetAutobackupPeriod instance, + TRes Function(Variables$Mutation$SetAutobackupPeriod) then, + ) = _CopyWithImpl$Variables$Mutation$SetAutobackupPeriod; + + factory CopyWith$Variables$Mutation$SetAutobackupPeriod.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$SetAutobackupPeriod; + + TRes call({int? period}); +} + +class _CopyWithImpl$Variables$Mutation$SetAutobackupPeriod + implements CopyWith$Variables$Mutation$SetAutobackupPeriod { + _CopyWithImpl$Variables$Mutation$SetAutobackupPeriod( + this._instance, + this._then, + ); + + final Variables$Mutation$SetAutobackupPeriod _instance; + + final TRes Function(Variables$Mutation$SetAutobackupPeriod) _then; + + static const _undefined = {}; + + TRes call({Object? period = _undefined}) => + _then(Variables$Mutation$SetAutobackupPeriod._({ + ..._instance._$data, + if (period != _undefined) 'period': (period as int?), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$SetAutobackupPeriod + implements CopyWith$Variables$Mutation$SetAutobackupPeriod { + _CopyWithStubImpl$Variables$Mutation$SetAutobackupPeriod(this._res); + + TRes _res; + + call({int? period}) => _res; +} + +class Mutation$SetAutobackupPeriod { + Mutation$SetAutobackupPeriod({ + required this.backup, + this.$__typename = 'Mutation', + }); + + factory Mutation$SetAutobackupPeriod.fromJson(Map json) { + final l$backup = json['backup']; + final l$$__typename = json['__typename']; + return Mutation$SetAutobackupPeriod( + backup: Mutation$SetAutobackupPeriod$backup.fromJson( + (l$backup as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$SetAutobackupPeriod$backup backup; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$backup = backup; + _resultData['backup'] = l$backup.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$backup = backup; + final l$$__typename = $__typename; + return Object.hashAll([ + l$backup, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$SetAutobackupPeriod) || + runtimeType != other.runtimeType) { + return false; + } + final l$backup = backup; + final lOther$backup = other.backup; + if (l$backup != lOther$backup) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$SetAutobackupPeriod + on Mutation$SetAutobackupPeriod { + CopyWith$Mutation$SetAutobackupPeriod + get copyWith => CopyWith$Mutation$SetAutobackupPeriod( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$SetAutobackupPeriod { + factory CopyWith$Mutation$SetAutobackupPeriod( + Mutation$SetAutobackupPeriod instance, + TRes Function(Mutation$SetAutobackupPeriod) then, + ) = _CopyWithImpl$Mutation$SetAutobackupPeriod; + + factory CopyWith$Mutation$SetAutobackupPeriod.stub(TRes res) = + _CopyWithStubImpl$Mutation$SetAutobackupPeriod; + + TRes call({ + Mutation$SetAutobackupPeriod$backup? backup, + String? $__typename, + }); + CopyWith$Mutation$SetAutobackupPeriod$backup get backup; +} + +class _CopyWithImpl$Mutation$SetAutobackupPeriod + implements CopyWith$Mutation$SetAutobackupPeriod { + _CopyWithImpl$Mutation$SetAutobackupPeriod( + this._instance, + this._then, + ); + + final Mutation$SetAutobackupPeriod _instance; + + final TRes Function(Mutation$SetAutobackupPeriod) _then; + + static const _undefined = {}; + + TRes call({ + Object? backup = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$SetAutobackupPeriod( + backup: backup == _undefined || backup == null + ? _instance.backup + : (backup as Mutation$SetAutobackupPeriod$backup), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$SetAutobackupPeriod$backup get backup { + final local$backup = _instance.backup; + return CopyWith$Mutation$SetAutobackupPeriod$backup( + local$backup, (e) => call(backup: e)); + } +} + +class _CopyWithStubImpl$Mutation$SetAutobackupPeriod + implements CopyWith$Mutation$SetAutobackupPeriod { + _CopyWithStubImpl$Mutation$SetAutobackupPeriod(this._res); + + TRes _res; + + call({ + Mutation$SetAutobackupPeriod$backup? backup, + String? $__typename, + }) => + _res; + CopyWith$Mutation$SetAutobackupPeriod$backup get backup => + CopyWith$Mutation$SetAutobackupPeriod$backup.stub(_res); +} + +const documentNodeMutationSetAutobackupPeriod = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'SetAutobackupPeriod'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'period')), + type: NamedTypeNode( + name: NameNode(value: 'Int'), + isNonNull: false, + ), + defaultValue: DefaultValueNode(value: NullValueNode()), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'backup'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'setAutobackupPeriod'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'period'), + value: VariableNode(name: NameNode(value: 'period')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'genericBackupConfigReturn'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitiongenericBackupConfigReturn, +]); +Mutation$SetAutobackupPeriod _parserFn$Mutation$SetAutobackupPeriod( + Map data) => + Mutation$SetAutobackupPeriod.fromJson(data); +typedef OnMutationCompleted$Mutation$SetAutobackupPeriod = FutureOr + Function( + Map?, + Mutation$SetAutobackupPeriod?, +); + +class Options$Mutation$SetAutobackupPeriod + extends graphql.MutationOptions { + Options$Mutation$SetAutobackupPeriod({ + String? operationName, + Variables$Mutation$SetAutobackupPeriod? variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$SetAutobackupPeriod? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$SetAutobackupPeriod? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables?.toJson() ?? {}, + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$SetAutobackupPeriod(data), + ), + update: update, + onError: onError, + document: documentNodeMutationSetAutobackupPeriod, + parserFn: _parserFn$Mutation$SetAutobackupPeriod, + ); + + final OnMutationCompleted$Mutation$SetAutobackupPeriod? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$SetAutobackupPeriod + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$SetAutobackupPeriod({ + String? operationName, + Variables$Mutation$SetAutobackupPeriod? variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$SetAutobackupPeriod? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables?.toJson() ?? {}, + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationSetAutobackupPeriod, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$SetAutobackupPeriod, + ); +} + +extension ClientExtension$Mutation$SetAutobackupPeriod + on graphql.GraphQLClient { + Future> + mutate$SetAutobackupPeriod( + [Options$Mutation$SetAutobackupPeriod? options]) async => + await this.mutate(options ?? Options$Mutation$SetAutobackupPeriod()); + graphql.ObservableQuery + watchMutation$SetAutobackupPeriod( + [WatchOptions$Mutation$SetAutobackupPeriod? options]) => + this.watchMutation( + options ?? WatchOptions$Mutation$SetAutobackupPeriod()); +} + +class Mutation$SetAutobackupPeriod$backup { + Mutation$SetAutobackupPeriod$backup({ + required this.setAutobackupPeriod, + this.$__typename = 'BackupMutations', + }); + + factory Mutation$SetAutobackupPeriod$backup.fromJson( + Map json) { + final l$setAutobackupPeriod = json['setAutobackupPeriod']; + final l$$__typename = json['__typename']; + return Mutation$SetAutobackupPeriod$backup( + setAutobackupPeriod: Fragment$genericBackupConfigReturn.fromJson( + (l$setAutobackupPeriod as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Fragment$genericBackupConfigReturn setAutobackupPeriod; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$setAutobackupPeriod = setAutobackupPeriod; + _resultData['setAutobackupPeriod'] = l$setAutobackupPeriod.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$setAutobackupPeriod = setAutobackupPeriod; + final l$$__typename = $__typename; + return Object.hashAll([ + l$setAutobackupPeriod, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$SetAutobackupPeriod$backup) || + runtimeType != other.runtimeType) { + return false; + } + final l$setAutobackupPeriod = setAutobackupPeriod; + final lOther$setAutobackupPeriod = other.setAutobackupPeriod; + if (l$setAutobackupPeriod != lOther$setAutobackupPeriod) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$SetAutobackupPeriod$backup + on Mutation$SetAutobackupPeriod$backup { + CopyWith$Mutation$SetAutobackupPeriod$backup< + Mutation$SetAutobackupPeriod$backup> + get copyWith => CopyWith$Mutation$SetAutobackupPeriod$backup( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$SetAutobackupPeriod$backup { + factory CopyWith$Mutation$SetAutobackupPeriod$backup( + Mutation$SetAutobackupPeriod$backup instance, + TRes Function(Mutation$SetAutobackupPeriod$backup) then, + ) = _CopyWithImpl$Mutation$SetAutobackupPeriod$backup; + + factory CopyWith$Mutation$SetAutobackupPeriod$backup.stub(TRes res) = + _CopyWithStubImpl$Mutation$SetAutobackupPeriod$backup; + + TRes call({ + Fragment$genericBackupConfigReturn? setAutobackupPeriod, + String? $__typename, + }); + CopyWith$Fragment$genericBackupConfigReturn get setAutobackupPeriod; +} + +class _CopyWithImpl$Mutation$SetAutobackupPeriod$backup + implements CopyWith$Mutation$SetAutobackupPeriod$backup { + _CopyWithImpl$Mutation$SetAutobackupPeriod$backup( + this._instance, + this._then, + ); + + final Mutation$SetAutobackupPeriod$backup _instance; + + final TRes Function(Mutation$SetAutobackupPeriod$backup) _then; + + static const _undefined = {}; + + TRes call({ + Object? setAutobackupPeriod = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$SetAutobackupPeriod$backup( + setAutobackupPeriod: + setAutobackupPeriod == _undefined || setAutobackupPeriod == null + ? _instance.setAutobackupPeriod + : (setAutobackupPeriod as Fragment$genericBackupConfigReturn), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Fragment$genericBackupConfigReturn get setAutobackupPeriod { + final local$setAutobackupPeriod = _instance.setAutobackupPeriod; + return CopyWith$Fragment$genericBackupConfigReturn( + local$setAutobackupPeriod, (e) => call(setAutobackupPeriod: e)); + } +} + +class _CopyWithStubImpl$Mutation$SetAutobackupPeriod$backup + implements CopyWith$Mutation$SetAutobackupPeriod$backup { + _CopyWithStubImpl$Mutation$SetAutobackupPeriod$backup(this._res); + + TRes _res; + + call({ + Fragment$genericBackupConfigReturn? setAutobackupPeriod, + String? $__typename, + }) => + _res; + CopyWith$Fragment$genericBackupConfigReturn get setAutobackupPeriod => + CopyWith$Fragment$genericBackupConfigReturn.stub(_res); +} + +class Variables$Mutation$setAutobackupQuotas { + factory Variables$Mutation$setAutobackupQuotas( + {required Input$AutobackupQuotasInput quotas}) => + Variables$Mutation$setAutobackupQuotas._({ + r'quotas': quotas, + }); + + Variables$Mutation$setAutobackupQuotas._(this._$data); + + factory Variables$Mutation$setAutobackupQuotas.fromJson( + Map data) { + final result$data = {}; + final l$quotas = data['quotas']; + result$data['quotas'] = Input$AutobackupQuotasInput.fromJson( + (l$quotas as Map)); + return Variables$Mutation$setAutobackupQuotas._(result$data); + } + + Map _$data; + + Input$AutobackupQuotasInput get quotas => + (_$data['quotas'] as Input$AutobackupQuotasInput); + Map toJson() { + final result$data = {}; + final l$quotas = quotas; + result$data['quotas'] = l$quotas.toJson(); + return result$data; + } + + CopyWith$Variables$Mutation$setAutobackupQuotas< + Variables$Mutation$setAutobackupQuotas> + get copyWith => CopyWith$Variables$Mutation$setAutobackupQuotas( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$setAutobackupQuotas) || + runtimeType != other.runtimeType) { + return false; + } + final l$quotas = quotas; + final lOther$quotas = other.quotas; + if (l$quotas != lOther$quotas) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$quotas = quotas; + return Object.hashAll([l$quotas]); + } +} + +abstract class CopyWith$Variables$Mutation$setAutobackupQuotas { + factory CopyWith$Variables$Mutation$setAutobackupQuotas( + Variables$Mutation$setAutobackupQuotas instance, + TRes Function(Variables$Mutation$setAutobackupQuotas) then, + ) = _CopyWithImpl$Variables$Mutation$setAutobackupQuotas; + + factory CopyWith$Variables$Mutation$setAutobackupQuotas.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$setAutobackupQuotas; + + TRes call({Input$AutobackupQuotasInput? quotas}); +} + +class _CopyWithImpl$Variables$Mutation$setAutobackupQuotas + implements CopyWith$Variables$Mutation$setAutobackupQuotas { + _CopyWithImpl$Variables$Mutation$setAutobackupQuotas( + this._instance, + this._then, + ); + + final Variables$Mutation$setAutobackupQuotas _instance; + + final TRes Function(Variables$Mutation$setAutobackupQuotas) _then; + + static const _undefined = {}; + + TRes call({Object? quotas = _undefined}) => + _then(Variables$Mutation$setAutobackupQuotas._({ + ..._instance._$data, + if (quotas != _undefined && quotas != null) + 'quotas': (quotas as Input$AutobackupQuotasInput), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$setAutobackupQuotas + implements CopyWith$Variables$Mutation$setAutobackupQuotas { + _CopyWithStubImpl$Variables$Mutation$setAutobackupQuotas(this._res); + + TRes _res; + + call({Input$AutobackupQuotasInput? quotas}) => _res; +} + +class Mutation$setAutobackupQuotas { + Mutation$setAutobackupQuotas({ + required this.backup, + this.$__typename = 'Mutation', + }); + + factory Mutation$setAutobackupQuotas.fromJson(Map json) { + final l$backup = json['backup']; + final l$$__typename = json['__typename']; + return Mutation$setAutobackupQuotas( + backup: Mutation$setAutobackupQuotas$backup.fromJson( + (l$backup as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$setAutobackupQuotas$backup backup; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$backup = backup; + _resultData['backup'] = l$backup.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$backup = backup; + final l$$__typename = $__typename; + return Object.hashAll([ + l$backup, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$setAutobackupQuotas) || + runtimeType != other.runtimeType) { + return false; + } + final l$backup = backup; + final lOther$backup = other.backup; + if (l$backup != lOther$backup) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$setAutobackupQuotas + on Mutation$setAutobackupQuotas { + CopyWith$Mutation$setAutobackupQuotas + get copyWith => CopyWith$Mutation$setAutobackupQuotas( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$setAutobackupQuotas { + factory CopyWith$Mutation$setAutobackupQuotas( + Mutation$setAutobackupQuotas instance, + TRes Function(Mutation$setAutobackupQuotas) then, + ) = _CopyWithImpl$Mutation$setAutobackupQuotas; + + factory CopyWith$Mutation$setAutobackupQuotas.stub(TRes res) = + _CopyWithStubImpl$Mutation$setAutobackupQuotas; + + TRes call({ + Mutation$setAutobackupQuotas$backup? backup, + String? $__typename, + }); + CopyWith$Mutation$setAutobackupQuotas$backup get backup; +} + +class _CopyWithImpl$Mutation$setAutobackupQuotas + implements CopyWith$Mutation$setAutobackupQuotas { + _CopyWithImpl$Mutation$setAutobackupQuotas( + this._instance, + this._then, + ); + + final Mutation$setAutobackupQuotas _instance; + + final TRes Function(Mutation$setAutobackupQuotas) _then; + + static const _undefined = {}; + + TRes call({ + Object? backup = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$setAutobackupQuotas( + backup: backup == _undefined || backup == null + ? _instance.backup + : (backup as Mutation$setAutobackupQuotas$backup), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$setAutobackupQuotas$backup get backup { + final local$backup = _instance.backup; + return CopyWith$Mutation$setAutobackupQuotas$backup( + local$backup, (e) => call(backup: e)); + } +} + +class _CopyWithStubImpl$Mutation$setAutobackupQuotas + implements CopyWith$Mutation$setAutobackupQuotas { + _CopyWithStubImpl$Mutation$setAutobackupQuotas(this._res); + + TRes _res; + + call({ + Mutation$setAutobackupQuotas$backup? backup, + String? $__typename, + }) => + _res; + CopyWith$Mutation$setAutobackupQuotas$backup get backup => + CopyWith$Mutation$setAutobackupQuotas$backup.stub(_res); +} + +const documentNodeMutationsetAutobackupQuotas = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'setAutobackupQuotas'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'quotas')), + type: NamedTypeNode( + name: NameNode(value: 'AutobackupQuotasInput'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'backup'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'setAutobackupQuotas'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'quotas'), + value: VariableNode(name: NameNode(value: 'quotas')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'genericBackupConfigReturn'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitiongenericBackupConfigReturn, +]); +Mutation$setAutobackupQuotas _parserFn$Mutation$setAutobackupQuotas( + Map data) => + Mutation$setAutobackupQuotas.fromJson(data); +typedef OnMutationCompleted$Mutation$setAutobackupQuotas = FutureOr + Function( + Map?, + Mutation$setAutobackupQuotas?, +); + +class Options$Mutation$setAutobackupQuotas + extends graphql.MutationOptions { + Options$Mutation$setAutobackupQuotas({ + String? operationName, + required Variables$Mutation$setAutobackupQuotas variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$setAutobackupQuotas? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$setAutobackupQuotas? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$setAutobackupQuotas(data), + ), + update: update, + onError: onError, + document: documentNodeMutationsetAutobackupQuotas, + parserFn: _parserFn$Mutation$setAutobackupQuotas, + ); + + final OnMutationCompleted$Mutation$setAutobackupQuotas? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$setAutobackupQuotas + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$setAutobackupQuotas({ + String? operationName, + required Variables$Mutation$setAutobackupQuotas variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$setAutobackupQuotas? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationsetAutobackupQuotas, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$setAutobackupQuotas, + ); +} + +extension ClientExtension$Mutation$setAutobackupQuotas + on graphql.GraphQLClient { + Future> + mutate$setAutobackupQuotas( + Options$Mutation$setAutobackupQuotas options) async => + await this.mutate(options); + graphql.ObservableQuery + watchMutation$setAutobackupQuotas( + WatchOptions$Mutation$setAutobackupQuotas options) => + this.watchMutation(options); +} + +class Mutation$setAutobackupQuotas$backup { + Mutation$setAutobackupQuotas$backup({ + required this.setAutobackupQuotas, + this.$__typename = 'BackupMutations', + }); + + factory Mutation$setAutobackupQuotas$backup.fromJson( + Map json) { + final l$setAutobackupQuotas = json['setAutobackupQuotas']; + final l$$__typename = json['__typename']; + return Mutation$setAutobackupQuotas$backup( + setAutobackupQuotas: Fragment$genericBackupConfigReturn.fromJson( + (l$setAutobackupQuotas as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Fragment$genericBackupConfigReturn setAutobackupQuotas; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$setAutobackupQuotas = setAutobackupQuotas; + _resultData['setAutobackupQuotas'] = l$setAutobackupQuotas.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$setAutobackupQuotas = setAutobackupQuotas; + final l$$__typename = $__typename; + return Object.hashAll([ + l$setAutobackupQuotas, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$setAutobackupQuotas$backup) || + runtimeType != other.runtimeType) { + return false; + } + final l$setAutobackupQuotas = setAutobackupQuotas; + final lOther$setAutobackupQuotas = other.setAutobackupQuotas; + if (l$setAutobackupQuotas != lOther$setAutobackupQuotas) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$setAutobackupQuotas$backup + on Mutation$setAutobackupQuotas$backup { + CopyWith$Mutation$setAutobackupQuotas$backup< + Mutation$setAutobackupQuotas$backup> + get copyWith => CopyWith$Mutation$setAutobackupQuotas$backup( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$setAutobackupQuotas$backup { + factory CopyWith$Mutation$setAutobackupQuotas$backup( + Mutation$setAutobackupQuotas$backup instance, + TRes Function(Mutation$setAutobackupQuotas$backup) then, + ) = _CopyWithImpl$Mutation$setAutobackupQuotas$backup; + + factory CopyWith$Mutation$setAutobackupQuotas$backup.stub(TRes res) = + _CopyWithStubImpl$Mutation$setAutobackupQuotas$backup; + + TRes call({ + Fragment$genericBackupConfigReturn? setAutobackupQuotas, + String? $__typename, + }); + CopyWith$Fragment$genericBackupConfigReturn get setAutobackupQuotas; +} + +class _CopyWithImpl$Mutation$setAutobackupQuotas$backup + implements CopyWith$Mutation$setAutobackupQuotas$backup { + _CopyWithImpl$Mutation$setAutobackupQuotas$backup( + this._instance, + this._then, + ); + + final Mutation$setAutobackupQuotas$backup _instance; + + final TRes Function(Mutation$setAutobackupQuotas$backup) _then; + + static const _undefined = {}; + + TRes call({ + Object? setAutobackupQuotas = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$setAutobackupQuotas$backup( + setAutobackupQuotas: + setAutobackupQuotas == _undefined || setAutobackupQuotas == null + ? _instance.setAutobackupQuotas + : (setAutobackupQuotas as Fragment$genericBackupConfigReturn), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Fragment$genericBackupConfigReturn get setAutobackupQuotas { + final local$setAutobackupQuotas = _instance.setAutobackupQuotas; + return CopyWith$Fragment$genericBackupConfigReturn( + local$setAutobackupQuotas, (e) => call(setAutobackupQuotas: e)); + } +} + +class _CopyWithStubImpl$Mutation$setAutobackupQuotas$backup + implements CopyWith$Mutation$setAutobackupQuotas$backup { + _CopyWithStubImpl$Mutation$setAutobackupQuotas$backup(this._res); + + TRes _res; + + call({ + Fragment$genericBackupConfigReturn? setAutobackupQuotas, + String? $__typename, + }) => + _res; + CopyWith$Fragment$genericBackupConfigReturn get setAutobackupQuotas => + CopyWith$Fragment$genericBackupConfigReturn.stub(_res); +} + +class Mutation$RemoveRepository { + Mutation$RemoveRepository({ + required this.backup, + this.$__typename = 'Mutation', + }); + + factory Mutation$RemoveRepository.fromJson(Map json) { + final l$backup = json['backup']; + final l$$__typename = json['__typename']; + return Mutation$RemoveRepository( + backup: Mutation$RemoveRepository$backup.fromJson( + (l$backup as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$RemoveRepository$backup backup; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$backup = backup; + _resultData['backup'] = l$backup.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$backup = backup; + final l$$__typename = $__typename; + return Object.hashAll([ + l$backup, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$RemoveRepository) || + runtimeType != other.runtimeType) { + return false; + } + final l$backup = backup; + final lOther$backup = other.backup; + if (l$backup != lOther$backup) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$RemoveRepository + on Mutation$RemoveRepository { + CopyWith$Mutation$RemoveRepository get copyWith => + CopyWith$Mutation$RemoveRepository( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$RemoveRepository { + factory CopyWith$Mutation$RemoveRepository( + Mutation$RemoveRepository instance, + TRes Function(Mutation$RemoveRepository) then, + ) = _CopyWithImpl$Mutation$RemoveRepository; + + factory CopyWith$Mutation$RemoveRepository.stub(TRes res) = + _CopyWithStubImpl$Mutation$RemoveRepository; + + TRes call({ + Mutation$RemoveRepository$backup? backup, + String? $__typename, + }); + CopyWith$Mutation$RemoveRepository$backup get backup; +} + +class _CopyWithImpl$Mutation$RemoveRepository + implements CopyWith$Mutation$RemoveRepository { + _CopyWithImpl$Mutation$RemoveRepository( + this._instance, + this._then, + ); + + final Mutation$RemoveRepository _instance; + + final TRes Function(Mutation$RemoveRepository) _then; + + static const _undefined = {}; + + TRes call({ + Object? backup = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$RemoveRepository( + backup: backup == _undefined || backup == null + ? _instance.backup + : (backup as Mutation$RemoveRepository$backup), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$RemoveRepository$backup get backup { + final local$backup = _instance.backup; + return CopyWith$Mutation$RemoveRepository$backup( + local$backup, (e) => call(backup: e)); + } +} + +class _CopyWithStubImpl$Mutation$RemoveRepository + implements CopyWith$Mutation$RemoveRepository { + _CopyWithStubImpl$Mutation$RemoveRepository(this._res); + + TRes _res; + + call({ + Mutation$RemoveRepository$backup? backup, + String? $__typename, + }) => + _res; + CopyWith$Mutation$RemoveRepository$backup get backup => + CopyWith$Mutation$RemoveRepository$backup.stub(_res); +} + +const documentNodeMutationRemoveRepository = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'RemoveRepository'), + variableDefinitions: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'backup'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'removeRepository'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'genericBackupConfigReturn'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitiongenericBackupConfigReturn, +]); +Mutation$RemoveRepository _parserFn$Mutation$RemoveRepository( + Map data) => + Mutation$RemoveRepository.fromJson(data); +typedef OnMutationCompleted$Mutation$RemoveRepository = FutureOr Function( + Map?, + Mutation$RemoveRepository?, +); + +class Options$Mutation$RemoveRepository + extends graphql.MutationOptions { + Options$Mutation$RemoveRepository({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RemoveRepository? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$RemoveRepository? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$RemoveRepository(data), + ), + update: update, + onError: onError, + document: documentNodeMutationRemoveRepository, + parserFn: _parserFn$Mutation$RemoveRepository, + ); + + final OnMutationCompleted$Mutation$RemoveRepository? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$RemoveRepository + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$RemoveRepository({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RemoveRepository? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationRemoveRepository, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$RemoveRepository, + ); +} + +extension ClientExtension$Mutation$RemoveRepository on graphql.GraphQLClient { + Future> + mutate$RemoveRepository( + [Options$Mutation$RemoveRepository? options]) async => + await this.mutate(options ?? Options$Mutation$RemoveRepository()); + graphql.ObservableQuery< + Mutation$RemoveRepository> watchMutation$RemoveRepository( + [WatchOptions$Mutation$RemoveRepository? options]) => + this.watchMutation(options ?? WatchOptions$Mutation$RemoveRepository()); +} + +class Mutation$RemoveRepository$backup { + Mutation$RemoveRepository$backup({ + required this.removeRepository, + this.$__typename = 'BackupMutations', + }); + + factory Mutation$RemoveRepository$backup.fromJson(Map json) { + final l$removeRepository = json['removeRepository']; + final l$$__typename = json['__typename']; + return Mutation$RemoveRepository$backup( + removeRepository: Fragment$genericBackupConfigReturn.fromJson( + (l$removeRepository as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Fragment$genericBackupConfigReturn removeRepository; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$removeRepository = removeRepository; + _resultData['removeRepository'] = l$removeRepository.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$removeRepository = removeRepository; + final l$$__typename = $__typename; + return Object.hashAll([ + l$removeRepository, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$RemoveRepository$backup) || + runtimeType != other.runtimeType) { + return false; + } + final l$removeRepository = removeRepository; + final lOther$removeRepository = other.removeRepository; + if (l$removeRepository != lOther$removeRepository) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$RemoveRepository$backup + on Mutation$RemoveRepository$backup { + CopyWith$Mutation$RemoveRepository$backup + get copyWith => CopyWith$Mutation$RemoveRepository$backup( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$RemoveRepository$backup { + factory CopyWith$Mutation$RemoveRepository$backup( + Mutation$RemoveRepository$backup instance, + TRes Function(Mutation$RemoveRepository$backup) then, + ) = _CopyWithImpl$Mutation$RemoveRepository$backup; + + factory CopyWith$Mutation$RemoveRepository$backup.stub(TRes res) = + _CopyWithStubImpl$Mutation$RemoveRepository$backup; + + TRes call({ + Fragment$genericBackupConfigReturn? removeRepository, + String? $__typename, + }); + CopyWith$Fragment$genericBackupConfigReturn get removeRepository; +} + +class _CopyWithImpl$Mutation$RemoveRepository$backup + implements CopyWith$Mutation$RemoveRepository$backup { + _CopyWithImpl$Mutation$RemoveRepository$backup( + this._instance, + this._then, + ); + + final Mutation$RemoveRepository$backup _instance; + + final TRes Function(Mutation$RemoveRepository$backup) _then; + + static const _undefined = {}; + + TRes call({ + Object? removeRepository = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$RemoveRepository$backup( + removeRepository: + removeRepository == _undefined || removeRepository == null + ? _instance.removeRepository + : (removeRepository as Fragment$genericBackupConfigReturn), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Fragment$genericBackupConfigReturn get removeRepository { + final local$removeRepository = _instance.removeRepository; + return CopyWith$Fragment$genericBackupConfigReturn( + local$removeRepository, (e) => call(removeRepository: e)); + } +} + +class _CopyWithStubImpl$Mutation$RemoveRepository$backup + implements CopyWith$Mutation$RemoveRepository$backup { + _CopyWithStubImpl$Mutation$RemoveRepository$backup(this._res); + + TRes _res; + + call({ + Fragment$genericBackupConfigReturn? removeRepository, + String? $__typename, + }) => + _res; + CopyWith$Fragment$genericBackupConfigReturn get removeRepository => + CopyWith$Fragment$genericBackupConfigReturn.stub(_res); +} + +class Variables$Mutation$InitializeRepository { + factory Variables$Mutation$InitializeRepository( + {required Input$InitializeRepositoryInput repository}) => + Variables$Mutation$InitializeRepository._({ + r'repository': repository, + }); + + Variables$Mutation$InitializeRepository._(this._$data); + + factory Variables$Mutation$InitializeRepository.fromJson( + Map data) { + final result$data = {}; + final l$repository = data['repository']; + result$data['repository'] = Input$InitializeRepositoryInput.fromJson( + (l$repository as Map)); + return Variables$Mutation$InitializeRepository._(result$data); + } + + Map _$data; + + Input$InitializeRepositoryInput get repository => + (_$data['repository'] as Input$InitializeRepositoryInput); + Map toJson() { + final result$data = {}; + final l$repository = repository; + result$data['repository'] = l$repository.toJson(); + return result$data; + } + + CopyWith$Variables$Mutation$InitializeRepository< + Variables$Mutation$InitializeRepository> + get copyWith => CopyWith$Variables$Mutation$InitializeRepository( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$InitializeRepository) || + runtimeType != other.runtimeType) { + return false; + } + final l$repository = repository; + final lOther$repository = other.repository; + if (l$repository != lOther$repository) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$repository = repository; + return Object.hashAll([l$repository]); + } +} + +abstract class CopyWith$Variables$Mutation$InitializeRepository { + factory CopyWith$Variables$Mutation$InitializeRepository( + Variables$Mutation$InitializeRepository instance, + TRes Function(Variables$Mutation$InitializeRepository) then, + ) = _CopyWithImpl$Variables$Mutation$InitializeRepository; + + factory CopyWith$Variables$Mutation$InitializeRepository.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$InitializeRepository; + + TRes call({Input$InitializeRepositoryInput? repository}); +} + +class _CopyWithImpl$Variables$Mutation$InitializeRepository + implements CopyWith$Variables$Mutation$InitializeRepository { + _CopyWithImpl$Variables$Mutation$InitializeRepository( + this._instance, + this._then, + ); + + final Variables$Mutation$InitializeRepository _instance; + + final TRes Function(Variables$Mutation$InitializeRepository) _then; + + static const _undefined = {}; + + TRes call({Object? repository = _undefined}) => + _then(Variables$Mutation$InitializeRepository._({ + ..._instance._$data, + if (repository != _undefined && repository != null) + 'repository': (repository as Input$InitializeRepositoryInput), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$InitializeRepository + implements CopyWith$Variables$Mutation$InitializeRepository { + _CopyWithStubImpl$Variables$Mutation$InitializeRepository(this._res); + + TRes _res; + + call({Input$InitializeRepositoryInput? repository}) => _res; +} + +class Mutation$InitializeRepository { + Mutation$InitializeRepository({ + required this.backup, + this.$__typename = 'Mutation', + }); + + factory Mutation$InitializeRepository.fromJson(Map json) { + final l$backup = json['backup']; + final l$$__typename = json['__typename']; + return Mutation$InitializeRepository( + backup: Mutation$InitializeRepository$backup.fromJson( + (l$backup as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$InitializeRepository$backup backup; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$backup = backup; + _resultData['backup'] = l$backup.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$backup = backup; + final l$$__typename = $__typename; + return Object.hashAll([ + l$backup, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$InitializeRepository) || + runtimeType != other.runtimeType) { + return false; + } + final l$backup = backup; + final lOther$backup = other.backup; + if (l$backup != lOther$backup) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$InitializeRepository + on Mutation$InitializeRepository { + CopyWith$Mutation$InitializeRepository + get copyWith => CopyWith$Mutation$InitializeRepository( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$InitializeRepository { + factory CopyWith$Mutation$InitializeRepository( + Mutation$InitializeRepository instance, + TRes Function(Mutation$InitializeRepository) then, + ) = _CopyWithImpl$Mutation$InitializeRepository; + + factory CopyWith$Mutation$InitializeRepository.stub(TRes res) = + _CopyWithStubImpl$Mutation$InitializeRepository; + + TRes call({ + Mutation$InitializeRepository$backup? backup, + String? $__typename, + }); + CopyWith$Mutation$InitializeRepository$backup get backup; +} + +class _CopyWithImpl$Mutation$InitializeRepository + implements CopyWith$Mutation$InitializeRepository { + _CopyWithImpl$Mutation$InitializeRepository( + this._instance, + this._then, + ); + + final Mutation$InitializeRepository _instance; + + final TRes Function(Mutation$InitializeRepository) _then; + + static const _undefined = {}; + + TRes call({ + Object? backup = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$InitializeRepository( + backup: backup == _undefined || backup == null + ? _instance.backup + : (backup as Mutation$InitializeRepository$backup), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$InitializeRepository$backup get backup { + final local$backup = _instance.backup; + return CopyWith$Mutation$InitializeRepository$backup( + local$backup, (e) => call(backup: e)); + } +} + +class _CopyWithStubImpl$Mutation$InitializeRepository + implements CopyWith$Mutation$InitializeRepository { + _CopyWithStubImpl$Mutation$InitializeRepository(this._res); + + TRes _res; + + call({ + Mutation$InitializeRepository$backup? backup, + String? $__typename, + }) => + _res; + CopyWith$Mutation$InitializeRepository$backup get backup => + CopyWith$Mutation$InitializeRepository$backup.stub(_res); +} + +const documentNodeMutationInitializeRepository = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'InitializeRepository'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'repository')), + type: NamedTypeNode( + name: NameNode(value: 'InitializeRepositoryInput'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'backup'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'initializeRepository'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'repository'), + value: VariableNode(name: NameNode(value: 'repository')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'genericBackupConfigReturn'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitiongenericBackupConfigReturn, +]); +Mutation$InitializeRepository _parserFn$Mutation$InitializeRepository( + Map data) => + Mutation$InitializeRepository.fromJson(data); +typedef OnMutationCompleted$Mutation$InitializeRepository = FutureOr + Function( + Map?, + Mutation$InitializeRepository?, +); + +class Options$Mutation$InitializeRepository + extends graphql.MutationOptions { + Options$Mutation$InitializeRepository({ + String? operationName, + required Variables$Mutation$InitializeRepository variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$InitializeRepository? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$InitializeRepository? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$InitializeRepository(data), + ), + update: update, + onError: onError, + document: documentNodeMutationInitializeRepository, + parserFn: _parserFn$Mutation$InitializeRepository, + ); + + final OnMutationCompleted$Mutation$InitializeRepository? + onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$InitializeRepository + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$InitializeRepository({ + String? operationName, + required Variables$Mutation$InitializeRepository variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$InitializeRepository? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationInitializeRepository, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$InitializeRepository, + ); +} + +extension ClientExtension$Mutation$InitializeRepository + on graphql.GraphQLClient { + Future> + mutate$InitializeRepository( + Options$Mutation$InitializeRepository options) async => + await this.mutate(options); + graphql.ObservableQuery + watchMutation$InitializeRepository( + WatchOptions$Mutation$InitializeRepository options) => + this.watchMutation(options); +} + +class Mutation$InitializeRepository$backup { + Mutation$InitializeRepository$backup({ + required this.initializeRepository, + this.$__typename = 'BackupMutations', + }); + + factory Mutation$InitializeRepository$backup.fromJson( + Map json) { + final l$initializeRepository = json['initializeRepository']; + final l$$__typename = json['__typename']; + return Mutation$InitializeRepository$backup( + initializeRepository: Fragment$genericBackupConfigReturn.fromJson( + (l$initializeRepository as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Fragment$genericBackupConfigReturn initializeRepository; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$initializeRepository = initializeRepository; + _resultData['initializeRepository'] = l$initializeRepository.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$initializeRepository = initializeRepository; + final l$$__typename = $__typename; + return Object.hashAll([ + l$initializeRepository, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$InitializeRepository$backup) || + runtimeType != other.runtimeType) { + return false; + } + final l$initializeRepository = initializeRepository; + final lOther$initializeRepository = other.initializeRepository; + if (l$initializeRepository != lOther$initializeRepository) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$InitializeRepository$backup + on Mutation$InitializeRepository$backup { + CopyWith$Mutation$InitializeRepository$backup< + Mutation$InitializeRepository$backup> + get copyWith => CopyWith$Mutation$InitializeRepository$backup( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$InitializeRepository$backup { + factory CopyWith$Mutation$InitializeRepository$backup( + Mutation$InitializeRepository$backup instance, + TRes Function(Mutation$InitializeRepository$backup) then, + ) = _CopyWithImpl$Mutation$InitializeRepository$backup; + + factory CopyWith$Mutation$InitializeRepository$backup.stub(TRes res) = + _CopyWithStubImpl$Mutation$InitializeRepository$backup; + + TRes call({ + Fragment$genericBackupConfigReturn? initializeRepository, + String? $__typename, + }); + CopyWith$Fragment$genericBackupConfigReturn get initializeRepository; +} + +class _CopyWithImpl$Mutation$InitializeRepository$backup + implements CopyWith$Mutation$InitializeRepository$backup { + _CopyWithImpl$Mutation$InitializeRepository$backup( + this._instance, + this._then, + ); + + final Mutation$InitializeRepository$backup _instance; + + final TRes Function(Mutation$InitializeRepository$backup) _then; + + static const _undefined = {}; + + TRes call({ + Object? initializeRepository = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$InitializeRepository$backup( + initializeRepository: + initializeRepository == _undefined || initializeRepository == null + ? _instance.initializeRepository + : (initializeRepository as Fragment$genericBackupConfigReturn), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Fragment$genericBackupConfigReturn get initializeRepository { + final local$initializeRepository = _instance.initializeRepository; + return CopyWith$Fragment$genericBackupConfigReturn( + local$initializeRepository, (e) => call(initializeRepository: e)); + } +} + +class _CopyWithStubImpl$Mutation$InitializeRepository$backup + implements CopyWith$Mutation$InitializeRepository$backup { + _CopyWithStubImpl$Mutation$InitializeRepository$backup(this._res); + + TRes _res; + + call({ + Fragment$genericBackupConfigReturn? initializeRepository, + String? $__typename, + }) => + _res; + CopyWith$Fragment$genericBackupConfigReturn get initializeRepository => + CopyWith$Fragment$genericBackupConfigReturn.stub(_res); +} + +class Variables$Mutation$RestoreBackup { + factory Variables$Mutation$RestoreBackup({ + required String snapshotId, + required Enum$RestoreStrategy strategy, + }) => + Variables$Mutation$RestoreBackup._({ + r'snapshotId': snapshotId, + r'strategy': strategy, + }); + + Variables$Mutation$RestoreBackup._(this._$data); + + factory Variables$Mutation$RestoreBackup.fromJson(Map data) { + final result$data = {}; + final l$snapshotId = data['snapshotId']; + result$data['snapshotId'] = (l$snapshotId as String); + final l$strategy = data['strategy']; + result$data['strategy'] = + fromJson$Enum$RestoreStrategy((l$strategy as String)); + return Variables$Mutation$RestoreBackup._(result$data); + } + + Map _$data; + + String get snapshotId => (_$data['snapshotId'] as String); + Enum$RestoreStrategy get strategy => + (_$data['strategy'] as Enum$RestoreStrategy); + Map toJson() { + final result$data = {}; + final l$snapshotId = snapshotId; + result$data['snapshotId'] = l$snapshotId; + final l$strategy = strategy; + result$data['strategy'] = toJson$Enum$RestoreStrategy(l$strategy); + return result$data; + } + + CopyWith$Variables$Mutation$RestoreBackup + get copyWith => CopyWith$Variables$Mutation$RestoreBackup( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$RestoreBackup) || + runtimeType != other.runtimeType) { + return false; + } + final l$snapshotId = snapshotId; + final lOther$snapshotId = other.snapshotId; + if (l$snapshotId != lOther$snapshotId) { + return false; + } + final l$strategy = strategy; + final lOther$strategy = other.strategy; + if (l$strategy != lOther$strategy) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$snapshotId = snapshotId; + final l$strategy = strategy; + return Object.hashAll([ + l$snapshotId, + l$strategy, + ]); + } +} + +abstract class CopyWith$Variables$Mutation$RestoreBackup { + factory CopyWith$Variables$Mutation$RestoreBackup( + Variables$Mutation$RestoreBackup instance, + TRes Function(Variables$Mutation$RestoreBackup) then, + ) = _CopyWithImpl$Variables$Mutation$RestoreBackup; + + factory CopyWith$Variables$Mutation$RestoreBackup.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$RestoreBackup; + + TRes call({ + String? snapshotId, + Enum$RestoreStrategy? strategy, + }); +} + +class _CopyWithImpl$Variables$Mutation$RestoreBackup + implements CopyWith$Variables$Mutation$RestoreBackup { + _CopyWithImpl$Variables$Mutation$RestoreBackup( + this._instance, + this._then, + ); + + final Variables$Mutation$RestoreBackup _instance; + + final TRes Function(Variables$Mutation$RestoreBackup) _then; + + static const _undefined = {}; + + TRes call({ + Object? snapshotId = _undefined, + Object? strategy = _undefined, + }) => + _then(Variables$Mutation$RestoreBackup._({ + ..._instance._$data, + if (snapshotId != _undefined && snapshotId != null) + 'snapshotId': (snapshotId as String), + if (strategy != _undefined && strategy != null) + 'strategy': (strategy as Enum$RestoreStrategy), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$RestoreBackup + implements CopyWith$Variables$Mutation$RestoreBackup { + _CopyWithStubImpl$Variables$Mutation$RestoreBackup(this._res); + + TRes _res; + + call({ + String? snapshotId, + Enum$RestoreStrategy? strategy, + }) => + _res; +} + +class Mutation$RestoreBackup { + Mutation$RestoreBackup({ + required this.backup, + this.$__typename = 'Mutation', + }); + + factory Mutation$RestoreBackup.fromJson(Map json) { + final l$backup = json['backup']; + final l$$__typename = json['__typename']; + return Mutation$RestoreBackup( + backup: Mutation$RestoreBackup$backup.fromJson( + (l$backup as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$RestoreBackup$backup backup; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$backup = backup; + _resultData['backup'] = l$backup.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$backup = backup; + final l$$__typename = $__typename; + return Object.hashAll([ + l$backup, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$RestoreBackup) || + runtimeType != other.runtimeType) { + return false; + } + final l$backup = backup; + final lOther$backup = other.backup; + if (l$backup != lOther$backup) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$RestoreBackup on Mutation$RestoreBackup { + CopyWith$Mutation$RestoreBackup get copyWith => + CopyWith$Mutation$RestoreBackup( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$RestoreBackup { + factory CopyWith$Mutation$RestoreBackup( + Mutation$RestoreBackup instance, + TRes Function(Mutation$RestoreBackup) then, + ) = _CopyWithImpl$Mutation$RestoreBackup; + + factory CopyWith$Mutation$RestoreBackup.stub(TRes res) = + _CopyWithStubImpl$Mutation$RestoreBackup; + + TRes call({ + Mutation$RestoreBackup$backup? backup, + String? $__typename, + }); + CopyWith$Mutation$RestoreBackup$backup get backup; +} + +class _CopyWithImpl$Mutation$RestoreBackup + implements CopyWith$Mutation$RestoreBackup { + _CopyWithImpl$Mutation$RestoreBackup( + this._instance, + this._then, + ); + + final Mutation$RestoreBackup _instance; + + final TRes Function(Mutation$RestoreBackup) _then; + + static const _undefined = {}; + + TRes call({ + Object? backup = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$RestoreBackup( + backup: backup == _undefined || backup == null + ? _instance.backup + : (backup as Mutation$RestoreBackup$backup), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$RestoreBackup$backup get backup { + final local$backup = _instance.backup; + return CopyWith$Mutation$RestoreBackup$backup( + local$backup, (e) => call(backup: e)); + } +} + +class _CopyWithStubImpl$Mutation$RestoreBackup + implements CopyWith$Mutation$RestoreBackup { + _CopyWithStubImpl$Mutation$RestoreBackup(this._res); + + TRes _res; + + call({ + Mutation$RestoreBackup$backup? backup, + String? $__typename, + }) => + _res; + CopyWith$Mutation$RestoreBackup$backup get backup => + CopyWith$Mutation$RestoreBackup$backup.stub(_res); +} + +const documentNodeMutationRestoreBackup = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'RestoreBackup'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'snapshotId')), + type: NamedTypeNode( + name: NameNode(value: 'String'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ), + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'strategy')), + type: NamedTypeNode( + name: NameNode(value: 'RestoreStrategy'), + isNonNull: true, + ), + defaultValue: DefaultValueNode( + value: EnumValueNode( + name: NameNode(value: 'DOWNLOAD_VERIFY_OVERWRITE'))), + directives: [], + ), + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'backup'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'restoreBackup'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'snapshotId'), + value: VariableNode(name: NameNode(value: 'snapshotId')), + ), + ArgumentNode( + name: NameNode(value: 'strategy'), + value: VariableNode(name: NameNode(value: 'strategy')), + ), + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: 'job'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicApiJobsFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, + fragmentDefinitionbasicApiJobsFields, +]); +Mutation$RestoreBackup _parserFn$Mutation$RestoreBackup( + Map data) => + Mutation$RestoreBackup.fromJson(data); +typedef OnMutationCompleted$Mutation$RestoreBackup = FutureOr Function( + Map?, + Mutation$RestoreBackup?, +); + +class Options$Mutation$RestoreBackup + extends graphql.MutationOptions { + Options$Mutation$RestoreBackup({ + String? operationName, + required Variables$Mutation$RestoreBackup variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RestoreBackup? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$RestoreBackup? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$RestoreBackup(data), + ), + update: update, + onError: onError, + document: documentNodeMutationRestoreBackup, + parserFn: _parserFn$Mutation$RestoreBackup, + ); + + final OnMutationCompleted$Mutation$RestoreBackup? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$RestoreBackup + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$RestoreBackup({ + String? operationName, + required Variables$Mutation$RestoreBackup variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RestoreBackup? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationRestoreBackup, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$RestoreBackup, + ); +} + +extension ClientExtension$Mutation$RestoreBackup on graphql.GraphQLClient { + Future> mutate$RestoreBackup( + Options$Mutation$RestoreBackup options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$RestoreBackup( + WatchOptions$Mutation$RestoreBackup options) => + this.watchMutation(options); +} + +class Mutation$RestoreBackup$backup { + Mutation$RestoreBackup$backup({ + required this.restoreBackup, + this.$__typename = 'BackupMutations', + }); + + factory Mutation$RestoreBackup$backup.fromJson(Map json) { + final l$restoreBackup = json['restoreBackup']; + final l$$__typename = json['__typename']; + return Mutation$RestoreBackup$backup( + restoreBackup: Mutation$RestoreBackup$backup$restoreBackup.fromJson( + (l$restoreBackup as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$RestoreBackup$backup$restoreBackup restoreBackup; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$restoreBackup = restoreBackup; + _resultData['restoreBackup'] = l$restoreBackup.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$restoreBackup = restoreBackup; + final l$$__typename = $__typename; + return Object.hashAll([ + l$restoreBackup, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$RestoreBackup$backup) || + runtimeType != other.runtimeType) { + return false; + } + final l$restoreBackup = restoreBackup; + final lOther$restoreBackup = other.restoreBackup; + if (l$restoreBackup != lOther$restoreBackup) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$RestoreBackup$backup + on Mutation$RestoreBackup$backup { + CopyWith$Mutation$RestoreBackup$backup + get copyWith => CopyWith$Mutation$RestoreBackup$backup( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$RestoreBackup$backup { + factory CopyWith$Mutation$RestoreBackup$backup( + Mutation$RestoreBackup$backup instance, + TRes Function(Mutation$RestoreBackup$backup) then, + ) = _CopyWithImpl$Mutation$RestoreBackup$backup; + + factory CopyWith$Mutation$RestoreBackup$backup.stub(TRes res) = + _CopyWithStubImpl$Mutation$RestoreBackup$backup; + + TRes call({ + Mutation$RestoreBackup$backup$restoreBackup? restoreBackup, + String? $__typename, + }); + CopyWith$Mutation$RestoreBackup$backup$restoreBackup get restoreBackup; +} + +class _CopyWithImpl$Mutation$RestoreBackup$backup + implements CopyWith$Mutation$RestoreBackup$backup { + _CopyWithImpl$Mutation$RestoreBackup$backup( + this._instance, + this._then, + ); + + final Mutation$RestoreBackup$backup _instance; + + final TRes Function(Mutation$RestoreBackup$backup) _then; + + static const _undefined = {}; + + TRes call({ + Object? restoreBackup = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$RestoreBackup$backup( + restoreBackup: restoreBackup == _undefined || restoreBackup == null + ? _instance.restoreBackup + : (restoreBackup as Mutation$RestoreBackup$backup$restoreBackup), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$RestoreBackup$backup$restoreBackup get restoreBackup { + final local$restoreBackup = _instance.restoreBackup; + return CopyWith$Mutation$RestoreBackup$backup$restoreBackup( + local$restoreBackup, (e) => call(restoreBackup: e)); + } +} + +class _CopyWithStubImpl$Mutation$RestoreBackup$backup + implements CopyWith$Mutation$RestoreBackup$backup { + _CopyWithStubImpl$Mutation$RestoreBackup$backup(this._res); + + TRes _res; + + call({ + Mutation$RestoreBackup$backup$restoreBackup? restoreBackup, + String? $__typename, + }) => + _res; + CopyWith$Mutation$RestoreBackup$backup$restoreBackup + get restoreBackup => + CopyWith$Mutation$RestoreBackup$backup$restoreBackup.stub(_res); +} + +class Mutation$RestoreBackup$backup$restoreBackup + implements Fragment$basicMutationReturnFields$$GenericJobMutationReturn { + Mutation$RestoreBackup$backup$restoreBackup({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'GenericJobMutationReturn', + this.job, + }); + + factory Mutation$RestoreBackup$backup$restoreBackup.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + final l$job = json['job']; + return Mutation$RestoreBackup$backup$restoreBackup( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + job: l$job == null + ? null + : Fragment$basicApiJobsFields.fromJson( + (l$job as Map)), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + final Fragment$basicApiJobsFields? job; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + final l$job = job; + _resultData['job'] = l$job?.toJson(); + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + final l$job = job; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + l$job, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$RestoreBackup$backup$restoreBackup) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + final l$job = job; + final lOther$job = other.job; + if (l$job != lOther$job) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$RestoreBackup$backup$restoreBackup + on Mutation$RestoreBackup$backup$restoreBackup { + CopyWith$Mutation$RestoreBackup$backup$restoreBackup< + Mutation$RestoreBackup$backup$restoreBackup> + get copyWith => CopyWith$Mutation$RestoreBackup$backup$restoreBackup( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$RestoreBackup$backup$restoreBackup { + factory CopyWith$Mutation$RestoreBackup$backup$restoreBackup( + Mutation$RestoreBackup$backup$restoreBackup instance, + TRes Function(Mutation$RestoreBackup$backup$restoreBackup) then, + ) = _CopyWithImpl$Mutation$RestoreBackup$backup$restoreBackup; + + factory CopyWith$Mutation$RestoreBackup$backup$restoreBackup.stub(TRes res) = + _CopyWithStubImpl$Mutation$RestoreBackup$backup$restoreBackup; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + Fragment$basicApiJobsFields? job, + }); + CopyWith$Fragment$basicApiJobsFields get job; +} + +class _CopyWithImpl$Mutation$RestoreBackup$backup$restoreBackup + implements CopyWith$Mutation$RestoreBackup$backup$restoreBackup { + _CopyWithImpl$Mutation$RestoreBackup$backup$restoreBackup( + this._instance, + this._then, + ); + + final Mutation$RestoreBackup$backup$restoreBackup _instance; + + final TRes Function(Mutation$RestoreBackup$backup$restoreBackup) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + Object? job = _undefined, + }) => + _then(Mutation$RestoreBackup$backup$restoreBackup( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + job: job == _undefined + ? _instance.job + : (job as Fragment$basicApiJobsFields?), + )); + CopyWith$Fragment$basicApiJobsFields get job { + final local$job = _instance.job; + return local$job == null + ? CopyWith$Fragment$basicApiJobsFields.stub(_then(_instance)) + : CopyWith$Fragment$basicApiJobsFields(local$job, (e) => call(job: e)); + } +} + +class _CopyWithStubImpl$Mutation$RestoreBackup$backup$restoreBackup + implements CopyWith$Mutation$RestoreBackup$backup$restoreBackup { + _CopyWithStubImpl$Mutation$RestoreBackup$backup$restoreBackup(this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + Fragment$basicApiJobsFields? job, + }) => + _res; + CopyWith$Fragment$basicApiJobsFields get job => + CopyWith$Fragment$basicApiJobsFields.stub(_res); +} + +class Variables$Mutation$ForgetSnapshot { + factory Variables$Mutation$ForgetSnapshot({required String snapshotId}) => + Variables$Mutation$ForgetSnapshot._({ + r'snapshotId': snapshotId, + }); + + Variables$Mutation$ForgetSnapshot._(this._$data); + + factory Variables$Mutation$ForgetSnapshot.fromJson( + Map data) { + final result$data = {}; + final l$snapshotId = data['snapshotId']; + result$data['snapshotId'] = (l$snapshotId as String); + return Variables$Mutation$ForgetSnapshot._(result$data); + } + + Map _$data; + + String get snapshotId => (_$data['snapshotId'] as String); + Map toJson() { + final result$data = {}; + final l$snapshotId = snapshotId; + result$data['snapshotId'] = l$snapshotId; + return result$data; + } + + CopyWith$Variables$Mutation$ForgetSnapshot + get copyWith => CopyWith$Variables$Mutation$ForgetSnapshot( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$ForgetSnapshot) || + runtimeType != other.runtimeType) { + return false; + } + final l$snapshotId = snapshotId; + final lOther$snapshotId = other.snapshotId; + if (l$snapshotId != lOther$snapshotId) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$snapshotId = snapshotId; + return Object.hashAll([l$snapshotId]); + } +} + +abstract class CopyWith$Variables$Mutation$ForgetSnapshot { + factory CopyWith$Variables$Mutation$ForgetSnapshot( + Variables$Mutation$ForgetSnapshot instance, + TRes Function(Variables$Mutation$ForgetSnapshot) then, + ) = _CopyWithImpl$Variables$Mutation$ForgetSnapshot; + + factory CopyWith$Variables$Mutation$ForgetSnapshot.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$ForgetSnapshot; + + TRes call({String? snapshotId}); +} + +class _CopyWithImpl$Variables$Mutation$ForgetSnapshot + implements CopyWith$Variables$Mutation$ForgetSnapshot { + _CopyWithImpl$Variables$Mutation$ForgetSnapshot( + this._instance, + this._then, + ); + + final Variables$Mutation$ForgetSnapshot _instance; + + final TRes Function(Variables$Mutation$ForgetSnapshot) _then; + + static const _undefined = {}; + + TRes call({Object? snapshotId = _undefined}) => + _then(Variables$Mutation$ForgetSnapshot._({ + ..._instance._$data, + if (snapshotId != _undefined && snapshotId != null) + 'snapshotId': (snapshotId as String), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$ForgetSnapshot + implements CopyWith$Variables$Mutation$ForgetSnapshot { + _CopyWithStubImpl$Variables$Mutation$ForgetSnapshot(this._res); + + TRes _res; + + call({String? snapshotId}) => _res; +} + +class Mutation$ForgetSnapshot { + Mutation$ForgetSnapshot({ + required this.backup, + this.$__typename = 'Mutation', + }); + + factory Mutation$ForgetSnapshot.fromJson(Map json) { + final l$backup = json['backup']; + final l$$__typename = json['__typename']; + return Mutation$ForgetSnapshot( + backup: Mutation$ForgetSnapshot$backup.fromJson( + (l$backup as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$ForgetSnapshot$backup backup; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$backup = backup; + _resultData['backup'] = l$backup.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$backup = backup; + final l$$__typename = $__typename; + return Object.hashAll([ + l$backup, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$ForgetSnapshot) || + runtimeType != other.runtimeType) { + return false; + } + final l$backup = backup; + final lOther$backup = other.backup; + if (l$backup != lOther$backup) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$ForgetSnapshot on Mutation$ForgetSnapshot { + CopyWith$Mutation$ForgetSnapshot get copyWith => + CopyWith$Mutation$ForgetSnapshot( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$ForgetSnapshot { + factory CopyWith$Mutation$ForgetSnapshot( + Mutation$ForgetSnapshot instance, + TRes Function(Mutation$ForgetSnapshot) then, + ) = _CopyWithImpl$Mutation$ForgetSnapshot; + + factory CopyWith$Mutation$ForgetSnapshot.stub(TRes res) = + _CopyWithStubImpl$Mutation$ForgetSnapshot; + + TRes call({ + Mutation$ForgetSnapshot$backup? backup, + String? $__typename, + }); + CopyWith$Mutation$ForgetSnapshot$backup get backup; +} + +class _CopyWithImpl$Mutation$ForgetSnapshot + implements CopyWith$Mutation$ForgetSnapshot { + _CopyWithImpl$Mutation$ForgetSnapshot( + this._instance, + this._then, + ); + + final Mutation$ForgetSnapshot _instance; + + final TRes Function(Mutation$ForgetSnapshot) _then; + + static const _undefined = {}; + + TRes call({ + Object? backup = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$ForgetSnapshot( + backup: backup == _undefined || backup == null + ? _instance.backup + : (backup as Mutation$ForgetSnapshot$backup), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$ForgetSnapshot$backup get backup { + final local$backup = _instance.backup; + return CopyWith$Mutation$ForgetSnapshot$backup( + local$backup, (e) => call(backup: e)); + } +} + +class _CopyWithStubImpl$Mutation$ForgetSnapshot + implements CopyWith$Mutation$ForgetSnapshot { + _CopyWithStubImpl$Mutation$ForgetSnapshot(this._res); + + TRes _res; + + call({ + Mutation$ForgetSnapshot$backup? backup, + String? $__typename, + }) => + _res; + CopyWith$Mutation$ForgetSnapshot$backup get backup => + CopyWith$Mutation$ForgetSnapshot$backup.stub(_res); +} + +const documentNodeMutationForgetSnapshot = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'ForgetSnapshot'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'snapshotId')), + type: NamedTypeNode( + name: NameNode(value: 'String'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'backup'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'forgetSnapshot'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'snapshotId'), + value: VariableNode(name: NameNode(value: 'snapshotId')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, +]); +Mutation$ForgetSnapshot _parserFn$Mutation$ForgetSnapshot( + Map data) => + Mutation$ForgetSnapshot.fromJson(data); +typedef OnMutationCompleted$Mutation$ForgetSnapshot = FutureOr Function( + Map?, + Mutation$ForgetSnapshot?, +); + +class Options$Mutation$ForgetSnapshot + extends graphql.MutationOptions { + Options$Mutation$ForgetSnapshot({ + String? operationName, + required Variables$Mutation$ForgetSnapshot variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$ForgetSnapshot? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$ForgetSnapshot? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$ForgetSnapshot(data), + ), + update: update, + onError: onError, + document: documentNodeMutationForgetSnapshot, + parserFn: _parserFn$Mutation$ForgetSnapshot, + ); + + final OnMutationCompleted$Mutation$ForgetSnapshot? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$ForgetSnapshot + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$ForgetSnapshot({ + String? operationName, + required Variables$Mutation$ForgetSnapshot variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$ForgetSnapshot? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationForgetSnapshot, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$ForgetSnapshot, + ); +} + +extension ClientExtension$Mutation$ForgetSnapshot on graphql.GraphQLClient { + Future> mutate$ForgetSnapshot( + Options$Mutation$ForgetSnapshot options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$ForgetSnapshot( + WatchOptions$Mutation$ForgetSnapshot options) => + this.watchMutation(options); +} + +class Mutation$ForgetSnapshot$backup { + Mutation$ForgetSnapshot$backup({ + required this.forgetSnapshot, + this.$__typename = 'BackupMutations', + }); + + factory Mutation$ForgetSnapshot$backup.fromJson(Map json) { + final l$forgetSnapshot = json['forgetSnapshot']; + final l$$__typename = json['__typename']; + return Mutation$ForgetSnapshot$backup( + forgetSnapshot: Mutation$ForgetSnapshot$backup$forgetSnapshot.fromJson( + (l$forgetSnapshot as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$ForgetSnapshot$backup$forgetSnapshot forgetSnapshot; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$forgetSnapshot = forgetSnapshot; + _resultData['forgetSnapshot'] = l$forgetSnapshot.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$forgetSnapshot = forgetSnapshot; + final l$$__typename = $__typename; + return Object.hashAll([ + l$forgetSnapshot, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$ForgetSnapshot$backup) || + runtimeType != other.runtimeType) { + return false; + } + final l$forgetSnapshot = forgetSnapshot; + final lOther$forgetSnapshot = other.forgetSnapshot; + if (l$forgetSnapshot != lOther$forgetSnapshot) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$ForgetSnapshot$backup + on Mutation$ForgetSnapshot$backup { + CopyWith$Mutation$ForgetSnapshot$backup + get copyWith => CopyWith$Mutation$ForgetSnapshot$backup( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$ForgetSnapshot$backup { + factory CopyWith$Mutation$ForgetSnapshot$backup( + Mutation$ForgetSnapshot$backup instance, + TRes Function(Mutation$ForgetSnapshot$backup) then, + ) = _CopyWithImpl$Mutation$ForgetSnapshot$backup; + + factory CopyWith$Mutation$ForgetSnapshot$backup.stub(TRes res) = + _CopyWithStubImpl$Mutation$ForgetSnapshot$backup; + + TRes call({ + Mutation$ForgetSnapshot$backup$forgetSnapshot? forgetSnapshot, + String? $__typename, + }); + CopyWith$Mutation$ForgetSnapshot$backup$forgetSnapshot + get forgetSnapshot; +} + +class _CopyWithImpl$Mutation$ForgetSnapshot$backup + implements CopyWith$Mutation$ForgetSnapshot$backup { + _CopyWithImpl$Mutation$ForgetSnapshot$backup( + this._instance, + this._then, + ); + + final Mutation$ForgetSnapshot$backup _instance; + + final TRes Function(Mutation$ForgetSnapshot$backup) _then; + + static const _undefined = {}; + + TRes call({ + Object? forgetSnapshot = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$ForgetSnapshot$backup( + forgetSnapshot: forgetSnapshot == _undefined || forgetSnapshot == null + ? _instance.forgetSnapshot + : (forgetSnapshot as Mutation$ForgetSnapshot$backup$forgetSnapshot), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$ForgetSnapshot$backup$forgetSnapshot + get forgetSnapshot { + final local$forgetSnapshot = _instance.forgetSnapshot; + return CopyWith$Mutation$ForgetSnapshot$backup$forgetSnapshot( + local$forgetSnapshot, (e) => call(forgetSnapshot: e)); + } +} + +class _CopyWithStubImpl$Mutation$ForgetSnapshot$backup + implements CopyWith$Mutation$ForgetSnapshot$backup { + _CopyWithStubImpl$Mutation$ForgetSnapshot$backup(this._res); + + TRes _res; + + call({ + Mutation$ForgetSnapshot$backup$forgetSnapshot? forgetSnapshot, + String? $__typename, + }) => + _res; + CopyWith$Mutation$ForgetSnapshot$backup$forgetSnapshot + get forgetSnapshot => + CopyWith$Mutation$ForgetSnapshot$backup$forgetSnapshot.stub(_res); +} + +class Mutation$ForgetSnapshot$backup$forgetSnapshot + implements Fragment$basicMutationReturnFields$$GenericMutationReturn { + Mutation$ForgetSnapshot$backup$forgetSnapshot({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'GenericMutationReturn', + }); + + factory Mutation$ForgetSnapshot$backup$forgetSnapshot.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Mutation$ForgetSnapshot$backup$forgetSnapshot( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$ForgetSnapshot$backup$forgetSnapshot) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$ForgetSnapshot$backup$forgetSnapshot + on Mutation$ForgetSnapshot$backup$forgetSnapshot { + CopyWith$Mutation$ForgetSnapshot$backup$forgetSnapshot< + Mutation$ForgetSnapshot$backup$forgetSnapshot> + get copyWith => CopyWith$Mutation$ForgetSnapshot$backup$forgetSnapshot( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$ForgetSnapshot$backup$forgetSnapshot { + factory CopyWith$Mutation$ForgetSnapshot$backup$forgetSnapshot( + Mutation$ForgetSnapshot$backup$forgetSnapshot instance, + TRes Function(Mutation$ForgetSnapshot$backup$forgetSnapshot) then, + ) = _CopyWithImpl$Mutation$ForgetSnapshot$backup$forgetSnapshot; + + factory CopyWith$Mutation$ForgetSnapshot$backup$forgetSnapshot.stub( + TRes res) = + _CopyWithStubImpl$Mutation$ForgetSnapshot$backup$forgetSnapshot; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$ForgetSnapshot$backup$forgetSnapshot + implements CopyWith$Mutation$ForgetSnapshot$backup$forgetSnapshot { + _CopyWithImpl$Mutation$ForgetSnapshot$backup$forgetSnapshot( + this._instance, + this._then, + ); + + final Mutation$ForgetSnapshot$backup$forgetSnapshot _instance; + + final TRes Function(Mutation$ForgetSnapshot$backup$forgetSnapshot) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$ForgetSnapshot$backup$forgetSnapshot( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$ForgetSnapshot$backup$forgetSnapshot + implements CopyWith$Mutation$ForgetSnapshot$backup$forgetSnapshot { + _CopyWithStubImpl$Mutation$ForgetSnapshot$backup$forgetSnapshot(this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} diff --git a/lib/logic/api_maps/graphql_maps/schema/disk_volumes.graphql b/lib/logic/api_maps/graphql_maps/schema/disk_volumes.graphql new file mode 100644 index 00000000..76ba5475 --- /dev/null +++ b/lib/logic/api_maps/graphql_maps/schema/disk_volumes.graphql @@ -0,0 +1,53 @@ +query GetServerDiskVolumes { + storage { + volumes { + freeSpace + model + name + root + serial + totalSpace + type + usages { + title + usedSpace + __typename + ... on ServiceStorageUsage { + service { + id + isMovable + displayName + } + } + } + usedSpace + } + } +} + +mutation MountVolume($name: String!) { + mountVolume(name: $name) { + ...basicMutationReturnFields + } +} + +mutation ResizeVolume($name: String!) { + resizeVolume(name: $name) { + ...basicMutationReturnFields + } +} + +mutation UnmountVolume($name: String!) { + unmountVolume(name: $name) { + ...basicMutationReturnFields + } +} + +mutation MigrateToBinds($input: MigrateToBindsInput!) { + migrateToBinds(input: $input) { + ...basicMutationReturnFields + job { + ...basicApiJobsFields + } + } +} diff --git a/lib/logic/api_maps/graphql_maps/schema/disk_volumes.graphql.dart b/lib/logic/api_maps/graphql_maps/schema/disk_volumes.graphql.dart new file mode 100644 index 00000000..d448df1d --- /dev/null +++ b/lib/logic/api_maps/graphql_maps/schema/disk_volumes.graphql.dart @@ -0,0 +1,3732 @@ +import 'dart:async'; +import 'package:gql/ast.dart'; +import 'package:graphql/client.dart' as graphql; +import 'schema.graphql.dart'; +import 'server_api.graphql.dart'; + +class Query$GetServerDiskVolumes { + Query$GetServerDiskVolumes({ + required this.storage, + this.$__typename = 'Query', + }); + + factory Query$GetServerDiskVolumes.fromJson(Map json) { + final l$storage = json['storage']; + final l$$__typename = json['__typename']; + return Query$GetServerDiskVolumes( + storage: Query$GetServerDiskVolumes$storage.fromJson( + (l$storage as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Query$GetServerDiskVolumes$storage storage; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$storage = storage; + _resultData['storage'] = l$storage.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$storage = storage; + final l$$__typename = $__typename; + return Object.hashAll([ + l$storage, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$GetServerDiskVolumes) || + runtimeType != other.runtimeType) { + return false; + } + final l$storage = storage; + final lOther$storage = other.storage; + if (l$storage != lOther$storage) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$GetServerDiskVolumes + on Query$GetServerDiskVolumes { + CopyWith$Query$GetServerDiskVolumes + get copyWith => CopyWith$Query$GetServerDiskVolumes( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$GetServerDiskVolumes { + factory CopyWith$Query$GetServerDiskVolumes( + Query$GetServerDiskVolumes instance, + TRes Function(Query$GetServerDiskVolumes) then, + ) = _CopyWithImpl$Query$GetServerDiskVolumes; + + factory CopyWith$Query$GetServerDiskVolumes.stub(TRes res) = + _CopyWithStubImpl$Query$GetServerDiskVolumes; + + TRes call({ + Query$GetServerDiskVolumes$storage? storage, + String? $__typename, + }); + CopyWith$Query$GetServerDiskVolumes$storage get storage; +} + +class _CopyWithImpl$Query$GetServerDiskVolumes + implements CopyWith$Query$GetServerDiskVolumes { + _CopyWithImpl$Query$GetServerDiskVolumes( + this._instance, + this._then, + ); + + final Query$GetServerDiskVolumes _instance; + + final TRes Function(Query$GetServerDiskVolumes) _then; + + static const _undefined = {}; + + TRes call({ + Object? storage = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$GetServerDiskVolumes( + storage: storage == _undefined || storage == null + ? _instance.storage + : (storage as Query$GetServerDiskVolumes$storage), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$GetServerDiskVolumes$storage get storage { + final local$storage = _instance.storage; + return CopyWith$Query$GetServerDiskVolumes$storage( + local$storage, (e) => call(storage: e)); + } +} + +class _CopyWithStubImpl$Query$GetServerDiskVolumes + implements CopyWith$Query$GetServerDiskVolumes { + _CopyWithStubImpl$Query$GetServerDiskVolumes(this._res); + + TRes _res; + + call({ + Query$GetServerDiskVolumes$storage? storage, + String? $__typename, + }) => + _res; + CopyWith$Query$GetServerDiskVolumes$storage get storage => + CopyWith$Query$GetServerDiskVolumes$storage.stub(_res); +} + +const documentNodeQueryGetServerDiskVolumes = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.query, + name: NameNode(value: 'GetServerDiskVolumes'), + variableDefinitions: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'storage'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'volumes'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'freeSpace'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'model'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'name'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'root'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'serial'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'totalSpace'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'type'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'usages'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'title'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'usedSpace'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + InlineFragmentNode( + typeCondition: TypeConditionNode( + on: NamedTypeNode( + name: NameNode(value: 'ServiceStorageUsage'), + isNonNull: false, + )), + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'service'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'id'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'isMovable'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'displayName'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + ]), + ), + FieldNode( + name: NameNode(value: 'usedSpace'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), +]); +Query$GetServerDiskVolumes _parserFn$Query$GetServerDiskVolumes( + Map data) => + Query$GetServerDiskVolumes.fromJson(data); +typedef OnQueryComplete$Query$GetServerDiskVolumes = FutureOr Function( + Map?, + Query$GetServerDiskVolumes?, +); + +class Options$Query$GetServerDiskVolumes + extends graphql.QueryOptions { + Options$Query$GetServerDiskVolumes({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$GetServerDiskVolumes? typedOptimisticResult, + Duration? pollInterval, + graphql.Context? context, + OnQueryComplete$Query$GetServerDiskVolumes? onComplete, + graphql.OnQueryError? onError, + }) : onCompleteWithParsed = onComplete, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + pollInterval: pollInterval, + context: context, + onComplete: onComplete == null + ? null + : (data) => onComplete( + data, + data == null + ? null + : _parserFn$Query$GetServerDiskVolumes(data), + ), + onError: onError, + document: documentNodeQueryGetServerDiskVolumes, + parserFn: _parserFn$Query$GetServerDiskVolumes, + ); + + final OnQueryComplete$Query$GetServerDiskVolumes? onCompleteWithParsed; + + @override + List get properties => [ + ...super.onComplete == null + ? super.properties + : super.properties.where((property) => property != onComplete), + onCompleteWithParsed, + ]; +} + +class WatchOptions$Query$GetServerDiskVolumes + extends graphql.WatchQueryOptions { + WatchOptions$Query$GetServerDiskVolumes({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$GetServerDiskVolumes? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeQueryGetServerDiskVolumes, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Query$GetServerDiskVolumes, + ); +} + +class FetchMoreOptions$Query$GetServerDiskVolumes + extends graphql.FetchMoreOptions { + FetchMoreOptions$Query$GetServerDiskVolumes( + {required graphql.UpdateQuery updateQuery}) + : super( + updateQuery: updateQuery, + document: documentNodeQueryGetServerDiskVolumes, + ); +} + +extension ClientExtension$Query$GetServerDiskVolumes on graphql.GraphQLClient { + Future> + query$GetServerDiskVolumes( + [Options$Query$GetServerDiskVolumes? options]) async => + await this.query(options ?? Options$Query$GetServerDiskVolumes()); + graphql.ObservableQuery + watchQuery$GetServerDiskVolumes( + [WatchOptions$Query$GetServerDiskVolumes? options]) => + this.watchQuery(options ?? WatchOptions$Query$GetServerDiskVolumes()); + void writeQuery$GetServerDiskVolumes({ + required Query$GetServerDiskVolumes data, + bool broadcast = true, + }) => + this.writeQuery( + graphql.Request( + operation: graphql.Operation( + document: documentNodeQueryGetServerDiskVolumes)), + data: data.toJson(), + broadcast: broadcast, + ); + Query$GetServerDiskVolumes? readQuery$GetServerDiskVolumes( + {bool optimistic = true}) { + final result = this.readQuery( + graphql.Request( + operation: graphql.Operation( + document: documentNodeQueryGetServerDiskVolumes)), + optimistic: optimistic, + ); + return result == null ? null : Query$GetServerDiskVolumes.fromJson(result); + } +} + +class Query$GetServerDiskVolumes$storage { + Query$GetServerDiskVolumes$storage({ + required this.volumes, + this.$__typename = 'Storage', + }); + + factory Query$GetServerDiskVolumes$storage.fromJson( + Map json) { + final l$volumes = json['volumes']; + final l$$__typename = json['__typename']; + return Query$GetServerDiskVolumes$storage( + volumes: (l$volumes as List) + .map((e) => Query$GetServerDiskVolumes$storage$volumes.fromJson( + (e as Map))) + .toList(), + $__typename: (l$$__typename as String), + ); + } + + final List volumes; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$volumes = volumes; + _resultData['volumes'] = l$volumes.map((e) => e.toJson()).toList(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$volumes = volumes; + final l$$__typename = $__typename; + return Object.hashAll([ + Object.hashAll(l$volumes.map((v) => v)), + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$GetServerDiskVolumes$storage) || + runtimeType != other.runtimeType) { + return false; + } + final l$volumes = volumes; + final lOther$volumes = other.volumes; + if (l$volumes.length != lOther$volumes.length) { + return false; + } + for (int i = 0; i < l$volumes.length; i++) { + final l$volumes$entry = l$volumes[i]; + final lOther$volumes$entry = lOther$volumes[i]; + if (l$volumes$entry != lOther$volumes$entry) { + return false; + } + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$GetServerDiskVolumes$storage + on Query$GetServerDiskVolumes$storage { + CopyWith$Query$GetServerDiskVolumes$storage< + Query$GetServerDiskVolumes$storage> + get copyWith => CopyWith$Query$GetServerDiskVolumes$storage( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$GetServerDiskVolumes$storage { + factory CopyWith$Query$GetServerDiskVolumes$storage( + Query$GetServerDiskVolumes$storage instance, + TRes Function(Query$GetServerDiskVolumes$storage) then, + ) = _CopyWithImpl$Query$GetServerDiskVolumes$storage; + + factory CopyWith$Query$GetServerDiskVolumes$storage.stub(TRes res) = + _CopyWithStubImpl$Query$GetServerDiskVolumes$storage; + + TRes call({ + List? volumes, + String? $__typename, + }); + TRes volumes( + Iterable Function( + Iterable< + CopyWith$Query$GetServerDiskVolumes$storage$volumes< + Query$GetServerDiskVolumes$storage$volumes>>) + _fn); +} + +class _CopyWithImpl$Query$GetServerDiskVolumes$storage + implements CopyWith$Query$GetServerDiskVolumes$storage { + _CopyWithImpl$Query$GetServerDiskVolumes$storage( + this._instance, + this._then, + ); + + final Query$GetServerDiskVolumes$storage _instance; + + final TRes Function(Query$GetServerDiskVolumes$storage) _then; + + static const _undefined = {}; + + TRes call({ + Object? volumes = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$GetServerDiskVolumes$storage( + volumes: volumes == _undefined || volumes == null + ? _instance.volumes + : (volumes as List), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + TRes volumes( + Iterable Function( + Iterable< + CopyWith$Query$GetServerDiskVolumes$storage$volumes< + Query$GetServerDiskVolumes$storage$volumes>>) + _fn) => + call( + volumes: _fn(_instance.volumes + .map((e) => CopyWith$Query$GetServerDiskVolumes$storage$volumes( + e, + (i) => i, + ))).toList()); +} + +class _CopyWithStubImpl$Query$GetServerDiskVolumes$storage + implements CopyWith$Query$GetServerDiskVolumes$storage { + _CopyWithStubImpl$Query$GetServerDiskVolumes$storage(this._res); + + TRes _res; + + call({ + List? volumes, + String? $__typename, + }) => + _res; + volumes(_fn) => _res; +} + +class Query$GetServerDiskVolumes$storage$volumes { + Query$GetServerDiskVolumes$storage$volumes({ + required this.freeSpace, + this.model, + required this.name, + required this.root, + this.serial, + required this.totalSpace, + required this.type, + required this.usages, + required this.usedSpace, + this.$__typename = 'StorageVolume', + }); + + factory Query$GetServerDiskVolumes$storage$volumes.fromJson( + Map json) { + final l$freeSpace = json['freeSpace']; + final l$model = json['model']; + final l$name = json['name']; + final l$root = json['root']; + final l$serial = json['serial']; + final l$totalSpace = json['totalSpace']; + final l$type = json['type']; + final l$usages = json['usages']; + final l$usedSpace = json['usedSpace']; + final l$$__typename = json['__typename']; + return Query$GetServerDiskVolumes$storage$volumes( + freeSpace: (l$freeSpace as String), + model: (l$model as String?), + name: (l$name as String), + root: (l$root as bool), + serial: (l$serial as String?), + totalSpace: (l$totalSpace as String), + type: (l$type as String), + usages: (l$usages as List) + .map((e) => + Query$GetServerDiskVolumes$storage$volumes$usages.fromJson( + (e as Map))) + .toList(), + usedSpace: (l$usedSpace as String), + $__typename: (l$$__typename as String), + ); + } + + final String freeSpace; + + final String? model; + + final String name; + + final bool root; + + final String? serial; + + final String totalSpace; + + final String type; + + final List usages; + + final String usedSpace; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$freeSpace = freeSpace; + _resultData['freeSpace'] = l$freeSpace; + final l$model = model; + _resultData['model'] = l$model; + final l$name = name; + _resultData['name'] = l$name; + final l$root = root; + _resultData['root'] = l$root; + final l$serial = serial; + _resultData['serial'] = l$serial; + final l$totalSpace = totalSpace; + _resultData['totalSpace'] = l$totalSpace; + final l$type = type; + _resultData['type'] = l$type; + final l$usages = usages; + _resultData['usages'] = l$usages.map((e) => e.toJson()).toList(); + final l$usedSpace = usedSpace; + _resultData['usedSpace'] = l$usedSpace; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$freeSpace = freeSpace; + final l$model = model; + final l$name = name; + final l$root = root; + final l$serial = serial; + final l$totalSpace = totalSpace; + final l$type = type; + final l$usages = usages; + final l$usedSpace = usedSpace; + final l$$__typename = $__typename; + return Object.hashAll([ + l$freeSpace, + l$model, + l$name, + l$root, + l$serial, + l$totalSpace, + l$type, + Object.hashAll(l$usages.map((v) => v)), + l$usedSpace, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$GetServerDiskVolumes$storage$volumes) || + runtimeType != other.runtimeType) { + return false; + } + final l$freeSpace = freeSpace; + final lOther$freeSpace = other.freeSpace; + if (l$freeSpace != lOther$freeSpace) { + return false; + } + final l$model = model; + final lOther$model = other.model; + if (l$model != lOther$model) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$root = root; + final lOther$root = other.root; + if (l$root != lOther$root) { + return false; + } + final l$serial = serial; + final lOther$serial = other.serial; + if (l$serial != lOther$serial) { + return false; + } + final l$totalSpace = totalSpace; + final lOther$totalSpace = other.totalSpace; + if (l$totalSpace != lOther$totalSpace) { + return false; + } + final l$type = type; + final lOther$type = other.type; + if (l$type != lOther$type) { + return false; + } + final l$usages = usages; + final lOther$usages = other.usages; + if (l$usages.length != lOther$usages.length) { + return false; + } + for (int i = 0; i < l$usages.length; i++) { + final l$usages$entry = l$usages[i]; + final lOther$usages$entry = lOther$usages[i]; + if (l$usages$entry != lOther$usages$entry) { + return false; + } + } + final l$usedSpace = usedSpace; + final lOther$usedSpace = other.usedSpace; + if (l$usedSpace != lOther$usedSpace) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$GetServerDiskVolumes$storage$volumes + on Query$GetServerDiskVolumes$storage$volumes { + CopyWith$Query$GetServerDiskVolumes$storage$volumes< + Query$GetServerDiskVolumes$storage$volumes> + get copyWith => CopyWith$Query$GetServerDiskVolumes$storage$volumes( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$GetServerDiskVolumes$storage$volumes { + factory CopyWith$Query$GetServerDiskVolumes$storage$volumes( + Query$GetServerDiskVolumes$storage$volumes instance, + TRes Function(Query$GetServerDiskVolumes$storage$volumes) then, + ) = _CopyWithImpl$Query$GetServerDiskVolumes$storage$volumes; + + factory CopyWith$Query$GetServerDiskVolumes$storage$volumes.stub(TRes res) = + _CopyWithStubImpl$Query$GetServerDiskVolumes$storage$volumes; + + TRes call({ + String? freeSpace, + String? model, + String? name, + bool? root, + String? serial, + String? totalSpace, + String? type, + List? usages, + String? usedSpace, + String? $__typename, + }); + TRes usages( + Iterable Function( + Iterable< + CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages< + Query$GetServerDiskVolumes$storage$volumes$usages>>) + _fn); +} + +class _CopyWithImpl$Query$GetServerDiskVolumes$storage$volumes + implements CopyWith$Query$GetServerDiskVolumes$storage$volumes { + _CopyWithImpl$Query$GetServerDiskVolumes$storage$volumes( + this._instance, + this._then, + ); + + final Query$GetServerDiskVolumes$storage$volumes _instance; + + final TRes Function(Query$GetServerDiskVolumes$storage$volumes) _then; + + static const _undefined = {}; + + TRes call({ + Object? freeSpace = _undefined, + Object? model = _undefined, + Object? name = _undefined, + Object? root = _undefined, + Object? serial = _undefined, + Object? totalSpace = _undefined, + Object? type = _undefined, + Object? usages = _undefined, + Object? usedSpace = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$GetServerDiskVolumes$storage$volumes( + freeSpace: freeSpace == _undefined || freeSpace == null + ? _instance.freeSpace + : (freeSpace as String), + model: model == _undefined ? _instance.model : (model as String?), + name: name == _undefined || name == null + ? _instance.name + : (name as String), + root: root == _undefined || root == null + ? _instance.root + : (root as bool), + serial: serial == _undefined ? _instance.serial : (serial as String?), + totalSpace: totalSpace == _undefined || totalSpace == null + ? _instance.totalSpace + : (totalSpace as String), + type: type == _undefined || type == null + ? _instance.type + : (type as String), + usages: usages == _undefined || usages == null + ? _instance.usages + : (usages + as List), + usedSpace: usedSpace == _undefined || usedSpace == null + ? _instance.usedSpace + : (usedSpace as String), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + TRes usages( + Iterable Function( + Iterable< + CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages< + Query$GetServerDiskVolumes$storage$volumes$usages>>) + _fn) => + call( + usages: _fn(_instance.usages.map( + (e) => CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages( + e, + (i) => i, + ))).toList()); +} + +class _CopyWithStubImpl$Query$GetServerDiskVolumes$storage$volumes + implements CopyWith$Query$GetServerDiskVolumes$storage$volumes { + _CopyWithStubImpl$Query$GetServerDiskVolumes$storage$volumes(this._res); + + TRes _res; + + call({ + String? freeSpace, + String? model, + String? name, + bool? root, + String? serial, + String? totalSpace, + String? type, + List? usages, + String? usedSpace, + String? $__typename, + }) => + _res; + usages(_fn) => _res; +} + +class Query$GetServerDiskVolumes$storage$volumes$usages { + Query$GetServerDiskVolumes$storage$volumes$usages({ + required this.title, + required this.usedSpace, + required this.$__typename, + }); + + factory Query$GetServerDiskVolumes$storage$volumes$usages.fromJson( + Map json) { + switch (json["__typename"] as String) { + case "ServiceStorageUsage": + return Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage + .fromJson(json); + + default: + final l$title = json['title']; + final l$usedSpace = json['usedSpace']; + final l$$__typename = json['__typename']; + return Query$GetServerDiskVolumes$storage$volumes$usages( + title: (l$title as String), + usedSpace: (l$usedSpace as String), + $__typename: (l$$__typename as String), + ); + } + } + + final String title; + + final String usedSpace; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$title = title; + _resultData['title'] = l$title; + final l$usedSpace = usedSpace; + _resultData['usedSpace'] = l$usedSpace; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$title = title; + final l$usedSpace = usedSpace; + final l$$__typename = $__typename; + return Object.hashAll([ + l$title, + l$usedSpace, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$GetServerDiskVolumes$storage$volumes$usages) || + runtimeType != other.runtimeType) { + return false; + } + final l$title = title; + final lOther$title = other.title; + if (l$title != lOther$title) { + return false; + } + final l$usedSpace = usedSpace; + final lOther$usedSpace = other.usedSpace; + if (l$usedSpace != lOther$usedSpace) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$GetServerDiskVolumes$storage$volumes$usages + on Query$GetServerDiskVolumes$storage$volumes$usages { + CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages< + Query$GetServerDiskVolumes$storage$volumes$usages> + get copyWith => + CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages( + this, + (i) => i, + ); + _T when<_T>({ + required _T Function( + Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage) + serviceStorageUsage, + required _T Function() orElse, + }) { + switch ($__typename) { + case "ServiceStorageUsage": + return serviceStorageUsage(this + as Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage); + + default: + return orElse(); + } + } + + _T maybeWhen<_T>({ + _T Function( + Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage)? + serviceStorageUsage, + required _T Function() orElse, + }) { + switch ($__typename) { + case "ServiceStorageUsage": + if (serviceStorageUsage != null) { + return serviceStorageUsage(this + as Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage); + } else { + return orElse(); + } + + default: + return orElse(); + } + } +} + +abstract class CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages< + TRes> { + factory CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages( + Query$GetServerDiskVolumes$storage$volumes$usages instance, + TRes Function(Query$GetServerDiskVolumes$storage$volumes$usages) then, + ) = _CopyWithImpl$Query$GetServerDiskVolumes$storage$volumes$usages; + + factory CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages.stub( + TRes res) = + _CopyWithStubImpl$Query$GetServerDiskVolumes$storage$volumes$usages; + + TRes call({ + String? title, + String? usedSpace, + String? $__typename, + }); +} + +class _CopyWithImpl$Query$GetServerDiskVolumes$storage$volumes$usages + implements + CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages { + _CopyWithImpl$Query$GetServerDiskVolumes$storage$volumes$usages( + this._instance, + this._then, + ); + + final Query$GetServerDiskVolumes$storage$volumes$usages _instance; + + final TRes Function(Query$GetServerDiskVolumes$storage$volumes$usages) _then; + + static const _undefined = {}; + + TRes call({ + Object? title = _undefined, + Object? usedSpace = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$GetServerDiskVolumes$storage$volumes$usages( + title: title == _undefined || title == null + ? _instance.title + : (title as String), + usedSpace: usedSpace == _undefined || usedSpace == null + ? _instance.usedSpace + : (usedSpace as String), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Query$GetServerDiskVolumes$storage$volumes$usages + implements + CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages { + _CopyWithStubImpl$Query$GetServerDiskVolumes$storage$volumes$usages( + this._res); + + TRes _res; + + call({ + String? title, + String? usedSpace, + String? $__typename, + }) => + _res; +} + +class Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage + implements Query$GetServerDiskVolumes$storage$volumes$usages { + Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage({ + this.service, + this.$__typename = 'ServiceStorageUsage', + required this.title, + required this.usedSpace, + }); + + factory Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage.fromJson( + Map json) { + final l$service = json['service']; + final l$$__typename = json['__typename']; + final l$title = json['title']; + final l$usedSpace = json['usedSpace']; + return Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage( + service: l$service == null + ? null + : Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service + .fromJson((l$service as Map)), + $__typename: (l$$__typename as String), + title: (l$title as String), + usedSpace: (l$usedSpace as String), + ); + } + + final Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service? + service; + + final String $__typename; + + final String title; + + final String usedSpace; + + Map toJson() { + final _resultData = {}; + final l$service = service; + _resultData['service'] = l$service?.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + final l$title = title; + _resultData['title'] = l$title; + final l$usedSpace = usedSpace; + _resultData['usedSpace'] = l$usedSpace; + return _resultData; + } + + @override + int get hashCode { + final l$service = service; + final l$$__typename = $__typename; + final l$title = title; + final l$usedSpace = usedSpace; + return Object.hashAll([ + l$service, + l$$__typename, + l$title, + l$usedSpace, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other + is Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage) || + runtimeType != other.runtimeType) { + return false; + } + final l$service = service; + final lOther$service = other.service; + if (l$service != lOther$service) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + final l$title = title; + final lOther$title = other.title; + if (l$title != lOther$title) { + return false; + } + final l$usedSpace = usedSpace; + final lOther$usedSpace = other.usedSpace; + if (l$usedSpace != lOther$usedSpace) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage + on Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage { + CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage< + Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage> + get copyWith => + CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage< + TRes> { + factory CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage( + Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage + instance, + TRes Function( + Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage) + then, + ) = _CopyWithImpl$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage; + + factory CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage.stub( + TRes res) = + _CopyWithStubImpl$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage; + + TRes call({ + Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service? + service, + String? $__typename, + String? title, + String? usedSpace, + }); + CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service< + TRes> get service; +} + +class _CopyWithImpl$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage< + TRes> + implements + CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage< + TRes> { + _CopyWithImpl$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage( + this._instance, + this._then, + ); + + final Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage + _instance; + + final TRes Function( + Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage) + _then; + + static const _undefined = {}; + + TRes call({ + Object? service = _undefined, + Object? $__typename = _undefined, + Object? title = _undefined, + Object? usedSpace = _undefined, + }) => + _then( + Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage( + service: service == _undefined + ? _instance.service + : (service + as Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service?), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + title: title == _undefined || title == null + ? _instance.title + : (title as String), + usedSpace: usedSpace == _undefined || usedSpace == null + ? _instance.usedSpace + : (usedSpace as String), + )); + CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service< + TRes> get service { + final local$service = _instance.service; + return local$service == null + ? CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service + .stub(_then(_instance)) + : CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service( + local$service, (e) => call(service: e)); + } +} + +class _CopyWithStubImpl$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage< + TRes> + implements + CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage< + TRes> { + _CopyWithStubImpl$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage( + this._res); + + TRes _res; + + call({ + Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service? + service, + String? $__typename, + String? title, + String? usedSpace, + }) => + _res; + CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service< + TRes> + get service => + CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service + .stub(_res); +} + +class Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service { + Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service({ + required this.id, + required this.isMovable, + required this.displayName, + this.$__typename = 'Service', + }); + + factory Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service.fromJson( + Map json) { + final l$id = json['id']; + final l$isMovable = json['isMovable']; + final l$displayName = json['displayName']; + final l$$__typename = json['__typename']; + return Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service( + id: (l$id as String), + isMovable: (l$isMovable as bool), + displayName: (l$displayName as String), + $__typename: (l$$__typename as String), + ); + } + + final String id; + + final bool isMovable; + + final String displayName; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$id = id; + _resultData['id'] = l$id; + final l$isMovable = isMovable; + _resultData['isMovable'] = l$isMovable; + final l$displayName = displayName; + _resultData['displayName'] = l$displayName; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$id = id; + final l$isMovable = isMovable; + final l$displayName = displayName; + final l$$__typename = $__typename; + return Object.hashAll([ + l$id, + l$isMovable, + l$displayName, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other + is Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service) || + runtimeType != other.runtimeType) { + return false; + } + final l$id = id; + final lOther$id = other.id; + if (l$id != lOther$id) { + return false; + } + final l$isMovable = isMovable; + final lOther$isMovable = other.isMovable; + if (l$isMovable != lOther$isMovable) { + return false; + } + final l$displayName = displayName; + final lOther$displayName = other.displayName; + if (l$displayName != lOther$displayName) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service + on Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service { + CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service< + Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service> + get copyWith => + CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service< + TRes> { + factory CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service( + Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service + instance, + TRes Function( + Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service) + then, + ) = _CopyWithImpl$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service; + + factory CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service.stub( + TRes res) = + _CopyWithStubImpl$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service; + + TRes call({ + String? id, + bool? isMovable, + String? displayName, + String? $__typename, + }); +} + +class _CopyWithImpl$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service< + TRes> + implements + CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service< + TRes> { + _CopyWithImpl$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service( + this._instance, + this._then, + ); + + final Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service + _instance; + + final TRes Function( + Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service) + _then; + + static const _undefined = {}; + + TRes call({ + Object? id = _undefined, + Object? isMovable = _undefined, + Object? displayName = _undefined, + Object? $__typename = _undefined, + }) => + _then( + Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service( + id: id == _undefined || id == null ? _instance.id : (id as String), + isMovable: isMovable == _undefined || isMovable == null + ? _instance.isMovable + : (isMovable as bool), + displayName: displayName == _undefined || displayName == null + ? _instance.displayName + : (displayName as String), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service< + TRes> + implements + CopyWith$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service< + TRes> { + _CopyWithStubImpl$Query$GetServerDiskVolumes$storage$volumes$usages$$ServiceStorageUsage$service( + this._res); + + TRes _res; + + call({ + String? id, + bool? isMovable, + String? displayName, + String? $__typename, + }) => + _res; +} + +class Variables$Mutation$MountVolume { + factory Variables$Mutation$MountVolume({required String name}) => + Variables$Mutation$MountVolume._({ + r'name': name, + }); + + Variables$Mutation$MountVolume._(this._$data); + + factory Variables$Mutation$MountVolume.fromJson(Map data) { + final result$data = {}; + final l$name = data['name']; + result$data['name'] = (l$name as String); + return Variables$Mutation$MountVolume._(result$data); + } + + Map _$data; + + String get name => (_$data['name'] as String); + Map toJson() { + final result$data = {}; + final l$name = name; + result$data['name'] = l$name; + return result$data; + } + + CopyWith$Variables$Mutation$MountVolume + get copyWith => CopyWith$Variables$Mutation$MountVolume( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$MountVolume) || + runtimeType != other.runtimeType) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$name = name; + return Object.hashAll([l$name]); + } +} + +abstract class CopyWith$Variables$Mutation$MountVolume { + factory CopyWith$Variables$Mutation$MountVolume( + Variables$Mutation$MountVolume instance, + TRes Function(Variables$Mutation$MountVolume) then, + ) = _CopyWithImpl$Variables$Mutation$MountVolume; + + factory CopyWith$Variables$Mutation$MountVolume.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$MountVolume; + + TRes call({String? name}); +} + +class _CopyWithImpl$Variables$Mutation$MountVolume + implements CopyWith$Variables$Mutation$MountVolume { + _CopyWithImpl$Variables$Mutation$MountVolume( + this._instance, + this._then, + ); + + final Variables$Mutation$MountVolume _instance; + + final TRes Function(Variables$Mutation$MountVolume) _then; + + static const _undefined = {}; + + TRes call({Object? name = _undefined}) => + _then(Variables$Mutation$MountVolume._({ + ..._instance._$data, + if (name != _undefined && name != null) 'name': (name as String), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$MountVolume + implements CopyWith$Variables$Mutation$MountVolume { + _CopyWithStubImpl$Variables$Mutation$MountVolume(this._res); + + TRes _res; + + call({String? name}) => _res; +} + +class Mutation$MountVolume { + Mutation$MountVolume({ + required this.mountVolume, + this.$__typename = 'Mutation', + }); + + factory Mutation$MountVolume.fromJson(Map json) { + final l$mountVolume = json['mountVolume']; + final l$$__typename = json['__typename']; + return Mutation$MountVolume( + mountVolume: Mutation$MountVolume$mountVolume.fromJson( + (l$mountVolume as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$MountVolume$mountVolume mountVolume; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$mountVolume = mountVolume; + _resultData['mountVolume'] = l$mountVolume.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$mountVolume = mountVolume; + final l$$__typename = $__typename; + return Object.hashAll([ + l$mountVolume, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$MountVolume) || runtimeType != other.runtimeType) { + return false; + } + final l$mountVolume = mountVolume; + final lOther$mountVolume = other.mountVolume; + if (l$mountVolume != lOther$mountVolume) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$MountVolume on Mutation$MountVolume { + CopyWith$Mutation$MountVolume get copyWith => + CopyWith$Mutation$MountVolume( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$MountVolume { + factory CopyWith$Mutation$MountVolume( + Mutation$MountVolume instance, + TRes Function(Mutation$MountVolume) then, + ) = _CopyWithImpl$Mutation$MountVolume; + + factory CopyWith$Mutation$MountVolume.stub(TRes res) = + _CopyWithStubImpl$Mutation$MountVolume; + + TRes call({ + Mutation$MountVolume$mountVolume? mountVolume, + String? $__typename, + }); + CopyWith$Mutation$MountVolume$mountVolume get mountVolume; +} + +class _CopyWithImpl$Mutation$MountVolume + implements CopyWith$Mutation$MountVolume { + _CopyWithImpl$Mutation$MountVolume( + this._instance, + this._then, + ); + + final Mutation$MountVolume _instance; + + final TRes Function(Mutation$MountVolume) _then; + + static const _undefined = {}; + + TRes call({ + Object? mountVolume = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$MountVolume( + mountVolume: mountVolume == _undefined || mountVolume == null + ? _instance.mountVolume + : (mountVolume as Mutation$MountVolume$mountVolume), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$MountVolume$mountVolume get mountVolume { + final local$mountVolume = _instance.mountVolume; + return CopyWith$Mutation$MountVolume$mountVolume( + local$mountVolume, (e) => call(mountVolume: e)); + } +} + +class _CopyWithStubImpl$Mutation$MountVolume + implements CopyWith$Mutation$MountVolume { + _CopyWithStubImpl$Mutation$MountVolume(this._res); + + TRes _res; + + call({ + Mutation$MountVolume$mountVolume? mountVolume, + String? $__typename, + }) => + _res; + CopyWith$Mutation$MountVolume$mountVolume get mountVolume => + CopyWith$Mutation$MountVolume$mountVolume.stub(_res); +} + +const documentNodeMutationMountVolume = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'MountVolume'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'name')), + type: NamedTypeNode( + name: NameNode(value: 'String'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'mountVolume'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'name'), + value: VariableNode(name: NameNode(value: 'name')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, +]); +Mutation$MountVolume _parserFn$Mutation$MountVolume( + Map data) => + Mutation$MountVolume.fromJson(data); +typedef OnMutationCompleted$Mutation$MountVolume = FutureOr Function( + Map?, + Mutation$MountVolume?, +); + +class Options$Mutation$MountVolume + extends graphql.MutationOptions { + Options$Mutation$MountVolume({ + String? operationName, + required Variables$Mutation$MountVolume variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$MountVolume? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$MountVolume? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null ? null : _parserFn$Mutation$MountVolume(data), + ), + update: update, + onError: onError, + document: documentNodeMutationMountVolume, + parserFn: _parserFn$Mutation$MountVolume, + ); + + final OnMutationCompleted$Mutation$MountVolume? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$MountVolume + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$MountVolume({ + String? operationName, + required Variables$Mutation$MountVolume variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$MountVolume? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationMountVolume, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$MountVolume, + ); +} + +extension ClientExtension$Mutation$MountVolume on graphql.GraphQLClient { + Future> mutate$MountVolume( + Options$Mutation$MountVolume options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$MountVolume( + WatchOptions$Mutation$MountVolume options) => + this.watchMutation(options); +} + +class Mutation$MountVolume$mountVolume + implements Fragment$basicMutationReturnFields$$GenericMutationReturn { + Mutation$MountVolume$mountVolume({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'GenericMutationReturn', + }); + + factory Mutation$MountVolume$mountVolume.fromJson(Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Mutation$MountVolume$mountVolume( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$MountVolume$mountVolume) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$MountVolume$mountVolume + on Mutation$MountVolume$mountVolume { + CopyWith$Mutation$MountVolume$mountVolume + get copyWith => CopyWith$Mutation$MountVolume$mountVolume( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$MountVolume$mountVolume { + factory CopyWith$Mutation$MountVolume$mountVolume( + Mutation$MountVolume$mountVolume instance, + TRes Function(Mutation$MountVolume$mountVolume) then, + ) = _CopyWithImpl$Mutation$MountVolume$mountVolume; + + factory CopyWith$Mutation$MountVolume$mountVolume.stub(TRes res) = + _CopyWithStubImpl$Mutation$MountVolume$mountVolume; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$MountVolume$mountVolume + implements CopyWith$Mutation$MountVolume$mountVolume { + _CopyWithImpl$Mutation$MountVolume$mountVolume( + this._instance, + this._then, + ); + + final Mutation$MountVolume$mountVolume _instance; + + final TRes Function(Mutation$MountVolume$mountVolume) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$MountVolume$mountVolume( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$MountVolume$mountVolume + implements CopyWith$Mutation$MountVolume$mountVolume { + _CopyWithStubImpl$Mutation$MountVolume$mountVolume(this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +class Variables$Mutation$ResizeVolume { + factory Variables$Mutation$ResizeVolume({required String name}) => + Variables$Mutation$ResizeVolume._({ + r'name': name, + }); + + Variables$Mutation$ResizeVolume._(this._$data); + + factory Variables$Mutation$ResizeVolume.fromJson(Map data) { + final result$data = {}; + final l$name = data['name']; + result$data['name'] = (l$name as String); + return Variables$Mutation$ResizeVolume._(result$data); + } + + Map _$data; + + String get name => (_$data['name'] as String); + Map toJson() { + final result$data = {}; + final l$name = name; + result$data['name'] = l$name; + return result$data; + } + + CopyWith$Variables$Mutation$ResizeVolume + get copyWith => CopyWith$Variables$Mutation$ResizeVolume( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$ResizeVolume) || + runtimeType != other.runtimeType) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$name = name; + return Object.hashAll([l$name]); + } +} + +abstract class CopyWith$Variables$Mutation$ResizeVolume { + factory CopyWith$Variables$Mutation$ResizeVolume( + Variables$Mutation$ResizeVolume instance, + TRes Function(Variables$Mutation$ResizeVolume) then, + ) = _CopyWithImpl$Variables$Mutation$ResizeVolume; + + factory CopyWith$Variables$Mutation$ResizeVolume.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$ResizeVolume; + + TRes call({String? name}); +} + +class _CopyWithImpl$Variables$Mutation$ResizeVolume + implements CopyWith$Variables$Mutation$ResizeVolume { + _CopyWithImpl$Variables$Mutation$ResizeVolume( + this._instance, + this._then, + ); + + final Variables$Mutation$ResizeVolume _instance; + + final TRes Function(Variables$Mutation$ResizeVolume) _then; + + static const _undefined = {}; + + TRes call({Object? name = _undefined}) => + _then(Variables$Mutation$ResizeVolume._({ + ..._instance._$data, + if (name != _undefined && name != null) 'name': (name as String), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$ResizeVolume + implements CopyWith$Variables$Mutation$ResizeVolume { + _CopyWithStubImpl$Variables$Mutation$ResizeVolume(this._res); + + TRes _res; + + call({String? name}) => _res; +} + +class Mutation$ResizeVolume { + Mutation$ResizeVolume({ + required this.resizeVolume, + this.$__typename = 'Mutation', + }); + + factory Mutation$ResizeVolume.fromJson(Map json) { + final l$resizeVolume = json['resizeVolume']; + final l$$__typename = json['__typename']; + return Mutation$ResizeVolume( + resizeVolume: Mutation$ResizeVolume$resizeVolume.fromJson( + (l$resizeVolume as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$ResizeVolume$resizeVolume resizeVolume; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$resizeVolume = resizeVolume; + _resultData['resizeVolume'] = l$resizeVolume.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$resizeVolume = resizeVolume; + final l$$__typename = $__typename; + return Object.hashAll([ + l$resizeVolume, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$ResizeVolume) || runtimeType != other.runtimeType) { + return false; + } + final l$resizeVolume = resizeVolume; + final lOther$resizeVolume = other.resizeVolume; + if (l$resizeVolume != lOther$resizeVolume) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$ResizeVolume on Mutation$ResizeVolume { + CopyWith$Mutation$ResizeVolume get copyWith => + CopyWith$Mutation$ResizeVolume( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$ResizeVolume { + factory CopyWith$Mutation$ResizeVolume( + Mutation$ResizeVolume instance, + TRes Function(Mutation$ResizeVolume) then, + ) = _CopyWithImpl$Mutation$ResizeVolume; + + factory CopyWith$Mutation$ResizeVolume.stub(TRes res) = + _CopyWithStubImpl$Mutation$ResizeVolume; + + TRes call({ + Mutation$ResizeVolume$resizeVolume? resizeVolume, + String? $__typename, + }); + CopyWith$Mutation$ResizeVolume$resizeVolume get resizeVolume; +} + +class _CopyWithImpl$Mutation$ResizeVolume + implements CopyWith$Mutation$ResizeVolume { + _CopyWithImpl$Mutation$ResizeVolume( + this._instance, + this._then, + ); + + final Mutation$ResizeVolume _instance; + + final TRes Function(Mutation$ResizeVolume) _then; + + static const _undefined = {}; + + TRes call({ + Object? resizeVolume = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$ResizeVolume( + resizeVolume: resizeVolume == _undefined || resizeVolume == null + ? _instance.resizeVolume + : (resizeVolume as Mutation$ResizeVolume$resizeVolume), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$ResizeVolume$resizeVolume get resizeVolume { + final local$resizeVolume = _instance.resizeVolume; + return CopyWith$Mutation$ResizeVolume$resizeVolume( + local$resizeVolume, (e) => call(resizeVolume: e)); + } +} + +class _CopyWithStubImpl$Mutation$ResizeVolume + implements CopyWith$Mutation$ResizeVolume { + _CopyWithStubImpl$Mutation$ResizeVolume(this._res); + + TRes _res; + + call({ + Mutation$ResizeVolume$resizeVolume? resizeVolume, + String? $__typename, + }) => + _res; + CopyWith$Mutation$ResizeVolume$resizeVolume get resizeVolume => + CopyWith$Mutation$ResizeVolume$resizeVolume.stub(_res); +} + +const documentNodeMutationResizeVolume = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'ResizeVolume'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'name')), + type: NamedTypeNode( + name: NameNode(value: 'String'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'resizeVolume'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'name'), + value: VariableNode(name: NameNode(value: 'name')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, +]); +Mutation$ResizeVolume _parserFn$Mutation$ResizeVolume( + Map data) => + Mutation$ResizeVolume.fromJson(data); +typedef OnMutationCompleted$Mutation$ResizeVolume = FutureOr Function( + Map?, + Mutation$ResizeVolume?, +); + +class Options$Mutation$ResizeVolume + extends graphql.MutationOptions { + Options$Mutation$ResizeVolume({ + String? operationName, + required Variables$Mutation$ResizeVolume variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$ResizeVolume? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$ResizeVolume? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null ? null : _parserFn$Mutation$ResizeVolume(data), + ), + update: update, + onError: onError, + document: documentNodeMutationResizeVolume, + parserFn: _parserFn$Mutation$ResizeVolume, + ); + + final OnMutationCompleted$Mutation$ResizeVolume? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$ResizeVolume + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$ResizeVolume({ + String? operationName, + required Variables$Mutation$ResizeVolume variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$ResizeVolume? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationResizeVolume, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$ResizeVolume, + ); +} + +extension ClientExtension$Mutation$ResizeVolume on graphql.GraphQLClient { + Future> mutate$ResizeVolume( + Options$Mutation$ResizeVolume options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$ResizeVolume( + WatchOptions$Mutation$ResizeVolume options) => + this.watchMutation(options); +} + +class Mutation$ResizeVolume$resizeVolume + implements Fragment$basicMutationReturnFields$$GenericMutationReturn { + Mutation$ResizeVolume$resizeVolume({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'GenericMutationReturn', + }); + + factory Mutation$ResizeVolume$resizeVolume.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Mutation$ResizeVolume$resizeVolume( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$ResizeVolume$resizeVolume) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$ResizeVolume$resizeVolume + on Mutation$ResizeVolume$resizeVolume { + CopyWith$Mutation$ResizeVolume$resizeVolume< + Mutation$ResizeVolume$resizeVolume> + get copyWith => CopyWith$Mutation$ResizeVolume$resizeVolume( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$ResizeVolume$resizeVolume { + factory CopyWith$Mutation$ResizeVolume$resizeVolume( + Mutation$ResizeVolume$resizeVolume instance, + TRes Function(Mutation$ResizeVolume$resizeVolume) then, + ) = _CopyWithImpl$Mutation$ResizeVolume$resizeVolume; + + factory CopyWith$Mutation$ResizeVolume$resizeVolume.stub(TRes res) = + _CopyWithStubImpl$Mutation$ResizeVolume$resizeVolume; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$ResizeVolume$resizeVolume + implements CopyWith$Mutation$ResizeVolume$resizeVolume { + _CopyWithImpl$Mutation$ResizeVolume$resizeVolume( + this._instance, + this._then, + ); + + final Mutation$ResizeVolume$resizeVolume _instance; + + final TRes Function(Mutation$ResizeVolume$resizeVolume) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$ResizeVolume$resizeVolume( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$ResizeVolume$resizeVolume + implements CopyWith$Mutation$ResizeVolume$resizeVolume { + _CopyWithStubImpl$Mutation$ResizeVolume$resizeVolume(this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +class Variables$Mutation$UnmountVolume { + factory Variables$Mutation$UnmountVolume({required String name}) => + Variables$Mutation$UnmountVolume._({ + r'name': name, + }); + + Variables$Mutation$UnmountVolume._(this._$data); + + factory Variables$Mutation$UnmountVolume.fromJson(Map data) { + final result$data = {}; + final l$name = data['name']; + result$data['name'] = (l$name as String); + return Variables$Mutation$UnmountVolume._(result$data); + } + + Map _$data; + + String get name => (_$data['name'] as String); + Map toJson() { + final result$data = {}; + final l$name = name; + result$data['name'] = l$name; + return result$data; + } + + CopyWith$Variables$Mutation$UnmountVolume + get copyWith => CopyWith$Variables$Mutation$UnmountVolume( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$UnmountVolume) || + runtimeType != other.runtimeType) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$name = name; + return Object.hashAll([l$name]); + } +} + +abstract class CopyWith$Variables$Mutation$UnmountVolume { + factory CopyWith$Variables$Mutation$UnmountVolume( + Variables$Mutation$UnmountVolume instance, + TRes Function(Variables$Mutation$UnmountVolume) then, + ) = _CopyWithImpl$Variables$Mutation$UnmountVolume; + + factory CopyWith$Variables$Mutation$UnmountVolume.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$UnmountVolume; + + TRes call({String? name}); +} + +class _CopyWithImpl$Variables$Mutation$UnmountVolume + implements CopyWith$Variables$Mutation$UnmountVolume { + _CopyWithImpl$Variables$Mutation$UnmountVolume( + this._instance, + this._then, + ); + + final Variables$Mutation$UnmountVolume _instance; + + final TRes Function(Variables$Mutation$UnmountVolume) _then; + + static const _undefined = {}; + + TRes call({Object? name = _undefined}) => + _then(Variables$Mutation$UnmountVolume._({ + ..._instance._$data, + if (name != _undefined && name != null) 'name': (name as String), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$UnmountVolume + implements CopyWith$Variables$Mutation$UnmountVolume { + _CopyWithStubImpl$Variables$Mutation$UnmountVolume(this._res); + + TRes _res; + + call({String? name}) => _res; +} + +class Mutation$UnmountVolume { + Mutation$UnmountVolume({ + required this.unmountVolume, + this.$__typename = 'Mutation', + }); + + factory Mutation$UnmountVolume.fromJson(Map json) { + final l$unmountVolume = json['unmountVolume']; + final l$$__typename = json['__typename']; + return Mutation$UnmountVolume( + unmountVolume: Mutation$UnmountVolume$unmountVolume.fromJson( + (l$unmountVolume as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$UnmountVolume$unmountVolume unmountVolume; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$unmountVolume = unmountVolume; + _resultData['unmountVolume'] = l$unmountVolume.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$unmountVolume = unmountVolume; + final l$$__typename = $__typename; + return Object.hashAll([ + l$unmountVolume, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$UnmountVolume) || + runtimeType != other.runtimeType) { + return false; + } + final l$unmountVolume = unmountVolume; + final lOther$unmountVolume = other.unmountVolume; + if (l$unmountVolume != lOther$unmountVolume) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$UnmountVolume on Mutation$UnmountVolume { + CopyWith$Mutation$UnmountVolume get copyWith => + CopyWith$Mutation$UnmountVolume( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$UnmountVolume { + factory CopyWith$Mutation$UnmountVolume( + Mutation$UnmountVolume instance, + TRes Function(Mutation$UnmountVolume) then, + ) = _CopyWithImpl$Mutation$UnmountVolume; + + factory CopyWith$Mutation$UnmountVolume.stub(TRes res) = + _CopyWithStubImpl$Mutation$UnmountVolume; + + TRes call({ + Mutation$UnmountVolume$unmountVolume? unmountVolume, + String? $__typename, + }); + CopyWith$Mutation$UnmountVolume$unmountVolume get unmountVolume; +} + +class _CopyWithImpl$Mutation$UnmountVolume + implements CopyWith$Mutation$UnmountVolume { + _CopyWithImpl$Mutation$UnmountVolume( + this._instance, + this._then, + ); + + final Mutation$UnmountVolume _instance; + + final TRes Function(Mutation$UnmountVolume) _then; + + static const _undefined = {}; + + TRes call({ + Object? unmountVolume = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$UnmountVolume( + unmountVolume: unmountVolume == _undefined || unmountVolume == null + ? _instance.unmountVolume + : (unmountVolume as Mutation$UnmountVolume$unmountVolume), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$UnmountVolume$unmountVolume get unmountVolume { + final local$unmountVolume = _instance.unmountVolume; + return CopyWith$Mutation$UnmountVolume$unmountVolume( + local$unmountVolume, (e) => call(unmountVolume: e)); + } +} + +class _CopyWithStubImpl$Mutation$UnmountVolume + implements CopyWith$Mutation$UnmountVolume { + _CopyWithStubImpl$Mutation$UnmountVolume(this._res); + + TRes _res; + + call({ + Mutation$UnmountVolume$unmountVolume? unmountVolume, + String? $__typename, + }) => + _res; + CopyWith$Mutation$UnmountVolume$unmountVolume get unmountVolume => + CopyWith$Mutation$UnmountVolume$unmountVolume.stub(_res); +} + +const documentNodeMutationUnmountVolume = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'UnmountVolume'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'name')), + type: NamedTypeNode( + name: NameNode(value: 'String'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'unmountVolume'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'name'), + value: VariableNode(name: NameNode(value: 'name')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, +]); +Mutation$UnmountVolume _parserFn$Mutation$UnmountVolume( + Map data) => + Mutation$UnmountVolume.fromJson(data); +typedef OnMutationCompleted$Mutation$UnmountVolume = FutureOr Function( + Map?, + Mutation$UnmountVolume?, +); + +class Options$Mutation$UnmountVolume + extends graphql.MutationOptions { + Options$Mutation$UnmountVolume({ + String? operationName, + required Variables$Mutation$UnmountVolume variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$UnmountVolume? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$UnmountVolume? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$UnmountVolume(data), + ), + update: update, + onError: onError, + document: documentNodeMutationUnmountVolume, + parserFn: _parserFn$Mutation$UnmountVolume, + ); + + final OnMutationCompleted$Mutation$UnmountVolume? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$UnmountVolume + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$UnmountVolume({ + String? operationName, + required Variables$Mutation$UnmountVolume variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$UnmountVolume? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationUnmountVolume, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$UnmountVolume, + ); +} + +extension ClientExtension$Mutation$UnmountVolume on graphql.GraphQLClient { + Future> mutate$UnmountVolume( + Options$Mutation$UnmountVolume options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$UnmountVolume( + WatchOptions$Mutation$UnmountVolume options) => + this.watchMutation(options); +} + +class Mutation$UnmountVolume$unmountVolume + implements Fragment$basicMutationReturnFields$$GenericMutationReturn { + Mutation$UnmountVolume$unmountVolume({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'GenericMutationReturn', + }); + + factory Mutation$UnmountVolume$unmountVolume.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Mutation$UnmountVolume$unmountVolume( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$UnmountVolume$unmountVolume) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$UnmountVolume$unmountVolume + on Mutation$UnmountVolume$unmountVolume { + CopyWith$Mutation$UnmountVolume$unmountVolume< + Mutation$UnmountVolume$unmountVolume> + get copyWith => CopyWith$Mutation$UnmountVolume$unmountVolume( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$UnmountVolume$unmountVolume { + factory CopyWith$Mutation$UnmountVolume$unmountVolume( + Mutation$UnmountVolume$unmountVolume instance, + TRes Function(Mutation$UnmountVolume$unmountVolume) then, + ) = _CopyWithImpl$Mutation$UnmountVolume$unmountVolume; + + factory CopyWith$Mutation$UnmountVolume$unmountVolume.stub(TRes res) = + _CopyWithStubImpl$Mutation$UnmountVolume$unmountVolume; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$UnmountVolume$unmountVolume + implements CopyWith$Mutation$UnmountVolume$unmountVolume { + _CopyWithImpl$Mutation$UnmountVolume$unmountVolume( + this._instance, + this._then, + ); + + final Mutation$UnmountVolume$unmountVolume _instance; + + final TRes Function(Mutation$UnmountVolume$unmountVolume) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$UnmountVolume$unmountVolume( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$UnmountVolume$unmountVolume + implements CopyWith$Mutation$UnmountVolume$unmountVolume { + _CopyWithStubImpl$Mutation$UnmountVolume$unmountVolume(this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +class Variables$Mutation$MigrateToBinds { + factory Variables$Mutation$MigrateToBinds( + {required Input$MigrateToBindsInput input}) => + Variables$Mutation$MigrateToBinds._({ + r'input': input, + }); + + Variables$Mutation$MigrateToBinds._(this._$data); + + factory Variables$Mutation$MigrateToBinds.fromJson( + Map data) { + final result$data = {}; + final l$input = data['input']; + result$data['input'] = + Input$MigrateToBindsInput.fromJson((l$input as Map)); + return Variables$Mutation$MigrateToBinds._(result$data); + } + + Map _$data; + + Input$MigrateToBindsInput get input => + (_$data['input'] as Input$MigrateToBindsInput); + Map toJson() { + final result$data = {}; + final l$input = input; + result$data['input'] = l$input.toJson(); + return result$data; + } + + CopyWith$Variables$Mutation$MigrateToBinds + get copyWith => CopyWith$Variables$Mutation$MigrateToBinds( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$MigrateToBinds) || + runtimeType != other.runtimeType) { + return false; + } + final l$input = input; + final lOther$input = other.input; + if (l$input != lOther$input) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$input = input; + return Object.hashAll([l$input]); + } +} + +abstract class CopyWith$Variables$Mutation$MigrateToBinds { + factory CopyWith$Variables$Mutation$MigrateToBinds( + Variables$Mutation$MigrateToBinds instance, + TRes Function(Variables$Mutation$MigrateToBinds) then, + ) = _CopyWithImpl$Variables$Mutation$MigrateToBinds; + + factory CopyWith$Variables$Mutation$MigrateToBinds.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$MigrateToBinds; + + TRes call({Input$MigrateToBindsInput? input}); +} + +class _CopyWithImpl$Variables$Mutation$MigrateToBinds + implements CopyWith$Variables$Mutation$MigrateToBinds { + _CopyWithImpl$Variables$Mutation$MigrateToBinds( + this._instance, + this._then, + ); + + final Variables$Mutation$MigrateToBinds _instance; + + final TRes Function(Variables$Mutation$MigrateToBinds) _then; + + static const _undefined = {}; + + TRes call({Object? input = _undefined}) => + _then(Variables$Mutation$MigrateToBinds._({ + ..._instance._$data, + if (input != _undefined && input != null) + 'input': (input as Input$MigrateToBindsInput), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$MigrateToBinds + implements CopyWith$Variables$Mutation$MigrateToBinds { + _CopyWithStubImpl$Variables$Mutation$MigrateToBinds(this._res); + + TRes _res; + + call({Input$MigrateToBindsInput? input}) => _res; +} + +class Mutation$MigrateToBinds { + Mutation$MigrateToBinds({ + required this.migrateToBinds, + this.$__typename = 'Mutation', + }); + + factory Mutation$MigrateToBinds.fromJson(Map json) { + final l$migrateToBinds = json['migrateToBinds']; + final l$$__typename = json['__typename']; + return Mutation$MigrateToBinds( + migrateToBinds: Mutation$MigrateToBinds$migrateToBinds.fromJson( + (l$migrateToBinds as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$MigrateToBinds$migrateToBinds migrateToBinds; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$migrateToBinds = migrateToBinds; + _resultData['migrateToBinds'] = l$migrateToBinds.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$migrateToBinds = migrateToBinds; + final l$$__typename = $__typename; + return Object.hashAll([ + l$migrateToBinds, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$MigrateToBinds) || + runtimeType != other.runtimeType) { + return false; + } + final l$migrateToBinds = migrateToBinds; + final lOther$migrateToBinds = other.migrateToBinds; + if (l$migrateToBinds != lOther$migrateToBinds) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$MigrateToBinds on Mutation$MigrateToBinds { + CopyWith$Mutation$MigrateToBinds get copyWith => + CopyWith$Mutation$MigrateToBinds( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$MigrateToBinds { + factory CopyWith$Mutation$MigrateToBinds( + Mutation$MigrateToBinds instance, + TRes Function(Mutation$MigrateToBinds) then, + ) = _CopyWithImpl$Mutation$MigrateToBinds; + + factory CopyWith$Mutation$MigrateToBinds.stub(TRes res) = + _CopyWithStubImpl$Mutation$MigrateToBinds; + + TRes call({ + Mutation$MigrateToBinds$migrateToBinds? migrateToBinds, + String? $__typename, + }); + CopyWith$Mutation$MigrateToBinds$migrateToBinds get migrateToBinds; +} + +class _CopyWithImpl$Mutation$MigrateToBinds + implements CopyWith$Mutation$MigrateToBinds { + _CopyWithImpl$Mutation$MigrateToBinds( + this._instance, + this._then, + ); + + final Mutation$MigrateToBinds _instance; + + final TRes Function(Mutation$MigrateToBinds) _then; + + static const _undefined = {}; + + TRes call({ + Object? migrateToBinds = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$MigrateToBinds( + migrateToBinds: migrateToBinds == _undefined || migrateToBinds == null + ? _instance.migrateToBinds + : (migrateToBinds as Mutation$MigrateToBinds$migrateToBinds), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$MigrateToBinds$migrateToBinds get migrateToBinds { + final local$migrateToBinds = _instance.migrateToBinds; + return CopyWith$Mutation$MigrateToBinds$migrateToBinds( + local$migrateToBinds, (e) => call(migrateToBinds: e)); + } +} + +class _CopyWithStubImpl$Mutation$MigrateToBinds + implements CopyWith$Mutation$MigrateToBinds { + _CopyWithStubImpl$Mutation$MigrateToBinds(this._res); + + TRes _res; + + call({ + Mutation$MigrateToBinds$migrateToBinds? migrateToBinds, + String? $__typename, + }) => + _res; + CopyWith$Mutation$MigrateToBinds$migrateToBinds get migrateToBinds => + CopyWith$Mutation$MigrateToBinds$migrateToBinds.stub(_res); +} + +const documentNodeMutationMigrateToBinds = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'MigrateToBinds'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'input')), + type: NamedTypeNode( + name: NameNode(value: 'MigrateToBindsInput'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'migrateToBinds'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'input'), + value: VariableNode(name: NameNode(value: 'input')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: 'job'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicApiJobsFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, + fragmentDefinitionbasicApiJobsFields, +]); +Mutation$MigrateToBinds _parserFn$Mutation$MigrateToBinds( + Map data) => + Mutation$MigrateToBinds.fromJson(data); +typedef OnMutationCompleted$Mutation$MigrateToBinds = FutureOr Function( + Map?, + Mutation$MigrateToBinds?, +); + +class Options$Mutation$MigrateToBinds + extends graphql.MutationOptions { + Options$Mutation$MigrateToBinds({ + String? operationName, + required Variables$Mutation$MigrateToBinds variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$MigrateToBinds? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$MigrateToBinds? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$MigrateToBinds(data), + ), + update: update, + onError: onError, + document: documentNodeMutationMigrateToBinds, + parserFn: _parserFn$Mutation$MigrateToBinds, + ); + + final OnMutationCompleted$Mutation$MigrateToBinds? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$MigrateToBinds + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$MigrateToBinds({ + String? operationName, + required Variables$Mutation$MigrateToBinds variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$MigrateToBinds? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationMigrateToBinds, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$MigrateToBinds, + ); +} + +extension ClientExtension$Mutation$MigrateToBinds on graphql.GraphQLClient { + Future> mutate$MigrateToBinds( + Options$Mutation$MigrateToBinds options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$MigrateToBinds( + WatchOptions$Mutation$MigrateToBinds options) => + this.watchMutation(options); +} + +class Mutation$MigrateToBinds$migrateToBinds + implements Fragment$basicMutationReturnFields$$GenericJobMutationReturn { + Mutation$MigrateToBinds$migrateToBinds({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'GenericJobMutationReturn', + this.job, + }); + + factory Mutation$MigrateToBinds$migrateToBinds.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + final l$job = json['job']; + return Mutation$MigrateToBinds$migrateToBinds( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + job: l$job == null + ? null + : Fragment$basicApiJobsFields.fromJson( + (l$job as Map)), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + final Fragment$basicApiJobsFields? job; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + final l$job = job; + _resultData['job'] = l$job?.toJson(); + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + final l$job = job; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + l$job, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$MigrateToBinds$migrateToBinds) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + final l$job = job; + final lOther$job = other.job; + if (l$job != lOther$job) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$MigrateToBinds$migrateToBinds + on Mutation$MigrateToBinds$migrateToBinds { + CopyWith$Mutation$MigrateToBinds$migrateToBinds< + Mutation$MigrateToBinds$migrateToBinds> + get copyWith => CopyWith$Mutation$MigrateToBinds$migrateToBinds( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$MigrateToBinds$migrateToBinds { + factory CopyWith$Mutation$MigrateToBinds$migrateToBinds( + Mutation$MigrateToBinds$migrateToBinds instance, + TRes Function(Mutation$MigrateToBinds$migrateToBinds) then, + ) = _CopyWithImpl$Mutation$MigrateToBinds$migrateToBinds; + + factory CopyWith$Mutation$MigrateToBinds$migrateToBinds.stub(TRes res) = + _CopyWithStubImpl$Mutation$MigrateToBinds$migrateToBinds; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + Fragment$basicApiJobsFields? job, + }); + CopyWith$Fragment$basicApiJobsFields get job; +} + +class _CopyWithImpl$Mutation$MigrateToBinds$migrateToBinds + implements CopyWith$Mutation$MigrateToBinds$migrateToBinds { + _CopyWithImpl$Mutation$MigrateToBinds$migrateToBinds( + this._instance, + this._then, + ); + + final Mutation$MigrateToBinds$migrateToBinds _instance; + + final TRes Function(Mutation$MigrateToBinds$migrateToBinds) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + Object? job = _undefined, + }) => + _then(Mutation$MigrateToBinds$migrateToBinds( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + job: job == _undefined + ? _instance.job + : (job as Fragment$basicApiJobsFields?), + )); + CopyWith$Fragment$basicApiJobsFields get job { + final local$job = _instance.job; + return local$job == null + ? CopyWith$Fragment$basicApiJobsFields.stub(_then(_instance)) + : CopyWith$Fragment$basicApiJobsFields(local$job, (e) => call(job: e)); + } +} + +class _CopyWithStubImpl$Mutation$MigrateToBinds$migrateToBinds + implements CopyWith$Mutation$MigrateToBinds$migrateToBinds { + _CopyWithStubImpl$Mutation$MigrateToBinds$migrateToBinds(this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + Fragment$basicApiJobsFields? job, + }) => + _res; + CopyWith$Fragment$basicApiJobsFields get job => + CopyWith$Fragment$basicApiJobsFields.stub(_res); +} diff --git a/lib/logic/api_maps/graphql_maps/schema/schema.graphql b/lib/logic/api_maps/graphql_maps/schema/schema.graphql new file mode 100644 index 00000000..cb154193 --- /dev/null +++ b/lib/logic/api_maps/graphql_maps/schema/schema.graphql @@ -0,0 +1,496 @@ +type Alert { + severity: Severity! + title: String! + message: String! + timestamp: DateTime +} + +type Api { + version: String! + recoveryKey: ApiRecoveryKeyStatus! + devices: [ApiDevice!]! +} + +type ApiDevice { + name: String! + creationDate: DateTime! + isCaller: Boolean! +} + +type ApiJob { + uid: String! + typeId: String! + name: String! + description: String! + status: String! + statusText: String + progress: Int + createdAt: DateTime! + updatedAt: DateTime! + finishedAt: DateTime + error: String + result: String +} + +type ApiKeyMutationReturn implements MutationReturnInterface { + success: Boolean! + message: String! + code: Int! + key: String +} + +type ApiMutations { + getNewRecoveryApiKey(limits: RecoveryKeyLimitsInput = null): ApiKeyMutationReturn! + useRecoveryApiKey(input: UseRecoveryKeyInput!): DeviceApiTokenMutationReturn! + refreshDeviceApiToken: DeviceApiTokenMutationReturn! + deleteDeviceApiToken(device: String!): GenericMutationReturn! + getNewDeviceApiKey: ApiKeyMutationReturn! + invalidateNewDeviceApiKey: GenericMutationReturn! + authorizeWithNewDeviceApiKey(input: UseNewDeviceKeyInput!): DeviceApiTokenMutationReturn! +} + +type ApiRecoveryKeyStatus { + exists: Boolean! + valid: Boolean! + creationDate: DateTime + expirationDate: DateTime + usesLeft: Int +} + +type AutoUpgradeOptions { + enable: Boolean! + allowReboot: Boolean! +} + +input AutoUpgradeSettingsInput { + enableAutoUpgrade: Boolean = null + allowReboot: Boolean = null +} + +type AutoUpgradeSettingsMutationReturn implements MutationReturnInterface { + success: Boolean! + message: String! + code: Int! + enableAutoUpgrade: Boolean! + allowReboot: Boolean! +} + +type AutobackupQuotas { + last: Int! + daily: Int! + weekly: Int! + monthly: Int! + yearly: Int! +} + +input AutobackupQuotasInput { + last: Int! + daily: Int! + weekly: Int! + monthly: Int! + yearly: Int! +} + +type Backup { + configuration: BackupConfiguration! + allSnapshots: [SnapshotInfo!]! +} + +type BackupConfiguration { + provider: BackupProvider! + encryptionKey: String! + isInitialized: Boolean! + autobackupPeriod: Int + autobackupQuotas: AutobackupQuotas! + locationName: String + locationId: String +} + +type BackupMutations { + initializeRepository(repository: InitializeRepositoryInput!): GenericBackupConfigReturn! + removeRepository: GenericBackupConfigReturn! + setAutobackupPeriod(period: Int = null): GenericBackupConfigReturn! + setAutobackupQuotas(quotas: AutobackupQuotasInput!): GenericBackupConfigReturn! + startBackup(serviceId: String!): GenericJobMutationReturn! + restoreBackup(snapshotId: String!, strategy: RestoreStrategy! = DOWNLOAD_VERIFY_OVERWRITE): GenericJobMutationReturn! + forgetSnapshot(snapshotId: String!): GenericMutationReturn! + forceSnapshotsReload: GenericMutationReturn! +} + +enum BackupProvider { + BACKBLAZE + NONE + MEMORY + FILE +} + +enum BackupReason { + EXPLICIT + AUTO + PRE_RESTORE +} + +"""Date with time (isoformat)""" +scalar DateTime + +type DeviceApiTokenMutationReturn implements MutationReturnInterface { + success: Boolean! + message: String! + code: Int! + token: String +} + +enum DnsProvider { + CLOUDFLARE + DIGITALOCEAN + DESEC +} + +type DnsRecord { + recordType: String! + name: String! + content: String! + ttl: Int! + priority: Int +} + +type GenericBackupConfigReturn implements MutationReturnInterface { + success: Boolean! + message: String! + code: Int! + configuration: BackupConfiguration +} + +type GenericJobMutationReturn implements MutationReturnInterface { + success: Boolean! + message: String! + code: Int! + job: ApiJob +} + +type GenericMutationReturn implements MutationReturnInterface { + success: Boolean! + message: String! + code: Int! +} + +input InitializeRepositoryInput { + provider: BackupProvider! + locationId: String! + locationName: String! + login: String! + password: String! +} + +type Job { + getJobs: [ApiJob!]! + getJob(jobId: String!): ApiJob +} + +type JobMutations { + removeJob(jobId: String!): GenericMutationReturn! +} + +input MigrateToBindsInput { + emailBlockDevice: String! + bitwardenBlockDevice: String! + giteaBlockDevice: String! + nextcloudBlockDevice: String! + pleromaBlockDevice: String! +} + +input MoveServiceInput { + serviceId: String! + location: String! +} + +type Mutation { + getNewRecoveryApiKey(limits: RecoveryKeyLimitsInput = null): ApiKeyMutationReturn! @deprecated(reason: "Use `api.get_new_recovery_api_key` instead") + useRecoveryApiKey(input: UseRecoveryKeyInput!): DeviceApiTokenMutationReturn! @deprecated(reason: "Use `api.use_recovery_api_key` instead") + refreshDeviceApiToken: DeviceApiTokenMutationReturn! @deprecated(reason: "Use `api.refresh_device_api_token` instead") + deleteDeviceApiToken(device: String!): GenericMutationReturn! @deprecated(reason: "Use `api.delete_device_api_token` instead") + getNewDeviceApiKey: ApiKeyMutationReturn! @deprecated(reason: "Use `api.get_new_device_api_key` instead") + invalidateNewDeviceApiKey: GenericMutationReturn! @deprecated(reason: "Use `api.invalidate_new_device_api_key` instead") + authorizeWithNewDeviceApiKey(input: UseNewDeviceKeyInput!): DeviceApiTokenMutationReturn! @deprecated(reason: "Use `api.authorize_with_new_device_api_key` instead") + changeTimezone(timezone: String!): TimezoneMutationReturn! @deprecated(reason: "Use `system.change_timezone` instead") + changeAutoUpgradeSettings(settings: AutoUpgradeSettingsInput!): AutoUpgradeSettingsMutationReturn! @deprecated(reason: "Use `system.change_auto_upgrade_settings` instead") + runSystemRebuild: GenericMutationReturn! @deprecated(reason: "Use `system.run_system_rebuild` instead") + runSystemRollback: GenericMutationReturn! @deprecated(reason: "Use `system.run_system_rollback` instead") + runSystemUpgrade: GenericMutationReturn! @deprecated(reason: "Use `system.run_system_upgrade` instead") + rebootSystem: GenericMutationReturn! @deprecated(reason: "Use `system.reboot_system` instead") + pullRepositoryChanges: GenericMutationReturn! @deprecated(reason: "Use `system.pull_repository_changes` instead") + createUser(user: UserMutationInput!): UserMutationReturn! @deprecated(reason: "Use `users.create_user` instead") + deleteUser(username: String!): GenericMutationReturn! @deprecated(reason: "Use `users.delete_user` instead") + updateUser(user: UserMutationInput!): UserMutationReturn! @deprecated(reason: "Use `users.update_user` instead") + addSshKey(sshInput: SshMutationInput!): UserMutationReturn! @deprecated(reason: "Use `users.add_ssh_key` instead") + removeSshKey(sshInput: SshMutationInput!): UserMutationReturn! @deprecated(reason: "Use `users.remove_ssh_key` instead") + resizeVolume(name: String!): GenericMutationReturn! @deprecated(reason: "Use `storage.resize_volume` instead") + mountVolume(name: String!): GenericMutationReturn! @deprecated(reason: "Use `storage.mount_volume` instead") + unmountVolume(name: String!): GenericMutationReturn! @deprecated(reason: "Use `storage.unmount_volume` instead") + migrateToBinds(input: MigrateToBindsInput!): GenericJobMutationReturn! @deprecated(reason: "Use `storage.migrate_to_binds` instead") + enableService(serviceId: String!): ServiceMutationReturn! @deprecated(reason: "Use `services.enable_service` instead") + disableService(serviceId: String!): ServiceMutationReturn! @deprecated(reason: "Use `services.disable_service` instead") + stopService(serviceId: String!): ServiceMutationReturn! @deprecated(reason: "Use `services.stop_service` instead") + startService(serviceId: String!): ServiceMutationReturn! @deprecated(reason: "Use `services.start_service` instead") + restartService(serviceId: String!): ServiceMutationReturn! @deprecated(reason: "Use `services.restart_service` instead") + moveService(input: MoveServiceInput!): ServiceJobMutationReturn! @deprecated(reason: "Use `services.move_service` instead") + removeJob(jobId: String!): GenericMutationReturn! @deprecated(reason: "Use `jobs.remove_job` instead") + api: ApiMutations! + system: SystemMutations! + users: UsersMutations! + storage: StorageMutations! + services: ServicesMutations! + jobs: JobMutations! + backup: BackupMutations! + testMutation: GenericMutationReturn! +} + +interface MutationReturnInterface { + success: Boolean! + message: String! + code: Int! +} + +type Query { + api: Api! + system: System! + users: Users! + storage: Storage! + jobs: Job! + services: Services! + backup: Backup! +} + +input RecoveryKeyLimitsInput { + expirationDate: DateTime = null + uses: Int = null +} + +enum RestoreStrategy { + INPLACE + DOWNLOAD_VERIFY_OVERWRITE +} + +enum ServerProvider { + HETZNER + DIGITALOCEAN +} + +type Service { + id: String! + displayName: String! + description: String! + svgIcon: String! + isMovable: Boolean! + isRequired: Boolean! + isEnabled: Boolean! + canBeBackedUp: Boolean! + backupDescription: String! + status: ServiceStatusEnum! + url: String + dnsRecords: [DnsRecord!] + storageUsage: ServiceStorageUsage! + backupSnapshots: [SnapshotInfo!] +} + +type ServiceJobMutationReturn implements MutationReturnInterface { + success: Boolean! + message: String! + code: Int! + job: ApiJob + service: Service +} + +type ServiceMutationReturn implements MutationReturnInterface { + success: Boolean! + message: String! + code: Int! + service: Service +} + +enum ServiceStatusEnum { + ACTIVE + RELOADING + INACTIVE + FAILED + ACTIVATING + DEACTIVATING + OFF +} + +type ServiceStorageUsage implements StorageUsageInterface { + usedSpace: String! + volume: StorageVolume + title: String! + service: Service +} + +type Services { + allServices: [Service!]! +} + +type ServicesMutations { + enableService(serviceId: String!): ServiceMutationReturn! + disableService(serviceId: String!): ServiceMutationReturn! + stopService(serviceId: String!): ServiceMutationReturn! + startService(serviceId: String!): ServiceMutationReturn! + restartService(serviceId: String!): ServiceMutationReturn! + moveService(input: MoveServiceInput!): ServiceJobMutationReturn! +} + +enum Severity { + INFO + WARNING + ERROR + CRITICAL + SUCCESS +} + +type SnapshotInfo { + id: String! + service: Service! + createdAt: DateTime! + reason: BackupReason! +} + +input SshMutationInput { + username: String! + sshKey: String! +} + +type SshSettings { + enable: Boolean! + passwordAuthentication: Boolean! + rootSshKeys: [String!]! +} + +type Storage { + volumes: [StorageVolume!]! +} + +type StorageMutations { + resizeVolume(name: String!): GenericMutationReturn! + mountVolume(name: String!): GenericMutationReturn! + unmountVolume(name: String!): GenericMutationReturn! + migrateToBinds(input: MigrateToBindsInput!): GenericJobMutationReturn! +} + +interface StorageUsageInterface { + usedSpace: String! + volume: StorageVolume + title: String! +} + +type StorageVolume { + totalSpace: String! + freeSpace: String! + usedSpace: String! + root: Boolean! + name: String! + model: String + serial: String + type: String! + usages: [StorageUsageInterface!]! +} + +type Subscription { + count(target: Int! = 100): Int! +} + +type System { + status: Alert! + domainInfo: SystemDomainInfo! + settings: SystemSettings! + info: SystemInfo! + provider: SystemProviderInfo! + busy: Boolean! + workingDirectory: String! +} + +type SystemDomainInfo { + domain: String! + hostname: String! + provider: DnsProvider! + requiredDnsRecords: [DnsRecord!]! +} + +type SystemInfo { + systemVersion: String! + pythonVersion: String! + usingBinds: Boolean! +} + +type SystemMutations { + changeTimezone(timezone: String!): TimezoneMutationReturn! + changeAutoUpgradeSettings(settings: AutoUpgradeSettingsInput!): AutoUpgradeSettingsMutationReturn! + runSystemRebuild: GenericMutationReturn! + runSystemRollback: GenericMutationReturn! + runSystemUpgrade: GenericMutationReturn! + rebootSystem: GenericMutationReturn! + pullRepositoryChanges: GenericMutationReturn! +} + +type SystemProviderInfo { + provider: ServerProvider! + id: String! +} + +type SystemSettings { + autoUpgrade: AutoUpgradeOptions! + ssh: SshSettings! + timezone: String! +} + +type TimezoneMutationReturn implements MutationReturnInterface { + success: Boolean! + message: String! + code: Int! + timezone: String +} + +input UseNewDeviceKeyInput { + key: String! + deviceName: String! +} + +input UseRecoveryKeyInput { + key: String! + deviceName: String! +} + +type User { + userType: UserType! + username: String! + sshKeys: [String!]! +} + +input UserMutationInput { + username: String! + password: String! +} + +type UserMutationReturn implements MutationReturnInterface { + success: Boolean! + message: String! + code: Int! + user: User +} + +enum UserType { + NORMAL + PRIMARY + ROOT +} + +type Users { + allUsers: [User!]! + getUser(username: String!): User +} + +type UsersMutations { + createUser(user: UserMutationInput!): UserMutationReturn! + deleteUser(username: String!): GenericMutationReturn! + updateUser(user: UserMutationInput!): UserMutationReturn! + addSshKey(sshInput: SshMutationInput!): UserMutationReturn! + removeSshKey(sshInput: SshMutationInput!): UserMutationReturn! +} diff --git a/lib/logic/api_maps/graphql_maps/schema/schema.graphql.dart b/lib/logic/api_maps/graphql_maps/schema/schema.graphql.dart new file mode 100644 index 00000000..538a05ff --- /dev/null +++ b/lib/logic/api_maps/graphql_maps/schema/schema.graphql.dart @@ -0,0 +1,1727 @@ +import 'package:selfprivacy/utils/scalars.dart'; + +class Input$AutoUpgradeSettingsInput { + factory Input$AutoUpgradeSettingsInput({ + bool? enableAutoUpgrade, + bool? allowReboot, + }) => + Input$AutoUpgradeSettingsInput._({ + if (enableAutoUpgrade != null) r'enableAutoUpgrade': enableAutoUpgrade, + if (allowReboot != null) r'allowReboot': allowReboot, + }); + + Input$AutoUpgradeSettingsInput._(this._$data); + + factory Input$AutoUpgradeSettingsInput.fromJson(Map data) { + final result$data = {}; + if (data.containsKey('enableAutoUpgrade')) { + final l$enableAutoUpgrade = data['enableAutoUpgrade']; + result$data['enableAutoUpgrade'] = (l$enableAutoUpgrade as bool?); + } + if (data.containsKey('allowReboot')) { + final l$allowReboot = data['allowReboot']; + result$data['allowReboot'] = (l$allowReboot as bool?); + } + return Input$AutoUpgradeSettingsInput._(result$data); + } + + Map _$data; + + bool? get enableAutoUpgrade => (_$data['enableAutoUpgrade'] as bool?); + bool? get allowReboot => (_$data['allowReboot'] as bool?); + Map toJson() { + final result$data = {}; + if (_$data.containsKey('enableAutoUpgrade')) { + final l$enableAutoUpgrade = enableAutoUpgrade; + result$data['enableAutoUpgrade'] = l$enableAutoUpgrade; + } + if (_$data.containsKey('allowReboot')) { + final l$allowReboot = allowReboot; + result$data['allowReboot'] = l$allowReboot; + } + return result$data; + } + + CopyWith$Input$AutoUpgradeSettingsInput + get copyWith => CopyWith$Input$AutoUpgradeSettingsInput( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Input$AutoUpgradeSettingsInput) || + runtimeType != other.runtimeType) { + return false; + } + final l$enableAutoUpgrade = enableAutoUpgrade; + final lOther$enableAutoUpgrade = other.enableAutoUpgrade; + if (_$data.containsKey('enableAutoUpgrade') != + other._$data.containsKey('enableAutoUpgrade')) { + return false; + } + if (l$enableAutoUpgrade != lOther$enableAutoUpgrade) { + return false; + } + final l$allowReboot = allowReboot; + final lOther$allowReboot = other.allowReboot; + if (_$data.containsKey('allowReboot') != + other._$data.containsKey('allowReboot')) { + return false; + } + if (l$allowReboot != lOther$allowReboot) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$enableAutoUpgrade = enableAutoUpgrade; + final l$allowReboot = allowReboot; + return Object.hashAll([ + _$data.containsKey('enableAutoUpgrade') ? l$enableAutoUpgrade : const {}, + _$data.containsKey('allowReboot') ? l$allowReboot : const {}, + ]); + } +} + +abstract class CopyWith$Input$AutoUpgradeSettingsInput { + factory CopyWith$Input$AutoUpgradeSettingsInput( + Input$AutoUpgradeSettingsInput instance, + TRes Function(Input$AutoUpgradeSettingsInput) then, + ) = _CopyWithImpl$Input$AutoUpgradeSettingsInput; + + factory CopyWith$Input$AutoUpgradeSettingsInput.stub(TRes res) = + _CopyWithStubImpl$Input$AutoUpgradeSettingsInput; + + TRes call({ + bool? enableAutoUpgrade, + bool? allowReboot, + }); +} + +class _CopyWithImpl$Input$AutoUpgradeSettingsInput + implements CopyWith$Input$AutoUpgradeSettingsInput { + _CopyWithImpl$Input$AutoUpgradeSettingsInput( + this._instance, + this._then, + ); + + final Input$AutoUpgradeSettingsInput _instance; + + final TRes Function(Input$AutoUpgradeSettingsInput) _then; + + static const _undefined = {}; + + TRes call({ + Object? enableAutoUpgrade = _undefined, + Object? allowReboot = _undefined, + }) => + _then(Input$AutoUpgradeSettingsInput._({ + ..._instance._$data, + if (enableAutoUpgrade != _undefined) + 'enableAutoUpgrade': (enableAutoUpgrade as bool?), + if (allowReboot != _undefined) 'allowReboot': (allowReboot as bool?), + })); +} + +class _CopyWithStubImpl$Input$AutoUpgradeSettingsInput + implements CopyWith$Input$AutoUpgradeSettingsInput { + _CopyWithStubImpl$Input$AutoUpgradeSettingsInput(this._res); + + TRes _res; + + call({ + bool? enableAutoUpgrade, + bool? allowReboot, + }) => + _res; +} + +class Input$AutobackupQuotasInput { + factory Input$AutobackupQuotasInput({ + required int last, + required int daily, + required int weekly, + required int monthly, + required int yearly, + }) => + Input$AutobackupQuotasInput._({ + r'last': last, + r'daily': daily, + r'weekly': weekly, + r'monthly': monthly, + r'yearly': yearly, + }); + + Input$AutobackupQuotasInput._(this._$data); + + factory Input$AutobackupQuotasInput.fromJson(Map data) { + final result$data = {}; + final l$last = data['last']; + result$data['last'] = (l$last as int); + final l$daily = data['daily']; + result$data['daily'] = (l$daily as int); + final l$weekly = data['weekly']; + result$data['weekly'] = (l$weekly as int); + final l$monthly = data['monthly']; + result$data['monthly'] = (l$monthly as int); + final l$yearly = data['yearly']; + result$data['yearly'] = (l$yearly as int); + return Input$AutobackupQuotasInput._(result$data); + } + + Map _$data; + + int get last => (_$data['last'] as int); + int get daily => (_$data['daily'] as int); + int get weekly => (_$data['weekly'] as int); + int get monthly => (_$data['monthly'] as int); + int get yearly => (_$data['yearly'] as int); + Map toJson() { + final result$data = {}; + final l$last = last; + result$data['last'] = l$last; + final l$daily = daily; + result$data['daily'] = l$daily; + final l$weekly = weekly; + result$data['weekly'] = l$weekly; + final l$monthly = monthly; + result$data['monthly'] = l$monthly; + final l$yearly = yearly; + result$data['yearly'] = l$yearly; + return result$data; + } + + CopyWith$Input$AutobackupQuotasInput + get copyWith => CopyWith$Input$AutobackupQuotasInput( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Input$AutobackupQuotasInput) || + runtimeType != other.runtimeType) { + return false; + } + final l$last = last; + final lOther$last = other.last; + if (l$last != lOther$last) { + return false; + } + final l$daily = daily; + final lOther$daily = other.daily; + if (l$daily != lOther$daily) { + return false; + } + final l$weekly = weekly; + final lOther$weekly = other.weekly; + if (l$weekly != lOther$weekly) { + return false; + } + final l$monthly = monthly; + final lOther$monthly = other.monthly; + if (l$monthly != lOther$monthly) { + return false; + } + final l$yearly = yearly; + final lOther$yearly = other.yearly; + if (l$yearly != lOther$yearly) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$last = last; + final l$daily = daily; + final l$weekly = weekly; + final l$monthly = monthly; + final l$yearly = yearly; + return Object.hashAll([ + l$last, + l$daily, + l$weekly, + l$monthly, + l$yearly, + ]); + } +} + +abstract class CopyWith$Input$AutobackupQuotasInput { + factory CopyWith$Input$AutobackupQuotasInput( + Input$AutobackupQuotasInput instance, + TRes Function(Input$AutobackupQuotasInput) then, + ) = _CopyWithImpl$Input$AutobackupQuotasInput; + + factory CopyWith$Input$AutobackupQuotasInput.stub(TRes res) = + _CopyWithStubImpl$Input$AutobackupQuotasInput; + + TRes call({ + int? last, + int? daily, + int? weekly, + int? monthly, + int? yearly, + }); +} + +class _CopyWithImpl$Input$AutobackupQuotasInput + implements CopyWith$Input$AutobackupQuotasInput { + _CopyWithImpl$Input$AutobackupQuotasInput( + this._instance, + this._then, + ); + + final Input$AutobackupQuotasInput _instance; + + final TRes Function(Input$AutobackupQuotasInput) _then; + + static const _undefined = {}; + + TRes call({ + Object? last = _undefined, + Object? daily = _undefined, + Object? weekly = _undefined, + Object? monthly = _undefined, + Object? yearly = _undefined, + }) => + _then(Input$AutobackupQuotasInput._({ + ..._instance._$data, + if (last != _undefined && last != null) 'last': (last as int), + if (daily != _undefined && daily != null) 'daily': (daily as int), + if (weekly != _undefined && weekly != null) 'weekly': (weekly as int), + if (monthly != _undefined && monthly != null) + 'monthly': (monthly as int), + if (yearly != _undefined && yearly != null) 'yearly': (yearly as int), + })); +} + +class _CopyWithStubImpl$Input$AutobackupQuotasInput + implements CopyWith$Input$AutobackupQuotasInput { + _CopyWithStubImpl$Input$AutobackupQuotasInput(this._res); + + TRes _res; + + call({ + int? last, + int? daily, + int? weekly, + int? monthly, + int? yearly, + }) => + _res; +} + +class Input$InitializeRepositoryInput { + factory Input$InitializeRepositoryInput({ + required Enum$BackupProvider provider, + required String locationId, + required String locationName, + required String login, + required String password, + }) => + Input$InitializeRepositoryInput._({ + r'provider': provider, + r'locationId': locationId, + r'locationName': locationName, + r'login': login, + r'password': password, + }); + + Input$InitializeRepositoryInput._(this._$data); + + factory Input$InitializeRepositoryInput.fromJson(Map data) { + final result$data = {}; + final l$provider = data['provider']; + result$data['provider'] = + fromJson$Enum$BackupProvider((l$provider as String)); + final l$locationId = data['locationId']; + result$data['locationId'] = (l$locationId as String); + final l$locationName = data['locationName']; + result$data['locationName'] = (l$locationName as String); + final l$login = data['login']; + result$data['login'] = (l$login as String); + final l$password = data['password']; + result$data['password'] = (l$password as String); + return Input$InitializeRepositoryInput._(result$data); + } + + Map _$data; + + Enum$BackupProvider get provider => + (_$data['provider'] as Enum$BackupProvider); + String get locationId => (_$data['locationId'] as String); + String get locationName => (_$data['locationName'] as String); + String get login => (_$data['login'] as String); + String get password => (_$data['password'] as String); + Map toJson() { + final result$data = {}; + final l$provider = provider; + result$data['provider'] = toJson$Enum$BackupProvider(l$provider); + final l$locationId = locationId; + result$data['locationId'] = l$locationId; + final l$locationName = locationName; + result$data['locationName'] = l$locationName; + final l$login = login; + result$data['login'] = l$login; + final l$password = password; + result$data['password'] = l$password; + return result$data; + } + + CopyWith$Input$InitializeRepositoryInput + get copyWith => CopyWith$Input$InitializeRepositoryInput( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Input$InitializeRepositoryInput) || + runtimeType != other.runtimeType) { + return false; + } + final l$provider = provider; + final lOther$provider = other.provider; + if (l$provider != lOther$provider) { + return false; + } + final l$locationId = locationId; + final lOther$locationId = other.locationId; + if (l$locationId != lOther$locationId) { + return false; + } + final l$locationName = locationName; + final lOther$locationName = other.locationName; + if (l$locationName != lOther$locationName) { + return false; + } + final l$login = login; + final lOther$login = other.login; + if (l$login != lOther$login) { + return false; + } + final l$password = password; + final lOther$password = other.password; + if (l$password != lOther$password) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$provider = provider; + final l$locationId = locationId; + final l$locationName = locationName; + final l$login = login; + final l$password = password; + return Object.hashAll([ + l$provider, + l$locationId, + l$locationName, + l$login, + l$password, + ]); + } +} + +abstract class CopyWith$Input$InitializeRepositoryInput { + factory CopyWith$Input$InitializeRepositoryInput( + Input$InitializeRepositoryInput instance, + TRes Function(Input$InitializeRepositoryInput) then, + ) = _CopyWithImpl$Input$InitializeRepositoryInput; + + factory CopyWith$Input$InitializeRepositoryInput.stub(TRes res) = + _CopyWithStubImpl$Input$InitializeRepositoryInput; + + TRes call({ + Enum$BackupProvider? provider, + String? locationId, + String? locationName, + String? login, + String? password, + }); +} + +class _CopyWithImpl$Input$InitializeRepositoryInput + implements CopyWith$Input$InitializeRepositoryInput { + _CopyWithImpl$Input$InitializeRepositoryInput( + this._instance, + this._then, + ); + + final Input$InitializeRepositoryInput _instance; + + final TRes Function(Input$InitializeRepositoryInput) _then; + + static const _undefined = {}; + + TRes call({ + Object? provider = _undefined, + Object? locationId = _undefined, + Object? locationName = _undefined, + Object? login = _undefined, + Object? password = _undefined, + }) => + _then(Input$InitializeRepositoryInput._({ + ..._instance._$data, + if (provider != _undefined && provider != null) + 'provider': (provider as Enum$BackupProvider), + if (locationId != _undefined && locationId != null) + 'locationId': (locationId as String), + if (locationName != _undefined && locationName != null) + 'locationName': (locationName as String), + if (login != _undefined && login != null) 'login': (login as String), + if (password != _undefined && password != null) + 'password': (password as String), + })); +} + +class _CopyWithStubImpl$Input$InitializeRepositoryInput + implements CopyWith$Input$InitializeRepositoryInput { + _CopyWithStubImpl$Input$InitializeRepositoryInput(this._res); + + TRes _res; + + call({ + Enum$BackupProvider? provider, + String? locationId, + String? locationName, + String? login, + String? password, + }) => + _res; +} + +class Input$MigrateToBindsInput { + factory Input$MigrateToBindsInput({ + required String emailBlockDevice, + required String bitwardenBlockDevice, + required String giteaBlockDevice, + required String nextcloudBlockDevice, + required String pleromaBlockDevice, + }) => + Input$MigrateToBindsInput._({ + r'emailBlockDevice': emailBlockDevice, + r'bitwardenBlockDevice': bitwardenBlockDevice, + r'giteaBlockDevice': giteaBlockDevice, + r'nextcloudBlockDevice': nextcloudBlockDevice, + r'pleromaBlockDevice': pleromaBlockDevice, + }); + + Input$MigrateToBindsInput._(this._$data); + + factory Input$MigrateToBindsInput.fromJson(Map data) { + final result$data = {}; + final l$emailBlockDevice = data['emailBlockDevice']; + result$data['emailBlockDevice'] = (l$emailBlockDevice as String); + final l$bitwardenBlockDevice = data['bitwardenBlockDevice']; + result$data['bitwardenBlockDevice'] = (l$bitwardenBlockDevice as String); + final l$giteaBlockDevice = data['giteaBlockDevice']; + result$data['giteaBlockDevice'] = (l$giteaBlockDevice as String); + final l$nextcloudBlockDevice = data['nextcloudBlockDevice']; + result$data['nextcloudBlockDevice'] = (l$nextcloudBlockDevice as String); + final l$pleromaBlockDevice = data['pleromaBlockDevice']; + result$data['pleromaBlockDevice'] = (l$pleromaBlockDevice as String); + return Input$MigrateToBindsInput._(result$data); + } + + Map _$data; + + String get emailBlockDevice => (_$data['emailBlockDevice'] as String); + String get bitwardenBlockDevice => (_$data['bitwardenBlockDevice'] as String); + String get giteaBlockDevice => (_$data['giteaBlockDevice'] as String); + String get nextcloudBlockDevice => (_$data['nextcloudBlockDevice'] as String); + String get pleromaBlockDevice => (_$data['pleromaBlockDevice'] as String); + Map toJson() { + final result$data = {}; + final l$emailBlockDevice = emailBlockDevice; + result$data['emailBlockDevice'] = l$emailBlockDevice; + final l$bitwardenBlockDevice = bitwardenBlockDevice; + result$data['bitwardenBlockDevice'] = l$bitwardenBlockDevice; + final l$giteaBlockDevice = giteaBlockDevice; + result$data['giteaBlockDevice'] = l$giteaBlockDevice; + final l$nextcloudBlockDevice = nextcloudBlockDevice; + result$data['nextcloudBlockDevice'] = l$nextcloudBlockDevice; + final l$pleromaBlockDevice = pleromaBlockDevice; + result$data['pleromaBlockDevice'] = l$pleromaBlockDevice; + return result$data; + } + + CopyWith$Input$MigrateToBindsInput get copyWith => + CopyWith$Input$MigrateToBindsInput( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Input$MigrateToBindsInput) || + runtimeType != other.runtimeType) { + return false; + } + final l$emailBlockDevice = emailBlockDevice; + final lOther$emailBlockDevice = other.emailBlockDevice; + if (l$emailBlockDevice != lOther$emailBlockDevice) { + return false; + } + final l$bitwardenBlockDevice = bitwardenBlockDevice; + final lOther$bitwardenBlockDevice = other.bitwardenBlockDevice; + if (l$bitwardenBlockDevice != lOther$bitwardenBlockDevice) { + return false; + } + final l$giteaBlockDevice = giteaBlockDevice; + final lOther$giteaBlockDevice = other.giteaBlockDevice; + if (l$giteaBlockDevice != lOther$giteaBlockDevice) { + return false; + } + final l$nextcloudBlockDevice = nextcloudBlockDevice; + final lOther$nextcloudBlockDevice = other.nextcloudBlockDevice; + if (l$nextcloudBlockDevice != lOther$nextcloudBlockDevice) { + return false; + } + final l$pleromaBlockDevice = pleromaBlockDevice; + final lOther$pleromaBlockDevice = other.pleromaBlockDevice; + if (l$pleromaBlockDevice != lOther$pleromaBlockDevice) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$emailBlockDevice = emailBlockDevice; + final l$bitwardenBlockDevice = bitwardenBlockDevice; + final l$giteaBlockDevice = giteaBlockDevice; + final l$nextcloudBlockDevice = nextcloudBlockDevice; + final l$pleromaBlockDevice = pleromaBlockDevice; + return Object.hashAll([ + l$emailBlockDevice, + l$bitwardenBlockDevice, + l$giteaBlockDevice, + l$nextcloudBlockDevice, + l$pleromaBlockDevice, + ]); + } +} + +abstract class CopyWith$Input$MigrateToBindsInput { + factory CopyWith$Input$MigrateToBindsInput( + Input$MigrateToBindsInput instance, + TRes Function(Input$MigrateToBindsInput) then, + ) = _CopyWithImpl$Input$MigrateToBindsInput; + + factory CopyWith$Input$MigrateToBindsInput.stub(TRes res) = + _CopyWithStubImpl$Input$MigrateToBindsInput; + + TRes call({ + String? emailBlockDevice, + String? bitwardenBlockDevice, + String? giteaBlockDevice, + String? nextcloudBlockDevice, + String? pleromaBlockDevice, + }); +} + +class _CopyWithImpl$Input$MigrateToBindsInput + implements CopyWith$Input$MigrateToBindsInput { + _CopyWithImpl$Input$MigrateToBindsInput( + this._instance, + this._then, + ); + + final Input$MigrateToBindsInput _instance; + + final TRes Function(Input$MigrateToBindsInput) _then; + + static const _undefined = {}; + + TRes call({ + Object? emailBlockDevice = _undefined, + Object? bitwardenBlockDevice = _undefined, + Object? giteaBlockDevice = _undefined, + Object? nextcloudBlockDevice = _undefined, + Object? pleromaBlockDevice = _undefined, + }) => + _then(Input$MigrateToBindsInput._({ + ..._instance._$data, + if (emailBlockDevice != _undefined && emailBlockDevice != null) + 'emailBlockDevice': (emailBlockDevice as String), + if (bitwardenBlockDevice != _undefined && bitwardenBlockDevice != null) + 'bitwardenBlockDevice': (bitwardenBlockDevice as String), + if (giteaBlockDevice != _undefined && giteaBlockDevice != null) + 'giteaBlockDevice': (giteaBlockDevice as String), + if (nextcloudBlockDevice != _undefined && nextcloudBlockDevice != null) + 'nextcloudBlockDevice': (nextcloudBlockDevice as String), + if (pleromaBlockDevice != _undefined && pleromaBlockDevice != null) + 'pleromaBlockDevice': (pleromaBlockDevice as String), + })); +} + +class _CopyWithStubImpl$Input$MigrateToBindsInput + implements CopyWith$Input$MigrateToBindsInput { + _CopyWithStubImpl$Input$MigrateToBindsInput(this._res); + + TRes _res; + + call({ + String? emailBlockDevice, + String? bitwardenBlockDevice, + String? giteaBlockDevice, + String? nextcloudBlockDevice, + String? pleromaBlockDevice, + }) => + _res; +} + +class Input$MoveServiceInput { + factory Input$MoveServiceInput({ + required String serviceId, + required String location, + }) => + Input$MoveServiceInput._({ + r'serviceId': serviceId, + r'location': location, + }); + + Input$MoveServiceInput._(this._$data); + + factory Input$MoveServiceInput.fromJson(Map data) { + final result$data = {}; + final l$serviceId = data['serviceId']; + result$data['serviceId'] = (l$serviceId as String); + final l$location = data['location']; + result$data['location'] = (l$location as String); + return Input$MoveServiceInput._(result$data); + } + + Map _$data; + + String get serviceId => (_$data['serviceId'] as String); + String get location => (_$data['location'] as String); + Map toJson() { + final result$data = {}; + final l$serviceId = serviceId; + result$data['serviceId'] = l$serviceId; + final l$location = location; + result$data['location'] = l$location; + return result$data; + } + + CopyWith$Input$MoveServiceInput get copyWith => + CopyWith$Input$MoveServiceInput( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Input$MoveServiceInput) || + runtimeType != other.runtimeType) { + return false; + } + final l$serviceId = serviceId; + final lOther$serviceId = other.serviceId; + if (l$serviceId != lOther$serviceId) { + return false; + } + final l$location = location; + final lOther$location = other.location; + if (l$location != lOther$location) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$serviceId = serviceId; + final l$location = location; + return Object.hashAll([ + l$serviceId, + l$location, + ]); + } +} + +abstract class CopyWith$Input$MoveServiceInput { + factory CopyWith$Input$MoveServiceInput( + Input$MoveServiceInput instance, + TRes Function(Input$MoveServiceInput) then, + ) = _CopyWithImpl$Input$MoveServiceInput; + + factory CopyWith$Input$MoveServiceInput.stub(TRes res) = + _CopyWithStubImpl$Input$MoveServiceInput; + + TRes call({ + String? serviceId, + String? location, + }); +} + +class _CopyWithImpl$Input$MoveServiceInput + implements CopyWith$Input$MoveServiceInput { + _CopyWithImpl$Input$MoveServiceInput( + this._instance, + this._then, + ); + + final Input$MoveServiceInput _instance; + + final TRes Function(Input$MoveServiceInput) _then; + + static const _undefined = {}; + + TRes call({ + Object? serviceId = _undefined, + Object? location = _undefined, + }) => + _then(Input$MoveServiceInput._({ + ..._instance._$data, + if (serviceId != _undefined && serviceId != null) + 'serviceId': (serviceId as String), + if (location != _undefined && location != null) + 'location': (location as String), + })); +} + +class _CopyWithStubImpl$Input$MoveServiceInput + implements CopyWith$Input$MoveServiceInput { + _CopyWithStubImpl$Input$MoveServiceInput(this._res); + + TRes _res; + + call({ + String? serviceId, + String? location, + }) => + _res; +} + +class Input$RecoveryKeyLimitsInput { + factory Input$RecoveryKeyLimitsInput({ + DateTime? expirationDate, + int? uses, + }) => + Input$RecoveryKeyLimitsInput._({ + if (expirationDate != null) r'expirationDate': expirationDate, + if (uses != null) r'uses': uses, + }); + + Input$RecoveryKeyLimitsInput._(this._$data); + + factory Input$RecoveryKeyLimitsInput.fromJson(Map data) { + final result$data = {}; + if (data.containsKey('expirationDate')) { + final l$expirationDate = data['expirationDate']; + result$data['expirationDate'] = + l$expirationDate == null ? null : dateTimeFromJson(l$expirationDate); + } + if (data.containsKey('uses')) { + final l$uses = data['uses']; + result$data['uses'] = (l$uses as int?); + } + return Input$RecoveryKeyLimitsInput._(result$data); + } + + Map _$data; + + DateTime? get expirationDate => (_$data['expirationDate'] as DateTime?); + int? get uses => (_$data['uses'] as int?); + Map toJson() { + final result$data = {}; + if (_$data.containsKey('expirationDate')) { + final l$expirationDate = expirationDate; + result$data['expirationDate'] = + l$expirationDate == null ? null : dateTimeToJson(l$expirationDate); + } + if (_$data.containsKey('uses')) { + final l$uses = uses; + result$data['uses'] = l$uses; + } + return result$data; + } + + CopyWith$Input$RecoveryKeyLimitsInput + get copyWith => CopyWith$Input$RecoveryKeyLimitsInput( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Input$RecoveryKeyLimitsInput) || + runtimeType != other.runtimeType) { + return false; + } + final l$expirationDate = expirationDate; + final lOther$expirationDate = other.expirationDate; + if (_$data.containsKey('expirationDate') != + other._$data.containsKey('expirationDate')) { + return false; + } + if (l$expirationDate != lOther$expirationDate) { + return false; + } + final l$uses = uses; + final lOther$uses = other.uses; + if (_$data.containsKey('uses') != other._$data.containsKey('uses')) { + return false; + } + if (l$uses != lOther$uses) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$expirationDate = expirationDate; + final l$uses = uses; + return Object.hashAll([ + _$data.containsKey('expirationDate') ? l$expirationDate : const {}, + _$data.containsKey('uses') ? l$uses : const {}, + ]); + } +} + +abstract class CopyWith$Input$RecoveryKeyLimitsInput { + factory CopyWith$Input$RecoveryKeyLimitsInput( + Input$RecoveryKeyLimitsInput instance, + TRes Function(Input$RecoveryKeyLimitsInput) then, + ) = _CopyWithImpl$Input$RecoveryKeyLimitsInput; + + factory CopyWith$Input$RecoveryKeyLimitsInput.stub(TRes res) = + _CopyWithStubImpl$Input$RecoveryKeyLimitsInput; + + TRes call({ + DateTime? expirationDate, + int? uses, + }); +} + +class _CopyWithImpl$Input$RecoveryKeyLimitsInput + implements CopyWith$Input$RecoveryKeyLimitsInput { + _CopyWithImpl$Input$RecoveryKeyLimitsInput( + this._instance, + this._then, + ); + + final Input$RecoveryKeyLimitsInput _instance; + + final TRes Function(Input$RecoveryKeyLimitsInput) _then; + + static const _undefined = {}; + + TRes call({ + Object? expirationDate = _undefined, + Object? uses = _undefined, + }) => + _then(Input$RecoveryKeyLimitsInput._({ + ..._instance._$data, + if (expirationDate != _undefined) + 'expirationDate': (expirationDate as DateTime?), + if (uses != _undefined) 'uses': (uses as int?), + })); +} + +class _CopyWithStubImpl$Input$RecoveryKeyLimitsInput + implements CopyWith$Input$RecoveryKeyLimitsInput { + _CopyWithStubImpl$Input$RecoveryKeyLimitsInput(this._res); + + TRes _res; + + call({ + DateTime? expirationDate, + int? uses, + }) => + _res; +} + +class Input$SshMutationInput { + factory Input$SshMutationInput({ + required String username, + required String sshKey, + }) => + Input$SshMutationInput._({ + r'username': username, + r'sshKey': sshKey, + }); + + Input$SshMutationInput._(this._$data); + + factory Input$SshMutationInput.fromJson(Map data) { + final result$data = {}; + final l$username = data['username']; + result$data['username'] = (l$username as String); + final l$sshKey = data['sshKey']; + result$data['sshKey'] = (l$sshKey as String); + return Input$SshMutationInput._(result$data); + } + + Map _$data; + + String get username => (_$data['username'] as String); + String get sshKey => (_$data['sshKey'] as String); + Map toJson() { + final result$data = {}; + final l$username = username; + result$data['username'] = l$username; + final l$sshKey = sshKey; + result$data['sshKey'] = l$sshKey; + return result$data; + } + + CopyWith$Input$SshMutationInput get copyWith => + CopyWith$Input$SshMutationInput( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Input$SshMutationInput) || + runtimeType != other.runtimeType) { + return false; + } + final l$username = username; + final lOther$username = other.username; + if (l$username != lOther$username) { + return false; + } + final l$sshKey = sshKey; + final lOther$sshKey = other.sshKey; + if (l$sshKey != lOther$sshKey) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$username = username; + final l$sshKey = sshKey; + return Object.hashAll([ + l$username, + l$sshKey, + ]); + } +} + +abstract class CopyWith$Input$SshMutationInput { + factory CopyWith$Input$SshMutationInput( + Input$SshMutationInput instance, + TRes Function(Input$SshMutationInput) then, + ) = _CopyWithImpl$Input$SshMutationInput; + + factory CopyWith$Input$SshMutationInput.stub(TRes res) = + _CopyWithStubImpl$Input$SshMutationInput; + + TRes call({ + String? username, + String? sshKey, + }); +} + +class _CopyWithImpl$Input$SshMutationInput + implements CopyWith$Input$SshMutationInput { + _CopyWithImpl$Input$SshMutationInput( + this._instance, + this._then, + ); + + final Input$SshMutationInput _instance; + + final TRes Function(Input$SshMutationInput) _then; + + static const _undefined = {}; + + TRes call({ + Object? username = _undefined, + Object? sshKey = _undefined, + }) => + _then(Input$SshMutationInput._({ + ..._instance._$data, + if (username != _undefined && username != null) + 'username': (username as String), + if (sshKey != _undefined && sshKey != null) + 'sshKey': (sshKey as String), + })); +} + +class _CopyWithStubImpl$Input$SshMutationInput + implements CopyWith$Input$SshMutationInput { + _CopyWithStubImpl$Input$SshMutationInput(this._res); + + TRes _res; + + call({ + String? username, + String? sshKey, + }) => + _res; +} + +class Input$UseNewDeviceKeyInput { + factory Input$UseNewDeviceKeyInput({ + required String key, + required String deviceName, + }) => + Input$UseNewDeviceKeyInput._({ + r'key': key, + r'deviceName': deviceName, + }); + + Input$UseNewDeviceKeyInput._(this._$data); + + factory Input$UseNewDeviceKeyInput.fromJson(Map data) { + final result$data = {}; + final l$key = data['key']; + result$data['key'] = (l$key as String); + final l$deviceName = data['deviceName']; + result$data['deviceName'] = (l$deviceName as String); + return Input$UseNewDeviceKeyInput._(result$data); + } + + Map _$data; + + String get key => (_$data['key'] as String); + String get deviceName => (_$data['deviceName'] as String); + Map toJson() { + final result$data = {}; + final l$key = key; + result$data['key'] = l$key; + final l$deviceName = deviceName; + result$data['deviceName'] = l$deviceName; + return result$data; + } + + CopyWith$Input$UseNewDeviceKeyInput + get copyWith => CopyWith$Input$UseNewDeviceKeyInput( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Input$UseNewDeviceKeyInput) || + runtimeType != other.runtimeType) { + return false; + } + final l$key = key; + final lOther$key = other.key; + if (l$key != lOther$key) { + return false; + } + final l$deviceName = deviceName; + final lOther$deviceName = other.deviceName; + if (l$deviceName != lOther$deviceName) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$key = key; + final l$deviceName = deviceName; + return Object.hashAll([ + l$key, + l$deviceName, + ]); + } +} + +abstract class CopyWith$Input$UseNewDeviceKeyInput { + factory CopyWith$Input$UseNewDeviceKeyInput( + Input$UseNewDeviceKeyInput instance, + TRes Function(Input$UseNewDeviceKeyInput) then, + ) = _CopyWithImpl$Input$UseNewDeviceKeyInput; + + factory CopyWith$Input$UseNewDeviceKeyInput.stub(TRes res) = + _CopyWithStubImpl$Input$UseNewDeviceKeyInput; + + TRes call({ + String? key, + String? deviceName, + }); +} + +class _CopyWithImpl$Input$UseNewDeviceKeyInput + implements CopyWith$Input$UseNewDeviceKeyInput { + _CopyWithImpl$Input$UseNewDeviceKeyInput( + this._instance, + this._then, + ); + + final Input$UseNewDeviceKeyInput _instance; + + final TRes Function(Input$UseNewDeviceKeyInput) _then; + + static const _undefined = {}; + + TRes call({ + Object? key = _undefined, + Object? deviceName = _undefined, + }) => + _then(Input$UseNewDeviceKeyInput._({ + ..._instance._$data, + if (key != _undefined && key != null) 'key': (key as String), + if (deviceName != _undefined && deviceName != null) + 'deviceName': (deviceName as String), + })); +} + +class _CopyWithStubImpl$Input$UseNewDeviceKeyInput + implements CopyWith$Input$UseNewDeviceKeyInput { + _CopyWithStubImpl$Input$UseNewDeviceKeyInput(this._res); + + TRes _res; + + call({ + String? key, + String? deviceName, + }) => + _res; +} + +class Input$UseRecoveryKeyInput { + factory Input$UseRecoveryKeyInput({ + required String key, + required String deviceName, + }) => + Input$UseRecoveryKeyInput._({ + r'key': key, + r'deviceName': deviceName, + }); + + Input$UseRecoveryKeyInput._(this._$data); + + factory Input$UseRecoveryKeyInput.fromJson(Map data) { + final result$data = {}; + final l$key = data['key']; + result$data['key'] = (l$key as String); + final l$deviceName = data['deviceName']; + result$data['deviceName'] = (l$deviceName as String); + return Input$UseRecoveryKeyInput._(result$data); + } + + Map _$data; + + String get key => (_$data['key'] as String); + String get deviceName => (_$data['deviceName'] as String); + Map toJson() { + final result$data = {}; + final l$key = key; + result$data['key'] = l$key; + final l$deviceName = deviceName; + result$data['deviceName'] = l$deviceName; + return result$data; + } + + CopyWith$Input$UseRecoveryKeyInput get copyWith => + CopyWith$Input$UseRecoveryKeyInput( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Input$UseRecoveryKeyInput) || + runtimeType != other.runtimeType) { + return false; + } + final l$key = key; + final lOther$key = other.key; + if (l$key != lOther$key) { + return false; + } + final l$deviceName = deviceName; + final lOther$deviceName = other.deviceName; + if (l$deviceName != lOther$deviceName) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$key = key; + final l$deviceName = deviceName; + return Object.hashAll([ + l$key, + l$deviceName, + ]); + } +} + +abstract class CopyWith$Input$UseRecoveryKeyInput { + factory CopyWith$Input$UseRecoveryKeyInput( + Input$UseRecoveryKeyInput instance, + TRes Function(Input$UseRecoveryKeyInput) then, + ) = _CopyWithImpl$Input$UseRecoveryKeyInput; + + factory CopyWith$Input$UseRecoveryKeyInput.stub(TRes res) = + _CopyWithStubImpl$Input$UseRecoveryKeyInput; + + TRes call({ + String? key, + String? deviceName, + }); +} + +class _CopyWithImpl$Input$UseRecoveryKeyInput + implements CopyWith$Input$UseRecoveryKeyInput { + _CopyWithImpl$Input$UseRecoveryKeyInput( + this._instance, + this._then, + ); + + final Input$UseRecoveryKeyInput _instance; + + final TRes Function(Input$UseRecoveryKeyInput) _then; + + static const _undefined = {}; + + TRes call({ + Object? key = _undefined, + Object? deviceName = _undefined, + }) => + _then(Input$UseRecoveryKeyInput._({ + ..._instance._$data, + if (key != _undefined && key != null) 'key': (key as String), + if (deviceName != _undefined && deviceName != null) + 'deviceName': (deviceName as String), + })); +} + +class _CopyWithStubImpl$Input$UseRecoveryKeyInput + implements CopyWith$Input$UseRecoveryKeyInput { + _CopyWithStubImpl$Input$UseRecoveryKeyInput(this._res); + + TRes _res; + + call({ + String? key, + String? deviceName, + }) => + _res; +} + +class Input$UserMutationInput { + factory Input$UserMutationInput({ + required String username, + required String password, + }) => + Input$UserMutationInput._({ + r'username': username, + r'password': password, + }); + + Input$UserMutationInput._(this._$data); + + factory Input$UserMutationInput.fromJson(Map data) { + final result$data = {}; + final l$username = data['username']; + result$data['username'] = (l$username as String); + final l$password = data['password']; + result$data['password'] = (l$password as String); + return Input$UserMutationInput._(result$data); + } + + Map _$data; + + String get username => (_$data['username'] as String); + String get password => (_$data['password'] as String); + Map toJson() { + final result$data = {}; + final l$username = username; + result$data['username'] = l$username; + final l$password = password; + result$data['password'] = l$password; + return result$data; + } + + CopyWith$Input$UserMutationInput get copyWith => + CopyWith$Input$UserMutationInput( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Input$UserMutationInput) || + runtimeType != other.runtimeType) { + return false; + } + final l$username = username; + final lOther$username = other.username; + if (l$username != lOther$username) { + return false; + } + final l$password = password; + final lOther$password = other.password; + if (l$password != lOther$password) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$username = username; + final l$password = password; + return Object.hashAll([ + l$username, + l$password, + ]); + } +} + +abstract class CopyWith$Input$UserMutationInput { + factory CopyWith$Input$UserMutationInput( + Input$UserMutationInput instance, + TRes Function(Input$UserMutationInput) then, + ) = _CopyWithImpl$Input$UserMutationInput; + + factory CopyWith$Input$UserMutationInput.stub(TRes res) = + _CopyWithStubImpl$Input$UserMutationInput; + + TRes call({ + String? username, + String? password, + }); +} + +class _CopyWithImpl$Input$UserMutationInput + implements CopyWith$Input$UserMutationInput { + _CopyWithImpl$Input$UserMutationInput( + this._instance, + this._then, + ); + + final Input$UserMutationInput _instance; + + final TRes Function(Input$UserMutationInput) _then; + + static const _undefined = {}; + + TRes call({ + Object? username = _undefined, + Object? password = _undefined, + }) => + _then(Input$UserMutationInput._({ + ..._instance._$data, + if (username != _undefined && username != null) + 'username': (username as String), + if (password != _undefined && password != null) + 'password': (password as String), + })); +} + +class _CopyWithStubImpl$Input$UserMutationInput + implements CopyWith$Input$UserMutationInput { + _CopyWithStubImpl$Input$UserMutationInput(this._res); + + TRes _res; + + call({ + String? username, + String? password, + }) => + _res; +} + +enum Enum$BackupProvider { BACKBLAZE, NONE, MEMORY, FILE, $unknown } + +String toJson$Enum$BackupProvider(Enum$BackupProvider e) { + switch (e) { + case Enum$BackupProvider.BACKBLAZE: + return r'BACKBLAZE'; + case Enum$BackupProvider.NONE: + return r'NONE'; + case Enum$BackupProvider.MEMORY: + return r'MEMORY'; + case Enum$BackupProvider.FILE: + return r'FILE'; + case Enum$BackupProvider.$unknown: + return r'$unknown'; + } +} + +Enum$BackupProvider fromJson$Enum$BackupProvider(String value) { + switch (value) { + case r'BACKBLAZE': + return Enum$BackupProvider.BACKBLAZE; + case r'NONE': + return Enum$BackupProvider.NONE; + case r'MEMORY': + return Enum$BackupProvider.MEMORY; + case r'FILE': + return Enum$BackupProvider.FILE; + default: + return Enum$BackupProvider.$unknown; + } +} + +enum Enum$BackupReason { EXPLICIT, AUTO, PRE_RESTORE, $unknown } + +String toJson$Enum$BackupReason(Enum$BackupReason e) { + switch (e) { + case Enum$BackupReason.EXPLICIT: + return r'EXPLICIT'; + case Enum$BackupReason.AUTO: + return r'AUTO'; + case Enum$BackupReason.PRE_RESTORE: + return r'PRE_RESTORE'; + case Enum$BackupReason.$unknown: + return r'$unknown'; + } +} + +Enum$BackupReason fromJson$Enum$BackupReason(String value) { + switch (value) { + case r'EXPLICIT': + return Enum$BackupReason.EXPLICIT; + case r'AUTO': + return Enum$BackupReason.AUTO; + case r'PRE_RESTORE': + return Enum$BackupReason.PRE_RESTORE; + default: + return Enum$BackupReason.$unknown; + } +} + +enum Enum$DnsProvider { CLOUDFLARE, DIGITALOCEAN, DESEC, $unknown } + +String toJson$Enum$DnsProvider(Enum$DnsProvider e) { + switch (e) { + case Enum$DnsProvider.CLOUDFLARE: + return r'CLOUDFLARE'; + case Enum$DnsProvider.DIGITALOCEAN: + return r'DIGITALOCEAN'; + case Enum$DnsProvider.DESEC: + return r'DESEC'; + case Enum$DnsProvider.$unknown: + return r'$unknown'; + } +} + +Enum$DnsProvider fromJson$Enum$DnsProvider(String value) { + switch (value) { + case r'CLOUDFLARE': + return Enum$DnsProvider.CLOUDFLARE; + case r'DIGITALOCEAN': + return Enum$DnsProvider.DIGITALOCEAN; + case r'DESEC': + return Enum$DnsProvider.DESEC; + default: + return Enum$DnsProvider.$unknown; + } +} + +enum Enum$RestoreStrategy { INPLACE, DOWNLOAD_VERIFY_OVERWRITE, $unknown } + +String toJson$Enum$RestoreStrategy(Enum$RestoreStrategy e) { + switch (e) { + case Enum$RestoreStrategy.INPLACE: + return r'INPLACE'; + case Enum$RestoreStrategy.DOWNLOAD_VERIFY_OVERWRITE: + return r'DOWNLOAD_VERIFY_OVERWRITE'; + case Enum$RestoreStrategy.$unknown: + return r'$unknown'; + } +} + +Enum$RestoreStrategy fromJson$Enum$RestoreStrategy(String value) { + switch (value) { + case r'INPLACE': + return Enum$RestoreStrategy.INPLACE; + case r'DOWNLOAD_VERIFY_OVERWRITE': + return Enum$RestoreStrategy.DOWNLOAD_VERIFY_OVERWRITE; + default: + return Enum$RestoreStrategy.$unknown; + } +} + +enum Enum$ServerProvider { HETZNER, DIGITALOCEAN, $unknown } + +String toJson$Enum$ServerProvider(Enum$ServerProvider e) { + switch (e) { + case Enum$ServerProvider.HETZNER: + return r'HETZNER'; + case Enum$ServerProvider.DIGITALOCEAN: + return r'DIGITALOCEAN'; + case Enum$ServerProvider.$unknown: + return r'$unknown'; + } +} + +Enum$ServerProvider fromJson$Enum$ServerProvider(String value) { + switch (value) { + case r'HETZNER': + return Enum$ServerProvider.HETZNER; + case r'DIGITALOCEAN': + return Enum$ServerProvider.DIGITALOCEAN; + default: + return Enum$ServerProvider.$unknown; + } +} + +enum Enum$ServiceStatusEnum { + ACTIVE, + RELOADING, + INACTIVE, + FAILED, + ACTIVATING, + DEACTIVATING, + OFF, + $unknown +} + +String toJson$Enum$ServiceStatusEnum(Enum$ServiceStatusEnum e) { + switch (e) { + case Enum$ServiceStatusEnum.ACTIVE: + return r'ACTIVE'; + case Enum$ServiceStatusEnum.RELOADING: + return r'RELOADING'; + case Enum$ServiceStatusEnum.INACTIVE: + return r'INACTIVE'; + case Enum$ServiceStatusEnum.FAILED: + return r'FAILED'; + case Enum$ServiceStatusEnum.ACTIVATING: + return r'ACTIVATING'; + case Enum$ServiceStatusEnum.DEACTIVATING: + return r'DEACTIVATING'; + case Enum$ServiceStatusEnum.OFF: + return r'OFF'; + case Enum$ServiceStatusEnum.$unknown: + return r'$unknown'; + } +} + +Enum$ServiceStatusEnum fromJson$Enum$ServiceStatusEnum(String value) { + switch (value) { + case r'ACTIVE': + return Enum$ServiceStatusEnum.ACTIVE; + case r'RELOADING': + return Enum$ServiceStatusEnum.RELOADING; + case r'INACTIVE': + return Enum$ServiceStatusEnum.INACTIVE; + case r'FAILED': + return Enum$ServiceStatusEnum.FAILED; + case r'ACTIVATING': + return Enum$ServiceStatusEnum.ACTIVATING; + case r'DEACTIVATING': + return Enum$ServiceStatusEnum.DEACTIVATING; + case r'OFF': + return Enum$ServiceStatusEnum.OFF; + default: + return Enum$ServiceStatusEnum.$unknown; + } +} + +enum Enum$Severity { INFO, WARNING, ERROR, CRITICAL, SUCCESS, $unknown } + +String toJson$Enum$Severity(Enum$Severity e) { + switch (e) { + case Enum$Severity.INFO: + return r'INFO'; + case Enum$Severity.WARNING: + return r'WARNING'; + case Enum$Severity.ERROR: + return r'ERROR'; + case Enum$Severity.CRITICAL: + return r'CRITICAL'; + case Enum$Severity.SUCCESS: + return r'SUCCESS'; + case Enum$Severity.$unknown: + return r'$unknown'; + } +} + +Enum$Severity fromJson$Enum$Severity(String value) { + switch (value) { + case r'INFO': + return Enum$Severity.INFO; + case r'WARNING': + return Enum$Severity.WARNING; + case r'ERROR': + return Enum$Severity.ERROR; + case r'CRITICAL': + return Enum$Severity.CRITICAL; + case r'SUCCESS': + return Enum$Severity.SUCCESS; + default: + return Enum$Severity.$unknown; + } +} + +enum Enum$UserType { NORMAL, PRIMARY, ROOT, $unknown } + +String toJson$Enum$UserType(Enum$UserType e) { + switch (e) { + case Enum$UserType.NORMAL: + return r'NORMAL'; + case Enum$UserType.PRIMARY: + return r'PRIMARY'; + case Enum$UserType.ROOT: + return r'ROOT'; + case Enum$UserType.$unknown: + return r'$unknown'; + } +} + +Enum$UserType fromJson$Enum$UserType(String value) { + switch (value) { + case r'NORMAL': + return Enum$UserType.NORMAL; + case r'PRIMARY': + return Enum$UserType.PRIMARY; + case r'ROOT': + return Enum$UserType.ROOT; + default: + return Enum$UserType.$unknown; + } +} + +const possibleTypesMap = >{ + 'MutationReturnInterface': { + 'ApiKeyMutationReturn', + 'AutoUpgradeSettingsMutationReturn', + 'DeviceApiTokenMutationReturn', + 'GenericBackupConfigReturn', + 'GenericJobMutationReturn', + 'GenericMutationReturn', + 'ServiceJobMutationReturn', + 'ServiceMutationReturn', + 'TimezoneMutationReturn', + 'UserMutationReturn', + }, + 'StorageUsageInterface': {'ServiceStorageUsage'}, +}; diff --git a/lib/logic/api_maps/graphql_maps/schema/server_api.graphql b/lib/logic/api_maps/graphql_maps/schema/server_api.graphql new file mode 100644 index 00000000..631454db --- /dev/null +++ b/lib/logic/api_maps/graphql_maps/schema/server_api.graphql @@ -0,0 +1,155 @@ +fragment basicMutationReturnFields on MutationReturnInterface{ + code + message + success +} + +fragment basicApiJobsFields on ApiJob{ + createdAt + description + error + finishedAt + name + progress + result + status + statusText + uid + typeId + updatedAt +} + +query GetApiVersion { + api { + version + } +} + +query GetApiJobs { + jobs { + getJobs { + ...basicApiJobsFields + } + } +} + +mutation RemoveJob($jobId: String!) { + removeJob(jobId: $jobId) { + ...basicMutationReturnFields + } +} + +mutation RunSystemRebuild { + runSystemRebuild { + ...basicMutationReturnFields + } +} + +mutation RunSystemRollback { + runSystemRollback { + ...basicMutationReturnFields + } +} + +mutation RunSystemUpgrade { + runSystemUpgrade { + ...basicMutationReturnFields + } +} + +mutation PullRepositoryChanges { + pullRepositoryChanges { + ...basicMutationReturnFields + } +} + +mutation RebootSystem { + rebootSystem { + ...basicMutationReturnFields + } +} + +query SystemServerProvider { + system { + provider { + provider + } + } +} + +query SystemDnsProvider { + system { + domainInfo { + provider + } + } +} + +query GetApiTokens { + api { + devices { + creationDate + isCaller + name + } + } +} + +query RecoveryKey { + api { + recoveryKey { + creationDate + exists + expirationDate + usesLeft + valid + } + } +} + +mutation GetNewRecoveryApiKey($limits: RecoveryKeyLimitsInput) { + getNewRecoveryApiKey(limits: $limits) { + ...basicMutationReturnFields + key + } +} + +mutation UseRecoveryApiKey($input: UseRecoveryKeyInput!) { + useRecoveryApiKey(input: $input) { + ...basicMutationReturnFields + token + } +} + +mutation RefreshDeviceApiToken { + refreshDeviceApiToken { + ...basicMutationReturnFields + token + } +} + +mutation DeleteDeviceApiToken($device: String!) { + deleteDeviceApiToken(device: $device) { + ...basicMutationReturnFields + } +} + +mutation GetNewDeviceApiKey { + getNewDeviceApiKey { + ...basicMutationReturnFields + key + } +} + +mutation InvalidateNewDeviceApiKey { + invalidateNewDeviceApiKey { + ...basicMutationReturnFields + } +} + +mutation AuthorizeWithNewDeviceApiKey($input: UseNewDeviceKeyInput!) { + authorizeWithNewDeviceApiKey(input: $input) { + ...basicMutationReturnFields + token + } +} diff --git a/lib/logic/api_maps/graphql_maps/schema/server_api.graphql.dart b/lib/logic/api_maps/graphql_maps/schema/server_api.graphql.dart new file mode 100644 index 00000000..3fed1110 --- /dev/null +++ b/lib/logic/api_maps/graphql_maps/schema/server_api.graphql.dart @@ -0,0 +1,12596 @@ +import 'dart:async'; +import 'package:gql/ast.dart'; +import 'package:graphql/client.dart' as graphql; +import 'package:selfprivacy/utils/scalars.dart'; +import 'schema.graphql.dart'; + +class Fragment$basicMutationReturnFields { + Fragment$basicMutationReturnFields({ + required this.code, + required this.message, + required this.success, + required this.$__typename, + }); + + factory Fragment$basicMutationReturnFields.fromJson( + Map json) { + switch (json["__typename"] as String) { + case "ApiKeyMutationReturn": + return Fragment$basicMutationReturnFields$$ApiKeyMutationReturn + .fromJson(json); + + case "AutoUpgradeSettingsMutationReturn": + return Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn + .fromJson(json); + + case "DeviceApiTokenMutationReturn": + return Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn + .fromJson(json); + + case "GenericBackupConfigReturn": + return Fragment$basicMutationReturnFields$$GenericBackupConfigReturn + .fromJson(json); + + case "GenericJobMutationReturn": + return Fragment$basicMutationReturnFields$$GenericJobMutationReturn + .fromJson(json); + + case "GenericMutationReturn": + return Fragment$basicMutationReturnFields$$GenericMutationReturn + .fromJson(json); + + case "ServiceJobMutationReturn": + return Fragment$basicMutationReturnFields$$ServiceJobMutationReturn + .fromJson(json); + + case "ServiceMutationReturn": + return Fragment$basicMutationReturnFields$$ServiceMutationReturn + .fromJson(json); + + case "TimezoneMutationReturn": + return Fragment$basicMutationReturnFields$$TimezoneMutationReturn + .fromJson(json); + + case "UserMutationReturn": + return Fragment$basicMutationReturnFields$$UserMutationReturn.fromJson( + json); + + default: + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Fragment$basicMutationReturnFields( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Fragment$basicMutationReturnFields) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$basicMutationReturnFields + on Fragment$basicMutationReturnFields { + CopyWith$Fragment$basicMutationReturnFields< + Fragment$basicMutationReturnFields> + get copyWith => CopyWith$Fragment$basicMutationReturnFields( + this, + (i) => i, + ); + _T when<_T>({ + required _T Function( + Fragment$basicMutationReturnFields$$ApiKeyMutationReturn) + apiKeyMutationReturn, + required _T Function( + Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn) + autoUpgradeSettingsMutationReturn, + required _T Function( + Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn) + deviceApiTokenMutationReturn, + required _T Function( + Fragment$basicMutationReturnFields$$GenericBackupConfigReturn) + genericBackupConfigReturn, + required _T Function( + Fragment$basicMutationReturnFields$$GenericJobMutationReturn) + genericJobMutationReturn, + required _T Function( + Fragment$basicMutationReturnFields$$GenericMutationReturn) + genericMutationReturn, + required _T Function( + Fragment$basicMutationReturnFields$$ServiceJobMutationReturn) + serviceJobMutationReturn, + required _T Function( + Fragment$basicMutationReturnFields$$ServiceMutationReturn) + serviceMutationReturn, + required _T Function( + Fragment$basicMutationReturnFields$$TimezoneMutationReturn) + timezoneMutationReturn, + required _T Function(Fragment$basicMutationReturnFields$$UserMutationReturn) + userMutationReturn, + required _T Function() orElse, + }) { + switch ($__typename) { + case "ApiKeyMutationReturn": + return apiKeyMutationReturn( + this as Fragment$basicMutationReturnFields$$ApiKeyMutationReturn); + + case "AutoUpgradeSettingsMutationReturn": + return autoUpgradeSettingsMutationReturn(this + as Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn); + + case "DeviceApiTokenMutationReturn": + return deviceApiTokenMutationReturn(this + as Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn); + + case "GenericBackupConfigReturn": + return genericBackupConfigReturn(this + as Fragment$basicMutationReturnFields$$GenericBackupConfigReturn); + + case "GenericJobMutationReturn": + return genericJobMutationReturn(this + as Fragment$basicMutationReturnFields$$GenericJobMutationReturn); + + case "GenericMutationReturn": + return genericMutationReturn( + this as Fragment$basicMutationReturnFields$$GenericMutationReturn); + + case "ServiceJobMutationReturn": + return serviceJobMutationReturn(this + as Fragment$basicMutationReturnFields$$ServiceJobMutationReturn); + + case "ServiceMutationReturn": + return serviceMutationReturn( + this as Fragment$basicMutationReturnFields$$ServiceMutationReturn); + + case "TimezoneMutationReturn": + return timezoneMutationReturn( + this as Fragment$basicMutationReturnFields$$TimezoneMutationReturn); + + case "UserMutationReturn": + return userMutationReturn( + this as Fragment$basicMutationReturnFields$$UserMutationReturn); + + default: + return orElse(); + } + } + + _T maybeWhen<_T>({ + _T Function(Fragment$basicMutationReturnFields$$ApiKeyMutationReturn)? + apiKeyMutationReturn, + _T Function( + Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn)? + autoUpgradeSettingsMutationReturn, + _T Function( + Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn)? + deviceApiTokenMutationReturn, + _T Function(Fragment$basicMutationReturnFields$$GenericBackupConfigReturn)? + genericBackupConfigReturn, + _T Function(Fragment$basicMutationReturnFields$$GenericJobMutationReturn)? + genericJobMutationReturn, + _T Function(Fragment$basicMutationReturnFields$$GenericMutationReturn)? + genericMutationReturn, + _T Function(Fragment$basicMutationReturnFields$$ServiceJobMutationReturn)? + serviceJobMutationReturn, + _T Function(Fragment$basicMutationReturnFields$$ServiceMutationReturn)? + serviceMutationReturn, + _T Function(Fragment$basicMutationReturnFields$$TimezoneMutationReturn)? + timezoneMutationReturn, + _T Function(Fragment$basicMutationReturnFields$$UserMutationReturn)? + userMutationReturn, + required _T Function() orElse, + }) { + switch ($__typename) { + case "ApiKeyMutationReturn": + if (apiKeyMutationReturn != null) { + return apiKeyMutationReturn( + this as Fragment$basicMutationReturnFields$$ApiKeyMutationReturn); + } else { + return orElse(); + } + + case "AutoUpgradeSettingsMutationReturn": + if (autoUpgradeSettingsMutationReturn != null) { + return autoUpgradeSettingsMutationReturn(this + as Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn); + } else { + return orElse(); + } + + case "DeviceApiTokenMutationReturn": + if (deviceApiTokenMutationReturn != null) { + return deviceApiTokenMutationReturn(this + as Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn); + } else { + return orElse(); + } + + case "GenericBackupConfigReturn": + if (genericBackupConfigReturn != null) { + return genericBackupConfigReturn(this + as Fragment$basicMutationReturnFields$$GenericBackupConfigReturn); + } else { + return orElse(); + } + + case "GenericJobMutationReturn": + if (genericJobMutationReturn != null) { + return genericJobMutationReturn(this + as Fragment$basicMutationReturnFields$$GenericJobMutationReturn); + } else { + return orElse(); + } + + case "GenericMutationReturn": + if (genericMutationReturn != null) { + return genericMutationReturn(this + as Fragment$basicMutationReturnFields$$GenericMutationReturn); + } else { + return orElse(); + } + + case "ServiceJobMutationReturn": + if (serviceJobMutationReturn != null) { + return serviceJobMutationReturn(this + as Fragment$basicMutationReturnFields$$ServiceJobMutationReturn); + } else { + return orElse(); + } + + case "ServiceMutationReturn": + if (serviceMutationReturn != null) { + return serviceMutationReturn(this + as Fragment$basicMutationReturnFields$$ServiceMutationReturn); + } else { + return orElse(); + } + + case "TimezoneMutationReturn": + if (timezoneMutationReturn != null) { + return timezoneMutationReturn(this + as Fragment$basicMutationReturnFields$$TimezoneMutationReturn); + } else { + return orElse(); + } + + case "UserMutationReturn": + if (userMutationReturn != null) { + return userMutationReturn( + this as Fragment$basicMutationReturnFields$$UserMutationReturn); + } else { + return orElse(); + } + + default: + return orElse(); + } + } +} + +abstract class CopyWith$Fragment$basicMutationReturnFields { + factory CopyWith$Fragment$basicMutationReturnFields( + Fragment$basicMutationReturnFields instance, + TRes Function(Fragment$basicMutationReturnFields) then, + ) = _CopyWithImpl$Fragment$basicMutationReturnFields; + + factory CopyWith$Fragment$basicMutationReturnFields.stub(TRes res) = + _CopyWithStubImpl$Fragment$basicMutationReturnFields; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$basicMutationReturnFields + implements CopyWith$Fragment$basicMutationReturnFields { + _CopyWithImpl$Fragment$basicMutationReturnFields( + this._instance, + this._then, + ); + + final Fragment$basicMutationReturnFields _instance; + + final TRes Function(Fragment$basicMutationReturnFields) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$basicMutationReturnFields( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$basicMutationReturnFields + implements CopyWith$Fragment$basicMutationReturnFields { + _CopyWithStubImpl$Fragment$basicMutationReturnFields(this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +const fragmentDefinitionbasicMutationReturnFields = FragmentDefinitionNode( + name: NameNode(value: 'basicMutationReturnFields'), + typeCondition: TypeConditionNode( + on: NamedTypeNode( + name: NameNode(value: 'MutationReturnInterface'), + isNonNull: false, + )), + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'code'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'message'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'success'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), +); +const documentNodeFragmentbasicMutationReturnFields = + DocumentNode(definitions: [ + fragmentDefinitionbasicMutationReturnFields, +]); + +extension ClientExtension$Fragment$basicMutationReturnFields + on graphql.GraphQLClient { + void writeFragment$basicMutationReturnFields({ + required Fragment$basicMutationReturnFields data, + required Map idFields, + bool broadcast = true, + }) => + this.writeFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'basicMutationReturnFields', + document: documentNodeFragmentbasicMutationReturnFields, + ), + ), + data: data.toJson(), + broadcast: broadcast, + ); + Fragment$basicMutationReturnFields? readFragment$basicMutationReturnFields({ + required Map idFields, + bool optimistic = true, + }) { + final result = this.readFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'basicMutationReturnFields', + document: documentNodeFragmentbasicMutationReturnFields, + ), + ), + optimistic: optimistic, + ); + return result == null + ? null + : Fragment$basicMutationReturnFields.fromJson(result); + } +} + +class Fragment$basicMutationReturnFields$$ApiKeyMutationReturn + implements Fragment$basicMutationReturnFields { + Fragment$basicMutationReturnFields$$ApiKeyMutationReturn({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'ApiKeyMutationReturn', + }); + + factory Fragment$basicMutationReturnFields$$ApiKeyMutationReturn.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Fragment$basicMutationReturnFields$$ApiKeyMutationReturn( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Fragment$basicMutationReturnFields$$ApiKeyMutationReturn) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$basicMutationReturnFields$$ApiKeyMutationReturn + on Fragment$basicMutationReturnFields$$ApiKeyMutationReturn { + CopyWith$Fragment$basicMutationReturnFields$$ApiKeyMutationReturn< + Fragment$basicMutationReturnFields$$ApiKeyMutationReturn> + get copyWith => + CopyWith$Fragment$basicMutationReturnFields$$ApiKeyMutationReturn( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$basicMutationReturnFields$$ApiKeyMutationReturn< + TRes> { + factory CopyWith$Fragment$basicMutationReturnFields$$ApiKeyMutationReturn( + Fragment$basicMutationReturnFields$$ApiKeyMutationReturn instance, + TRes Function(Fragment$basicMutationReturnFields$$ApiKeyMutationReturn) + then, + ) = _CopyWithImpl$Fragment$basicMutationReturnFields$$ApiKeyMutationReturn; + + factory CopyWith$Fragment$basicMutationReturnFields$$ApiKeyMutationReturn.stub( + TRes res) = + _CopyWithStubImpl$Fragment$basicMutationReturnFields$$ApiKeyMutationReturn; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$basicMutationReturnFields$$ApiKeyMutationReturn< + TRes> + implements + CopyWith$Fragment$basicMutationReturnFields$$ApiKeyMutationReturn< + TRes> { + _CopyWithImpl$Fragment$basicMutationReturnFields$$ApiKeyMutationReturn( + this._instance, + this._then, + ); + + final Fragment$basicMutationReturnFields$$ApiKeyMutationReturn _instance; + + final TRes Function(Fragment$basicMutationReturnFields$$ApiKeyMutationReturn) + _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$basicMutationReturnFields$$ApiKeyMutationReturn( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$basicMutationReturnFields$$ApiKeyMutationReturn< + TRes> + implements + CopyWith$Fragment$basicMutationReturnFields$$ApiKeyMutationReturn< + TRes> { + _CopyWithStubImpl$Fragment$basicMutationReturnFields$$ApiKeyMutationReturn( + this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +class Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn + implements Fragment$basicMutationReturnFields { + Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'AutoUpgradeSettingsMutationReturn', + }); + + factory Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other + is Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn + on Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn { + CopyWith$Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn< + Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn> + get copyWith => + CopyWith$Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn< + TRes> { + factory CopyWith$Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn( + Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn + instance, + TRes Function( + Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn) + then, + ) = _CopyWithImpl$Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn; + + factory CopyWith$Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn.stub( + TRes res) = + _CopyWithStubImpl$Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn< + TRes> + implements + CopyWith$Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn< + TRes> { + _CopyWithImpl$Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn( + this._instance, + this._then, + ); + + final Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn + _instance; + + final TRes Function( + Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn) + _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then( + Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn< + TRes> + implements + CopyWith$Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn< + TRes> { + _CopyWithStubImpl$Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn( + this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +class Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn + implements Fragment$basicMutationReturnFields { + Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'DeviceApiTokenMutationReturn', + }); + + factory Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other + is Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn + on Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn { + CopyWith$Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn< + Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn> + get copyWith => + CopyWith$Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn< + TRes> { + factory CopyWith$Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn( + Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn instance, + TRes Function( + Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn) + then, + ) = _CopyWithImpl$Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn; + + factory CopyWith$Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn.stub( + TRes res) = + _CopyWithStubImpl$Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn< + TRes> + implements + CopyWith$Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn< + TRes> { + _CopyWithImpl$Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn( + this._instance, + this._then, + ); + + final Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn + _instance; + + final TRes Function( + Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn< + TRes> + implements + CopyWith$Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn< + TRes> { + _CopyWithStubImpl$Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn( + this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +class Fragment$basicMutationReturnFields$$GenericBackupConfigReturn + implements Fragment$basicMutationReturnFields { + Fragment$basicMutationReturnFields$$GenericBackupConfigReturn({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'GenericBackupConfigReturn', + }); + + factory Fragment$basicMutationReturnFields$$GenericBackupConfigReturn.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Fragment$basicMutationReturnFields$$GenericBackupConfigReturn( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other + is Fragment$basicMutationReturnFields$$GenericBackupConfigReturn) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$basicMutationReturnFields$$GenericBackupConfigReturn + on Fragment$basicMutationReturnFields$$GenericBackupConfigReturn { + CopyWith$Fragment$basicMutationReturnFields$$GenericBackupConfigReturn< + Fragment$basicMutationReturnFields$$GenericBackupConfigReturn> + get copyWith => + CopyWith$Fragment$basicMutationReturnFields$$GenericBackupConfigReturn( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$basicMutationReturnFields$$GenericBackupConfigReturn< + TRes> { + factory CopyWith$Fragment$basicMutationReturnFields$$GenericBackupConfigReturn( + Fragment$basicMutationReturnFields$$GenericBackupConfigReturn instance, + TRes Function(Fragment$basicMutationReturnFields$$GenericBackupConfigReturn) + then, + ) = _CopyWithImpl$Fragment$basicMutationReturnFields$$GenericBackupConfigReturn; + + factory CopyWith$Fragment$basicMutationReturnFields$$GenericBackupConfigReturn.stub( + TRes res) = + _CopyWithStubImpl$Fragment$basicMutationReturnFields$$GenericBackupConfigReturn; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$basicMutationReturnFields$$GenericBackupConfigReturn< + TRes> + implements + CopyWith$Fragment$basicMutationReturnFields$$GenericBackupConfigReturn< + TRes> { + _CopyWithImpl$Fragment$basicMutationReturnFields$$GenericBackupConfigReturn( + this._instance, + this._then, + ); + + final Fragment$basicMutationReturnFields$$GenericBackupConfigReturn _instance; + + final TRes Function( + Fragment$basicMutationReturnFields$$GenericBackupConfigReturn) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$basicMutationReturnFields$$GenericBackupConfigReturn( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$basicMutationReturnFields$$GenericBackupConfigReturn< + TRes> + implements + CopyWith$Fragment$basicMutationReturnFields$$GenericBackupConfigReturn< + TRes> { + _CopyWithStubImpl$Fragment$basicMutationReturnFields$$GenericBackupConfigReturn( + this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +class Fragment$basicMutationReturnFields$$GenericJobMutationReturn + implements Fragment$basicMutationReturnFields { + Fragment$basicMutationReturnFields$$GenericJobMutationReturn({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'GenericJobMutationReturn', + }); + + factory Fragment$basicMutationReturnFields$$GenericJobMutationReturn.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Fragment$basicMutationReturnFields$$GenericJobMutationReturn( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other + is Fragment$basicMutationReturnFields$$GenericJobMutationReturn) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$basicMutationReturnFields$$GenericJobMutationReturn + on Fragment$basicMutationReturnFields$$GenericJobMutationReturn { + CopyWith$Fragment$basicMutationReturnFields$$GenericJobMutationReturn< + Fragment$basicMutationReturnFields$$GenericJobMutationReturn> + get copyWith => + CopyWith$Fragment$basicMutationReturnFields$$GenericJobMutationReturn( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$basicMutationReturnFields$$GenericJobMutationReturn< + TRes> { + factory CopyWith$Fragment$basicMutationReturnFields$$GenericJobMutationReturn( + Fragment$basicMutationReturnFields$$GenericJobMutationReturn instance, + TRes Function(Fragment$basicMutationReturnFields$$GenericJobMutationReturn) + then, + ) = _CopyWithImpl$Fragment$basicMutationReturnFields$$GenericJobMutationReturn; + + factory CopyWith$Fragment$basicMutationReturnFields$$GenericJobMutationReturn.stub( + TRes res) = + _CopyWithStubImpl$Fragment$basicMutationReturnFields$$GenericJobMutationReturn; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$basicMutationReturnFields$$GenericJobMutationReturn< + TRes> + implements + CopyWith$Fragment$basicMutationReturnFields$$GenericJobMutationReturn< + TRes> { + _CopyWithImpl$Fragment$basicMutationReturnFields$$GenericJobMutationReturn( + this._instance, + this._then, + ); + + final Fragment$basicMutationReturnFields$$GenericJobMutationReturn _instance; + + final TRes Function( + Fragment$basicMutationReturnFields$$GenericJobMutationReturn) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$basicMutationReturnFields$$GenericJobMutationReturn( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$basicMutationReturnFields$$GenericJobMutationReturn< + TRes> + implements + CopyWith$Fragment$basicMutationReturnFields$$GenericJobMutationReturn< + TRes> { + _CopyWithStubImpl$Fragment$basicMutationReturnFields$$GenericJobMutationReturn( + this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +class Fragment$basicMutationReturnFields$$GenericMutationReturn + implements Fragment$basicMutationReturnFields { + Fragment$basicMutationReturnFields$$GenericMutationReturn({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'GenericMutationReturn', + }); + + factory Fragment$basicMutationReturnFields$$GenericMutationReturn.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Fragment$basicMutationReturnFields$$GenericMutationReturn( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Fragment$basicMutationReturnFields$$GenericMutationReturn) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$basicMutationReturnFields$$GenericMutationReturn + on Fragment$basicMutationReturnFields$$GenericMutationReturn { + CopyWith$Fragment$basicMutationReturnFields$$GenericMutationReturn< + Fragment$basicMutationReturnFields$$GenericMutationReturn> + get copyWith => + CopyWith$Fragment$basicMutationReturnFields$$GenericMutationReturn( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$basicMutationReturnFields$$GenericMutationReturn< + TRes> { + factory CopyWith$Fragment$basicMutationReturnFields$$GenericMutationReturn( + Fragment$basicMutationReturnFields$$GenericMutationReturn instance, + TRes Function(Fragment$basicMutationReturnFields$$GenericMutationReturn) + then, + ) = _CopyWithImpl$Fragment$basicMutationReturnFields$$GenericMutationReturn; + + factory CopyWith$Fragment$basicMutationReturnFields$$GenericMutationReturn.stub( + TRes res) = + _CopyWithStubImpl$Fragment$basicMutationReturnFields$$GenericMutationReturn; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$basicMutationReturnFields$$GenericMutationReturn< + TRes> + implements + CopyWith$Fragment$basicMutationReturnFields$$GenericMutationReturn< + TRes> { + _CopyWithImpl$Fragment$basicMutationReturnFields$$GenericMutationReturn( + this._instance, + this._then, + ); + + final Fragment$basicMutationReturnFields$$GenericMutationReturn _instance; + + final TRes Function(Fragment$basicMutationReturnFields$$GenericMutationReturn) + _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$basicMutationReturnFields$$GenericMutationReturn( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$basicMutationReturnFields$$GenericMutationReturn< + TRes> + implements + CopyWith$Fragment$basicMutationReturnFields$$GenericMutationReturn< + TRes> { + _CopyWithStubImpl$Fragment$basicMutationReturnFields$$GenericMutationReturn( + this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +class Fragment$basicMutationReturnFields$$ServiceJobMutationReturn + implements Fragment$basicMutationReturnFields { + Fragment$basicMutationReturnFields$$ServiceJobMutationReturn({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'ServiceJobMutationReturn', + }); + + factory Fragment$basicMutationReturnFields$$ServiceJobMutationReturn.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Fragment$basicMutationReturnFields$$ServiceJobMutationReturn( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other + is Fragment$basicMutationReturnFields$$ServiceJobMutationReturn) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$basicMutationReturnFields$$ServiceJobMutationReturn + on Fragment$basicMutationReturnFields$$ServiceJobMutationReturn { + CopyWith$Fragment$basicMutationReturnFields$$ServiceJobMutationReturn< + Fragment$basicMutationReturnFields$$ServiceJobMutationReturn> + get copyWith => + CopyWith$Fragment$basicMutationReturnFields$$ServiceJobMutationReturn( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$basicMutationReturnFields$$ServiceJobMutationReturn< + TRes> { + factory CopyWith$Fragment$basicMutationReturnFields$$ServiceJobMutationReturn( + Fragment$basicMutationReturnFields$$ServiceJobMutationReturn instance, + TRes Function(Fragment$basicMutationReturnFields$$ServiceJobMutationReturn) + then, + ) = _CopyWithImpl$Fragment$basicMutationReturnFields$$ServiceJobMutationReturn; + + factory CopyWith$Fragment$basicMutationReturnFields$$ServiceJobMutationReturn.stub( + TRes res) = + _CopyWithStubImpl$Fragment$basicMutationReturnFields$$ServiceJobMutationReturn; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$basicMutationReturnFields$$ServiceJobMutationReturn< + TRes> + implements + CopyWith$Fragment$basicMutationReturnFields$$ServiceJobMutationReturn< + TRes> { + _CopyWithImpl$Fragment$basicMutationReturnFields$$ServiceJobMutationReturn( + this._instance, + this._then, + ); + + final Fragment$basicMutationReturnFields$$ServiceJobMutationReturn _instance; + + final TRes Function( + Fragment$basicMutationReturnFields$$ServiceJobMutationReturn) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$basicMutationReturnFields$$ServiceJobMutationReturn( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$basicMutationReturnFields$$ServiceJobMutationReturn< + TRes> + implements + CopyWith$Fragment$basicMutationReturnFields$$ServiceJobMutationReturn< + TRes> { + _CopyWithStubImpl$Fragment$basicMutationReturnFields$$ServiceJobMutationReturn( + this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +class Fragment$basicMutationReturnFields$$ServiceMutationReturn + implements Fragment$basicMutationReturnFields { + Fragment$basicMutationReturnFields$$ServiceMutationReturn({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'ServiceMutationReturn', + }); + + factory Fragment$basicMutationReturnFields$$ServiceMutationReturn.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Fragment$basicMutationReturnFields$$ServiceMutationReturn( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Fragment$basicMutationReturnFields$$ServiceMutationReturn) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$basicMutationReturnFields$$ServiceMutationReturn + on Fragment$basicMutationReturnFields$$ServiceMutationReturn { + CopyWith$Fragment$basicMutationReturnFields$$ServiceMutationReturn< + Fragment$basicMutationReturnFields$$ServiceMutationReturn> + get copyWith => + CopyWith$Fragment$basicMutationReturnFields$$ServiceMutationReturn( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$basicMutationReturnFields$$ServiceMutationReturn< + TRes> { + factory CopyWith$Fragment$basicMutationReturnFields$$ServiceMutationReturn( + Fragment$basicMutationReturnFields$$ServiceMutationReturn instance, + TRes Function(Fragment$basicMutationReturnFields$$ServiceMutationReturn) + then, + ) = _CopyWithImpl$Fragment$basicMutationReturnFields$$ServiceMutationReturn; + + factory CopyWith$Fragment$basicMutationReturnFields$$ServiceMutationReturn.stub( + TRes res) = + _CopyWithStubImpl$Fragment$basicMutationReturnFields$$ServiceMutationReturn; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$basicMutationReturnFields$$ServiceMutationReturn< + TRes> + implements + CopyWith$Fragment$basicMutationReturnFields$$ServiceMutationReturn< + TRes> { + _CopyWithImpl$Fragment$basicMutationReturnFields$$ServiceMutationReturn( + this._instance, + this._then, + ); + + final Fragment$basicMutationReturnFields$$ServiceMutationReturn _instance; + + final TRes Function(Fragment$basicMutationReturnFields$$ServiceMutationReturn) + _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$basicMutationReturnFields$$ServiceMutationReturn( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$basicMutationReturnFields$$ServiceMutationReturn< + TRes> + implements + CopyWith$Fragment$basicMutationReturnFields$$ServiceMutationReturn< + TRes> { + _CopyWithStubImpl$Fragment$basicMutationReturnFields$$ServiceMutationReturn( + this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +class Fragment$basicMutationReturnFields$$TimezoneMutationReturn + implements Fragment$basicMutationReturnFields { + Fragment$basicMutationReturnFields$$TimezoneMutationReturn({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'TimezoneMutationReturn', + }); + + factory Fragment$basicMutationReturnFields$$TimezoneMutationReturn.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Fragment$basicMutationReturnFields$$TimezoneMutationReturn( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other + is Fragment$basicMutationReturnFields$$TimezoneMutationReturn) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$basicMutationReturnFields$$TimezoneMutationReturn + on Fragment$basicMutationReturnFields$$TimezoneMutationReturn { + CopyWith$Fragment$basicMutationReturnFields$$TimezoneMutationReturn< + Fragment$basicMutationReturnFields$$TimezoneMutationReturn> + get copyWith => + CopyWith$Fragment$basicMutationReturnFields$$TimezoneMutationReturn( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$basicMutationReturnFields$$TimezoneMutationReturn< + TRes> { + factory CopyWith$Fragment$basicMutationReturnFields$$TimezoneMutationReturn( + Fragment$basicMutationReturnFields$$TimezoneMutationReturn instance, + TRes Function(Fragment$basicMutationReturnFields$$TimezoneMutationReturn) + then, + ) = _CopyWithImpl$Fragment$basicMutationReturnFields$$TimezoneMutationReturn; + + factory CopyWith$Fragment$basicMutationReturnFields$$TimezoneMutationReturn.stub( + TRes res) = + _CopyWithStubImpl$Fragment$basicMutationReturnFields$$TimezoneMutationReturn; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$basicMutationReturnFields$$TimezoneMutationReturn< + TRes> + implements + CopyWith$Fragment$basicMutationReturnFields$$TimezoneMutationReturn< + TRes> { + _CopyWithImpl$Fragment$basicMutationReturnFields$$TimezoneMutationReturn( + this._instance, + this._then, + ); + + final Fragment$basicMutationReturnFields$$TimezoneMutationReturn _instance; + + final TRes Function( + Fragment$basicMutationReturnFields$$TimezoneMutationReturn) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$basicMutationReturnFields$$TimezoneMutationReturn( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$basicMutationReturnFields$$TimezoneMutationReturn< + TRes> + implements + CopyWith$Fragment$basicMutationReturnFields$$TimezoneMutationReturn< + TRes> { + _CopyWithStubImpl$Fragment$basicMutationReturnFields$$TimezoneMutationReturn( + this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +class Fragment$basicMutationReturnFields$$UserMutationReturn + implements Fragment$basicMutationReturnFields { + Fragment$basicMutationReturnFields$$UserMutationReturn({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'UserMutationReturn', + }); + + factory Fragment$basicMutationReturnFields$$UserMutationReturn.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Fragment$basicMutationReturnFields$$UserMutationReturn( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Fragment$basicMutationReturnFields$$UserMutationReturn) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$basicMutationReturnFields$$UserMutationReturn + on Fragment$basicMutationReturnFields$$UserMutationReturn { + CopyWith$Fragment$basicMutationReturnFields$$UserMutationReturn< + Fragment$basicMutationReturnFields$$UserMutationReturn> + get copyWith => + CopyWith$Fragment$basicMutationReturnFields$$UserMutationReturn( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$basicMutationReturnFields$$UserMutationReturn< + TRes> { + factory CopyWith$Fragment$basicMutationReturnFields$$UserMutationReturn( + Fragment$basicMutationReturnFields$$UserMutationReturn instance, + TRes Function(Fragment$basicMutationReturnFields$$UserMutationReturn) then, + ) = _CopyWithImpl$Fragment$basicMutationReturnFields$$UserMutationReturn; + + factory CopyWith$Fragment$basicMutationReturnFields$$UserMutationReturn.stub( + TRes res) = + _CopyWithStubImpl$Fragment$basicMutationReturnFields$$UserMutationReturn; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$basicMutationReturnFields$$UserMutationReturn + implements + CopyWith$Fragment$basicMutationReturnFields$$UserMutationReturn { + _CopyWithImpl$Fragment$basicMutationReturnFields$$UserMutationReturn( + this._instance, + this._then, + ); + + final Fragment$basicMutationReturnFields$$UserMutationReturn _instance; + + final TRes Function(Fragment$basicMutationReturnFields$$UserMutationReturn) + _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$basicMutationReturnFields$$UserMutationReturn( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$basicMutationReturnFields$$UserMutationReturn< + TRes> + implements + CopyWith$Fragment$basicMutationReturnFields$$UserMutationReturn { + _CopyWithStubImpl$Fragment$basicMutationReturnFields$$UserMutationReturn( + this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +class Fragment$basicApiJobsFields { + Fragment$basicApiJobsFields({ + required this.createdAt, + required this.description, + this.error, + this.finishedAt, + required this.name, + this.progress, + this.result, + required this.status, + this.statusText, + required this.uid, + required this.typeId, + required this.updatedAt, + this.$__typename = 'ApiJob', + }); + + factory Fragment$basicApiJobsFields.fromJson(Map json) { + final l$createdAt = json['createdAt']; + final l$description = json['description']; + final l$error = json['error']; + final l$finishedAt = json['finishedAt']; + final l$name = json['name']; + final l$progress = json['progress']; + final l$result = json['result']; + final l$status = json['status']; + final l$statusText = json['statusText']; + final l$uid = json['uid']; + final l$typeId = json['typeId']; + final l$updatedAt = json['updatedAt']; + final l$$__typename = json['__typename']; + return Fragment$basicApiJobsFields( + createdAt: dateTimeFromJson(l$createdAt), + description: (l$description as String), + error: (l$error as String?), + finishedAt: l$finishedAt == null ? null : dateTimeFromJson(l$finishedAt), + name: (l$name as String), + progress: (l$progress as int?), + result: (l$result as String?), + status: (l$status as String), + statusText: (l$statusText as String?), + uid: (l$uid as String), + typeId: (l$typeId as String), + updatedAt: dateTimeFromJson(l$updatedAt), + $__typename: (l$$__typename as String), + ); + } + + final DateTime createdAt; + + final String description; + + final String? error; + + final DateTime? finishedAt; + + final String name; + + final int? progress; + + final String? result; + + final String status; + + final String? statusText; + + final String uid; + + final String typeId; + + final DateTime updatedAt; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$createdAt = createdAt; + _resultData['createdAt'] = dateTimeToJson(l$createdAt); + final l$description = description; + _resultData['description'] = l$description; + final l$error = error; + _resultData['error'] = l$error; + final l$finishedAt = finishedAt; + _resultData['finishedAt'] = + l$finishedAt == null ? null : dateTimeToJson(l$finishedAt); + final l$name = name; + _resultData['name'] = l$name; + final l$progress = progress; + _resultData['progress'] = l$progress; + final l$result = result; + _resultData['result'] = l$result; + final l$status = status; + _resultData['status'] = l$status; + final l$statusText = statusText; + _resultData['statusText'] = l$statusText; + final l$uid = uid; + _resultData['uid'] = l$uid; + final l$typeId = typeId; + _resultData['typeId'] = l$typeId; + final l$updatedAt = updatedAt; + _resultData['updatedAt'] = dateTimeToJson(l$updatedAt); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$createdAt = createdAt; + final l$description = description; + final l$error = error; + final l$finishedAt = finishedAt; + final l$name = name; + final l$progress = progress; + final l$result = result; + final l$status = status; + final l$statusText = statusText; + final l$uid = uid; + final l$typeId = typeId; + final l$updatedAt = updatedAt; + final l$$__typename = $__typename; + return Object.hashAll([ + l$createdAt, + l$description, + l$error, + l$finishedAt, + l$name, + l$progress, + l$result, + l$status, + l$statusText, + l$uid, + l$typeId, + l$updatedAt, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Fragment$basicApiJobsFields) || + runtimeType != other.runtimeType) { + return false; + } + final l$createdAt = createdAt; + final lOther$createdAt = other.createdAt; + if (l$createdAt != lOther$createdAt) { + return false; + } + final l$description = description; + final lOther$description = other.description; + if (l$description != lOther$description) { + return false; + } + final l$error = error; + final lOther$error = other.error; + if (l$error != lOther$error) { + return false; + } + final l$finishedAt = finishedAt; + final lOther$finishedAt = other.finishedAt; + if (l$finishedAt != lOther$finishedAt) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$progress = progress; + final lOther$progress = other.progress; + if (l$progress != lOther$progress) { + return false; + } + final l$result = result; + final lOther$result = other.result; + if (l$result != lOther$result) { + return false; + } + final l$status = status; + final lOther$status = other.status; + if (l$status != lOther$status) { + return false; + } + final l$statusText = statusText; + final lOther$statusText = other.statusText; + if (l$statusText != lOther$statusText) { + return false; + } + final l$uid = uid; + final lOther$uid = other.uid; + if (l$uid != lOther$uid) { + return false; + } + final l$typeId = typeId; + final lOther$typeId = other.typeId; + if (l$typeId != lOther$typeId) { + return false; + } + final l$updatedAt = updatedAt; + final lOther$updatedAt = other.updatedAt; + if (l$updatedAt != lOther$updatedAt) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$basicApiJobsFields + on Fragment$basicApiJobsFields { + CopyWith$Fragment$basicApiJobsFields + get copyWith => CopyWith$Fragment$basicApiJobsFields( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$basicApiJobsFields { + factory CopyWith$Fragment$basicApiJobsFields( + Fragment$basicApiJobsFields instance, + TRes Function(Fragment$basicApiJobsFields) then, + ) = _CopyWithImpl$Fragment$basicApiJobsFields; + + factory CopyWith$Fragment$basicApiJobsFields.stub(TRes res) = + _CopyWithStubImpl$Fragment$basicApiJobsFields; + + TRes call({ + DateTime? createdAt, + String? description, + String? error, + DateTime? finishedAt, + String? name, + int? progress, + String? result, + String? status, + String? statusText, + String? uid, + String? typeId, + DateTime? updatedAt, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$basicApiJobsFields + implements CopyWith$Fragment$basicApiJobsFields { + _CopyWithImpl$Fragment$basicApiJobsFields( + this._instance, + this._then, + ); + + final Fragment$basicApiJobsFields _instance; + + final TRes Function(Fragment$basicApiJobsFields) _then; + + static const _undefined = {}; + + TRes call({ + Object? createdAt = _undefined, + Object? description = _undefined, + Object? error = _undefined, + Object? finishedAt = _undefined, + Object? name = _undefined, + Object? progress = _undefined, + Object? result = _undefined, + Object? status = _undefined, + Object? statusText = _undefined, + Object? uid = _undefined, + Object? typeId = _undefined, + Object? updatedAt = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$basicApiJobsFields( + createdAt: createdAt == _undefined || createdAt == null + ? _instance.createdAt + : (createdAt as DateTime), + description: description == _undefined || description == null + ? _instance.description + : (description as String), + error: error == _undefined ? _instance.error : (error as String?), + finishedAt: finishedAt == _undefined + ? _instance.finishedAt + : (finishedAt as DateTime?), + name: name == _undefined || name == null + ? _instance.name + : (name as String), + progress: + progress == _undefined ? _instance.progress : (progress as int?), + result: result == _undefined ? _instance.result : (result as String?), + status: status == _undefined || status == null + ? _instance.status + : (status as String), + statusText: statusText == _undefined + ? _instance.statusText + : (statusText as String?), + uid: uid == _undefined || uid == null ? _instance.uid : (uid as String), + typeId: typeId == _undefined || typeId == null + ? _instance.typeId + : (typeId as String), + updatedAt: updatedAt == _undefined || updatedAt == null + ? _instance.updatedAt + : (updatedAt as DateTime), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$basicApiJobsFields + implements CopyWith$Fragment$basicApiJobsFields { + _CopyWithStubImpl$Fragment$basicApiJobsFields(this._res); + + TRes _res; + + call({ + DateTime? createdAt, + String? description, + String? error, + DateTime? finishedAt, + String? name, + int? progress, + String? result, + String? status, + String? statusText, + String? uid, + String? typeId, + DateTime? updatedAt, + String? $__typename, + }) => + _res; +} + +const fragmentDefinitionbasicApiJobsFields = FragmentDefinitionNode( + name: NameNode(value: 'basicApiJobsFields'), + typeCondition: TypeConditionNode( + on: NamedTypeNode( + name: NameNode(value: 'ApiJob'), + isNonNull: false, + )), + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'createdAt'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'description'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'error'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'finishedAt'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'name'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'progress'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'result'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'status'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'statusText'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'uid'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'typeId'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'updatedAt'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), +); +const documentNodeFragmentbasicApiJobsFields = DocumentNode(definitions: [ + fragmentDefinitionbasicApiJobsFields, +]); + +extension ClientExtension$Fragment$basicApiJobsFields on graphql.GraphQLClient { + void writeFragment$basicApiJobsFields({ + required Fragment$basicApiJobsFields data, + required Map idFields, + bool broadcast = true, + }) => + this.writeFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'basicApiJobsFields', + document: documentNodeFragmentbasicApiJobsFields, + ), + ), + data: data.toJson(), + broadcast: broadcast, + ); + Fragment$basicApiJobsFields? readFragment$basicApiJobsFields({ + required Map idFields, + bool optimistic = true, + }) { + final result = this.readFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'basicApiJobsFields', + document: documentNodeFragmentbasicApiJobsFields, + ), + ), + optimistic: optimistic, + ); + return result == null ? null : Fragment$basicApiJobsFields.fromJson(result); + } +} + +class Query$GetApiVersion { + Query$GetApiVersion({ + required this.api, + this.$__typename = 'Query', + }); + + factory Query$GetApiVersion.fromJson(Map json) { + final l$api = json['api']; + final l$$__typename = json['__typename']; + return Query$GetApiVersion( + api: Query$GetApiVersion$api.fromJson((l$api as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Query$GetApiVersion$api api; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$api = api; + _resultData['api'] = l$api.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$api = api; + final l$$__typename = $__typename; + return Object.hashAll([ + l$api, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$GetApiVersion) || runtimeType != other.runtimeType) { + return false; + } + final l$api = api; + final lOther$api = other.api; + if (l$api != lOther$api) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$GetApiVersion on Query$GetApiVersion { + CopyWith$Query$GetApiVersion get copyWith => + CopyWith$Query$GetApiVersion( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$GetApiVersion { + factory CopyWith$Query$GetApiVersion( + Query$GetApiVersion instance, + TRes Function(Query$GetApiVersion) then, + ) = _CopyWithImpl$Query$GetApiVersion; + + factory CopyWith$Query$GetApiVersion.stub(TRes res) = + _CopyWithStubImpl$Query$GetApiVersion; + + TRes call({ + Query$GetApiVersion$api? api, + String? $__typename, + }); + CopyWith$Query$GetApiVersion$api get api; +} + +class _CopyWithImpl$Query$GetApiVersion + implements CopyWith$Query$GetApiVersion { + _CopyWithImpl$Query$GetApiVersion( + this._instance, + this._then, + ); + + final Query$GetApiVersion _instance; + + final TRes Function(Query$GetApiVersion) _then; + + static const _undefined = {}; + + TRes call({ + Object? api = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$GetApiVersion( + api: api == _undefined || api == null + ? _instance.api + : (api as Query$GetApiVersion$api), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$GetApiVersion$api get api { + final local$api = _instance.api; + return CopyWith$Query$GetApiVersion$api(local$api, (e) => call(api: e)); + } +} + +class _CopyWithStubImpl$Query$GetApiVersion + implements CopyWith$Query$GetApiVersion { + _CopyWithStubImpl$Query$GetApiVersion(this._res); + + TRes _res; + + call({ + Query$GetApiVersion$api? api, + String? $__typename, + }) => + _res; + CopyWith$Query$GetApiVersion$api get api => + CopyWith$Query$GetApiVersion$api.stub(_res); +} + +const documentNodeQueryGetApiVersion = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.query, + name: NameNode(value: 'GetApiVersion'), + variableDefinitions: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'api'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'version'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), +]); +Query$GetApiVersion _parserFn$Query$GetApiVersion(Map data) => + Query$GetApiVersion.fromJson(data); +typedef OnQueryComplete$Query$GetApiVersion = FutureOr Function( + Map?, + Query$GetApiVersion?, +); + +class Options$Query$GetApiVersion + extends graphql.QueryOptions { + Options$Query$GetApiVersion({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$GetApiVersion? typedOptimisticResult, + Duration? pollInterval, + graphql.Context? context, + OnQueryComplete$Query$GetApiVersion? onComplete, + graphql.OnQueryError? onError, + }) : onCompleteWithParsed = onComplete, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + pollInterval: pollInterval, + context: context, + onComplete: onComplete == null + ? null + : (data) => onComplete( + data, + data == null ? null : _parserFn$Query$GetApiVersion(data), + ), + onError: onError, + document: documentNodeQueryGetApiVersion, + parserFn: _parserFn$Query$GetApiVersion, + ); + + final OnQueryComplete$Query$GetApiVersion? onCompleteWithParsed; + + @override + List get properties => [ + ...super.onComplete == null + ? super.properties + : super.properties.where((property) => property != onComplete), + onCompleteWithParsed, + ]; +} + +class WatchOptions$Query$GetApiVersion + extends graphql.WatchQueryOptions { + WatchOptions$Query$GetApiVersion({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$GetApiVersion? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeQueryGetApiVersion, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Query$GetApiVersion, + ); +} + +class FetchMoreOptions$Query$GetApiVersion extends graphql.FetchMoreOptions { + FetchMoreOptions$Query$GetApiVersion( + {required graphql.UpdateQuery updateQuery}) + : super( + updateQuery: updateQuery, + document: documentNodeQueryGetApiVersion, + ); +} + +extension ClientExtension$Query$GetApiVersion on graphql.GraphQLClient { + Future> query$GetApiVersion( + [Options$Query$GetApiVersion? options]) async => + await this.query(options ?? Options$Query$GetApiVersion()); + graphql.ObservableQuery watchQuery$GetApiVersion( + [WatchOptions$Query$GetApiVersion? options]) => + this.watchQuery(options ?? WatchOptions$Query$GetApiVersion()); + void writeQuery$GetApiVersion({ + required Query$GetApiVersion data, + bool broadcast = true, + }) => + this.writeQuery( + graphql.Request( + operation: + graphql.Operation(document: documentNodeQueryGetApiVersion)), + data: data.toJson(), + broadcast: broadcast, + ); + Query$GetApiVersion? readQuery$GetApiVersion({bool optimistic = true}) { + final result = this.readQuery( + graphql.Request( + operation: + graphql.Operation(document: documentNodeQueryGetApiVersion)), + optimistic: optimistic, + ); + return result == null ? null : Query$GetApiVersion.fromJson(result); + } +} + +class Query$GetApiVersion$api { + Query$GetApiVersion$api({ + required this.version, + this.$__typename = 'Api', + }); + + factory Query$GetApiVersion$api.fromJson(Map json) { + final l$version = json['version']; + final l$$__typename = json['__typename']; + return Query$GetApiVersion$api( + version: (l$version as String), + $__typename: (l$$__typename as String), + ); + } + + final String version; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$version = version; + _resultData['version'] = l$version; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$version = version; + final l$$__typename = $__typename; + return Object.hashAll([ + l$version, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$GetApiVersion$api) || + runtimeType != other.runtimeType) { + return false; + } + final l$version = version; + final lOther$version = other.version; + if (l$version != lOther$version) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$GetApiVersion$api on Query$GetApiVersion$api { + CopyWith$Query$GetApiVersion$api get copyWith => + CopyWith$Query$GetApiVersion$api( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$GetApiVersion$api { + factory CopyWith$Query$GetApiVersion$api( + Query$GetApiVersion$api instance, + TRes Function(Query$GetApiVersion$api) then, + ) = _CopyWithImpl$Query$GetApiVersion$api; + + factory CopyWith$Query$GetApiVersion$api.stub(TRes res) = + _CopyWithStubImpl$Query$GetApiVersion$api; + + TRes call({ + String? version, + String? $__typename, + }); +} + +class _CopyWithImpl$Query$GetApiVersion$api + implements CopyWith$Query$GetApiVersion$api { + _CopyWithImpl$Query$GetApiVersion$api( + this._instance, + this._then, + ); + + final Query$GetApiVersion$api _instance; + + final TRes Function(Query$GetApiVersion$api) _then; + + static const _undefined = {}; + + TRes call({ + Object? version = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$GetApiVersion$api( + version: version == _undefined || version == null + ? _instance.version + : (version as String), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Query$GetApiVersion$api + implements CopyWith$Query$GetApiVersion$api { + _CopyWithStubImpl$Query$GetApiVersion$api(this._res); + + TRes _res; + + call({ + String? version, + String? $__typename, + }) => + _res; +} + +class Query$GetApiJobs { + Query$GetApiJobs({ + required this.jobs, + this.$__typename = 'Query', + }); + + factory Query$GetApiJobs.fromJson(Map json) { + final l$jobs = json['jobs']; + final l$$__typename = json['__typename']; + return Query$GetApiJobs( + jobs: Query$GetApiJobs$jobs.fromJson((l$jobs as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Query$GetApiJobs$jobs jobs; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$jobs = jobs; + _resultData['jobs'] = l$jobs.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$jobs = jobs; + final l$$__typename = $__typename; + return Object.hashAll([ + l$jobs, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$GetApiJobs) || runtimeType != other.runtimeType) { + return false; + } + final l$jobs = jobs; + final lOther$jobs = other.jobs; + if (l$jobs != lOther$jobs) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$GetApiJobs on Query$GetApiJobs { + CopyWith$Query$GetApiJobs get copyWith => + CopyWith$Query$GetApiJobs( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$GetApiJobs { + factory CopyWith$Query$GetApiJobs( + Query$GetApiJobs instance, + TRes Function(Query$GetApiJobs) then, + ) = _CopyWithImpl$Query$GetApiJobs; + + factory CopyWith$Query$GetApiJobs.stub(TRes res) = + _CopyWithStubImpl$Query$GetApiJobs; + + TRes call({ + Query$GetApiJobs$jobs? jobs, + String? $__typename, + }); + CopyWith$Query$GetApiJobs$jobs get jobs; +} + +class _CopyWithImpl$Query$GetApiJobs + implements CopyWith$Query$GetApiJobs { + _CopyWithImpl$Query$GetApiJobs( + this._instance, + this._then, + ); + + final Query$GetApiJobs _instance; + + final TRes Function(Query$GetApiJobs) _then; + + static const _undefined = {}; + + TRes call({ + Object? jobs = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$GetApiJobs( + jobs: jobs == _undefined || jobs == null + ? _instance.jobs + : (jobs as Query$GetApiJobs$jobs), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$GetApiJobs$jobs get jobs { + final local$jobs = _instance.jobs; + return CopyWith$Query$GetApiJobs$jobs(local$jobs, (e) => call(jobs: e)); + } +} + +class _CopyWithStubImpl$Query$GetApiJobs + implements CopyWith$Query$GetApiJobs { + _CopyWithStubImpl$Query$GetApiJobs(this._res); + + TRes _res; + + call({ + Query$GetApiJobs$jobs? jobs, + String? $__typename, + }) => + _res; + CopyWith$Query$GetApiJobs$jobs get jobs => + CopyWith$Query$GetApiJobs$jobs.stub(_res); +} + +const documentNodeQueryGetApiJobs = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.query, + name: NameNode(value: 'GetApiJobs'), + variableDefinitions: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'jobs'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'getJobs'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicApiJobsFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicApiJobsFields, +]); +Query$GetApiJobs _parserFn$Query$GetApiJobs(Map data) => + Query$GetApiJobs.fromJson(data); +typedef OnQueryComplete$Query$GetApiJobs = FutureOr Function( + Map?, + Query$GetApiJobs?, +); + +class Options$Query$GetApiJobs extends graphql.QueryOptions { + Options$Query$GetApiJobs({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$GetApiJobs? typedOptimisticResult, + Duration? pollInterval, + graphql.Context? context, + OnQueryComplete$Query$GetApiJobs? onComplete, + graphql.OnQueryError? onError, + }) : onCompleteWithParsed = onComplete, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + pollInterval: pollInterval, + context: context, + onComplete: onComplete == null + ? null + : (data) => onComplete( + data, + data == null ? null : _parserFn$Query$GetApiJobs(data), + ), + onError: onError, + document: documentNodeQueryGetApiJobs, + parserFn: _parserFn$Query$GetApiJobs, + ); + + final OnQueryComplete$Query$GetApiJobs? onCompleteWithParsed; + + @override + List get properties => [ + ...super.onComplete == null + ? super.properties + : super.properties.where((property) => property != onComplete), + onCompleteWithParsed, + ]; +} + +class WatchOptions$Query$GetApiJobs + extends graphql.WatchQueryOptions { + WatchOptions$Query$GetApiJobs({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$GetApiJobs? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeQueryGetApiJobs, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Query$GetApiJobs, + ); +} + +class FetchMoreOptions$Query$GetApiJobs extends graphql.FetchMoreOptions { + FetchMoreOptions$Query$GetApiJobs({required graphql.UpdateQuery updateQuery}) + : super( + updateQuery: updateQuery, + document: documentNodeQueryGetApiJobs, + ); +} + +extension ClientExtension$Query$GetApiJobs on graphql.GraphQLClient { + Future> query$GetApiJobs( + [Options$Query$GetApiJobs? options]) async => + await this.query(options ?? Options$Query$GetApiJobs()); + graphql.ObservableQuery watchQuery$GetApiJobs( + [WatchOptions$Query$GetApiJobs? options]) => + this.watchQuery(options ?? WatchOptions$Query$GetApiJobs()); + void writeQuery$GetApiJobs({ + required Query$GetApiJobs data, + bool broadcast = true, + }) => + this.writeQuery( + graphql.Request( + operation: + graphql.Operation(document: documentNodeQueryGetApiJobs)), + data: data.toJson(), + broadcast: broadcast, + ); + Query$GetApiJobs? readQuery$GetApiJobs({bool optimistic = true}) { + final result = this.readQuery( + graphql.Request( + operation: graphql.Operation(document: documentNodeQueryGetApiJobs)), + optimistic: optimistic, + ); + return result == null ? null : Query$GetApiJobs.fromJson(result); + } +} + +class Query$GetApiJobs$jobs { + Query$GetApiJobs$jobs({ + required this.getJobs, + this.$__typename = 'Job', + }); + + factory Query$GetApiJobs$jobs.fromJson(Map json) { + final l$getJobs = json['getJobs']; + final l$$__typename = json['__typename']; + return Query$GetApiJobs$jobs( + getJobs: (l$getJobs as List) + .map((e) => + Fragment$basicApiJobsFields.fromJson((e as Map))) + .toList(), + $__typename: (l$$__typename as String), + ); + } + + final List getJobs; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$getJobs = getJobs; + _resultData['getJobs'] = l$getJobs.map((e) => e.toJson()).toList(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$getJobs = getJobs; + final l$$__typename = $__typename; + return Object.hashAll([ + Object.hashAll(l$getJobs.map((v) => v)), + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$GetApiJobs$jobs) || runtimeType != other.runtimeType) { + return false; + } + final l$getJobs = getJobs; + final lOther$getJobs = other.getJobs; + if (l$getJobs.length != lOther$getJobs.length) { + return false; + } + for (int i = 0; i < l$getJobs.length; i++) { + final l$getJobs$entry = l$getJobs[i]; + final lOther$getJobs$entry = lOther$getJobs[i]; + if (l$getJobs$entry != lOther$getJobs$entry) { + return false; + } + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$GetApiJobs$jobs on Query$GetApiJobs$jobs { + CopyWith$Query$GetApiJobs$jobs get copyWith => + CopyWith$Query$GetApiJobs$jobs( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$GetApiJobs$jobs { + factory CopyWith$Query$GetApiJobs$jobs( + Query$GetApiJobs$jobs instance, + TRes Function(Query$GetApiJobs$jobs) then, + ) = _CopyWithImpl$Query$GetApiJobs$jobs; + + factory CopyWith$Query$GetApiJobs$jobs.stub(TRes res) = + _CopyWithStubImpl$Query$GetApiJobs$jobs; + + TRes call({ + List? getJobs, + String? $__typename, + }); + TRes getJobs( + Iterable Function( + Iterable< + CopyWith$Fragment$basicApiJobsFields< + Fragment$basicApiJobsFields>>) + _fn); +} + +class _CopyWithImpl$Query$GetApiJobs$jobs + implements CopyWith$Query$GetApiJobs$jobs { + _CopyWithImpl$Query$GetApiJobs$jobs( + this._instance, + this._then, + ); + + final Query$GetApiJobs$jobs _instance; + + final TRes Function(Query$GetApiJobs$jobs) _then; + + static const _undefined = {}; + + TRes call({ + Object? getJobs = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$GetApiJobs$jobs( + getJobs: getJobs == _undefined || getJobs == null + ? _instance.getJobs + : (getJobs as List), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + TRes getJobs( + Iterable Function( + Iterable< + CopyWith$Fragment$basicApiJobsFields< + Fragment$basicApiJobsFields>>) + _fn) => + call( + getJobs: _fn( + _instance.getJobs.map((e) => CopyWith$Fragment$basicApiJobsFields( + e, + (i) => i, + ))).toList()); +} + +class _CopyWithStubImpl$Query$GetApiJobs$jobs + implements CopyWith$Query$GetApiJobs$jobs { + _CopyWithStubImpl$Query$GetApiJobs$jobs(this._res); + + TRes _res; + + call({ + List? getJobs, + String? $__typename, + }) => + _res; + getJobs(_fn) => _res; +} + +class Variables$Mutation$RemoveJob { + factory Variables$Mutation$RemoveJob({required String jobId}) => + Variables$Mutation$RemoveJob._({ + r'jobId': jobId, + }); + + Variables$Mutation$RemoveJob._(this._$data); + + factory Variables$Mutation$RemoveJob.fromJson(Map data) { + final result$data = {}; + final l$jobId = data['jobId']; + result$data['jobId'] = (l$jobId as String); + return Variables$Mutation$RemoveJob._(result$data); + } + + Map _$data; + + String get jobId => (_$data['jobId'] as String); + Map toJson() { + final result$data = {}; + final l$jobId = jobId; + result$data['jobId'] = l$jobId; + return result$data; + } + + CopyWith$Variables$Mutation$RemoveJob + get copyWith => CopyWith$Variables$Mutation$RemoveJob( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$RemoveJob) || + runtimeType != other.runtimeType) { + return false; + } + final l$jobId = jobId; + final lOther$jobId = other.jobId; + if (l$jobId != lOther$jobId) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$jobId = jobId; + return Object.hashAll([l$jobId]); + } +} + +abstract class CopyWith$Variables$Mutation$RemoveJob { + factory CopyWith$Variables$Mutation$RemoveJob( + Variables$Mutation$RemoveJob instance, + TRes Function(Variables$Mutation$RemoveJob) then, + ) = _CopyWithImpl$Variables$Mutation$RemoveJob; + + factory CopyWith$Variables$Mutation$RemoveJob.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$RemoveJob; + + TRes call({String? jobId}); +} + +class _CopyWithImpl$Variables$Mutation$RemoveJob + implements CopyWith$Variables$Mutation$RemoveJob { + _CopyWithImpl$Variables$Mutation$RemoveJob( + this._instance, + this._then, + ); + + final Variables$Mutation$RemoveJob _instance; + + final TRes Function(Variables$Mutation$RemoveJob) _then; + + static const _undefined = {}; + + TRes call({Object? jobId = _undefined}) => + _then(Variables$Mutation$RemoveJob._({ + ..._instance._$data, + if (jobId != _undefined && jobId != null) 'jobId': (jobId as String), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$RemoveJob + implements CopyWith$Variables$Mutation$RemoveJob { + _CopyWithStubImpl$Variables$Mutation$RemoveJob(this._res); + + TRes _res; + + call({String? jobId}) => _res; +} + +class Mutation$RemoveJob { + Mutation$RemoveJob({ + required this.removeJob, + this.$__typename = 'Mutation', + }); + + factory Mutation$RemoveJob.fromJson(Map json) { + final l$removeJob = json['removeJob']; + final l$$__typename = json['__typename']; + return Mutation$RemoveJob( + removeJob: Mutation$RemoveJob$removeJob.fromJson( + (l$removeJob as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$RemoveJob$removeJob removeJob; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$removeJob = removeJob; + _resultData['removeJob'] = l$removeJob.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$removeJob = removeJob; + final l$$__typename = $__typename; + return Object.hashAll([ + l$removeJob, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$RemoveJob) || runtimeType != other.runtimeType) { + return false; + } + final l$removeJob = removeJob; + final lOther$removeJob = other.removeJob; + if (l$removeJob != lOther$removeJob) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$RemoveJob on Mutation$RemoveJob { + CopyWith$Mutation$RemoveJob get copyWith => + CopyWith$Mutation$RemoveJob( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$RemoveJob { + factory CopyWith$Mutation$RemoveJob( + Mutation$RemoveJob instance, + TRes Function(Mutation$RemoveJob) then, + ) = _CopyWithImpl$Mutation$RemoveJob; + + factory CopyWith$Mutation$RemoveJob.stub(TRes res) = + _CopyWithStubImpl$Mutation$RemoveJob; + + TRes call({ + Mutation$RemoveJob$removeJob? removeJob, + String? $__typename, + }); + CopyWith$Mutation$RemoveJob$removeJob get removeJob; +} + +class _CopyWithImpl$Mutation$RemoveJob + implements CopyWith$Mutation$RemoveJob { + _CopyWithImpl$Mutation$RemoveJob( + this._instance, + this._then, + ); + + final Mutation$RemoveJob _instance; + + final TRes Function(Mutation$RemoveJob) _then; + + static const _undefined = {}; + + TRes call({ + Object? removeJob = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$RemoveJob( + removeJob: removeJob == _undefined || removeJob == null + ? _instance.removeJob + : (removeJob as Mutation$RemoveJob$removeJob), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$RemoveJob$removeJob get removeJob { + final local$removeJob = _instance.removeJob; + return CopyWith$Mutation$RemoveJob$removeJob( + local$removeJob, (e) => call(removeJob: e)); + } +} + +class _CopyWithStubImpl$Mutation$RemoveJob + implements CopyWith$Mutation$RemoveJob { + _CopyWithStubImpl$Mutation$RemoveJob(this._res); + + TRes _res; + + call({ + Mutation$RemoveJob$removeJob? removeJob, + String? $__typename, + }) => + _res; + CopyWith$Mutation$RemoveJob$removeJob get removeJob => + CopyWith$Mutation$RemoveJob$removeJob.stub(_res); +} + +const documentNodeMutationRemoveJob = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'RemoveJob'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'jobId')), + type: NamedTypeNode( + name: NameNode(value: 'String'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'removeJob'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'jobId'), + value: VariableNode(name: NameNode(value: 'jobId')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, +]); +Mutation$RemoveJob _parserFn$Mutation$RemoveJob(Map data) => + Mutation$RemoveJob.fromJson(data); +typedef OnMutationCompleted$Mutation$RemoveJob = FutureOr Function( + Map?, + Mutation$RemoveJob?, +); + +class Options$Mutation$RemoveJob + extends graphql.MutationOptions { + Options$Mutation$RemoveJob({ + String? operationName, + required Variables$Mutation$RemoveJob variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RemoveJob? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$RemoveJob? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null ? null : _parserFn$Mutation$RemoveJob(data), + ), + update: update, + onError: onError, + document: documentNodeMutationRemoveJob, + parserFn: _parserFn$Mutation$RemoveJob, + ); + + final OnMutationCompleted$Mutation$RemoveJob? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$RemoveJob + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$RemoveJob({ + String? operationName, + required Variables$Mutation$RemoveJob variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RemoveJob? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationRemoveJob, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$RemoveJob, + ); +} + +extension ClientExtension$Mutation$RemoveJob on graphql.GraphQLClient { + Future> mutate$RemoveJob( + Options$Mutation$RemoveJob options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$RemoveJob( + WatchOptions$Mutation$RemoveJob options) => + this.watchMutation(options); +} + +class Mutation$RemoveJob$removeJob + implements Fragment$basicMutationReturnFields$$GenericMutationReturn { + Mutation$RemoveJob$removeJob({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'GenericMutationReturn', + }); + + factory Mutation$RemoveJob$removeJob.fromJson(Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Mutation$RemoveJob$removeJob( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$RemoveJob$removeJob) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$RemoveJob$removeJob + on Mutation$RemoveJob$removeJob { + CopyWith$Mutation$RemoveJob$removeJob + get copyWith => CopyWith$Mutation$RemoveJob$removeJob( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$RemoveJob$removeJob { + factory CopyWith$Mutation$RemoveJob$removeJob( + Mutation$RemoveJob$removeJob instance, + TRes Function(Mutation$RemoveJob$removeJob) then, + ) = _CopyWithImpl$Mutation$RemoveJob$removeJob; + + factory CopyWith$Mutation$RemoveJob$removeJob.stub(TRes res) = + _CopyWithStubImpl$Mutation$RemoveJob$removeJob; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$RemoveJob$removeJob + implements CopyWith$Mutation$RemoveJob$removeJob { + _CopyWithImpl$Mutation$RemoveJob$removeJob( + this._instance, + this._then, + ); + + final Mutation$RemoveJob$removeJob _instance; + + final TRes Function(Mutation$RemoveJob$removeJob) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$RemoveJob$removeJob( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$RemoveJob$removeJob + implements CopyWith$Mutation$RemoveJob$removeJob { + _CopyWithStubImpl$Mutation$RemoveJob$removeJob(this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +class Mutation$RunSystemRebuild { + Mutation$RunSystemRebuild({ + required this.runSystemRebuild, + this.$__typename = 'Mutation', + }); + + factory Mutation$RunSystemRebuild.fromJson(Map json) { + final l$runSystemRebuild = json['runSystemRebuild']; + final l$$__typename = json['__typename']; + return Mutation$RunSystemRebuild( + runSystemRebuild: Mutation$RunSystemRebuild$runSystemRebuild.fromJson( + (l$runSystemRebuild as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$RunSystemRebuild$runSystemRebuild runSystemRebuild; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$runSystemRebuild = runSystemRebuild; + _resultData['runSystemRebuild'] = l$runSystemRebuild.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$runSystemRebuild = runSystemRebuild; + final l$$__typename = $__typename; + return Object.hashAll([ + l$runSystemRebuild, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$RunSystemRebuild) || + runtimeType != other.runtimeType) { + return false; + } + final l$runSystemRebuild = runSystemRebuild; + final lOther$runSystemRebuild = other.runSystemRebuild; + if (l$runSystemRebuild != lOther$runSystemRebuild) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$RunSystemRebuild + on Mutation$RunSystemRebuild { + CopyWith$Mutation$RunSystemRebuild get copyWith => + CopyWith$Mutation$RunSystemRebuild( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$RunSystemRebuild { + factory CopyWith$Mutation$RunSystemRebuild( + Mutation$RunSystemRebuild instance, + TRes Function(Mutation$RunSystemRebuild) then, + ) = _CopyWithImpl$Mutation$RunSystemRebuild; + + factory CopyWith$Mutation$RunSystemRebuild.stub(TRes res) = + _CopyWithStubImpl$Mutation$RunSystemRebuild; + + TRes call({ + Mutation$RunSystemRebuild$runSystemRebuild? runSystemRebuild, + String? $__typename, + }); + CopyWith$Mutation$RunSystemRebuild$runSystemRebuild + get runSystemRebuild; +} + +class _CopyWithImpl$Mutation$RunSystemRebuild + implements CopyWith$Mutation$RunSystemRebuild { + _CopyWithImpl$Mutation$RunSystemRebuild( + this._instance, + this._then, + ); + + final Mutation$RunSystemRebuild _instance; + + final TRes Function(Mutation$RunSystemRebuild) _then; + + static const _undefined = {}; + + TRes call({ + Object? runSystemRebuild = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$RunSystemRebuild( + runSystemRebuild: runSystemRebuild == _undefined || + runSystemRebuild == null + ? _instance.runSystemRebuild + : (runSystemRebuild as Mutation$RunSystemRebuild$runSystemRebuild), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$RunSystemRebuild$runSystemRebuild + get runSystemRebuild { + final local$runSystemRebuild = _instance.runSystemRebuild; + return CopyWith$Mutation$RunSystemRebuild$runSystemRebuild( + local$runSystemRebuild, (e) => call(runSystemRebuild: e)); + } +} + +class _CopyWithStubImpl$Mutation$RunSystemRebuild + implements CopyWith$Mutation$RunSystemRebuild { + _CopyWithStubImpl$Mutation$RunSystemRebuild(this._res); + + TRes _res; + + call({ + Mutation$RunSystemRebuild$runSystemRebuild? runSystemRebuild, + String? $__typename, + }) => + _res; + CopyWith$Mutation$RunSystemRebuild$runSystemRebuild + get runSystemRebuild => + CopyWith$Mutation$RunSystemRebuild$runSystemRebuild.stub(_res); +} + +const documentNodeMutationRunSystemRebuild = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'RunSystemRebuild'), + variableDefinitions: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'runSystemRebuild'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, +]); +Mutation$RunSystemRebuild _parserFn$Mutation$RunSystemRebuild( + Map data) => + Mutation$RunSystemRebuild.fromJson(data); +typedef OnMutationCompleted$Mutation$RunSystemRebuild = FutureOr Function( + Map?, + Mutation$RunSystemRebuild?, +); + +class Options$Mutation$RunSystemRebuild + extends graphql.MutationOptions { + Options$Mutation$RunSystemRebuild({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RunSystemRebuild? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$RunSystemRebuild? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$RunSystemRebuild(data), + ), + update: update, + onError: onError, + document: documentNodeMutationRunSystemRebuild, + parserFn: _parserFn$Mutation$RunSystemRebuild, + ); + + final OnMutationCompleted$Mutation$RunSystemRebuild? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$RunSystemRebuild + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$RunSystemRebuild({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RunSystemRebuild? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationRunSystemRebuild, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$RunSystemRebuild, + ); +} + +extension ClientExtension$Mutation$RunSystemRebuild on graphql.GraphQLClient { + Future> + mutate$RunSystemRebuild( + [Options$Mutation$RunSystemRebuild? options]) async => + await this.mutate(options ?? Options$Mutation$RunSystemRebuild()); + graphql.ObservableQuery< + Mutation$RunSystemRebuild> watchMutation$RunSystemRebuild( + [WatchOptions$Mutation$RunSystemRebuild? options]) => + this.watchMutation(options ?? WatchOptions$Mutation$RunSystemRebuild()); +} + +class Mutation$RunSystemRebuild$runSystemRebuild + implements Fragment$basicMutationReturnFields$$GenericMutationReturn { + Mutation$RunSystemRebuild$runSystemRebuild({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'GenericMutationReturn', + }); + + factory Mutation$RunSystemRebuild$runSystemRebuild.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Mutation$RunSystemRebuild$runSystemRebuild( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$RunSystemRebuild$runSystemRebuild) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$RunSystemRebuild$runSystemRebuild + on Mutation$RunSystemRebuild$runSystemRebuild { + CopyWith$Mutation$RunSystemRebuild$runSystemRebuild< + Mutation$RunSystemRebuild$runSystemRebuild> + get copyWith => CopyWith$Mutation$RunSystemRebuild$runSystemRebuild( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$RunSystemRebuild$runSystemRebuild { + factory CopyWith$Mutation$RunSystemRebuild$runSystemRebuild( + Mutation$RunSystemRebuild$runSystemRebuild instance, + TRes Function(Mutation$RunSystemRebuild$runSystemRebuild) then, + ) = _CopyWithImpl$Mutation$RunSystemRebuild$runSystemRebuild; + + factory CopyWith$Mutation$RunSystemRebuild$runSystemRebuild.stub(TRes res) = + _CopyWithStubImpl$Mutation$RunSystemRebuild$runSystemRebuild; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$RunSystemRebuild$runSystemRebuild + implements CopyWith$Mutation$RunSystemRebuild$runSystemRebuild { + _CopyWithImpl$Mutation$RunSystemRebuild$runSystemRebuild( + this._instance, + this._then, + ); + + final Mutation$RunSystemRebuild$runSystemRebuild _instance; + + final TRes Function(Mutation$RunSystemRebuild$runSystemRebuild) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$RunSystemRebuild$runSystemRebuild( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$RunSystemRebuild$runSystemRebuild + implements CopyWith$Mutation$RunSystemRebuild$runSystemRebuild { + _CopyWithStubImpl$Mutation$RunSystemRebuild$runSystemRebuild(this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +class Mutation$RunSystemRollback { + Mutation$RunSystemRollback({ + required this.runSystemRollback, + this.$__typename = 'Mutation', + }); + + factory Mutation$RunSystemRollback.fromJson(Map json) { + final l$runSystemRollback = json['runSystemRollback']; + final l$$__typename = json['__typename']; + return Mutation$RunSystemRollback( + runSystemRollback: Mutation$RunSystemRollback$runSystemRollback.fromJson( + (l$runSystemRollback as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$RunSystemRollback$runSystemRollback runSystemRollback; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$runSystemRollback = runSystemRollback; + _resultData['runSystemRollback'] = l$runSystemRollback.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$runSystemRollback = runSystemRollback; + final l$$__typename = $__typename; + return Object.hashAll([ + l$runSystemRollback, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$RunSystemRollback) || + runtimeType != other.runtimeType) { + return false; + } + final l$runSystemRollback = runSystemRollback; + final lOther$runSystemRollback = other.runSystemRollback; + if (l$runSystemRollback != lOther$runSystemRollback) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$RunSystemRollback + on Mutation$RunSystemRollback { + CopyWith$Mutation$RunSystemRollback + get copyWith => CopyWith$Mutation$RunSystemRollback( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$RunSystemRollback { + factory CopyWith$Mutation$RunSystemRollback( + Mutation$RunSystemRollback instance, + TRes Function(Mutation$RunSystemRollback) then, + ) = _CopyWithImpl$Mutation$RunSystemRollback; + + factory CopyWith$Mutation$RunSystemRollback.stub(TRes res) = + _CopyWithStubImpl$Mutation$RunSystemRollback; + + TRes call({ + Mutation$RunSystemRollback$runSystemRollback? runSystemRollback, + String? $__typename, + }); + CopyWith$Mutation$RunSystemRollback$runSystemRollback + get runSystemRollback; +} + +class _CopyWithImpl$Mutation$RunSystemRollback + implements CopyWith$Mutation$RunSystemRollback { + _CopyWithImpl$Mutation$RunSystemRollback( + this._instance, + this._then, + ); + + final Mutation$RunSystemRollback _instance; + + final TRes Function(Mutation$RunSystemRollback) _then; + + static const _undefined = {}; + + TRes call({ + Object? runSystemRollback = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$RunSystemRollback( + runSystemRollback: + runSystemRollback == _undefined || runSystemRollback == null + ? _instance.runSystemRollback + : (runSystemRollback + as Mutation$RunSystemRollback$runSystemRollback), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$RunSystemRollback$runSystemRollback + get runSystemRollback { + final local$runSystemRollback = _instance.runSystemRollback; + return CopyWith$Mutation$RunSystemRollback$runSystemRollback( + local$runSystemRollback, (e) => call(runSystemRollback: e)); + } +} + +class _CopyWithStubImpl$Mutation$RunSystemRollback + implements CopyWith$Mutation$RunSystemRollback { + _CopyWithStubImpl$Mutation$RunSystemRollback(this._res); + + TRes _res; + + call({ + Mutation$RunSystemRollback$runSystemRollback? runSystemRollback, + String? $__typename, + }) => + _res; + CopyWith$Mutation$RunSystemRollback$runSystemRollback + get runSystemRollback => + CopyWith$Mutation$RunSystemRollback$runSystemRollback.stub(_res); +} + +const documentNodeMutationRunSystemRollback = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'RunSystemRollback'), + variableDefinitions: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'runSystemRollback'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, +]); +Mutation$RunSystemRollback _parserFn$Mutation$RunSystemRollback( + Map data) => + Mutation$RunSystemRollback.fromJson(data); +typedef OnMutationCompleted$Mutation$RunSystemRollback = FutureOr + Function( + Map?, + Mutation$RunSystemRollback?, +); + +class Options$Mutation$RunSystemRollback + extends graphql.MutationOptions { + Options$Mutation$RunSystemRollback({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RunSystemRollback? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$RunSystemRollback? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$RunSystemRollback(data), + ), + update: update, + onError: onError, + document: documentNodeMutationRunSystemRollback, + parserFn: _parserFn$Mutation$RunSystemRollback, + ); + + final OnMutationCompleted$Mutation$RunSystemRollback? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$RunSystemRollback + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$RunSystemRollback({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RunSystemRollback? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationRunSystemRollback, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$RunSystemRollback, + ); +} + +extension ClientExtension$Mutation$RunSystemRollback on graphql.GraphQLClient { + Future> + mutate$RunSystemRollback( + [Options$Mutation$RunSystemRollback? options]) async => + await this.mutate(options ?? Options$Mutation$RunSystemRollback()); + graphql.ObservableQuery< + Mutation$RunSystemRollback> watchMutation$RunSystemRollback( + [WatchOptions$Mutation$RunSystemRollback? options]) => + this.watchMutation(options ?? WatchOptions$Mutation$RunSystemRollback()); +} + +class Mutation$RunSystemRollback$runSystemRollback + implements Fragment$basicMutationReturnFields$$GenericMutationReturn { + Mutation$RunSystemRollback$runSystemRollback({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'GenericMutationReturn', + }); + + factory Mutation$RunSystemRollback$runSystemRollback.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Mutation$RunSystemRollback$runSystemRollback( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$RunSystemRollback$runSystemRollback) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$RunSystemRollback$runSystemRollback + on Mutation$RunSystemRollback$runSystemRollback { + CopyWith$Mutation$RunSystemRollback$runSystemRollback< + Mutation$RunSystemRollback$runSystemRollback> + get copyWith => CopyWith$Mutation$RunSystemRollback$runSystemRollback( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$RunSystemRollback$runSystemRollback { + factory CopyWith$Mutation$RunSystemRollback$runSystemRollback( + Mutation$RunSystemRollback$runSystemRollback instance, + TRes Function(Mutation$RunSystemRollback$runSystemRollback) then, + ) = _CopyWithImpl$Mutation$RunSystemRollback$runSystemRollback; + + factory CopyWith$Mutation$RunSystemRollback$runSystemRollback.stub(TRes res) = + _CopyWithStubImpl$Mutation$RunSystemRollback$runSystemRollback; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$RunSystemRollback$runSystemRollback + implements CopyWith$Mutation$RunSystemRollback$runSystemRollback { + _CopyWithImpl$Mutation$RunSystemRollback$runSystemRollback( + this._instance, + this._then, + ); + + final Mutation$RunSystemRollback$runSystemRollback _instance; + + final TRes Function(Mutation$RunSystemRollback$runSystemRollback) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$RunSystemRollback$runSystemRollback( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$RunSystemRollback$runSystemRollback + implements CopyWith$Mutation$RunSystemRollback$runSystemRollback { + _CopyWithStubImpl$Mutation$RunSystemRollback$runSystemRollback(this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +class Mutation$RunSystemUpgrade { + Mutation$RunSystemUpgrade({ + required this.runSystemUpgrade, + this.$__typename = 'Mutation', + }); + + factory Mutation$RunSystemUpgrade.fromJson(Map json) { + final l$runSystemUpgrade = json['runSystemUpgrade']; + final l$$__typename = json['__typename']; + return Mutation$RunSystemUpgrade( + runSystemUpgrade: Mutation$RunSystemUpgrade$runSystemUpgrade.fromJson( + (l$runSystemUpgrade as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$RunSystemUpgrade$runSystemUpgrade runSystemUpgrade; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$runSystemUpgrade = runSystemUpgrade; + _resultData['runSystemUpgrade'] = l$runSystemUpgrade.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$runSystemUpgrade = runSystemUpgrade; + final l$$__typename = $__typename; + return Object.hashAll([ + l$runSystemUpgrade, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$RunSystemUpgrade) || + runtimeType != other.runtimeType) { + return false; + } + final l$runSystemUpgrade = runSystemUpgrade; + final lOther$runSystemUpgrade = other.runSystemUpgrade; + if (l$runSystemUpgrade != lOther$runSystemUpgrade) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$RunSystemUpgrade + on Mutation$RunSystemUpgrade { + CopyWith$Mutation$RunSystemUpgrade get copyWith => + CopyWith$Mutation$RunSystemUpgrade( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$RunSystemUpgrade { + factory CopyWith$Mutation$RunSystemUpgrade( + Mutation$RunSystemUpgrade instance, + TRes Function(Mutation$RunSystemUpgrade) then, + ) = _CopyWithImpl$Mutation$RunSystemUpgrade; + + factory CopyWith$Mutation$RunSystemUpgrade.stub(TRes res) = + _CopyWithStubImpl$Mutation$RunSystemUpgrade; + + TRes call({ + Mutation$RunSystemUpgrade$runSystemUpgrade? runSystemUpgrade, + String? $__typename, + }); + CopyWith$Mutation$RunSystemUpgrade$runSystemUpgrade + get runSystemUpgrade; +} + +class _CopyWithImpl$Mutation$RunSystemUpgrade + implements CopyWith$Mutation$RunSystemUpgrade { + _CopyWithImpl$Mutation$RunSystemUpgrade( + this._instance, + this._then, + ); + + final Mutation$RunSystemUpgrade _instance; + + final TRes Function(Mutation$RunSystemUpgrade) _then; + + static const _undefined = {}; + + TRes call({ + Object? runSystemUpgrade = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$RunSystemUpgrade( + runSystemUpgrade: runSystemUpgrade == _undefined || + runSystemUpgrade == null + ? _instance.runSystemUpgrade + : (runSystemUpgrade as Mutation$RunSystemUpgrade$runSystemUpgrade), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$RunSystemUpgrade$runSystemUpgrade + get runSystemUpgrade { + final local$runSystemUpgrade = _instance.runSystemUpgrade; + return CopyWith$Mutation$RunSystemUpgrade$runSystemUpgrade( + local$runSystemUpgrade, (e) => call(runSystemUpgrade: e)); + } +} + +class _CopyWithStubImpl$Mutation$RunSystemUpgrade + implements CopyWith$Mutation$RunSystemUpgrade { + _CopyWithStubImpl$Mutation$RunSystemUpgrade(this._res); + + TRes _res; + + call({ + Mutation$RunSystemUpgrade$runSystemUpgrade? runSystemUpgrade, + String? $__typename, + }) => + _res; + CopyWith$Mutation$RunSystemUpgrade$runSystemUpgrade + get runSystemUpgrade => + CopyWith$Mutation$RunSystemUpgrade$runSystemUpgrade.stub(_res); +} + +const documentNodeMutationRunSystemUpgrade = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'RunSystemUpgrade'), + variableDefinitions: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'runSystemUpgrade'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, +]); +Mutation$RunSystemUpgrade _parserFn$Mutation$RunSystemUpgrade( + Map data) => + Mutation$RunSystemUpgrade.fromJson(data); +typedef OnMutationCompleted$Mutation$RunSystemUpgrade = FutureOr Function( + Map?, + Mutation$RunSystemUpgrade?, +); + +class Options$Mutation$RunSystemUpgrade + extends graphql.MutationOptions { + Options$Mutation$RunSystemUpgrade({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RunSystemUpgrade? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$RunSystemUpgrade? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$RunSystemUpgrade(data), + ), + update: update, + onError: onError, + document: documentNodeMutationRunSystemUpgrade, + parserFn: _parserFn$Mutation$RunSystemUpgrade, + ); + + final OnMutationCompleted$Mutation$RunSystemUpgrade? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$RunSystemUpgrade + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$RunSystemUpgrade({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RunSystemUpgrade? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationRunSystemUpgrade, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$RunSystemUpgrade, + ); +} + +extension ClientExtension$Mutation$RunSystemUpgrade on graphql.GraphQLClient { + Future> + mutate$RunSystemUpgrade( + [Options$Mutation$RunSystemUpgrade? options]) async => + await this.mutate(options ?? Options$Mutation$RunSystemUpgrade()); + graphql.ObservableQuery< + Mutation$RunSystemUpgrade> watchMutation$RunSystemUpgrade( + [WatchOptions$Mutation$RunSystemUpgrade? options]) => + this.watchMutation(options ?? WatchOptions$Mutation$RunSystemUpgrade()); +} + +class Mutation$RunSystemUpgrade$runSystemUpgrade + implements Fragment$basicMutationReturnFields$$GenericMutationReturn { + Mutation$RunSystemUpgrade$runSystemUpgrade({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'GenericMutationReturn', + }); + + factory Mutation$RunSystemUpgrade$runSystemUpgrade.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Mutation$RunSystemUpgrade$runSystemUpgrade( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$RunSystemUpgrade$runSystemUpgrade) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$RunSystemUpgrade$runSystemUpgrade + on Mutation$RunSystemUpgrade$runSystemUpgrade { + CopyWith$Mutation$RunSystemUpgrade$runSystemUpgrade< + Mutation$RunSystemUpgrade$runSystemUpgrade> + get copyWith => CopyWith$Mutation$RunSystemUpgrade$runSystemUpgrade( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$RunSystemUpgrade$runSystemUpgrade { + factory CopyWith$Mutation$RunSystemUpgrade$runSystemUpgrade( + Mutation$RunSystemUpgrade$runSystemUpgrade instance, + TRes Function(Mutation$RunSystemUpgrade$runSystemUpgrade) then, + ) = _CopyWithImpl$Mutation$RunSystemUpgrade$runSystemUpgrade; + + factory CopyWith$Mutation$RunSystemUpgrade$runSystemUpgrade.stub(TRes res) = + _CopyWithStubImpl$Mutation$RunSystemUpgrade$runSystemUpgrade; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$RunSystemUpgrade$runSystemUpgrade + implements CopyWith$Mutation$RunSystemUpgrade$runSystemUpgrade { + _CopyWithImpl$Mutation$RunSystemUpgrade$runSystemUpgrade( + this._instance, + this._then, + ); + + final Mutation$RunSystemUpgrade$runSystemUpgrade _instance; + + final TRes Function(Mutation$RunSystemUpgrade$runSystemUpgrade) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$RunSystemUpgrade$runSystemUpgrade( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$RunSystemUpgrade$runSystemUpgrade + implements CopyWith$Mutation$RunSystemUpgrade$runSystemUpgrade { + _CopyWithStubImpl$Mutation$RunSystemUpgrade$runSystemUpgrade(this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +class Mutation$PullRepositoryChanges { + Mutation$PullRepositoryChanges({ + required this.pullRepositoryChanges, + this.$__typename = 'Mutation', + }); + + factory Mutation$PullRepositoryChanges.fromJson(Map json) { + final l$pullRepositoryChanges = json['pullRepositoryChanges']; + final l$$__typename = json['__typename']; + return Mutation$PullRepositoryChanges( + pullRepositoryChanges: + Mutation$PullRepositoryChanges$pullRepositoryChanges.fromJson( + (l$pullRepositoryChanges as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$PullRepositoryChanges$pullRepositoryChanges + pullRepositoryChanges; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$pullRepositoryChanges = pullRepositoryChanges; + _resultData['pullRepositoryChanges'] = l$pullRepositoryChanges.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$pullRepositoryChanges = pullRepositoryChanges; + final l$$__typename = $__typename; + return Object.hashAll([ + l$pullRepositoryChanges, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$PullRepositoryChanges) || + runtimeType != other.runtimeType) { + return false; + } + final l$pullRepositoryChanges = pullRepositoryChanges; + final lOther$pullRepositoryChanges = other.pullRepositoryChanges; + if (l$pullRepositoryChanges != lOther$pullRepositoryChanges) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$PullRepositoryChanges + on Mutation$PullRepositoryChanges { + CopyWith$Mutation$PullRepositoryChanges + get copyWith => CopyWith$Mutation$PullRepositoryChanges( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$PullRepositoryChanges { + factory CopyWith$Mutation$PullRepositoryChanges( + Mutation$PullRepositoryChanges instance, + TRes Function(Mutation$PullRepositoryChanges) then, + ) = _CopyWithImpl$Mutation$PullRepositoryChanges; + + factory CopyWith$Mutation$PullRepositoryChanges.stub(TRes res) = + _CopyWithStubImpl$Mutation$PullRepositoryChanges; + + TRes call({ + Mutation$PullRepositoryChanges$pullRepositoryChanges? pullRepositoryChanges, + String? $__typename, + }); + CopyWith$Mutation$PullRepositoryChanges$pullRepositoryChanges + get pullRepositoryChanges; +} + +class _CopyWithImpl$Mutation$PullRepositoryChanges + implements CopyWith$Mutation$PullRepositoryChanges { + _CopyWithImpl$Mutation$PullRepositoryChanges( + this._instance, + this._then, + ); + + final Mutation$PullRepositoryChanges _instance; + + final TRes Function(Mutation$PullRepositoryChanges) _then; + + static const _undefined = {}; + + TRes call({ + Object? pullRepositoryChanges = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$PullRepositoryChanges( + pullRepositoryChanges: + pullRepositoryChanges == _undefined || pullRepositoryChanges == null + ? _instance.pullRepositoryChanges + : (pullRepositoryChanges + as Mutation$PullRepositoryChanges$pullRepositoryChanges), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$PullRepositoryChanges$pullRepositoryChanges + get pullRepositoryChanges { + final local$pullRepositoryChanges = _instance.pullRepositoryChanges; + return CopyWith$Mutation$PullRepositoryChanges$pullRepositoryChanges( + local$pullRepositoryChanges, (e) => call(pullRepositoryChanges: e)); + } +} + +class _CopyWithStubImpl$Mutation$PullRepositoryChanges + implements CopyWith$Mutation$PullRepositoryChanges { + _CopyWithStubImpl$Mutation$PullRepositoryChanges(this._res); + + TRes _res; + + call({ + Mutation$PullRepositoryChanges$pullRepositoryChanges? pullRepositoryChanges, + String? $__typename, + }) => + _res; + CopyWith$Mutation$PullRepositoryChanges$pullRepositoryChanges + get pullRepositoryChanges => + CopyWith$Mutation$PullRepositoryChanges$pullRepositoryChanges.stub( + _res); +} + +const documentNodeMutationPullRepositoryChanges = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'PullRepositoryChanges'), + variableDefinitions: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'pullRepositoryChanges'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, +]); +Mutation$PullRepositoryChanges _parserFn$Mutation$PullRepositoryChanges( + Map data) => + Mutation$PullRepositoryChanges.fromJson(data); +typedef OnMutationCompleted$Mutation$PullRepositoryChanges = FutureOr + Function( + Map?, + Mutation$PullRepositoryChanges?, +); + +class Options$Mutation$PullRepositoryChanges + extends graphql.MutationOptions { + Options$Mutation$PullRepositoryChanges({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$PullRepositoryChanges? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$PullRepositoryChanges? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$PullRepositoryChanges(data), + ), + update: update, + onError: onError, + document: documentNodeMutationPullRepositoryChanges, + parserFn: _parserFn$Mutation$PullRepositoryChanges, + ); + + final OnMutationCompleted$Mutation$PullRepositoryChanges? + onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$PullRepositoryChanges + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$PullRepositoryChanges({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$PullRepositoryChanges? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationPullRepositoryChanges, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$PullRepositoryChanges, + ); +} + +extension ClientExtension$Mutation$PullRepositoryChanges + on graphql.GraphQLClient { + Future> + mutate$PullRepositoryChanges( + [Options$Mutation$PullRepositoryChanges? options]) async => + await this + .mutate(options ?? Options$Mutation$PullRepositoryChanges()); + graphql.ObservableQuery + watchMutation$PullRepositoryChanges( + [WatchOptions$Mutation$PullRepositoryChanges? options]) => + this.watchMutation( + options ?? WatchOptions$Mutation$PullRepositoryChanges()); +} + +class Mutation$PullRepositoryChanges$pullRepositoryChanges + implements Fragment$basicMutationReturnFields$$GenericMutationReturn { + Mutation$PullRepositoryChanges$pullRepositoryChanges({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'GenericMutationReturn', + }); + + factory Mutation$PullRepositoryChanges$pullRepositoryChanges.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Mutation$PullRepositoryChanges$pullRepositoryChanges( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$PullRepositoryChanges$pullRepositoryChanges) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$PullRepositoryChanges$pullRepositoryChanges + on Mutation$PullRepositoryChanges$pullRepositoryChanges { + CopyWith$Mutation$PullRepositoryChanges$pullRepositoryChanges< + Mutation$PullRepositoryChanges$pullRepositoryChanges> + get copyWith => + CopyWith$Mutation$PullRepositoryChanges$pullRepositoryChanges( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$PullRepositoryChanges$pullRepositoryChanges< + TRes> { + factory CopyWith$Mutation$PullRepositoryChanges$pullRepositoryChanges( + Mutation$PullRepositoryChanges$pullRepositoryChanges instance, + TRes Function(Mutation$PullRepositoryChanges$pullRepositoryChanges) then, + ) = _CopyWithImpl$Mutation$PullRepositoryChanges$pullRepositoryChanges; + + factory CopyWith$Mutation$PullRepositoryChanges$pullRepositoryChanges.stub( + TRes res) = + _CopyWithStubImpl$Mutation$PullRepositoryChanges$pullRepositoryChanges; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$PullRepositoryChanges$pullRepositoryChanges + implements + CopyWith$Mutation$PullRepositoryChanges$pullRepositoryChanges { + _CopyWithImpl$Mutation$PullRepositoryChanges$pullRepositoryChanges( + this._instance, + this._then, + ); + + final Mutation$PullRepositoryChanges$pullRepositoryChanges _instance; + + final TRes Function(Mutation$PullRepositoryChanges$pullRepositoryChanges) + _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$PullRepositoryChanges$pullRepositoryChanges( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$PullRepositoryChanges$pullRepositoryChanges< + TRes> + implements + CopyWith$Mutation$PullRepositoryChanges$pullRepositoryChanges { + _CopyWithStubImpl$Mutation$PullRepositoryChanges$pullRepositoryChanges( + this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +class Mutation$RebootSystem { + Mutation$RebootSystem({ + required this.rebootSystem, + this.$__typename = 'Mutation', + }); + + factory Mutation$RebootSystem.fromJson(Map json) { + final l$rebootSystem = json['rebootSystem']; + final l$$__typename = json['__typename']; + return Mutation$RebootSystem( + rebootSystem: Mutation$RebootSystem$rebootSystem.fromJson( + (l$rebootSystem as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$RebootSystem$rebootSystem rebootSystem; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$rebootSystem = rebootSystem; + _resultData['rebootSystem'] = l$rebootSystem.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$rebootSystem = rebootSystem; + final l$$__typename = $__typename; + return Object.hashAll([ + l$rebootSystem, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$RebootSystem) || runtimeType != other.runtimeType) { + return false; + } + final l$rebootSystem = rebootSystem; + final lOther$rebootSystem = other.rebootSystem; + if (l$rebootSystem != lOther$rebootSystem) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$RebootSystem on Mutation$RebootSystem { + CopyWith$Mutation$RebootSystem get copyWith => + CopyWith$Mutation$RebootSystem( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$RebootSystem { + factory CopyWith$Mutation$RebootSystem( + Mutation$RebootSystem instance, + TRes Function(Mutation$RebootSystem) then, + ) = _CopyWithImpl$Mutation$RebootSystem; + + factory CopyWith$Mutation$RebootSystem.stub(TRes res) = + _CopyWithStubImpl$Mutation$RebootSystem; + + TRes call({ + Mutation$RebootSystem$rebootSystem? rebootSystem, + String? $__typename, + }); + CopyWith$Mutation$RebootSystem$rebootSystem get rebootSystem; +} + +class _CopyWithImpl$Mutation$RebootSystem + implements CopyWith$Mutation$RebootSystem { + _CopyWithImpl$Mutation$RebootSystem( + this._instance, + this._then, + ); + + final Mutation$RebootSystem _instance; + + final TRes Function(Mutation$RebootSystem) _then; + + static const _undefined = {}; + + TRes call({ + Object? rebootSystem = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$RebootSystem( + rebootSystem: rebootSystem == _undefined || rebootSystem == null + ? _instance.rebootSystem + : (rebootSystem as Mutation$RebootSystem$rebootSystem), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$RebootSystem$rebootSystem get rebootSystem { + final local$rebootSystem = _instance.rebootSystem; + return CopyWith$Mutation$RebootSystem$rebootSystem( + local$rebootSystem, (e) => call(rebootSystem: e)); + } +} + +class _CopyWithStubImpl$Mutation$RebootSystem + implements CopyWith$Mutation$RebootSystem { + _CopyWithStubImpl$Mutation$RebootSystem(this._res); + + TRes _res; + + call({ + Mutation$RebootSystem$rebootSystem? rebootSystem, + String? $__typename, + }) => + _res; + CopyWith$Mutation$RebootSystem$rebootSystem get rebootSystem => + CopyWith$Mutation$RebootSystem$rebootSystem.stub(_res); +} + +const documentNodeMutationRebootSystem = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'RebootSystem'), + variableDefinitions: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'rebootSystem'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, +]); +Mutation$RebootSystem _parserFn$Mutation$RebootSystem( + Map data) => + Mutation$RebootSystem.fromJson(data); +typedef OnMutationCompleted$Mutation$RebootSystem = FutureOr Function( + Map?, + Mutation$RebootSystem?, +); + +class Options$Mutation$RebootSystem + extends graphql.MutationOptions { + Options$Mutation$RebootSystem({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RebootSystem? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$RebootSystem? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null ? null : _parserFn$Mutation$RebootSystem(data), + ), + update: update, + onError: onError, + document: documentNodeMutationRebootSystem, + parserFn: _parserFn$Mutation$RebootSystem, + ); + + final OnMutationCompleted$Mutation$RebootSystem? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$RebootSystem + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$RebootSystem({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RebootSystem? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationRebootSystem, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$RebootSystem, + ); +} + +extension ClientExtension$Mutation$RebootSystem on graphql.GraphQLClient { + Future> mutate$RebootSystem( + [Options$Mutation$RebootSystem? options]) async => + await this.mutate(options ?? Options$Mutation$RebootSystem()); + graphql.ObservableQuery watchMutation$RebootSystem( + [WatchOptions$Mutation$RebootSystem? options]) => + this.watchMutation(options ?? WatchOptions$Mutation$RebootSystem()); +} + +class Mutation$RebootSystem$rebootSystem + implements Fragment$basicMutationReturnFields$$GenericMutationReturn { + Mutation$RebootSystem$rebootSystem({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'GenericMutationReturn', + }); + + factory Mutation$RebootSystem$rebootSystem.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Mutation$RebootSystem$rebootSystem( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$RebootSystem$rebootSystem) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$RebootSystem$rebootSystem + on Mutation$RebootSystem$rebootSystem { + CopyWith$Mutation$RebootSystem$rebootSystem< + Mutation$RebootSystem$rebootSystem> + get copyWith => CopyWith$Mutation$RebootSystem$rebootSystem( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$RebootSystem$rebootSystem { + factory CopyWith$Mutation$RebootSystem$rebootSystem( + Mutation$RebootSystem$rebootSystem instance, + TRes Function(Mutation$RebootSystem$rebootSystem) then, + ) = _CopyWithImpl$Mutation$RebootSystem$rebootSystem; + + factory CopyWith$Mutation$RebootSystem$rebootSystem.stub(TRes res) = + _CopyWithStubImpl$Mutation$RebootSystem$rebootSystem; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$RebootSystem$rebootSystem + implements CopyWith$Mutation$RebootSystem$rebootSystem { + _CopyWithImpl$Mutation$RebootSystem$rebootSystem( + this._instance, + this._then, + ); + + final Mutation$RebootSystem$rebootSystem _instance; + + final TRes Function(Mutation$RebootSystem$rebootSystem) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$RebootSystem$rebootSystem( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$RebootSystem$rebootSystem + implements CopyWith$Mutation$RebootSystem$rebootSystem { + _CopyWithStubImpl$Mutation$RebootSystem$rebootSystem(this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +class Query$SystemServerProvider { + Query$SystemServerProvider({ + required this.system, + this.$__typename = 'Query', + }); + + factory Query$SystemServerProvider.fromJson(Map json) { + final l$system = json['system']; + final l$$__typename = json['__typename']; + return Query$SystemServerProvider( + system: Query$SystemServerProvider$system.fromJson( + (l$system as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Query$SystemServerProvider$system system; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$system = system; + _resultData['system'] = l$system.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$system = system; + final l$$__typename = $__typename; + return Object.hashAll([ + l$system, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$SystemServerProvider) || + runtimeType != other.runtimeType) { + return false; + } + final l$system = system; + final lOther$system = other.system; + if (l$system != lOther$system) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$SystemServerProvider + on Query$SystemServerProvider { + CopyWith$Query$SystemServerProvider + get copyWith => CopyWith$Query$SystemServerProvider( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$SystemServerProvider { + factory CopyWith$Query$SystemServerProvider( + Query$SystemServerProvider instance, + TRes Function(Query$SystemServerProvider) then, + ) = _CopyWithImpl$Query$SystemServerProvider; + + factory CopyWith$Query$SystemServerProvider.stub(TRes res) = + _CopyWithStubImpl$Query$SystemServerProvider; + + TRes call({ + Query$SystemServerProvider$system? system, + String? $__typename, + }); + CopyWith$Query$SystemServerProvider$system get system; +} + +class _CopyWithImpl$Query$SystemServerProvider + implements CopyWith$Query$SystemServerProvider { + _CopyWithImpl$Query$SystemServerProvider( + this._instance, + this._then, + ); + + final Query$SystemServerProvider _instance; + + final TRes Function(Query$SystemServerProvider) _then; + + static const _undefined = {}; + + TRes call({ + Object? system = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$SystemServerProvider( + system: system == _undefined || system == null + ? _instance.system + : (system as Query$SystemServerProvider$system), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$SystemServerProvider$system get system { + final local$system = _instance.system; + return CopyWith$Query$SystemServerProvider$system( + local$system, (e) => call(system: e)); + } +} + +class _CopyWithStubImpl$Query$SystemServerProvider + implements CopyWith$Query$SystemServerProvider { + _CopyWithStubImpl$Query$SystemServerProvider(this._res); + + TRes _res; + + call({ + Query$SystemServerProvider$system? system, + String? $__typename, + }) => + _res; + CopyWith$Query$SystemServerProvider$system get system => + CopyWith$Query$SystemServerProvider$system.stub(_res); +} + +const documentNodeQuerySystemServerProvider = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.query, + name: NameNode(value: 'SystemServerProvider'), + variableDefinitions: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'system'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'provider'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'provider'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), +]); +Query$SystemServerProvider _parserFn$Query$SystemServerProvider( + Map data) => + Query$SystemServerProvider.fromJson(data); +typedef OnQueryComplete$Query$SystemServerProvider = FutureOr Function( + Map?, + Query$SystemServerProvider?, +); + +class Options$Query$SystemServerProvider + extends graphql.QueryOptions { + Options$Query$SystemServerProvider({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$SystemServerProvider? typedOptimisticResult, + Duration? pollInterval, + graphql.Context? context, + OnQueryComplete$Query$SystemServerProvider? onComplete, + graphql.OnQueryError? onError, + }) : onCompleteWithParsed = onComplete, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + pollInterval: pollInterval, + context: context, + onComplete: onComplete == null + ? null + : (data) => onComplete( + data, + data == null + ? null + : _parserFn$Query$SystemServerProvider(data), + ), + onError: onError, + document: documentNodeQuerySystemServerProvider, + parserFn: _parserFn$Query$SystemServerProvider, + ); + + final OnQueryComplete$Query$SystemServerProvider? onCompleteWithParsed; + + @override + List get properties => [ + ...super.onComplete == null + ? super.properties + : super.properties.where((property) => property != onComplete), + onCompleteWithParsed, + ]; +} + +class WatchOptions$Query$SystemServerProvider + extends graphql.WatchQueryOptions { + WatchOptions$Query$SystemServerProvider({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$SystemServerProvider? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeQuerySystemServerProvider, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Query$SystemServerProvider, + ); +} + +class FetchMoreOptions$Query$SystemServerProvider + extends graphql.FetchMoreOptions { + FetchMoreOptions$Query$SystemServerProvider( + {required graphql.UpdateQuery updateQuery}) + : super( + updateQuery: updateQuery, + document: documentNodeQuerySystemServerProvider, + ); +} + +extension ClientExtension$Query$SystemServerProvider on graphql.GraphQLClient { + Future> + query$SystemServerProvider( + [Options$Query$SystemServerProvider? options]) async => + await this.query(options ?? Options$Query$SystemServerProvider()); + graphql.ObservableQuery + watchQuery$SystemServerProvider( + [WatchOptions$Query$SystemServerProvider? options]) => + this.watchQuery(options ?? WatchOptions$Query$SystemServerProvider()); + void writeQuery$SystemServerProvider({ + required Query$SystemServerProvider data, + bool broadcast = true, + }) => + this.writeQuery( + graphql.Request( + operation: graphql.Operation( + document: documentNodeQuerySystemServerProvider)), + data: data.toJson(), + broadcast: broadcast, + ); + Query$SystemServerProvider? readQuery$SystemServerProvider( + {bool optimistic = true}) { + final result = this.readQuery( + graphql.Request( + operation: graphql.Operation( + document: documentNodeQuerySystemServerProvider)), + optimistic: optimistic, + ); + return result == null ? null : Query$SystemServerProvider.fromJson(result); + } +} + +class Query$SystemServerProvider$system { + Query$SystemServerProvider$system({ + required this.provider, + this.$__typename = 'System', + }); + + factory Query$SystemServerProvider$system.fromJson( + Map json) { + final l$provider = json['provider']; + final l$$__typename = json['__typename']; + return Query$SystemServerProvider$system( + provider: Query$SystemServerProvider$system$provider.fromJson( + (l$provider as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Query$SystemServerProvider$system$provider provider; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$provider = provider; + _resultData['provider'] = l$provider.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$provider = provider; + final l$$__typename = $__typename; + return Object.hashAll([ + l$provider, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$SystemServerProvider$system) || + runtimeType != other.runtimeType) { + return false; + } + final l$provider = provider; + final lOther$provider = other.provider; + if (l$provider != lOther$provider) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$SystemServerProvider$system + on Query$SystemServerProvider$system { + CopyWith$Query$SystemServerProvider$system + get copyWith => CopyWith$Query$SystemServerProvider$system( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$SystemServerProvider$system { + factory CopyWith$Query$SystemServerProvider$system( + Query$SystemServerProvider$system instance, + TRes Function(Query$SystemServerProvider$system) then, + ) = _CopyWithImpl$Query$SystemServerProvider$system; + + factory CopyWith$Query$SystemServerProvider$system.stub(TRes res) = + _CopyWithStubImpl$Query$SystemServerProvider$system; + + TRes call({ + Query$SystemServerProvider$system$provider? provider, + String? $__typename, + }); + CopyWith$Query$SystemServerProvider$system$provider get provider; +} + +class _CopyWithImpl$Query$SystemServerProvider$system + implements CopyWith$Query$SystemServerProvider$system { + _CopyWithImpl$Query$SystemServerProvider$system( + this._instance, + this._then, + ); + + final Query$SystemServerProvider$system _instance; + + final TRes Function(Query$SystemServerProvider$system) _then; + + static const _undefined = {}; + + TRes call({ + Object? provider = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$SystemServerProvider$system( + provider: provider == _undefined || provider == null + ? _instance.provider + : (provider as Query$SystemServerProvider$system$provider), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$SystemServerProvider$system$provider get provider { + final local$provider = _instance.provider; + return CopyWith$Query$SystemServerProvider$system$provider( + local$provider, (e) => call(provider: e)); + } +} + +class _CopyWithStubImpl$Query$SystemServerProvider$system + implements CopyWith$Query$SystemServerProvider$system { + _CopyWithStubImpl$Query$SystemServerProvider$system(this._res); + + TRes _res; + + call({ + Query$SystemServerProvider$system$provider? provider, + String? $__typename, + }) => + _res; + CopyWith$Query$SystemServerProvider$system$provider get provider => + CopyWith$Query$SystemServerProvider$system$provider.stub(_res); +} + +class Query$SystemServerProvider$system$provider { + Query$SystemServerProvider$system$provider({ + required this.provider, + this.$__typename = 'SystemProviderInfo', + }); + + factory Query$SystemServerProvider$system$provider.fromJson( + Map json) { + final l$provider = json['provider']; + final l$$__typename = json['__typename']; + return Query$SystemServerProvider$system$provider( + provider: fromJson$Enum$ServerProvider((l$provider as String)), + $__typename: (l$$__typename as String), + ); + } + + final Enum$ServerProvider provider; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$provider = provider; + _resultData['provider'] = toJson$Enum$ServerProvider(l$provider); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$provider = provider; + final l$$__typename = $__typename; + return Object.hashAll([ + l$provider, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$SystemServerProvider$system$provider) || + runtimeType != other.runtimeType) { + return false; + } + final l$provider = provider; + final lOther$provider = other.provider; + if (l$provider != lOther$provider) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$SystemServerProvider$system$provider + on Query$SystemServerProvider$system$provider { + CopyWith$Query$SystemServerProvider$system$provider< + Query$SystemServerProvider$system$provider> + get copyWith => CopyWith$Query$SystemServerProvider$system$provider( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$SystemServerProvider$system$provider { + factory CopyWith$Query$SystemServerProvider$system$provider( + Query$SystemServerProvider$system$provider instance, + TRes Function(Query$SystemServerProvider$system$provider) then, + ) = _CopyWithImpl$Query$SystemServerProvider$system$provider; + + factory CopyWith$Query$SystemServerProvider$system$provider.stub(TRes res) = + _CopyWithStubImpl$Query$SystemServerProvider$system$provider; + + TRes call({ + Enum$ServerProvider? provider, + String? $__typename, + }); +} + +class _CopyWithImpl$Query$SystemServerProvider$system$provider + implements CopyWith$Query$SystemServerProvider$system$provider { + _CopyWithImpl$Query$SystemServerProvider$system$provider( + this._instance, + this._then, + ); + + final Query$SystemServerProvider$system$provider _instance; + + final TRes Function(Query$SystemServerProvider$system$provider) _then; + + static const _undefined = {}; + + TRes call({ + Object? provider = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$SystemServerProvider$system$provider( + provider: provider == _undefined || provider == null + ? _instance.provider + : (provider as Enum$ServerProvider), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Query$SystemServerProvider$system$provider + implements CopyWith$Query$SystemServerProvider$system$provider { + _CopyWithStubImpl$Query$SystemServerProvider$system$provider(this._res); + + TRes _res; + + call({ + Enum$ServerProvider? provider, + String? $__typename, + }) => + _res; +} + +class Query$SystemDnsProvider { + Query$SystemDnsProvider({ + required this.system, + this.$__typename = 'Query', + }); + + factory Query$SystemDnsProvider.fromJson(Map json) { + final l$system = json['system']; + final l$$__typename = json['__typename']; + return Query$SystemDnsProvider( + system: Query$SystemDnsProvider$system.fromJson( + (l$system as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Query$SystemDnsProvider$system system; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$system = system; + _resultData['system'] = l$system.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$system = system; + final l$$__typename = $__typename; + return Object.hashAll([ + l$system, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$SystemDnsProvider) || + runtimeType != other.runtimeType) { + return false; + } + final l$system = system; + final lOther$system = other.system; + if (l$system != lOther$system) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$SystemDnsProvider on Query$SystemDnsProvider { + CopyWith$Query$SystemDnsProvider get copyWith => + CopyWith$Query$SystemDnsProvider( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$SystemDnsProvider { + factory CopyWith$Query$SystemDnsProvider( + Query$SystemDnsProvider instance, + TRes Function(Query$SystemDnsProvider) then, + ) = _CopyWithImpl$Query$SystemDnsProvider; + + factory CopyWith$Query$SystemDnsProvider.stub(TRes res) = + _CopyWithStubImpl$Query$SystemDnsProvider; + + TRes call({ + Query$SystemDnsProvider$system? system, + String? $__typename, + }); + CopyWith$Query$SystemDnsProvider$system get system; +} + +class _CopyWithImpl$Query$SystemDnsProvider + implements CopyWith$Query$SystemDnsProvider { + _CopyWithImpl$Query$SystemDnsProvider( + this._instance, + this._then, + ); + + final Query$SystemDnsProvider _instance; + + final TRes Function(Query$SystemDnsProvider) _then; + + static const _undefined = {}; + + TRes call({ + Object? system = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$SystemDnsProvider( + system: system == _undefined || system == null + ? _instance.system + : (system as Query$SystemDnsProvider$system), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$SystemDnsProvider$system get system { + final local$system = _instance.system; + return CopyWith$Query$SystemDnsProvider$system( + local$system, (e) => call(system: e)); + } +} + +class _CopyWithStubImpl$Query$SystemDnsProvider + implements CopyWith$Query$SystemDnsProvider { + _CopyWithStubImpl$Query$SystemDnsProvider(this._res); + + TRes _res; + + call({ + Query$SystemDnsProvider$system? system, + String? $__typename, + }) => + _res; + CopyWith$Query$SystemDnsProvider$system get system => + CopyWith$Query$SystemDnsProvider$system.stub(_res); +} + +const documentNodeQuerySystemDnsProvider = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.query, + name: NameNode(value: 'SystemDnsProvider'), + variableDefinitions: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'system'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'domainInfo'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'provider'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), +]); +Query$SystemDnsProvider _parserFn$Query$SystemDnsProvider( + Map data) => + Query$SystemDnsProvider.fromJson(data); +typedef OnQueryComplete$Query$SystemDnsProvider = FutureOr Function( + Map?, + Query$SystemDnsProvider?, +); + +class Options$Query$SystemDnsProvider + extends graphql.QueryOptions { + Options$Query$SystemDnsProvider({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$SystemDnsProvider? typedOptimisticResult, + Duration? pollInterval, + graphql.Context? context, + OnQueryComplete$Query$SystemDnsProvider? onComplete, + graphql.OnQueryError? onError, + }) : onCompleteWithParsed = onComplete, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + pollInterval: pollInterval, + context: context, + onComplete: onComplete == null + ? null + : (data) => onComplete( + data, + data == null + ? null + : _parserFn$Query$SystemDnsProvider(data), + ), + onError: onError, + document: documentNodeQuerySystemDnsProvider, + parserFn: _parserFn$Query$SystemDnsProvider, + ); + + final OnQueryComplete$Query$SystemDnsProvider? onCompleteWithParsed; + + @override + List get properties => [ + ...super.onComplete == null + ? super.properties + : super.properties.where((property) => property != onComplete), + onCompleteWithParsed, + ]; +} + +class WatchOptions$Query$SystemDnsProvider + extends graphql.WatchQueryOptions { + WatchOptions$Query$SystemDnsProvider({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$SystemDnsProvider? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeQuerySystemDnsProvider, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Query$SystemDnsProvider, + ); +} + +class FetchMoreOptions$Query$SystemDnsProvider + extends graphql.FetchMoreOptions { + FetchMoreOptions$Query$SystemDnsProvider( + {required graphql.UpdateQuery updateQuery}) + : super( + updateQuery: updateQuery, + document: documentNodeQuerySystemDnsProvider, + ); +} + +extension ClientExtension$Query$SystemDnsProvider on graphql.GraphQLClient { + Future> query$SystemDnsProvider( + [Options$Query$SystemDnsProvider? options]) async => + await this.query(options ?? Options$Query$SystemDnsProvider()); + graphql.ObservableQuery watchQuery$SystemDnsProvider( + [WatchOptions$Query$SystemDnsProvider? options]) => + this.watchQuery(options ?? WatchOptions$Query$SystemDnsProvider()); + void writeQuery$SystemDnsProvider({ + required Query$SystemDnsProvider data, + bool broadcast = true, + }) => + this.writeQuery( + graphql.Request( + operation: graphql.Operation( + document: documentNodeQuerySystemDnsProvider)), + data: data.toJson(), + broadcast: broadcast, + ); + Query$SystemDnsProvider? readQuery$SystemDnsProvider( + {bool optimistic = true}) { + final result = this.readQuery( + graphql.Request( + operation: + graphql.Operation(document: documentNodeQuerySystemDnsProvider)), + optimistic: optimistic, + ); + return result == null ? null : Query$SystemDnsProvider.fromJson(result); + } +} + +class Query$SystemDnsProvider$system { + Query$SystemDnsProvider$system({ + required this.domainInfo, + this.$__typename = 'System', + }); + + factory Query$SystemDnsProvider$system.fromJson(Map json) { + final l$domainInfo = json['domainInfo']; + final l$$__typename = json['__typename']; + return Query$SystemDnsProvider$system( + domainInfo: Query$SystemDnsProvider$system$domainInfo.fromJson( + (l$domainInfo as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Query$SystemDnsProvider$system$domainInfo domainInfo; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$domainInfo = domainInfo; + _resultData['domainInfo'] = l$domainInfo.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$domainInfo = domainInfo; + final l$$__typename = $__typename; + return Object.hashAll([ + l$domainInfo, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$SystemDnsProvider$system) || + runtimeType != other.runtimeType) { + return false; + } + final l$domainInfo = domainInfo; + final lOther$domainInfo = other.domainInfo; + if (l$domainInfo != lOther$domainInfo) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$SystemDnsProvider$system + on Query$SystemDnsProvider$system { + CopyWith$Query$SystemDnsProvider$system + get copyWith => CopyWith$Query$SystemDnsProvider$system( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$SystemDnsProvider$system { + factory CopyWith$Query$SystemDnsProvider$system( + Query$SystemDnsProvider$system instance, + TRes Function(Query$SystemDnsProvider$system) then, + ) = _CopyWithImpl$Query$SystemDnsProvider$system; + + factory CopyWith$Query$SystemDnsProvider$system.stub(TRes res) = + _CopyWithStubImpl$Query$SystemDnsProvider$system; + + TRes call({ + Query$SystemDnsProvider$system$domainInfo? domainInfo, + String? $__typename, + }); + CopyWith$Query$SystemDnsProvider$system$domainInfo get domainInfo; +} + +class _CopyWithImpl$Query$SystemDnsProvider$system + implements CopyWith$Query$SystemDnsProvider$system { + _CopyWithImpl$Query$SystemDnsProvider$system( + this._instance, + this._then, + ); + + final Query$SystemDnsProvider$system _instance; + + final TRes Function(Query$SystemDnsProvider$system) _then; + + static const _undefined = {}; + + TRes call({ + Object? domainInfo = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$SystemDnsProvider$system( + domainInfo: domainInfo == _undefined || domainInfo == null + ? _instance.domainInfo + : (domainInfo as Query$SystemDnsProvider$system$domainInfo), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$SystemDnsProvider$system$domainInfo get domainInfo { + final local$domainInfo = _instance.domainInfo; + return CopyWith$Query$SystemDnsProvider$system$domainInfo( + local$domainInfo, (e) => call(domainInfo: e)); + } +} + +class _CopyWithStubImpl$Query$SystemDnsProvider$system + implements CopyWith$Query$SystemDnsProvider$system { + _CopyWithStubImpl$Query$SystemDnsProvider$system(this._res); + + TRes _res; + + call({ + Query$SystemDnsProvider$system$domainInfo? domainInfo, + String? $__typename, + }) => + _res; + CopyWith$Query$SystemDnsProvider$system$domainInfo get domainInfo => + CopyWith$Query$SystemDnsProvider$system$domainInfo.stub(_res); +} + +class Query$SystemDnsProvider$system$domainInfo { + Query$SystemDnsProvider$system$domainInfo({ + required this.provider, + this.$__typename = 'SystemDomainInfo', + }); + + factory Query$SystemDnsProvider$system$domainInfo.fromJson( + Map json) { + final l$provider = json['provider']; + final l$$__typename = json['__typename']; + return Query$SystemDnsProvider$system$domainInfo( + provider: fromJson$Enum$DnsProvider((l$provider as String)), + $__typename: (l$$__typename as String), + ); + } + + final Enum$DnsProvider provider; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$provider = provider; + _resultData['provider'] = toJson$Enum$DnsProvider(l$provider); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$provider = provider; + final l$$__typename = $__typename; + return Object.hashAll([ + l$provider, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$SystemDnsProvider$system$domainInfo) || + runtimeType != other.runtimeType) { + return false; + } + final l$provider = provider; + final lOther$provider = other.provider; + if (l$provider != lOther$provider) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$SystemDnsProvider$system$domainInfo + on Query$SystemDnsProvider$system$domainInfo { + CopyWith$Query$SystemDnsProvider$system$domainInfo< + Query$SystemDnsProvider$system$domainInfo> + get copyWith => CopyWith$Query$SystemDnsProvider$system$domainInfo( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$SystemDnsProvider$system$domainInfo { + factory CopyWith$Query$SystemDnsProvider$system$domainInfo( + Query$SystemDnsProvider$system$domainInfo instance, + TRes Function(Query$SystemDnsProvider$system$domainInfo) then, + ) = _CopyWithImpl$Query$SystemDnsProvider$system$domainInfo; + + factory CopyWith$Query$SystemDnsProvider$system$domainInfo.stub(TRes res) = + _CopyWithStubImpl$Query$SystemDnsProvider$system$domainInfo; + + TRes call({ + Enum$DnsProvider? provider, + String? $__typename, + }); +} + +class _CopyWithImpl$Query$SystemDnsProvider$system$domainInfo + implements CopyWith$Query$SystemDnsProvider$system$domainInfo { + _CopyWithImpl$Query$SystemDnsProvider$system$domainInfo( + this._instance, + this._then, + ); + + final Query$SystemDnsProvider$system$domainInfo _instance; + + final TRes Function(Query$SystemDnsProvider$system$domainInfo) _then; + + static const _undefined = {}; + + TRes call({ + Object? provider = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$SystemDnsProvider$system$domainInfo( + provider: provider == _undefined || provider == null + ? _instance.provider + : (provider as Enum$DnsProvider), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Query$SystemDnsProvider$system$domainInfo + implements CopyWith$Query$SystemDnsProvider$system$domainInfo { + _CopyWithStubImpl$Query$SystemDnsProvider$system$domainInfo(this._res); + + TRes _res; + + call({ + Enum$DnsProvider? provider, + String? $__typename, + }) => + _res; +} + +class Query$GetApiTokens { + Query$GetApiTokens({ + required this.api, + this.$__typename = 'Query', + }); + + factory Query$GetApiTokens.fromJson(Map json) { + final l$api = json['api']; + final l$$__typename = json['__typename']; + return Query$GetApiTokens( + api: Query$GetApiTokens$api.fromJson((l$api as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Query$GetApiTokens$api api; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$api = api; + _resultData['api'] = l$api.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$api = api; + final l$$__typename = $__typename; + return Object.hashAll([ + l$api, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$GetApiTokens) || runtimeType != other.runtimeType) { + return false; + } + final l$api = api; + final lOther$api = other.api; + if (l$api != lOther$api) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$GetApiTokens on Query$GetApiTokens { + CopyWith$Query$GetApiTokens get copyWith => + CopyWith$Query$GetApiTokens( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$GetApiTokens { + factory CopyWith$Query$GetApiTokens( + Query$GetApiTokens instance, + TRes Function(Query$GetApiTokens) then, + ) = _CopyWithImpl$Query$GetApiTokens; + + factory CopyWith$Query$GetApiTokens.stub(TRes res) = + _CopyWithStubImpl$Query$GetApiTokens; + + TRes call({ + Query$GetApiTokens$api? api, + String? $__typename, + }); + CopyWith$Query$GetApiTokens$api get api; +} + +class _CopyWithImpl$Query$GetApiTokens + implements CopyWith$Query$GetApiTokens { + _CopyWithImpl$Query$GetApiTokens( + this._instance, + this._then, + ); + + final Query$GetApiTokens _instance; + + final TRes Function(Query$GetApiTokens) _then; + + static const _undefined = {}; + + TRes call({ + Object? api = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$GetApiTokens( + api: api == _undefined || api == null + ? _instance.api + : (api as Query$GetApiTokens$api), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$GetApiTokens$api get api { + final local$api = _instance.api; + return CopyWith$Query$GetApiTokens$api(local$api, (e) => call(api: e)); + } +} + +class _CopyWithStubImpl$Query$GetApiTokens + implements CopyWith$Query$GetApiTokens { + _CopyWithStubImpl$Query$GetApiTokens(this._res); + + TRes _res; + + call({ + Query$GetApiTokens$api? api, + String? $__typename, + }) => + _res; + CopyWith$Query$GetApiTokens$api get api => + CopyWith$Query$GetApiTokens$api.stub(_res); +} + +const documentNodeQueryGetApiTokens = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.query, + name: NameNode(value: 'GetApiTokens'), + variableDefinitions: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'api'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'devices'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'creationDate'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'isCaller'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'name'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), +]); +Query$GetApiTokens _parserFn$Query$GetApiTokens(Map data) => + Query$GetApiTokens.fromJson(data); +typedef OnQueryComplete$Query$GetApiTokens = FutureOr Function( + Map?, + Query$GetApiTokens?, +); + +class Options$Query$GetApiTokens + extends graphql.QueryOptions { + Options$Query$GetApiTokens({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$GetApiTokens? typedOptimisticResult, + Duration? pollInterval, + graphql.Context? context, + OnQueryComplete$Query$GetApiTokens? onComplete, + graphql.OnQueryError? onError, + }) : onCompleteWithParsed = onComplete, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + pollInterval: pollInterval, + context: context, + onComplete: onComplete == null + ? null + : (data) => onComplete( + data, + data == null ? null : _parserFn$Query$GetApiTokens(data), + ), + onError: onError, + document: documentNodeQueryGetApiTokens, + parserFn: _parserFn$Query$GetApiTokens, + ); + + final OnQueryComplete$Query$GetApiTokens? onCompleteWithParsed; + + @override + List get properties => [ + ...super.onComplete == null + ? super.properties + : super.properties.where((property) => property != onComplete), + onCompleteWithParsed, + ]; +} + +class WatchOptions$Query$GetApiTokens + extends graphql.WatchQueryOptions { + WatchOptions$Query$GetApiTokens({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$GetApiTokens? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeQueryGetApiTokens, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Query$GetApiTokens, + ); +} + +class FetchMoreOptions$Query$GetApiTokens extends graphql.FetchMoreOptions { + FetchMoreOptions$Query$GetApiTokens( + {required graphql.UpdateQuery updateQuery}) + : super( + updateQuery: updateQuery, + document: documentNodeQueryGetApiTokens, + ); +} + +extension ClientExtension$Query$GetApiTokens on graphql.GraphQLClient { + Future> query$GetApiTokens( + [Options$Query$GetApiTokens? options]) async => + await this.query(options ?? Options$Query$GetApiTokens()); + graphql.ObservableQuery watchQuery$GetApiTokens( + [WatchOptions$Query$GetApiTokens? options]) => + this.watchQuery(options ?? WatchOptions$Query$GetApiTokens()); + void writeQuery$GetApiTokens({ + required Query$GetApiTokens data, + bool broadcast = true, + }) => + this.writeQuery( + graphql.Request( + operation: + graphql.Operation(document: documentNodeQueryGetApiTokens)), + data: data.toJson(), + broadcast: broadcast, + ); + Query$GetApiTokens? readQuery$GetApiTokens({bool optimistic = true}) { + final result = this.readQuery( + graphql.Request( + operation: + graphql.Operation(document: documentNodeQueryGetApiTokens)), + optimistic: optimistic, + ); + return result == null ? null : Query$GetApiTokens.fromJson(result); + } +} + +class Query$GetApiTokens$api { + Query$GetApiTokens$api({ + required this.devices, + this.$__typename = 'Api', + }); + + factory Query$GetApiTokens$api.fromJson(Map json) { + final l$devices = json['devices']; + final l$$__typename = json['__typename']; + return Query$GetApiTokens$api( + devices: (l$devices as List) + .map((e) => Query$GetApiTokens$api$devices.fromJson( + (e as Map))) + .toList(), + $__typename: (l$$__typename as String), + ); + } + + final List devices; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$devices = devices; + _resultData['devices'] = l$devices.map((e) => e.toJson()).toList(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$devices = devices; + final l$$__typename = $__typename; + return Object.hashAll([ + Object.hashAll(l$devices.map((v) => v)), + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$GetApiTokens$api) || + runtimeType != other.runtimeType) { + return false; + } + final l$devices = devices; + final lOther$devices = other.devices; + if (l$devices.length != lOther$devices.length) { + return false; + } + for (int i = 0; i < l$devices.length; i++) { + final l$devices$entry = l$devices[i]; + final lOther$devices$entry = lOther$devices[i]; + if (l$devices$entry != lOther$devices$entry) { + return false; + } + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$GetApiTokens$api on Query$GetApiTokens$api { + CopyWith$Query$GetApiTokens$api get copyWith => + CopyWith$Query$GetApiTokens$api( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$GetApiTokens$api { + factory CopyWith$Query$GetApiTokens$api( + Query$GetApiTokens$api instance, + TRes Function(Query$GetApiTokens$api) then, + ) = _CopyWithImpl$Query$GetApiTokens$api; + + factory CopyWith$Query$GetApiTokens$api.stub(TRes res) = + _CopyWithStubImpl$Query$GetApiTokens$api; + + TRes call({ + List? devices, + String? $__typename, + }); + TRes devices( + Iterable Function( + Iterable< + CopyWith$Query$GetApiTokens$api$devices< + Query$GetApiTokens$api$devices>>) + _fn); +} + +class _CopyWithImpl$Query$GetApiTokens$api + implements CopyWith$Query$GetApiTokens$api { + _CopyWithImpl$Query$GetApiTokens$api( + this._instance, + this._then, + ); + + final Query$GetApiTokens$api _instance; + + final TRes Function(Query$GetApiTokens$api) _then; + + static const _undefined = {}; + + TRes call({ + Object? devices = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$GetApiTokens$api( + devices: devices == _undefined || devices == null + ? _instance.devices + : (devices as List), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + TRes devices( + Iterable Function( + Iterable< + CopyWith$Query$GetApiTokens$api$devices< + Query$GetApiTokens$api$devices>>) + _fn) => + call( + devices: _fn(_instance.devices + .map((e) => CopyWith$Query$GetApiTokens$api$devices( + e, + (i) => i, + ))).toList()); +} + +class _CopyWithStubImpl$Query$GetApiTokens$api + implements CopyWith$Query$GetApiTokens$api { + _CopyWithStubImpl$Query$GetApiTokens$api(this._res); + + TRes _res; + + call({ + List? devices, + String? $__typename, + }) => + _res; + devices(_fn) => _res; +} + +class Query$GetApiTokens$api$devices { + Query$GetApiTokens$api$devices({ + required this.creationDate, + required this.isCaller, + required this.name, + this.$__typename = 'ApiDevice', + }); + + factory Query$GetApiTokens$api$devices.fromJson(Map json) { + final l$creationDate = json['creationDate']; + final l$isCaller = json['isCaller']; + final l$name = json['name']; + final l$$__typename = json['__typename']; + return Query$GetApiTokens$api$devices( + creationDate: dateTimeFromJson(l$creationDate), + isCaller: (l$isCaller as bool), + name: (l$name as String), + $__typename: (l$$__typename as String), + ); + } + + final DateTime creationDate; + + final bool isCaller; + + final String name; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$creationDate = creationDate; + _resultData['creationDate'] = dateTimeToJson(l$creationDate); + final l$isCaller = isCaller; + _resultData['isCaller'] = l$isCaller; + final l$name = name; + _resultData['name'] = l$name; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$creationDate = creationDate; + final l$isCaller = isCaller; + final l$name = name; + final l$$__typename = $__typename; + return Object.hashAll([ + l$creationDate, + l$isCaller, + l$name, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$GetApiTokens$api$devices) || + runtimeType != other.runtimeType) { + return false; + } + final l$creationDate = creationDate; + final lOther$creationDate = other.creationDate; + if (l$creationDate != lOther$creationDate) { + return false; + } + final l$isCaller = isCaller; + final lOther$isCaller = other.isCaller; + if (l$isCaller != lOther$isCaller) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$GetApiTokens$api$devices + on Query$GetApiTokens$api$devices { + CopyWith$Query$GetApiTokens$api$devices + get copyWith => CopyWith$Query$GetApiTokens$api$devices( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$GetApiTokens$api$devices { + factory CopyWith$Query$GetApiTokens$api$devices( + Query$GetApiTokens$api$devices instance, + TRes Function(Query$GetApiTokens$api$devices) then, + ) = _CopyWithImpl$Query$GetApiTokens$api$devices; + + factory CopyWith$Query$GetApiTokens$api$devices.stub(TRes res) = + _CopyWithStubImpl$Query$GetApiTokens$api$devices; + + TRes call({ + DateTime? creationDate, + bool? isCaller, + String? name, + String? $__typename, + }); +} + +class _CopyWithImpl$Query$GetApiTokens$api$devices + implements CopyWith$Query$GetApiTokens$api$devices { + _CopyWithImpl$Query$GetApiTokens$api$devices( + this._instance, + this._then, + ); + + final Query$GetApiTokens$api$devices _instance; + + final TRes Function(Query$GetApiTokens$api$devices) _then; + + static const _undefined = {}; + + TRes call({ + Object? creationDate = _undefined, + Object? isCaller = _undefined, + Object? name = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$GetApiTokens$api$devices( + creationDate: creationDate == _undefined || creationDate == null + ? _instance.creationDate + : (creationDate as DateTime), + isCaller: isCaller == _undefined || isCaller == null + ? _instance.isCaller + : (isCaller as bool), + name: name == _undefined || name == null + ? _instance.name + : (name as String), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Query$GetApiTokens$api$devices + implements CopyWith$Query$GetApiTokens$api$devices { + _CopyWithStubImpl$Query$GetApiTokens$api$devices(this._res); + + TRes _res; + + call({ + DateTime? creationDate, + bool? isCaller, + String? name, + String? $__typename, + }) => + _res; +} + +class Query$RecoveryKey { + Query$RecoveryKey({ + required this.api, + this.$__typename = 'Query', + }); + + factory Query$RecoveryKey.fromJson(Map json) { + final l$api = json['api']; + final l$$__typename = json['__typename']; + return Query$RecoveryKey( + api: Query$RecoveryKey$api.fromJson((l$api as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Query$RecoveryKey$api api; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$api = api; + _resultData['api'] = l$api.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$api = api; + final l$$__typename = $__typename; + return Object.hashAll([ + l$api, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$RecoveryKey) || runtimeType != other.runtimeType) { + return false; + } + final l$api = api; + final lOther$api = other.api; + if (l$api != lOther$api) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$RecoveryKey on Query$RecoveryKey { + CopyWith$Query$RecoveryKey get copyWith => + CopyWith$Query$RecoveryKey( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$RecoveryKey { + factory CopyWith$Query$RecoveryKey( + Query$RecoveryKey instance, + TRes Function(Query$RecoveryKey) then, + ) = _CopyWithImpl$Query$RecoveryKey; + + factory CopyWith$Query$RecoveryKey.stub(TRes res) = + _CopyWithStubImpl$Query$RecoveryKey; + + TRes call({ + Query$RecoveryKey$api? api, + String? $__typename, + }); + CopyWith$Query$RecoveryKey$api get api; +} + +class _CopyWithImpl$Query$RecoveryKey + implements CopyWith$Query$RecoveryKey { + _CopyWithImpl$Query$RecoveryKey( + this._instance, + this._then, + ); + + final Query$RecoveryKey _instance; + + final TRes Function(Query$RecoveryKey) _then; + + static const _undefined = {}; + + TRes call({ + Object? api = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$RecoveryKey( + api: api == _undefined || api == null + ? _instance.api + : (api as Query$RecoveryKey$api), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$RecoveryKey$api get api { + final local$api = _instance.api; + return CopyWith$Query$RecoveryKey$api(local$api, (e) => call(api: e)); + } +} + +class _CopyWithStubImpl$Query$RecoveryKey + implements CopyWith$Query$RecoveryKey { + _CopyWithStubImpl$Query$RecoveryKey(this._res); + + TRes _res; + + call({ + Query$RecoveryKey$api? api, + String? $__typename, + }) => + _res; + CopyWith$Query$RecoveryKey$api get api => + CopyWith$Query$RecoveryKey$api.stub(_res); +} + +const documentNodeQueryRecoveryKey = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.query, + name: NameNode(value: 'RecoveryKey'), + variableDefinitions: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'api'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'recoveryKey'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'creationDate'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'exists'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'expirationDate'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'usesLeft'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'valid'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), +]); +Query$RecoveryKey _parserFn$Query$RecoveryKey(Map data) => + Query$RecoveryKey.fromJson(data); +typedef OnQueryComplete$Query$RecoveryKey = FutureOr Function( + Map?, + Query$RecoveryKey?, +); + +class Options$Query$RecoveryKey + extends graphql.QueryOptions { + Options$Query$RecoveryKey({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$RecoveryKey? typedOptimisticResult, + Duration? pollInterval, + graphql.Context? context, + OnQueryComplete$Query$RecoveryKey? onComplete, + graphql.OnQueryError? onError, + }) : onCompleteWithParsed = onComplete, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + pollInterval: pollInterval, + context: context, + onComplete: onComplete == null + ? null + : (data) => onComplete( + data, + data == null ? null : _parserFn$Query$RecoveryKey(data), + ), + onError: onError, + document: documentNodeQueryRecoveryKey, + parserFn: _parserFn$Query$RecoveryKey, + ); + + final OnQueryComplete$Query$RecoveryKey? onCompleteWithParsed; + + @override + List get properties => [ + ...super.onComplete == null + ? super.properties + : super.properties.where((property) => property != onComplete), + onCompleteWithParsed, + ]; +} + +class WatchOptions$Query$RecoveryKey + extends graphql.WatchQueryOptions { + WatchOptions$Query$RecoveryKey({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$RecoveryKey? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeQueryRecoveryKey, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Query$RecoveryKey, + ); +} + +class FetchMoreOptions$Query$RecoveryKey extends graphql.FetchMoreOptions { + FetchMoreOptions$Query$RecoveryKey({required graphql.UpdateQuery updateQuery}) + : super( + updateQuery: updateQuery, + document: documentNodeQueryRecoveryKey, + ); +} + +extension ClientExtension$Query$RecoveryKey on graphql.GraphQLClient { + Future> query$RecoveryKey( + [Options$Query$RecoveryKey? options]) async => + await this.query(options ?? Options$Query$RecoveryKey()); + graphql.ObservableQuery watchQuery$RecoveryKey( + [WatchOptions$Query$RecoveryKey? options]) => + this.watchQuery(options ?? WatchOptions$Query$RecoveryKey()); + void writeQuery$RecoveryKey({ + required Query$RecoveryKey data, + bool broadcast = true, + }) => + this.writeQuery( + graphql.Request( + operation: + graphql.Operation(document: documentNodeQueryRecoveryKey)), + data: data.toJson(), + broadcast: broadcast, + ); + Query$RecoveryKey? readQuery$RecoveryKey({bool optimistic = true}) { + final result = this.readQuery( + graphql.Request( + operation: graphql.Operation(document: documentNodeQueryRecoveryKey)), + optimistic: optimistic, + ); + return result == null ? null : Query$RecoveryKey.fromJson(result); + } +} + +class Query$RecoveryKey$api { + Query$RecoveryKey$api({ + required this.recoveryKey, + this.$__typename = 'Api', + }); + + factory Query$RecoveryKey$api.fromJson(Map json) { + final l$recoveryKey = json['recoveryKey']; + final l$$__typename = json['__typename']; + return Query$RecoveryKey$api( + recoveryKey: Query$RecoveryKey$api$recoveryKey.fromJson( + (l$recoveryKey as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Query$RecoveryKey$api$recoveryKey recoveryKey; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$recoveryKey = recoveryKey; + _resultData['recoveryKey'] = l$recoveryKey.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$recoveryKey = recoveryKey; + final l$$__typename = $__typename; + return Object.hashAll([ + l$recoveryKey, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$RecoveryKey$api) || runtimeType != other.runtimeType) { + return false; + } + final l$recoveryKey = recoveryKey; + final lOther$recoveryKey = other.recoveryKey; + if (l$recoveryKey != lOther$recoveryKey) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$RecoveryKey$api on Query$RecoveryKey$api { + CopyWith$Query$RecoveryKey$api get copyWith => + CopyWith$Query$RecoveryKey$api( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$RecoveryKey$api { + factory CopyWith$Query$RecoveryKey$api( + Query$RecoveryKey$api instance, + TRes Function(Query$RecoveryKey$api) then, + ) = _CopyWithImpl$Query$RecoveryKey$api; + + factory CopyWith$Query$RecoveryKey$api.stub(TRes res) = + _CopyWithStubImpl$Query$RecoveryKey$api; + + TRes call({ + Query$RecoveryKey$api$recoveryKey? recoveryKey, + String? $__typename, + }); + CopyWith$Query$RecoveryKey$api$recoveryKey get recoveryKey; +} + +class _CopyWithImpl$Query$RecoveryKey$api + implements CopyWith$Query$RecoveryKey$api { + _CopyWithImpl$Query$RecoveryKey$api( + this._instance, + this._then, + ); + + final Query$RecoveryKey$api _instance; + + final TRes Function(Query$RecoveryKey$api) _then; + + static const _undefined = {}; + + TRes call({ + Object? recoveryKey = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$RecoveryKey$api( + recoveryKey: recoveryKey == _undefined || recoveryKey == null + ? _instance.recoveryKey + : (recoveryKey as Query$RecoveryKey$api$recoveryKey), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$RecoveryKey$api$recoveryKey get recoveryKey { + final local$recoveryKey = _instance.recoveryKey; + return CopyWith$Query$RecoveryKey$api$recoveryKey( + local$recoveryKey, (e) => call(recoveryKey: e)); + } +} + +class _CopyWithStubImpl$Query$RecoveryKey$api + implements CopyWith$Query$RecoveryKey$api { + _CopyWithStubImpl$Query$RecoveryKey$api(this._res); + + TRes _res; + + call({ + Query$RecoveryKey$api$recoveryKey? recoveryKey, + String? $__typename, + }) => + _res; + CopyWith$Query$RecoveryKey$api$recoveryKey get recoveryKey => + CopyWith$Query$RecoveryKey$api$recoveryKey.stub(_res); +} + +class Query$RecoveryKey$api$recoveryKey { + Query$RecoveryKey$api$recoveryKey({ + this.creationDate, + required this.exists, + this.expirationDate, + this.usesLeft, + required this.valid, + this.$__typename = 'ApiRecoveryKeyStatus', + }); + + factory Query$RecoveryKey$api$recoveryKey.fromJson( + Map json) { + final l$creationDate = json['creationDate']; + final l$exists = json['exists']; + final l$expirationDate = json['expirationDate']; + final l$usesLeft = json['usesLeft']; + final l$valid = json['valid']; + final l$$__typename = json['__typename']; + return Query$RecoveryKey$api$recoveryKey( + creationDate: + l$creationDate == null ? null : dateTimeFromJson(l$creationDate), + exists: (l$exists as bool), + expirationDate: + l$expirationDate == null ? null : dateTimeFromJson(l$expirationDate), + usesLeft: (l$usesLeft as int?), + valid: (l$valid as bool), + $__typename: (l$$__typename as String), + ); + } + + final DateTime? creationDate; + + final bool exists; + + final DateTime? expirationDate; + + final int? usesLeft; + + final bool valid; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$creationDate = creationDate; + _resultData['creationDate'] = + l$creationDate == null ? null : dateTimeToJson(l$creationDate); + final l$exists = exists; + _resultData['exists'] = l$exists; + final l$expirationDate = expirationDate; + _resultData['expirationDate'] = + l$expirationDate == null ? null : dateTimeToJson(l$expirationDate); + final l$usesLeft = usesLeft; + _resultData['usesLeft'] = l$usesLeft; + final l$valid = valid; + _resultData['valid'] = l$valid; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$creationDate = creationDate; + final l$exists = exists; + final l$expirationDate = expirationDate; + final l$usesLeft = usesLeft; + final l$valid = valid; + final l$$__typename = $__typename; + return Object.hashAll([ + l$creationDate, + l$exists, + l$expirationDate, + l$usesLeft, + l$valid, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$RecoveryKey$api$recoveryKey) || + runtimeType != other.runtimeType) { + return false; + } + final l$creationDate = creationDate; + final lOther$creationDate = other.creationDate; + if (l$creationDate != lOther$creationDate) { + return false; + } + final l$exists = exists; + final lOther$exists = other.exists; + if (l$exists != lOther$exists) { + return false; + } + final l$expirationDate = expirationDate; + final lOther$expirationDate = other.expirationDate; + if (l$expirationDate != lOther$expirationDate) { + return false; + } + final l$usesLeft = usesLeft; + final lOther$usesLeft = other.usesLeft; + if (l$usesLeft != lOther$usesLeft) { + return false; + } + final l$valid = valid; + final lOther$valid = other.valid; + if (l$valid != lOther$valid) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$RecoveryKey$api$recoveryKey + on Query$RecoveryKey$api$recoveryKey { + CopyWith$Query$RecoveryKey$api$recoveryKey + get copyWith => CopyWith$Query$RecoveryKey$api$recoveryKey( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$RecoveryKey$api$recoveryKey { + factory CopyWith$Query$RecoveryKey$api$recoveryKey( + Query$RecoveryKey$api$recoveryKey instance, + TRes Function(Query$RecoveryKey$api$recoveryKey) then, + ) = _CopyWithImpl$Query$RecoveryKey$api$recoveryKey; + + factory CopyWith$Query$RecoveryKey$api$recoveryKey.stub(TRes res) = + _CopyWithStubImpl$Query$RecoveryKey$api$recoveryKey; + + TRes call({ + DateTime? creationDate, + bool? exists, + DateTime? expirationDate, + int? usesLeft, + bool? valid, + String? $__typename, + }); +} + +class _CopyWithImpl$Query$RecoveryKey$api$recoveryKey + implements CopyWith$Query$RecoveryKey$api$recoveryKey { + _CopyWithImpl$Query$RecoveryKey$api$recoveryKey( + this._instance, + this._then, + ); + + final Query$RecoveryKey$api$recoveryKey _instance; + + final TRes Function(Query$RecoveryKey$api$recoveryKey) _then; + + static const _undefined = {}; + + TRes call({ + Object? creationDate = _undefined, + Object? exists = _undefined, + Object? expirationDate = _undefined, + Object? usesLeft = _undefined, + Object? valid = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$RecoveryKey$api$recoveryKey( + creationDate: creationDate == _undefined + ? _instance.creationDate + : (creationDate as DateTime?), + exists: exists == _undefined || exists == null + ? _instance.exists + : (exists as bool), + expirationDate: expirationDate == _undefined + ? _instance.expirationDate + : (expirationDate as DateTime?), + usesLeft: + usesLeft == _undefined ? _instance.usesLeft : (usesLeft as int?), + valid: valid == _undefined || valid == null + ? _instance.valid + : (valid as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Query$RecoveryKey$api$recoveryKey + implements CopyWith$Query$RecoveryKey$api$recoveryKey { + _CopyWithStubImpl$Query$RecoveryKey$api$recoveryKey(this._res); + + TRes _res; + + call({ + DateTime? creationDate, + bool? exists, + DateTime? expirationDate, + int? usesLeft, + bool? valid, + String? $__typename, + }) => + _res; +} + +class Variables$Mutation$GetNewRecoveryApiKey { + factory Variables$Mutation$GetNewRecoveryApiKey( + {Input$RecoveryKeyLimitsInput? limits}) => + Variables$Mutation$GetNewRecoveryApiKey._({ + if (limits != null) r'limits': limits, + }); + + Variables$Mutation$GetNewRecoveryApiKey._(this._$data); + + factory Variables$Mutation$GetNewRecoveryApiKey.fromJson( + Map data) { + final result$data = {}; + if (data.containsKey('limits')) { + final l$limits = data['limits']; + result$data['limits'] = l$limits == null + ? null + : Input$RecoveryKeyLimitsInput.fromJson( + (l$limits as Map)); + } + return Variables$Mutation$GetNewRecoveryApiKey._(result$data); + } + + Map _$data; + + Input$RecoveryKeyLimitsInput? get limits => + (_$data['limits'] as Input$RecoveryKeyLimitsInput?); + Map toJson() { + final result$data = {}; + if (_$data.containsKey('limits')) { + final l$limits = limits; + result$data['limits'] = l$limits?.toJson(); + } + return result$data; + } + + CopyWith$Variables$Mutation$GetNewRecoveryApiKey< + Variables$Mutation$GetNewRecoveryApiKey> + get copyWith => CopyWith$Variables$Mutation$GetNewRecoveryApiKey( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$GetNewRecoveryApiKey) || + runtimeType != other.runtimeType) { + return false; + } + final l$limits = limits; + final lOther$limits = other.limits; + if (_$data.containsKey('limits') != other._$data.containsKey('limits')) { + return false; + } + if (l$limits != lOther$limits) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$limits = limits; + return Object.hashAll([_$data.containsKey('limits') ? l$limits : const {}]); + } +} + +abstract class CopyWith$Variables$Mutation$GetNewRecoveryApiKey { + factory CopyWith$Variables$Mutation$GetNewRecoveryApiKey( + Variables$Mutation$GetNewRecoveryApiKey instance, + TRes Function(Variables$Mutation$GetNewRecoveryApiKey) then, + ) = _CopyWithImpl$Variables$Mutation$GetNewRecoveryApiKey; + + factory CopyWith$Variables$Mutation$GetNewRecoveryApiKey.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$GetNewRecoveryApiKey; + + TRes call({Input$RecoveryKeyLimitsInput? limits}); +} + +class _CopyWithImpl$Variables$Mutation$GetNewRecoveryApiKey + implements CopyWith$Variables$Mutation$GetNewRecoveryApiKey { + _CopyWithImpl$Variables$Mutation$GetNewRecoveryApiKey( + this._instance, + this._then, + ); + + final Variables$Mutation$GetNewRecoveryApiKey _instance; + + final TRes Function(Variables$Mutation$GetNewRecoveryApiKey) _then; + + static const _undefined = {}; + + TRes call({Object? limits = _undefined}) => + _then(Variables$Mutation$GetNewRecoveryApiKey._({ + ..._instance._$data, + if (limits != _undefined) + 'limits': (limits as Input$RecoveryKeyLimitsInput?), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$GetNewRecoveryApiKey + implements CopyWith$Variables$Mutation$GetNewRecoveryApiKey { + _CopyWithStubImpl$Variables$Mutation$GetNewRecoveryApiKey(this._res); + + TRes _res; + + call({Input$RecoveryKeyLimitsInput? limits}) => _res; +} + +class Mutation$GetNewRecoveryApiKey { + Mutation$GetNewRecoveryApiKey({ + required this.getNewRecoveryApiKey, + this.$__typename = 'Mutation', + }); + + factory Mutation$GetNewRecoveryApiKey.fromJson(Map json) { + final l$getNewRecoveryApiKey = json['getNewRecoveryApiKey']; + final l$$__typename = json['__typename']; + return Mutation$GetNewRecoveryApiKey( + getNewRecoveryApiKey: + Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey.fromJson( + (l$getNewRecoveryApiKey as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey getNewRecoveryApiKey; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$getNewRecoveryApiKey = getNewRecoveryApiKey; + _resultData['getNewRecoveryApiKey'] = l$getNewRecoveryApiKey.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$getNewRecoveryApiKey = getNewRecoveryApiKey; + final l$$__typename = $__typename; + return Object.hashAll([ + l$getNewRecoveryApiKey, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$GetNewRecoveryApiKey) || + runtimeType != other.runtimeType) { + return false; + } + final l$getNewRecoveryApiKey = getNewRecoveryApiKey; + final lOther$getNewRecoveryApiKey = other.getNewRecoveryApiKey; + if (l$getNewRecoveryApiKey != lOther$getNewRecoveryApiKey) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$GetNewRecoveryApiKey + on Mutation$GetNewRecoveryApiKey { + CopyWith$Mutation$GetNewRecoveryApiKey + get copyWith => CopyWith$Mutation$GetNewRecoveryApiKey( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$GetNewRecoveryApiKey { + factory CopyWith$Mutation$GetNewRecoveryApiKey( + Mutation$GetNewRecoveryApiKey instance, + TRes Function(Mutation$GetNewRecoveryApiKey) then, + ) = _CopyWithImpl$Mutation$GetNewRecoveryApiKey; + + factory CopyWith$Mutation$GetNewRecoveryApiKey.stub(TRes res) = + _CopyWithStubImpl$Mutation$GetNewRecoveryApiKey; + + TRes call({ + Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey? getNewRecoveryApiKey, + String? $__typename, + }); + CopyWith$Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey + get getNewRecoveryApiKey; +} + +class _CopyWithImpl$Mutation$GetNewRecoveryApiKey + implements CopyWith$Mutation$GetNewRecoveryApiKey { + _CopyWithImpl$Mutation$GetNewRecoveryApiKey( + this._instance, + this._then, + ); + + final Mutation$GetNewRecoveryApiKey _instance; + + final TRes Function(Mutation$GetNewRecoveryApiKey) _then; + + static const _undefined = {}; + + TRes call({ + Object? getNewRecoveryApiKey = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$GetNewRecoveryApiKey( + getNewRecoveryApiKey: + getNewRecoveryApiKey == _undefined || getNewRecoveryApiKey == null + ? _instance.getNewRecoveryApiKey + : (getNewRecoveryApiKey + as Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey + get getNewRecoveryApiKey { + final local$getNewRecoveryApiKey = _instance.getNewRecoveryApiKey; + return CopyWith$Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey( + local$getNewRecoveryApiKey, (e) => call(getNewRecoveryApiKey: e)); + } +} + +class _CopyWithStubImpl$Mutation$GetNewRecoveryApiKey + implements CopyWith$Mutation$GetNewRecoveryApiKey { + _CopyWithStubImpl$Mutation$GetNewRecoveryApiKey(this._res); + + TRes _res; + + call({ + Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey? getNewRecoveryApiKey, + String? $__typename, + }) => + _res; + CopyWith$Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey + get getNewRecoveryApiKey => + CopyWith$Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey.stub( + _res); +} + +const documentNodeMutationGetNewRecoveryApiKey = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'GetNewRecoveryApiKey'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'limits')), + type: NamedTypeNode( + name: NameNode(value: 'RecoveryKeyLimitsInput'), + isNonNull: false, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'getNewRecoveryApiKey'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'limits'), + value: VariableNode(name: NameNode(value: 'limits')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: 'key'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, +]); +Mutation$GetNewRecoveryApiKey _parserFn$Mutation$GetNewRecoveryApiKey( + Map data) => + Mutation$GetNewRecoveryApiKey.fromJson(data); +typedef OnMutationCompleted$Mutation$GetNewRecoveryApiKey = FutureOr + Function( + Map?, + Mutation$GetNewRecoveryApiKey?, +); + +class Options$Mutation$GetNewRecoveryApiKey + extends graphql.MutationOptions { + Options$Mutation$GetNewRecoveryApiKey({ + String? operationName, + Variables$Mutation$GetNewRecoveryApiKey? variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$GetNewRecoveryApiKey? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$GetNewRecoveryApiKey? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables?.toJson() ?? {}, + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$GetNewRecoveryApiKey(data), + ), + update: update, + onError: onError, + document: documentNodeMutationGetNewRecoveryApiKey, + parserFn: _parserFn$Mutation$GetNewRecoveryApiKey, + ); + + final OnMutationCompleted$Mutation$GetNewRecoveryApiKey? + onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$GetNewRecoveryApiKey + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$GetNewRecoveryApiKey({ + String? operationName, + Variables$Mutation$GetNewRecoveryApiKey? variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$GetNewRecoveryApiKey? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables?.toJson() ?? {}, + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationGetNewRecoveryApiKey, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$GetNewRecoveryApiKey, + ); +} + +extension ClientExtension$Mutation$GetNewRecoveryApiKey + on graphql.GraphQLClient { + Future> + mutate$GetNewRecoveryApiKey( + [Options$Mutation$GetNewRecoveryApiKey? options]) async => + await this.mutate(options ?? Options$Mutation$GetNewRecoveryApiKey()); + graphql.ObservableQuery + watchMutation$GetNewRecoveryApiKey( + [WatchOptions$Mutation$GetNewRecoveryApiKey? options]) => + this.watchMutation( + options ?? WatchOptions$Mutation$GetNewRecoveryApiKey()); +} + +class Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey + implements Fragment$basicMutationReturnFields$$ApiKeyMutationReturn { + Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'ApiKeyMutationReturn', + this.key, + }); + + factory Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + final l$key = json['key']; + return Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + key: (l$key as String?), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + final String? key; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + final l$key = key; + _resultData['key'] = l$key; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + final l$key = key; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + l$key, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + final l$key = key; + final lOther$key = other.key; + if (l$key != lOther$key) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey + on Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey { + CopyWith$Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey< + Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey> + get copyWith => + CopyWith$Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey< + TRes> { + factory CopyWith$Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey( + Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey instance, + TRes Function(Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey) then, + ) = _CopyWithImpl$Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey; + + factory CopyWith$Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey.stub( + TRes res) = + _CopyWithStubImpl$Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + String? key, + }); +} + +class _CopyWithImpl$Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey + implements + CopyWith$Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey { + _CopyWithImpl$Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey( + this._instance, + this._then, + ); + + final Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey _instance; + + final TRes Function(Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + Object? key = _undefined, + }) => + _then(Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + key: key == _undefined ? _instance.key : (key as String?), + )); +} + +class _CopyWithStubImpl$Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey + implements + CopyWith$Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey { + _CopyWithStubImpl$Mutation$GetNewRecoveryApiKey$getNewRecoveryApiKey( + this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + String? key, + }) => + _res; +} + +class Variables$Mutation$UseRecoveryApiKey { + factory Variables$Mutation$UseRecoveryApiKey( + {required Input$UseRecoveryKeyInput input}) => + Variables$Mutation$UseRecoveryApiKey._({ + r'input': input, + }); + + Variables$Mutation$UseRecoveryApiKey._(this._$data); + + factory Variables$Mutation$UseRecoveryApiKey.fromJson( + Map data) { + final result$data = {}; + final l$input = data['input']; + result$data['input'] = + Input$UseRecoveryKeyInput.fromJson((l$input as Map)); + return Variables$Mutation$UseRecoveryApiKey._(result$data); + } + + Map _$data; + + Input$UseRecoveryKeyInput get input => + (_$data['input'] as Input$UseRecoveryKeyInput); + Map toJson() { + final result$data = {}; + final l$input = input; + result$data['input'] = l$input.toJson(); + return result$data; + } + + CopyWith$Variables$Mutation$UseRecoveryApiKey< + Variables$Mutation$UseRecoveryApiKey> + get copyWith => CopyWith$Variables$Mutation$UseRecoveryApiKey( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$UseRecoveryApiKey) || + runtimeType != other.runtimeType) { + return false; + } + final l$input = input; + final lOther$input = other.input; + if (l$input != lOther$input) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$input = input; + return Object.hashAll([l$input]); + } +} + +abstract class CopyWith$Variables$Mutation$UseRecoveryApiKey { + factory CopyWith$Variables$Mutation$UseRecoveryApiKey( + Variables$Mutation$UseRecoveryApiKey instance, + TRes Function(Variables$Mutation$UseRecoveryApiKey) then, + ) = _CopyWithImpl$Variables$Mutation$UseRecoveryApiKey; + + factory CopyWith$Variables$Mutation$UseRecoveryApiKey.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$UseRecoveryApiKey; + + TRes call({Input$UseRecoveryKeyInput? input}); +} + +class _CopyWithImpl$Variables$Mutation$UseRecoveryApiKey + implements CopyWith$Variables$Mutation$UseRecoveryApiKey { + _CopyWithImpl$Variables$Mutation$UseRecoveryApiKey( + this._instance, + this._then, + ); + + final Variables$Mutation$UseRecoveryApiKey _instance; + + final TRes Function(Variables$Mutation$UseRecoveryApiKey) _then; + + static const _undefined = {}; + + TRes call({Object? input = _undefined}) => + _then(Variables$Mutation$UseRecoveryApiKey._({ + ..._instance._$data, + if (input != _undefined && input != null) + 'input': (input as Input$UseRecoveryKeyInput), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$UseRecoveryApiKey + implements CopyWith$Variables$Mutation$UseRecoveryApiKey { + _CopyWithStubImpl$Variables$Mutation$UseRecoveryApiKey(this._res); + + TRes _res; + + call({Input$UseRecoveryKeyInput? input}) => _res; +} + +class Mutation$UseRecoveryApiKey { + Mutation$UseRecoveryApiKey({ + required this.useRecoveryApiKey, + this.$__typename = 'Mutation', + }); + + factory Mutation$UseRecoveryApiKey.fromJson(Map json) { + final l$useRecoveryApiKey = json['useRecoveryApiKey']; + final l$$__typename = json['__typename']; + return Mutation$UseRecoveryApiKey( + useRecoveryApiKey: Mutation$UseRecoveryApiKey$useRecoveryApiKey.fromJson( + (l$useRecoveryApiKey as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$UseRecoveryApiKey$useRecoveryApiKey useRecoveryApiKey; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$useRecoveryApiKey = useRecoveryApiKey; + _resultData['useRecoveryApiKey'] = l$useRecoveryApiKey.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$useRecoveryApiKey = useRecoveryApiKey; + final l$$__typename = $__typename; + return Object.hashAll([ + l$useRecoveryApiKey, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$UseRecoveryApiKey) || + runtimeType != other.runtimeType) { + return false; + } + final l$useRecoveryApiKey = useRecoveryApiKey; + final lOther$useRecoveryApiKey = other.useRecoveryApiKey; + if (l$useRecoveryApiKey != lOther$useRecoveryApiKey) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$UseRecoveryApiKey + on Mutation$UseRecoveryApiKey { + CopyWith$Mutation$UseRecoveryApiKey + get copyWith => CopyWith$Mutation$UseRecoveryApiKey( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$UseRecoveryApiKey { + factory CopyWith$Mutation$UseRecoveryApiKey( + Mutation$UseRecoveryApiKey instance, + TRes Function(Mutation$UseRecoveryApiKey) then, + ) = _CopyWithImpl$Mutation$UseRecoveryApiKey; + + factory CopyWith$Mutation$UseRecoveryApiKey.stub(TRes res) = + _CopyWithStubImpl$Mutation$UseRecoveryApiKey; + + TRes call({ + Mutation$UseRecoveryApiKey$useRecoveryApiKey? useRecoveryApiKey, + String? $__typename, + }); + CopyWith$Mutation$UseRecoveryApiKey$useRecoveryApiKey + get useRecoveryApiKey; +} + +class _CopyWithImpl$Mutation$UseRecoveryApiKey + implements CopyWith$Mutation$UseRecoveryApiKey { + _CopyWithImpl$Mutation$UseRecoveryApiKey( + this._instance, + this._then, + ); + + final Mutation$UseRecoveryApiKey _instance; + + final TRes Function(Mutation$UseRecoveryApiKey) _then; + + static const _undefined = {}; + + TRes call({ + Object? useRecoveryApiKey = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$UseRecoveryApiKey( + useRecoveryApiKey: + useRecoveryApiKey == _undefined || useRecoveryApiKey == null + ? _instance.useRecoveryApiKey + : (useRecoveryApiKey + as Mutation$UseRecoveryApiKey$useRecoveryApiKey), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$UseRecoveryApiKey$useRecoveryApiKey + get useRecoveryApiKey { + final local$useRecoveryApiKey = _instance.useRecoveryApiKey; + return CopyWith$Mutation$UseRecoveryApiKey$useRecoveryApiKey( + local$useRecoveryApiKey, (e) => call(useRecoveryApiKey: e)); + } +} + +class _CopyWithStubImpl$Mutation$UseRecoveryApiKey + implements CopyWith$Mutation$UseRecoveryApiKey { + _CopyWithStubImpl$Mutation$UseRecoveryApiKey(this._res); + + TRes _res; + + call({ + Mutation$UseRecoveryApiKey$useRecoveryApiKey? useRecoveryApiKey, + String? $__typename, + }) => + _res; + CopyWith$Mutation$UseRecoveryApiKey$useRecoveryApiKey + get useRecoveryApiKey => + CopyWith$Mutation$UseRecoveryApiKey$useRecoveryApiKey.stub(_res); +} + +const documentNodeMutationUseRecoveryApiKey = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'UseRecoveryApiKey'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'input')), + type: NamedTypeNode( + name: NameNode(value: 'UseRecoveryKeyInput'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'useRecoveryApiKey'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'input'), + value: VariableNode(name: NameNode(value: 'input')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: 'token'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, +]); +Mutation$UseRecoveryApiKey _parserFn$Mutation$UseRecoveryApiKey( + Map data) => + Mutation$UseRecoveryApiKey.fromJson(data); +typedef OnMutationCompleted$Mutation$UseRecoveryApiKey = FutureOr + Function( + Map?, + Mutation$UseRecoveryApiKey?, +); + +class Options$Mutation$UseRecoveryApiKey + extends graphql.MutationOptions { + Options$Mutation$UseRecoveryApiKey({ + String? operationName, + required Variables$Mutation$UseRecoveryApiKey variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$UseRecoveryApiKey? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$UseRecoveryApiKey? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$UseRecoveryApiKey(data), + ), + update: update, + onError: onError, + document: documentNodeMutationUseRecoveryApiKey, + parserFn: _parserFn$Mutation$UseRecoveryApiKey, + ); + + final OnMutationCompleted$Mutation$UseRecoveryApiKey? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$UseRecoveryApiKey + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$UseRecoveryApiKey({ + String? operationName, + required Variables$Mutation$UseRecoveryApiKey variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$UseRecoveryApiKey? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationUseRecoveryApiKey, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$UseRecoveryApiKey, + ); +} + +extension ClientExtension$Mutation$UseRecoveryApiKey on graphql.GraphQLClient { + Future> + mutate$UseRecoveryApiKey( + Options$Mutation$UseRecoveryApiKey options) async => + await this.mutate(options); + graphql.ObservableQuery + watchMutation$UseRecoveryApiKey( + WatchOptions$Mutation$UseRecoveryApiKey options) => + this.watchMutation(options); +} + +class Mutation$UseRecoveryApiKey$useRecoveryApiKey + implements + Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn { + Mutation$UseRecoveryApiKey$useRecoveryApiKey({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'DeviceApiTokenMutationReturn', + this.token, + }); + + factory Mutation$UseRecoveryApiKey$useRecoveryApiKey.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + final l$token = json['token']; + return Mutation$UseRecoveryApiKey$useRecoveryApiKey( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + token: (l$token as String?), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + final String? token; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + final l$token = token; + _resultData['token'] = l$token; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + final l$token = token; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + l$token, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$UseRecoveryApiKey$useRecoveryApiKey) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + final l$token = token; + final lOther$token = other.token; + if (l$token != lOther$token) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$UseRecoveryApiKey$useRecoveryApiKey + on Mutation$UseRecoveryApiKey$useRecoveryApiKey { + CopyWith$Mutation$UseRecoveryApiKey$useRecoveryApiKey< + Mutation$UseRecoveryApiKey$useRecoveryApiKey> + get copyWith => CopyWith$Mutation$UseRecoveryApiKey$useRecoveryApiKey( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$UseRecoveryApiKey$useRecoveryApiKey { + factory CopyWith$Mutation$UseRecoveryApiKey$useRecoveryApiKey( + Mutation$UseRecoveryApiKey$useRecoveryApiKey instance, + TRes Function(Mutation$UseRecoveryApiKey$useRecoveryApiKey) then, + ) = _CopyWithImpl$Mutation$UseRecoveryApiKey$useRecoveryApiKey; + + factory CopyWith$Mutation$UseRecoveryApiKey$useRecoveryApiKey.stub(TRes res) = + _CopyWithStubImpl$Mutation$UseRecoveryApiKey$useRecoveryApiKey; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + String? token, + }); +} + +class _CopyWithImpl$Mutation$UseRecoveryApiKey$useRecoveryApiKey + implements CopyWith$Mutation$UseRecoveryApiKey$useRecoveryApiKey { + _CopyWithImpl$Mutation$UseRecoveryApiKey$useRecoveryApiKey( + this._instance, + this._then, + ); + + final Mutation$UseRecoveryApiKey$useRecoveryApiKey _instance; + + final TRes Function(Mutation$UseRecoveryApiKey$useRecoveryApiKey) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + Object? token = _undefined, + }) => + _then(Mutation$UseRecoveryApiKey$useRecoveryApiKey( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + token: token == _undefined ? _instance.token : (token as String?), + )); +} + +class _CopyWithStubImpl$Mutation$UseRecoveryApiKey$useRecoveryApiKey + implements CopyWith$Mutation$UseRecoveryApiKey$useRecoveryApiKey { + _CopyWithStubImpl$Mutation$UseRecoveryApiKey$useRecoveryApiKey(this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + String? token, + }) => + _res; +} + +class Mutation$RefreshDeviceApiToken { + Mutation$RefreshDeviceApiToken({ + required this.refreshDeviceApiToken, + this.$__typename = 'Mutation', + }); + + factory Mutation$RefreshDeviceApiToken.fromJson(Map json) { + final l$refreshDeviceApiToken = json['refreshDeviceApiToken']; + final l$$__typename = json['__typename']; + return Mutation$RefreshDeviceApiToken( + refreshDeviceApiToken: + Mutation$RefreshDeviceApiToken$refreshDeviceApiToken.fromJson( + (l$refreshDeviceApiToken as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$RefreshDeviceApiToken$refreshDeviceApiToken + refreshDeviceApiToken; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$refreshDeviceApiToken = refreshDeviceApiToken; + _resultData['refreshDeviceApiToken'] = l$refreshDeviceApiToken.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$refreshDeviceApiToken = refreshDeviceApiToken; + final l$$__typename = $__typename; + return Object.hashAll([ + l$refreshDeviceApiToken, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$RefreshDeviceApiToken) || + runtimeType != other.runtimeType) { + return false; + } + final l$refreshDeviceApiToken = refreshDeviceApiToken; + final lOther$refreshDeviceApiToken = other.refreshDeviceApiToken; + if (l$refreshDeviceApiToken != lOther$refreshDeviceApiToken) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$RefreshDeviceApiToken + on Mutation$RefreshDeviceApiToken { + CopyWith$Mutation$RefreshDeviceApiToken + get copyWith => CopyWith$Mutation$RefreshDeviceApiToken( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$RefreshDeviceApiToken { + factory CopyWith$Mutation$RefreshDeviceApiToken( + Mutation$RefreshDeviceApiToken instance, + TRes Function(Mutation$RefreshDeviceApiToken) then, + ) = _CopyWithImpl$Mutation$RefreshDeviceApiToken; + + factory CopyWith$Mutation$RefreshDeviceApiToken.stub(TRes res) = + _CopyWithStubImpl$Mutation$RefreshDeviceApiToken; + + TRes call({ + Mutation$RefreshDeviceApiToken$refreshDeviceApiToken? refreshDeviceApiToken, + String? $__typename, + }); + CopyWith$Mutation$RefreshDeviceApiToken$refreshDeviceApiToken + get refreshDeviceApiToken; +} + +class _CopyWithImpl$Mutation$RefreshDeviceApiToken + implements CopyWith$Mutation$RefreshDeviceApiToken { + _CopyWithImpl$Mutation$RefreshDeviceApiToken( + this._instance, + this._then, + ); + + final Mutation$RefreshDeviceApiToken _instance; + + final TRes Function(Mutation$RefreshDeviceApiToken) _then; + + static const _undefined = {}; + + TRes call({ + Object? refreshDeviceApiToken = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$RefreshDeviceApiToken( + refreshDeviceApiToken: + refreshDeviceApiToken == _undefined || refreshDeviceApiToken == null + ? _instance.refreshDeviceApiToken + : (refreshDeviceApiToken + as Mutation$RefreshDeviceApiToken$refreshDeviceApiToken), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$RefreshDeviceApiToken$refreshDeviceApiToken + get refreshDeviceApiToken { + final local$refreshDeviceApiToken = _instance.refreshDeviceApiToken; + return CopyWith$Mutation$RefreshDeviceApiToken$refreshDeviceApiToken( + local$refreshDeviceApiToken, (e) => call(refreshDeviceApiToken: e)); + } +} + +class _CopyWithStubImpl$Mutation$RefreshDeviceApiToken + implements CopyWith$Mutation$RefreshDeviceApiToken { + _CopyWithStubImpl$Mutation$RefreshDeviceApiToken(this._res); + + TRes _res; + + call({ + Mutation$RefreshDeviceApiToken$refreshDeviceApiToken? refreshDeviceApiToken, + String? $__typename, + }) => + _res; + CopyWith$Mutation$RefreshDeviceApiToken$refreshDeviceApiToken + get refreshDeviceApiToken => + CopyWith$Mutation$RefreshDeviceApiToken$refreshDeviceApiToken.stub( + _res); +} + +const documentNodeMutationRefreshDeviceApiToken = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'RefreshDeviceApiToken'), + variableDefinitions: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'refreshDeviceApiToken'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: 'token'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, +]); +Mutation$RefreshDeviceApiToken _parserFn$Mutation$RefreshDeviceApiToken( + Map data) => + Mutation$RefreshDeviceApiToken.fromJson(data); +typedef OnMutationCompleted$Mutation$RefreshDeviceApiToken = FutureOr + Function( + Map?, + Mutation$RefreshDeviceApiToken?, +); + +class Options$Mutation$RefreshDeviceApiToken + extends graphql.MutationOptions { + Options$Mutation$RefreshDeviceApiToken({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RefreshDeviceApiToken? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$RefreshDeviceApiToken? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$RefreshDeviceApiToken(data), + ), + update: update, + onError: onError, + document: documentNodeMutationRefreshDeviceApiToken, + parserFn: _parserFn$Mutation$RefreshDeviceApiToken, + ); + + final OnMutationCompleted$Mutation$RefreshDeviceApiToken? + onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$RefreshDeviceApiToken + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$RefreshDeviceApiToken({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RefreshDeviceApiToken? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationRefreshDeviceApiToken, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$RefreshDeviceApiToken, + ); +} + +extension ClientExtension$Mutation$RefreshDeviceApiToken + on graphql.GraphQLClient { + Future> + mutate$RefreshDeviceApiToken( + [Options$Mutation$RefreshDeviceApiToken? options]) async => + await this + .mutate(options ?? Options$Mutation$RefreshDeviceApiToken()); + graphql.ObservableQuery + watchMutation$RefreshDeviceApiToken( + [WatchOptions$Mutation$RefreshDeviceApiToken? options]) => + this.watchMutation( + options ?? WatchOptions$Mutation$RefreshDeviceApiToken()); +} + +class Mutation$RefreshDeviceApiToken$refreshDeviceApiToken + implements + Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn { + Mutation$RefreshDeviceApiToken$refreshDeviceApiToken({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'DeviceApiTokenMutationReturn', + this.token, + }); + + factory Mutation$RefreshDeviceApiToken$refreshDeviceApiToken.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + final l$token = json['token']; + return Mutation$RefreshDeviceApiToken$refreshDeviceApiToken( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + token: (l$token as String?), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + final String? token; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + final l$token = token; + _resultData['token'] = l$token; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + final l$token = token; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + l$token, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$RefreshDeviceApiToken$refreshDeviceApiToken) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + final l$token = token; + final lOther$token = other.token; + if (l$token != lOther$token) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$RefreshDeviceApiToken$refreshDeviceApiToken + on Mutation$RefreshDeviceApiToken$refreshDeviceApiToken { + CopyWith$Mutation$RefreshDeviceApiToken$refreshDeviceApiToken< + Mutation$RefreshDeviceApiToken$refreshDeviceApiToken> + get copyWith => + CopyWith$Mutation$RefreshDeviceApiToken$refreshDeviceApiToken( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$RefreshDeviceApiToken$refreshDeviceApiToken< + TRes> { + factory CopyWith$Mutation$RefreshDeviceApiToken$refreshDeviceApiToken( + Mutation$RefreshDeviceApiToken$refreshDeviceApiToken instance, + TRes Function(Mutation$RefreshDeviceApiToken$refreshDeviceApiToken) then, + ) = _CopyWithImpl$Mutation$RefreshDeviceApiToken$refreshDeviceApiToken; + + factory CopyWith$Mutation$RefreshDeviceApiToken$refreshDeviceApiToken.stub( + TRes res) = + _CopyWithStubImpl$Mutation$RefreshDeviceApiToken$refreshDeviceApiToken; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + String? token, + }); +} + +class _CopyWithImpl$Mutation$RefreshDeviceApiToken$refreshDeviceApiToken + implements + CopyWith$Mutation$RefreshDeviceApiToken$refreshDeviceApiToken { + _CopyWithImpl$Mutation$RefreshDeviceApiToken$refreshDeviceApiToken( + this._instance, + this._then, + ); + + final Mutation$RefreshDeviceApiToken$refreshDeviceApiToken _instance; + + final TRes Function(Mutation$RefreshDeviceApiToken$refreshDeviceApiToken) + _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + Object? token = _undefined, + }) => + _then(Mutation$RefreshDeviceApiToken$refreshDeviceApiToken( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + token: token == _undefined ? _instance.token : (token as String?), + )); +} + +class _CopyWithStubImpl$Mutation$RefreshDeviceApiToken$refreshDeviceApiToken< + TRes> + implements + CopyWith$Mutation$RefreshDeviceApiToken$refreshDeviceApiToken { + _CopyWithStubImpl$Mutation$RefreshDeviceApiToken$refreshDeviceApiToken( + this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + String? token, + }) => + _res; +} + +class Variables$Mutation$DeleteDeviceApiToken { + factory Variables$Mutation$DeleteDeviceApiToken({required String device}) => + Variables$Mutation$DeleteDeviceApiToken._({ + r'device': device, + }); + + Variables$Mutation$DeleteDeviceApiToken._(this._$data); + + factory Variables$Mutation$DeleteDeviceApiToken.fromJson( + Map data) { + final result$data = {}; + final l$device = data['device']; + result$data['device'] = (l$device as String); + return Variables$Mutation$DeleteDeviceApiToken._(result$data); + } + + Map _$data; + + String get device => (_$data['device'] as String); + Map toJson() { + final result$data = {}; + final l$device = device; + result$data['device'] = l$device; + return result$data; + } + + CopyWith$Variables$Mutation$DeleteDeviceApiToken< + Variables$Mutation$DeleteDeviceApiToken> + get copyWith => CopyWith$Variables$Mutation$DeleteDeviceApiToken( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$DeleteDeviceApiToken) || + runtimeType != other.runtimeType) { + return false; + } + final l$device = device; + final lOther$device = other.device; + if (l$device != lOther$device) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$device = device; + return Object.hashAll([l$device]); + } +} + +abstract class CopyWith$Variables$Mutation$DeleteDeviceApiToken { + factory CopyWith$Variables$Mutation$DeleteDeviceApiToken( + Variables$Mutation$DeleteDeviceApiToken instance, + TRes Function(Variables$Mutation$DeleteDeviceApiToken) then, + ) = _CopyWithImpl$Variables$Mutation$DeleteDeviceApiToken; + + factory CopyWith$Variables$Mutation$DeleteDeviceApiToken.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$DeleteDeviceApiToken; + + TRes call({String? device}); +} + +class _CopyWithImpl$Variables$Mutation$DeleteDeviceApiToken + implements CopyWith$Variables$Mutation$DeleteDeviceApiToken { + _CopyWithImpl$Variables$Mutation$DeleteDeviceApiToken( + this._instance, + this._then, + ); + + final Variables$Mutation$DeleteDeviceApiToken _instance; + + final TRes Function(Variables$Mutation$DeleteDeviceApiToken) _then; + + static const _undefined = {}; + + TRes call({Object? device = _undefined}) => + _then(Variables$Mutation$DeleteDeviceApiToken._({ + ..._instance._$data, + if (device != _undefined && device != null) + 'device': (device as String), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$DeleteDeviceApiToken + implements CopyWith$Variables$Mutation$DeleteDeviceApiToken { + _CopyWithStubImpl$Variables$Mutation$DeleteDeviceApiToken(this._res); + + TRes _res; + + call({String? device}) => _res; +} + +class Mutation$DeleteDeviceApiToken { + Mutation$DeleteDeviceApiToken({ + required this.deleteDeviceApiToken, + this.$__typename = 'Mutation', + }); + + factory Mutation$DeleteDeviceApiToken.fromJson(Map json) { + final l$deleteDeviceApiToken = json['deleteDeviceApiToken']; + final l$$__typename = json['__typename']; + return Mutation$DeleteDeviceApiToken( + deleteDeviceApiToken: + Mutation$DeleteDeviceApiToken$deleteDeviceApiToken.fromJson( + (l$deleteDeviceApiToken as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$DeleteDeviceApiToken$deleteDeviceApiToken deleteDeviceApiToken; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$deleteDeviceApiToken = deleteDeviceApiToken; + _resultData['deleteDeviceApiToken'] = l$deleteDeviceApiToken.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$deleteDeviceApiToken = deleteDeviceApiToken; + final l$$__typename = $__typename; + return Object.hashAll([ + l$deleteDeviceApiToken, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$DeleteDeviceApiToken) || + runtimeType != other.runtimeType) { + return false; + } + final l$deleteDeviceApiToken = deleteDeviceApiToken; + final lOther$deleteDeviceApiToken = other.deleteDeviceApiToken; + if (l$deleteDeviceApiToken != lOther$deleteDeviceApiToken) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$DeleteDeviceApiToken + on Mutation$DeleteDeviceApiToken { + CopyWith$Mutation$DeleteDeviceApiToken + get copyWith => CopyWith$Mutation$DeleteDeviceApiToken( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$DeleteDeviceApiToken { + factory CopyWith$Mutation$DeleteDeviceApiToken( + Mutation$DeleteDeviceApiToken instance, + TRes Function(Mutation$DeleteDeviceApiToken) then, + ) = _CopyWithImpl$Mutation$DeleteDeviceApiToken; + + factory CopyWith$Mutation$DeleteDeviceApiToken.stub(TRes res) = + _CopyWithStubImpl$Mutation$DeleteDeviceApiToken; + + TRes call({ + Mutation$DeleteDeviceApiToken$deleteDeviceApiToken? deleteDeviceApiToken, + String? $__typename, + }); + CopyWith$Mutation$DeleteDeviceApiToken$deleteDeviceApiToken + get deleteDeviceApiToken; +} + +class _CopyWithImpl$Mutation$DeleteDeviceApiToken + implements CopyWith$Mutation$DeleteDeviceApiToken { + _CopyWithImpl$Mutation$DeleteDeviceApiToken( + this._instance, + this._then, + ); + + final Mutation$DeleteDeviceApiToken _instance; + + final TRes Function(Mutation$DeleteDeviceApiToken) _then; + + static const _undefined = {}; + + TRes call({ + Object? deleteDeviceApiToken = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$DeleteDeviceApiToken( + deleteDeviceApiToken: + deleteDeviceApiToken == _undefined || deleteDeviceApiToken == null + ? _instance.deleteDeviceApiToken + : (deleteDeviceApiToken + as Mutation$DeleteDeviceApiToken$deleteDeviceApiToken), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$DeleteDeviceApiToken$deleteDeviceApiToken + get deleteDeviceApiToken { + final local$deleteDeviceApiToken = _instance.deleteDeviceApiToken; + return CopyWith$Mutation$DeleteDeviceApiToken$deleteDeviceApiToken( + local$deleteDeviceApiToken, (e) => call(deleteDeviceApiToken: e)); + } +} + +class _CopyWithStubImpl$Mutation$DeleteDeviceApiToken + implements CopyWith$Mutation$DeleteDeviceApiToken { + _CopyWithStubImpl$Mutation$DeleteDeviceApiToken(this._res); + + TRes _res; + + call({ + Mutation$DeleteDeviceApiToken$deleteDeviceApiToken? deleteDeviceApiToken, + String? $__typename, + }) => + _res; + CopyWith$Mutation$DeleteDeviceApiToken$deleteDeviceApiToken + get deleteDeviceApiToken => + CopyWith$Mutation$DeleteDeviceApiToken$deleteDeviceApiToken.stub( + _res); +} + +const documentNodeMutationDeleteDeviceApiToken = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'DeleteDeviceApiToken'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'device')), + type: NamedTypeNode( + name: NameNode(value: 'String'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'deleteDeviceApiToken'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'device'), + value: VariableNode(name: NameNode(value: 'device')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, +]); +Mutation$DeleteDeviceApiToken _parserFn$Mutation$DeleteDeviceApiToken( + Map data) => + Mutation$DeleteDeviceApiToken.fromJson(data); +typedef OnMutationCompleted$Mutation$DeleteDeviceApiToken = FutureOr + Function( + Map?, + Mutation$DeleteDeviceApiToken?, +); + +class Options$Mutation$DeleteDeviceApiToken + extends graphql.MutationOptions { + Options$Mutation$DeleteDeviceApiToken({ + String? operationName, + required Variables$Mutation$DeleteDeviceApiToken variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$DeleteDeviceApiToken? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$DeleteDeviceApiToken? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$DeleteDeviceApiToken(data), + ), + update: update, + onError: onError, + document: documentNodeMutationDeleteDeviceApiToken, + parserFn: _parserFn$Mutation$DeleteDeviceApiToken, + ); + + final OnMutationCompleted$Mutation$DeleteDeviceApiToken? + onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$DeleteDeviceApiToken + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$DeleteDeviceApiToken({ + String? operationName, + required Variables$Mutation$DeleteDeviceApiToken variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$DeleteDeviceApiToken? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationDeleteDeviceApiToken, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$DeleteDeviceApiToken, + ); +} + +extension ClientExtension$Mutation$DeleteDeviceApiToken + on graphql.GraphQLClient { + Future> + mutate$DeleteDeviceApiToken( + Options$Mutation$DeleteDeviceApiToken options) async => + await this.mutate(options); + graphql.ObservableQuery + watchMutation$DeleteDeviceApiToken( + WatchOptions$Mutation$DeleteDeviceApiToken options) => + this.watchMutation(options); +} + +class Mutation$DeleteDeviceApiToken$deleteDeviceApiToken + implements Fragment$basicMutationReturnFields$$GenericMutationReturn { + Mutation$DeleteDeviceApiToken$deleteDeviceApiToken({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'GenericMutationReturn', + }); + + factory Mutation$DeleteDeviceApiToken$deleteDeviceApiToken.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Mutation$DeleteDeviceApiToken$deleteDeviceApiToken( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$DeleteDeviceApiToken$deleteDeviceApiToken) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$DeleteDeviceApiToken$deleteDeviceApiToken + on Mutation$DeleteDeviceApiToken$deleteDeviceApiToken { + CopyWith$Mutation$DeleteDeviceApiToken$deleteDeviceApiToken< + Mutation$DeleteDeviceApiToken$deleteDeviceApiToken> + get copyWith => + CopyWith$Mutation$DeleteDeviceApiToken$deleteDeviceApiToken( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$DeleteDeviceApiToken$deleteDeviceApiToken< + TRes> { + factory CopyWith$Mutation$DeleteDeviceApiToken$deleteDeviceApiToken( + Mutation$DeleteDeviceApiToken$deleteDeviceApiToken instance, + TRes Function(Mutation$DeleteDeviceApiToken$deleteDeviceApiToken) then, + ) = _CopyWithImpl$Mutation$DeleteDeviceApiToken$deleteDeviceApiToken; + + factory CopyWith$Mutation$DeleteDeviceApiToken$deleteDeviceApiToken.stub( + TRes res) = + _CopyWithStubImpl$Mutation$DeleteDeviceApiToken$deleteDeviceApiToken; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$DeleteDeviceApiToken$deleteDeviceApiToken + implements + CopyWith$Mutation$DeleteDeviceApiToken$deleteDeviceApiToken { + _CopyWithImpl$Mutation$DeleteDeviceApiToken$deleteDeviceApiToken( + this._instance, + this._then, + ); + + final Mutation$DeleteDeviceApiToken$deleteDeviceApiToken _instance; + + final TRes Function(Mutation$DeleteDeviceApiToken$deleteDeviceApiToken) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$DeleteDeviceApiToken$deleteDeviceApiToken( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$DeleteDeviceApiToken$deleteDeviceApiToken + implements + CopyWith$Mutation$DeleteDeviceApiToken$deleteDeviceApiToken { + _CopyWithStubImpl$Mutation$DeleteDeviceApiToken$deleteDeviceApiToken( + this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +class Mutation$GetNewDeviceApiKey { + Mutation$GetNewDeviceApiKey({ + required this.getNewDeviceApiKey, + this.$__typename = 'Mutation', + }); + + factory Mutation$GetNewDeviceApiKey.fromJson(Map json) { + final l$getNewDeviceApiKey = json['getNewDeviceApiKey']; + final l$$__typename = json['__typename']; + return Mutation$GetNewDeviceApiKey( + getNewDeviceApiKey: + Mutation$GetNewDeviceApiKey$getNewDeviceApiKey.fromJson( + (l$getNewDeviceApiKey as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$GetNewDeviceApiKey$getNewDeviceApiKey getNewDeviceApiKey; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$getNewDeviceApiKey = getNewDeviceApiKey; + _resultData['getNewDeviceApiKey'] = l$getNewDeviceApiKey.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$getNewDeviceApiKey = getNewDeviceApiKey; + final l$$__typename = $__typename; + return Object.hashAll([ + l$getNewDeviceApiKey, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$GetNewDeviceApiKey) || + runtimeType != other.runtimeType) { + return false; + } + final l$getNewDeviceApiKey = getNewDeviceApiKey; + final lOther$getNewDeviceApiKey = other.getNewDeviceApiKey; + if (l$getNewDeviceApiKey != lOther$getNewDeviceApiKey) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$GetNewDeviceApiKey + on Mutation$GetNewDeviceApiKey { + CopyWith$Mutation$GetNewDeviceApiKey + get copyWith => CopyWith$Mutation$GetNewDeviceApiKey( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$GetNewDeviceApiKey { + factory CopyWith$Mutation$GetNewDeviceApiKey( + Mutation$GetNewDeviceApiKey instance, + TRes Function(Mutation$GetNewDeviceApiKey) then, + ) = _CopyWithImpl$Mutation$GetNewDeviceApiKey; + + factory CopyWith$Mutation$GetNewDeviceApiKey.stub(TRes res) = + _CopyWithStubImpl$Mutation$GetNewDeviceApiKey; + + TRes call({ + Mutation$GetNewDeviceApiKey$getNewDeviceApiKey? getNewDeviceApiKey, + String? $__typename, + }); + CopyWith$Mutation$GetNewDeviceApiKey$getNewDeviceApiKey + get getNewDeviceApiKey; +} + +class _CopyWithImpl$Mutation$GetNewDeviceApiKey + implements CopyWith$Mutation$GetNewDeviceApiKey { + _CopyWithImpl$Mutation$GetNewDeviceApiKey( + this._instance, + this._then, + ); + + final Mutation$GetNewDeviceApiKey _instance; + + final TRes Function(Mutation$GetNewDeviceApiKey) _then; + + static const _undefined = {}; + + TRes call({ + Object? getNewDeviceApiKey = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$GetNewDeviceApiKey( + getNewDeviceApiKey: + getNewDeviceApiKey == _undefined || getNewDeviceApiKey == null + ? _instance.getNewDeviceApiKey + : (getNewDeviceApiKey + as Mutation$GetNewDeviceApiKey$getNewDeviceApiKey), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$GetNewDeviceApiKey$getNewDeviceApiKey + get getNewDeviceApiKey { + final local$getNewDeviceApiKey = _instance.getNewDeviceApiKey; + return CopyWith$Mutation$GetNewDeviceApiKey$getNewDeviceApiKey( + local$getNewDeviceApiKey, (e) => call(getNewDeviceApiKey: e)); + } +} + +class _CopyWithStubImpl$Mutation$GetNewDeviceApiKey + implements CopyWith$Mutation$GetNewDeviceApiKey { + _CopyWithStubImpl$Mutation$GetNewDeviceApiKey(this._res); + + TRes _res; + + call({ + Mutation$GetNewDeviceApiKey$getNewDeviceApiKey? getNewDeviceApiKey, + String? $__typename, + }) => + _res; + CopyWith$Mutation$GetNewDeviceApiKey$getNewDeviceApiKey + get getNewDeviceApiKey => + CopyWith$Mutation$GetNewDeviceApiKey$getNewDeviceApiKey.stub(_res); +} + +const documentNodeMutationGetNewDeviceApiKey = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'GetNewDeviceApiKey'), + variableDefinitions: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'getNewDeviceApiKey'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: 'key'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, +]); +Mutation$GetNewDeviceApiKey _parserFn$Mutation$GetNewDeviceApiKey( + Map data) => + Mutation$GetNewDeviceApiKey.fromJson(data); +typedef OnMutationCompleted$Mutation$GetNewDeviceApiKey = FutureOr + Function( + Map?, + Mutation$GetNewDeviceApiKey?, +); + +class Options$Mutation$GetNewDeviceApiKey + extends graphql.MutationOptions { + Options$Mutation$GetNewDeviceApiKey({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$GetNewDeviceApiKey? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$GetNewDeviceApiKey? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$GetNewDeviceApiKey(data), + ), + update: update, + onError: onError, + document: documentNodeMutationGetNewDeviceApiKey, + parserFn: _parserFn$Mutation$GetNewDeviceApiKey, + ); + + final OnMutationCompleted$Mutation$GetNewDeviceApiKey? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$GetNewDeviceApiKey + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$GetNewDeviceApiKey({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$GetNewDeviceApiKey? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationGetNewDeviceApiKey, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$GetNewDeviceApiKey, + ); +} + +extension ClientExtension$Mutation$GetNewDeviceApiKey on graphql.GraphQLClient { + Future> + mutate$GetNewDeviceApiKey( + [Options$Mutation$GetNewDeviceApiKey? options]) async => + await this.mutate(options ?? Options$Mutation$GetNewDeviceApiKey()); + graphql.ObservableQuery< + Mutation$GetNewDeviceApiKey> watchMutation$GetNewDeviceApiKey( + [WatchOptions$Mutation$GetNewDeviceApiKey? options]) => + this.watchMutation(options ?? WatchOptions$Mutation$GetNewDeviceApiKey()); +} + +class Mutation$GetNewDeviceApiKey$getNewDeviceApiKey + implements Fragment$basicMutationReturnFields$$ApiKeyMutationReturn { + Mutation$GetNewDeviceApiKey$getNewDeviceApiKey({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'ApiKeyMutationReturn', + this.key, + }); + + factory Mutation$GetNewDeviceApiKey$getNewDeviceApiKey.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + final l$key = json['key']; + return Mutation$GetNewDeviceApiKey$getNewDeviceApiKey( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + key: (l$key as String?), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + final String? key; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + final l$key = key; + _resultData['key'] = l$key; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + final l$key = key; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + l$key, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$GetNewDeviceApiKey$getNewDeviceApiKey) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + final l$key = key; + final lOther$key = other.key; + if (l$key != lOther$key) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$GetNewDeviceApiKey$getNewDeviceApiKey + on Mutation$GetNewDeviceApiKey$getNewDeviceApiKey { + CopyWith$Mutation$GetNewDeviceApiKey$getNewDeviceApiKey< + Mutation$GetNewDeviceApiKey$getNewDeviceApiKey> + get copyWith => CopyWith$Mutation$GetNewDeviceApiKey$getNewDeviceApiKey( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$GetNewDeviceApiKey$getNewDeviceApiKey { + factory CopyWith$Mutation$GetNewDeviceApiKey$getNewDeviceApiKey( + Mutation$GetNewDeviceApiKey$getNewDeviceApiKey instance, + TRes Function(Mutation$GetNewDeviceApiKey$getNewDeviceApiKey) then, + ) = _CopyWithImpl$Mutation$GetNewDeviceApiKey$getNewDeviceApiKey; + + factory CopyWith$Mutation$GetNewDeviceApiKey$getNewDeviceApiKey.stub( + TRes res) = + _CopyWithStubImpl$Mutation$GetNewDeviceApiKey$getNewDeviceApiKey; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + String? key, + }); +} + +class _CopyWithImpl$Mutation$GetNewDeviceApiKey$getNewDeviceApiKey + implements CopyWith$Mutation$GetNewDeviceApiKey$getNewDeviceApiKey { + _CopyWithImpl$Mutation$GetNewDeviceApiKey$getNewDeviceApiKey( + this._instance, + this._then, + ); + + final Mutation$GetNewDeviceApiKey$getNewDeviceApiKey _instance; + + final TRes Function(Mutation$GetNewDeviceApiKey$getNewDeviceApiKey) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + Object? key = _undefined, + }) => + _then(Mutation$GetNewDeviceApiKey$getNewDeviceApiKey( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + key: key == _undefined ? _instance.key : (key as String?), + )); +} + +class _CopyWithStubImpl$Mutation$GetNewDeviceApiKey$getNewDeviceApiKey + implements CopyWith$Mutation$GetNewDeviceApiKey$getNewDeviceApiKey { + _CopyWithStubImpl$Mutation$GetNewDeviceApiKey$getNewDeviceApiKey(this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + String? key, + }) => + _res; +} + +class Mutation$InvalidateNewDeviceApiKey { + Mutation$InvalidateNewDeviceApiKey({ + required this.invalidateNewDeviceApiKey, + this.$__typename = 'Mutation', + }); + + factory Mutation$InvalidateNewDeviceApiKey.fromJson( + Map json) { + final l$invalidateNewDeviceApiKey = json['invalidateNewDeviceApiKey']; + final l$$__typename = json['__typename']; + return Mutation$InvalidateNewDeviceApiKey( + invalidateNewDeviceApiKey: + Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey.fromJson( + (l$invalidateNewDeviceApiKey as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey + invalidateNewDeviceApiKey; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$invalidateNewDeviceApiKey = invalidateNewDeviceApiKey; + _resultData['invalidateNewDeviceApiKey'] = + l$invalidateNewDeviceApiKey.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$invalidateNewDeviceApiKey = invalidateNewDeviceApiKey; + final l$$__typename = $__typename; + return Object.hashAll([ + l$invalidateNewDeviceApiKey, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$InvalidateNewDeviceApiKey) || + runtimeType != other.runtimeType) { + return false; + } + final l$invalidateNewDeviceApiKey = invalidateNewDeviceApiKey; + final lOther$invalidateNewDeviceApiKey = other.invalidateNewDeviceApiKey; + if (l$invalidateNewDeviceApiKey != lOther$invalidateNewDeviceApiKey) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$InvalidateNewDeviceApiKey + on Mutation$InvalidateNewDeviceApiKey { + CopyWith$Mutation$InvalidateNewDeviceApiKey< + Mutation$InvalidateNewDeviceApiKey> + get copyWith => CopyWith$Mutation$InvalidateNewDeviceApiKey( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$InvalidateNewDeviceApiKey { + factory CopyWith$Mutation$InvalidateNewDeviceApiKey( + Mutation$InvalidateNewDeviceApiKey instance, + TRes Function(Mutation$InvalidateNewDeviceApiKey) then, + ) = _CopyWithImpl$Mutation$InvalidateNewDeviceApiKey; + + factory CopyWith$Mutation$InvalidateNewDeviceApiKey.stub(TRes res) = + _CopyWithStubImpl$Mutation$InvalidateNewDeviceApiKey; + + TRes call({ + Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey? + invalidateNewDeviceApiKey, + String? $__typename, + }); + CopyWith$Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey + get invalidateNewDeviceApiKey; +} + +class _CopyWithImpl$Mutation$InvalidateNewDeviceApiKey + implements CopyWith$Mutation$InvalidateNewDeviceApiKey { + _CopyWithImpl$Mutation$InvalidateNewDeviceApiKey( + this._instance, + this._then, + ); + + final Mutation$InvalidateNewDeviceApiKey _instance; + + final TRes Function(Mutation$InvalidateNewDeviceApiKey) _then; + + static const _undefined = {}; + + TRes call({ + Object? invalidateNewDeviceApiKey = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$InvalidateNewDeviceApiKey( + invalidateNewDeviceApiKey: invalidateNewDeviceApiKey == _undefined || + invalidateNewDeviceApiKey == null + ? _instance.invalidateNewDeviceApiKey + : (invalidateNewDeviceApiKey + as Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey + get invalidateNewDeviceApiKey { + final local$invalidateNewDeviceApiKey = _instance.invalidateNewDeviceApiKey; + return CopyWith$Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey( + local$invalidateNewDeviceApiKey, + (e) => call(invalidateNewDeviceApiKey: e)); + } +} + +class _CopyWithStubImpl$Mutation$InvalidateNewDeviceApiKey + implements CopyWith$Mutation$InvalidateNewDeviceApiKey { + _CopyWithStubImpl$Mutation$InvalidateNewDeviceApiKey(this._res); + + TRes _res; + + call({ + Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey? + invalidateNewDeviceApiKey, + String? $__typename, + }) => + _res; + CopyWith$Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey + get invalidateNewDeviceApiKey => + CopyWith$Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey + .stub(_res); +} + +const documentNodeMutationInvalidateNewDeviceApiKey = + DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'InvalidateNewDeviceApiKey'), + variableDefinitions: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'invalidateNewDeviceApiKey'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, +]); +Mutation$InvalidateNewDeviceApiKey _parserFn$Mutation$InvalidateNewDeviceApiKey( + Map data) => + Mutation$InvalidateNewDeviceApiKey.fromJson(data); +typedef OnMutationCompleted$Mutation$InvalidateNewDeviceApiKey = FutureOr + Function( + Map?, + Mutation$InvalidateNewDeviceApiKey?, +); + +class Options$Mutation$InvalidateNewDeviceApiKey + extends graphql.MutationOptions { + Options$Mutation$InvalidateNewDeviceApiKey({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$InvalidateNewDeviceApiKey? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$InvalidateNewDeviceApiKey? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$InvalidateNewDeviceApiKey(data), + ), + update: update, + onError: onError, + document: documentNodeMutationInvalidateNewDeviceApiKey, + parserFn: _parserFn$Mutation$InvalidateNewDeviceApiKey, + ); + + final OnMutationCompleted$Mutation$InvalidateNewDeviceApiKey? + onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$InvalidateNewDeviceApiKey + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$InvalidateNewDeviceApiKey({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$InvalidateNewDeviceApiKey? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationInvalidateNewDeviceApiKey, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$InvalidateNewDeviceApiKey, + ); +} + +extension ClientExtension$Mutation$InvalidateNewDeviceApiKey + on graphql.GraphQLClient { + Future> + mutate$InvalidateNewDeviceApiKey( + [Options$Mutation$InvalidateNewDeviceApiKey? options]) async => + await this + .mutate(options ?? Options$Mutation$InvalidateNewDeviceApiKey()); + graphql.ObservableQuery + watchMutation$InvalidateNewDeviceApiKey( + [WatchOptions$Mutation$InvalidateNewDeviceApiKey? options]) => + this.watchMutation( + options ?? WatchOptions$Mutation$InvalidateNewDeviceApiKey()); +} + +class Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey + implements Fragment$basicMutationReturnFields$$GenericMutationReturn { + Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'GenericMutationReturn', + }); + + factory Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other + is Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey + on Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey { + CopyWith$Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey< + Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey> + get copyWith => + CopyWith$Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey< + TRes> { + factory CopyWith$Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey( + Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey instance, + TRes Function(Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey) + then, + ) = _CopyWithImpl$Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey; + + factory CopyWith$Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey.stub( + TRes res) = + _CopyWithStubImpl$Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey< + TRes> + implements + CopyWith$Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey< + TRes> { + _CopyWithImpl$Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey( + this._instance, + this._then, + ); + + final Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey _instance; + + final TRes Function( + Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey< + TRes> + implements + CopyWith$Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey< + TRes> { + _CopyWithStubImpl$Mutation$InvalidateNewDeviceApiKey$invalidateNewDeviceApiKey( + this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +class Variables$Mutation$AuthorizeWithNewDeviceApiKey { + factory Variables$Mutation$AuthorizeWithNewDeviceApiKey( + {required Input$UseNewDeviceKeyInput input}) => + Variables$Mutation$AuthorizeWithNewDeviceApiKey._({ + r'input': input, + }); + + Variables$Mutation$AuthorizeWithNewDeviceApiKey._(this._$data); + + factory Variables$Mutation$AuthorizeWithNewDeviceApiKey.fromJson( + Map data) { + final result$data = {}; + final l$input = data['input']; + result$data['input'] = + Input$UseNewDeviceKeyInput.fromJson((l$input as Map)); + return Variables$Mutation$AuthorizeWithNewDeviceApiKey._(result$data); + } + + Map _$data; + + Input$UseNewDeviceKeyInput get input => + (_$data['input'] as Input$UseNewDeviceKeyInput); + Map toJson() { + final result$data = {}; + final l$input = input; + result$data['input'] = l$input.toJson(); + return result$data; + } + + CopyWith$Variables$Mutation$AuthorizeWithNewDeviceApiKey< + Variables$Mutation$AuthorizeWithNewDeviceApiKey> + get copyWith => CopyWith$Variables$Mutation$AuthorizeWithNewDeviceApiKey( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$AuthorizeWithNewDeviceApiKey) || + runtimeType != other.runtimeType) { + return false; + } + final l$input = input; + final lOther$input = other.input; + if (l$input != lOther$input) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$input = input; + return Object.hashAll([l$input]); + } +} + +abstract class CopyWith$Variables$Mutation$AuthorizeWithNewDeviceApiKey { + factory CopyWith$Variables$Mutation$AuthorizeWithNewDeviceApiKey( + Variables$Mutation$AuthorizeWithNewDeviceApiKey instance, + TRes Function(Variables$Mutation$AuthorizeWithNewDeviceApiKey) then, + ) = _CopyWithImpl$Variables$Mutation$AuthorizeWithNewDeviceApiKey; + + factory CopyWith$Variables$Mutation$AuthorizeWithNewDeviceApiKey.stub( + TRes res) = + _CopyWithStubImpl$Variables$Mutation$AuthorizeWithNewDeviceApiKey; + + TRes call({Input$UseNewDeviceKeyInput? input}); +} + +class _CopyWithImpl$Variables$Mutation$AuthorizeWithNewDeviceApiKey + implements CopyWith$Variables$Mutation$AuthorizeWithNewDeviceApiKey { + _CopyWithImpl$Variables$Mutation$AuthorizeWithNewDeviceApiKey( + this._instance, + this._then, + ); + + final Variables$Mutation$AuthorizeWithNewDeviceApiKey _instance; + + final TRes Function(Variables$Mutation$AuthorizeWithNewDeviceApiKey) _then; + + static const _undefined = {}; + + TRes call({Object? input = _undefined}) => + _then(Variables$Mutation$AuthorizeWithNewDeviceApiKey._({ + ..._instance._$data, + if (input != _undefined && input != null) + 'input': (input as Input$UseNewDeviceKeyInput), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$AuthorizeWithNewDeviceApiKey + implements CopyWith$Variables$Mutation$AuthorizeWithNewDeviceApiKey { + _CopyWithStubImpl$Variables$Mutation$AuthorizeWithNewDeviceApiKey(this._res); + + TRes _res; + + call({Input$UseNewDeviceKeyInput? input}) => _res; +} + +class Mutation$AuthorizeWithNewDeviceApiKey { + Mutation$AuthorizeWithNewDeviceApiKey({ + required this.authorizeWithNewDeviceApiKey, + this.$__typename = 'Mutation', + }); + + factory Mutation$AuthorizeWithNewDeviceApiKey.fromJson( + Map json) { + final l$authorizeWithNewDeviceApiKey = json['authorizeWithNewDeviceApiKey']; + final l$$__typename = json['__typename']; + return Mutation$AuthorizeWithNewDeviceApiKey( + authorizeWithNewDeviceApiKey: + Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey + .fromJson( + (l$authorizeWithNewDeviceApiKey as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey + authorizeWithNewDeviceApiKey; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$authorizeWithNewDeviceApiKey = authorizeWithNewDeviceApiKey; + _resultData['authorizeWithNewDeviceApiKey'] = + l$authorizeWithNewDeviceApiKey.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$authorizeWithNewDeviceApiKey = authorizeWithNewDeviceApiKey; + final l$$__typename = $__typename; + return Object.hashAll([ + l$authorizeWithNewDeviceApiKey, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$AuthorizeWithNewDeviceApiKey) || + runtimeType != other.runtimeType) { + return false; + } + final l$authorizeWithNewDeviceApiKey = authorizeWithNewDeviceApiKey; + final lOther$authorizeWithNewDeviceApiKey = + other.authorizeWithNewDeviceApiKey; + if (l$authorizeWithNewDeviceApiKey != lOther$authorizeWithNewDeviceApiKey) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$AuthorizeWithNewDeviceApiKey + on Mutation$AuthorizeWithNewDeviceApiKey { + CopyWith$Mutation$AuthorizeWithNewDeviceApiKey< + Mutation$AuthorizeWithNewDeviceApiKey> + get copyWith => CopyWith$Mutation$AuthorizeWithNewDeviceApiKey( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$AuthorizeWithNewDeviceApiKey { + factory CopyWith$Mutation$AuthorizeWithNewDeviceApiKey( + Mutation$AuthorizeWithNewDeviceApiKey instance, + TRes Function(Mutation$AuthorizeWithNewDeviceApiKey) then, + ) = _CopyWithImpl$Mutation$AuthorizeWithNewDeviceApiKey; + + factory CopyWith$Mutation$AuthorizeWithNewDeviceApiKey.stub(TRes res) = + _CopyWithStubImpl$Mutation$AuthorizeWithNewDeviceApiKey; + + TRes call({ + Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey? + authorizeWithNewDeviceApiKey, + String? $__typename, + }); + CopyWith$Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey< + TRes> get authorizeWithNewDeviceApiKey; +} + +class _CopyWithImpl$Mutation$AuthorizeWithNewDeviceApiKey + implements CopyWith$Mutation$AuthorizeWithNewDeviceApiKey { + _CopyWithImpl$Mutation$AuthorizeWithNewDeviceApiKey( + this._instance, + this._then, + ); + + final Mutation$AuthorizeWithNewDeviceApiKey _instance; + + final TRes Function(Mutation$AuthorizeWithNewDeviceApiKey) _then; + + static const _undefined = {}; + + TRes call({ + Object? authorizeWithNewDeviceApiKey = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$AuthorizeWithNewDeviceApiKey( + authorizeWithNewDeviceApiKey: authorizeWithNewDeviceApiKey == + _undefined || + authorizeWithNewDeviceApiKey == null + ? _instance.authorizeWithNewDeviceApiKey + : (authorizeWithNewDeviceApiKey + as Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey< + TRes> get authorizeWithNewDeviceApiKey { + final local$authorizeWithNewDeviceApiKey = + _instance.authorizeWithNewDeviceApiKey; + return CopyWith$Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey( + local$authorizeWithNewDeviceApiKey, + (e) => call(authorizeWithNewDeviceApiKey: e)); + } +} + +class _CopyWithStubImpl$Mutation$AuthorizeWithNewDeviceApiKey + implements CopyWith$Mutation$AuthorizeWithNewDeviceApiKey { + _CopyWithStubImpl$Mutation$AuthorizeWithNewDeviceApiKey(this._res); + + TRes _res; + + call({ + Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey? + authorizeWithNewDeviceApiKey, + String? $__typename, + }) => + _res; + CopyWith$Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey< + TRes> + get authorizeWithNewDeviceApiKey => + CopyWith$Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey + .stub(_res); +} + +const documentNodeMutationAuthorizeWithNewDeviceApiKey = + DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'AuthorizeWithNewDeviceApiKey'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'input')), + type: NamedTypeNode( + name: NameNode(value: 'UseNewDeviceKeyInput'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'authorizeWithNewDeviceApiKey'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'input'), + value: VariableNode(name: NameNode(value: 'input')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: 'token'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, +]); +Mutation$AuthorizeWithNewDeviceApiKey + _parserFn$Mutation$AuthorizeWithNewDeviceApiKey( + Map data) => + Mutation$AuthorizeWithNewDeviceApiKey.fromJson(data); +typedef OnMutationCompleted$Mutation$AuthorizeWithNewDeviceApiKey + = FutureOr Function( + Map?, + Mutation$AuthorizeWithNewDeviceApiKey?, +); + +class Options$Mutation$AuthorizeWithNewDeviceApiKey + extends graphql.MutationOptions { + Options$Mutation$AuthorizeWithNewDeviceApiKey({ + String? operationName, + required Variables$Mutation$AuthorizeWithNewDeviceApiKey variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$AuthorizeWithNewDeviceApiKey? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$AuthorizeWithNewDeviceApiKey? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$AuthorizeWithNewDeviceApiKey(data), + ), + update: update, + onError: onError, + document: documentNodeMutationAuthorizeWithNewDeviceApiKey, + parserFn: _parserFn$Mutation$AuthorizeWithNewDeviceApiKey, + ); + + final OnMutationCompleted$Mutation$AuthorizeWithNewDeviceApiKey? + onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$AuthorizeWithNewDeviceApiKey + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$AuthorizeWithNewDeviceApiKey({ + String? operationName, + required Variables$Mutation$AuthorizeWithNewDeviceApiKey variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$AuthorizeWithNewDeviceApiKey? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationAuthorizeWithNewDeviceApiKey, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$AuthorizeWithNewDeviceApiKey, + ); +} + +extension ClientExtension$Mutation$AuthorizeWithNewDeviceApiKey + on graphql.GraphQLClient { + Future> + mutate$AuthorizeWithNewDeviceApiKey( + Options$Mutation$AuthorizeWithNewDeviceApiKey options) async => + await this.mutate(options); + graphql.ObservableQuery + watchMutation$AuthorizeWithNewDeviceApiKey( + WatchOptions$Mutation$AuthorizeWithNewDeviceApiKey options) => + this.watchMutation(options); +} + +class Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey + implements + Fragment$basicMutationReturnFields$$DeviceApiTokenMutationReturn { + Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'DeviceApiTokenMutationReturn', + this.token, + }); + + factory Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + final l$token = json['token']; + return Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + token: (l$token as String?), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + final String? token; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + final l$token = token; + _resultData['token'] = l$token; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + final l$token = token; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + l$token, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other + is Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + final l$token = token; + final lOther$token = other.token; + if (l$token != lOther$token) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey + on Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey { + CopyWith$Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey< + Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey> + get copyWith => + CopyWith$Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey< + TRes> { + factory CopyWith$Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey( + Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey instance, + TRes Function( + Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey) + then, + ) = _CopyWithImpl$Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey; + + factory CopyWith$Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey.stub( + TRes res) = + _CopyWithStubImpl$Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + String? token, + }); +} + +class _CopyWithImpl$Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey< + TRes> + implements + CopyWith$Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey< + TRes> { + _CopyWithImpl$Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey( + this._instance, + this._then, + ); + + final Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey + _instance; + + final TRes Function( + Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + Object? token = _undefined, + }) => + _then(Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + token: token == _undefined ? _instance.token : (token as String?), + )); +} + +class _CopyWithStubImpl$Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey< + TRes> + implements + CopyWith$Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey< + TRes> { + _CopyWithStubImpl$Mutation$AuthorizeWithNewDeviceApiKey$authorizeWithNewDeviceApiKey( + this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + String? token, + }) => + _res; +} diff --git a/lib/logic/api_maps/graphql_maps/schema/server_settings.graphql b/lib/logic/api_maps/graphql_maps/schema/server_settings.graphql new file mode 100644 index 00000000..f0fbedd6 --- /dev/null +++ b/lib/logic/api_maps/graphql_maps/schema/server_settings.graphql @@ -0,0 +1,59 @@ +query SystemSettings { + system { + settings { + autoUpgrade { + allowReboot + enable + } + ssh { + enable + passwordAuthentication + } + timezone + } + } +} + +query SystemIsUsingBinds { + system { + info { + usingBinds + } + } +} + +fragment fragmentDnsRecords on DnsRecord { + recordType + name + content + ttl + priority +} + +query DomainInfo { + system { + domainInfo { + domain + hostname + provider + requiredDnsRecords { + ...fragmentDnsRecords + } + } + } +} + +mutation ChangeTimezone($timezone: String!) { + changeTimezone(timezone: $timezone) { + ...basicMutationReturnFields + timezone + } +} + +mutation ChangeAutoUpgradeSettings($settings: AutoUpgradeSettingsInput!) { + changeAutoUpgradeSettings(settings: $settings) { + ...basicMutationReturnFields + allowReboot + enableAutoUpgrade + } +} diff --git a/lib/logic/api_maps/graphql_maps/schema/server_settings.graphql.dart b/lib/logic/api_maps/graphql_maps/schema/server_settings.graphql.dart new file mode 100644 index 00000000..bcc677e8 --- /dev/null +++ b/lib/logic/api_maps/graphql_maps/schema/server_settings.graphql.dart @@ -0,0 +1,3756 @@ +import 'dart:async'; +import 'package:gql/ast.dart'; +import 'package:graphql/client.dart' as graphql; +import 'schema.graphql.dart'; +import 'server_api.graphql.dart'; + +class Fragment$fragmentDnsRecords { + Fragment$fragmentDnsRecords({ + required this.recordType, + required this.name, + required this.content, + required this.ttl, + this.priority, + this.$__typename = 'DnsRecord', + }); + + factory Fragment$fragmentDnsRecords.fromJson(Map json) { + final l$recordType = json['recordType']; + final l$name = json['name']; + final l$content = json['content']; + final l$ttl = json['ttl']; + final l$priority = json['priority']; + final l$$__typename = json['__typename']; + return Fragment$fragmentDnsRecords( + recordType: (l$recordType as String), + name: (l$name as String), + content: (l$content as String), + ttl: (l$ttl as int), + priority: (l$priority as int?), + $__typename: (l$$__typename as String), + ); + } + + final String recordType; + + final String name; + + final String content; + + final int ttl; + + final int? priority; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$recordType = recordType; + _resultData['recordType'] = l$recordType; + final l$name = name; + _resultData['name'] = l$name; + final l$content = content; + _resultData['content'] = l$content; + final l$ttl = ttl; + _resultData['ttl'] = l$ttl; + final l$priority = priority; + _resultData['priority'] = l$priority; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$recordType = recordType; + final l$name = name; + final l$content = content; + final l$ttl = ttl; + final l$priority = priority; + final l$$__typename = $__typename; + return Object.hashAll([ + l$recordType, + l$name, + l$content, + l$ttl, + l$priority, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Fragment$fragmentDnsRecords) || + runtimeType != other.runtimeType) { + return false; + } + final l$recordType = recordType; + final lOther$recordType = other.recordType; + if (l$recordType != lOther$recordType) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$content = content; + final lOther$content = other.content; + if (l$content != lOther$content) { + return false; + } + final l$ttl = ttl; + final lOther$ttl = other.ttl; + if (l$ttl != lOther$ttl) { + return false; + } + final l$priority = priority; + final lOther$priority = other.priority; + if (l$priority != lOther$priority) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$fragmentDnsRecords + on Fragment$fragmentDnsRecords { + CopyWith$Fragment$fragmentDnsRecords + get copyWith => CopyWith$Fragment$fragmentDnsRecords( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$fragmentDnsRecords { + factory CopyWith$Fragment$fragmentDnsRecords( + Fragment$fragmentDnsRecords instance, + TRes Function(Fragment$fragmentDnsRecords) then, + ) = _CopyWithImpl$Fragment$fragmentDnsRecords; + + factory CopyWith$Fragment$fragmentDnsRecords.stub(TRes res) = + _CopyWithStubImpl$Fragment$fragmentDnsRecords; + + TRes call({ + String? recordType, + String? name, + String? content, + int? ttl, + int? priority, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$fragmentDnsRecords + implements CopyWith$Fragment$fragmentDnsRecords { + _CopyWithImpl$Fragment$fragmentDnsRecords( + this._instance, + this._then, + ); + + final Fragment$fragmentDnsRecords _instance; + + final TRes Function(Fragment$fragmentDnsRecords) _then; + + static const _undefined = {}; + + TRes call({ + Object? recordType = _undefined, + Object? name = _undefined, + Object? content = _undefined, + Object? ttl = _undefined, + Object? priority = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$fragmentDnsRecords( + recordType: recordType == _undefined || recordType == null + ? _instance.recordType + : (recordType as String), + name: name == _undefined || name == null + ? _instance.name + : (name as String), + content: content == _undefined || content == null + ? _instance.content + : (content as String), + ttl: ttl == _undefined || ttl == null ? _instance.ttl : (ttl as int), + priority: + priority == _undefined ? _instance.priority : (priority as int?), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$fragmentDnsRecords + implements CopyWith$Fragment$fragmentDnsRecords { + _CopyWithStubImpl$Fragment$fragmentDnsRecords(this._res); + + TRes _res; + + call({ + String? recordType, + String? name, + String? content, + int? ttl, + int? priority, + String? $__typename, + }) => + _res; +} + +const fragmentDefinitionfragmentDnsRecords = FragmentDefinitionNode( + name: NameNode(value: 'fragmentDnsRecords'), + typeCondition: TypeConditionNode( + on: NamedTypeNode( + name: NameNode(value: 'DnsRecord'), + isNonNull: false, + )), + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'recordType'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'name'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'content'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'ttl'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'priority'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), +); +const documentNodeFragmentfragmentDnsRecords = DocumentNode(definitions: [ + fragmentDefinitionfragmentDnsRecords, +]); + +extension ClientExtension$Fragment$fragmentDnsRecords on graphql.GraphQLClient { + void writeFragment$fragmentDnsRecords({ + required Fragment$fragmentDnsRecords data, + required Map idFields, + bool broadcast = true, + }) => + this.writeFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'fragmentDnsRecords', + document: documentNodeFragmentfragmentDnsRecords, + ), + ), + data: data.toJson(), + broadcast: broadcast, + ); + Fragment$fragmentDnsRecords? readFragment$fragmentDnsRecords({ + required Map idFields, + bool optimistic = true, + }) { + final result = this.readFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'fragmentDnsRecords', + document: documentNodeFragmentfragmentDnsRecords, + ), + ), + optimistic: optimistic, + ); + return result == null ? null : Fragment$fragmentDnsRecords.fromJson(result); + } +} + +class Query$SystemSettings { + Query$SystemSettings({ + required this.system, + this.$__typename = 'Query', + }); + + factory Query$SystemSettings.fromJson(Map json) { + final l$system = json['system']; + final l$$__typename = json['__typename']; + return Query$SystemSettings( + system: Query$SystemSettings$system.fromJson( + (l$system as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Query$SystemSettings$system system; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$system = system; + _resultData['system'] = l$system.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$system = system; + final l$$__typename = $__typename; + return Object.hashAll([ + l$system, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$SystemSettings) || runtimeType != other.runtimeType) { + return false; + } + final l$system = system; + final lOther$system = other.system; + if (l$system != lOther$system) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$SystemSettings on Query$SystemSettings { + CopyWith$Query$SystemSettings get copyWith => + CopyWith$Query$SystemSettings( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$SystemSettings { + factory CopyWith$Query$SystemSettings( + Query$SystemSettings instance, + TRes Function(Query$SystemSettings) then, + ) = _CopyWithImpl$Query$SystemSettings; + + factory CopyWith$Query$SystemSettings.stub(TRes res) = + _CopyWithStubImpl$Query$SystemSettings; + + TRes call({ + Query$SystemSettings$system? system, + String? $__typename, + }); + CopyWith$Query$SystemSettings$system get system; +} + +class _CopyWithImpl$Query$SystemSettings + implements CopyWith$Query$SystemSettings { + _CopyWithImpl$Query$SystemSettings( + this._instance, + this._then, + ); + + final Query$SystemSettings _instance; + + final TRes Function(Query$SystemSettings) _then; + + static const _undefined = {}; + + TRes call({ + Object? system = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$SystemSettings( + system: system == _undefined || system == null + ? _instance.system + : (system as Query$SystemSettings$system), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$SystemSettings$system get system { + final local$system = _instance.system; + return CopyWith$Query$SystemSettings$system( + local$system, (e) => call(system: e)); + } +} + +class _CopyWithStubImpl$Query$SystemSettings + implements CopyWith$Query$SystemSettings { + _CopyWithStubImpl$Query$SystemSettings(this._res); + + TRes _res; + + call({ + Query$SystemSettings$system? system, + String? $__typename, + }) => + _res; + CopyWith$Query$SystemSettings$system get system => + CopyWith$Query$SystemSettings$system.stub(_res); +} + +const documentNodeQuerySystemSettings = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.query, + name: NameNode(value: 'SystemSettings'), + variableDefinitions: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'system'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'settings'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'autoUpgrade'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'allowReboot'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'enable'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: 'ssh'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'enable'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'passwordAuthentication'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: 'timezone'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), +]); +Query$SystemSettings _parserFn$Query$SystemSettings( + Map data) => + Query$SystemSettings.fromJson(data); +typedef OnQueryComplete$Query$SystemSettings = FutureOr Function( + Map?, + Query$SystemSettings?, +); + +class Options$Query$SystemSettings + extends graphql.QueryOptions { + Options$Query$SystemSettings({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$SystemSettings? typedOptimisticResult, + Duration? pollInterval, + graphql.Context? context, + OnQueryComplete$Query$SystemSettings? onComplete, + graphql.OnQueryError? onError, + }) : onCompleteWithParsed = onComplete, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + pollInterval: pollInterval, + context: context, + onComplete: onComplete == null + ? null + : (data) => onComplete( + data, + data == null ? null : _parserFn$Query$SystemSettings(data), + ), + onError: onError, + document: documentNodeQuerySystemSettings, + parserFn: _parserFn$Query$SystemSettings, + ); + + final OnQueryComplete$Query$SystemSettings? onCompleteWithParsed; + + @override + List get properties => [ + ...super.onComplete == null + ? super.properties + : super.properties.where((property) => property != onComplete), + onCompleteWithParsed, + ]; +} + +class WatchOptions$Query$SystemSettings + extends graphql.WatchQueryOptions { + WatchOptions$Query$SystemSettings({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$SystemSettings? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeQuerySystemSettings, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Query$SystemSettings, + ); +} + +class FetchMoreOptions$Query$SystemSettings extends graphql.FetchMoreOptions { + FetchMoreOptions$Query$SystemSettings( + {required graphql.UpdateQuery updateQuery}) + : super( + updateQuery: updateQuery, + document: documentNodeQuerySystemSettings, + ); +} + +extension ClientExtension$Query$SystemSettings on graphql.GraphQLClient { + Future> query$SystemSettings( + [Options$Query$SystemSettings? options]) async => + await this.query(options ?? Options$Query$SystemSettings()); + graphql.ObservableQuery watchQuery$SystemSettings( + [WatchOptions$Query$SystemSettings? options]) => + this.watchQuery(options ?? WatchOptions$Query$SystemSettings()); + void writeQuery$SystemSettings({ + required Query$SystemSettings data, + bool broadcast = true, + }) => + this.writeQuery( + graphql.Request( + operation: + graphql.Operation(document: documentNodeQuerySystemSettings)), + data: data.toJson(), + broadcast: broadcast, + ); + Query$SystemSettings? readQuery$SystemSettings({bool optimistic = true}) { + final result = this.readQuery( + graphql.Request( + operation: + graphql.Operation(document: documentNodeQuerySystemSettings)), + optimistic: optimistic, + ); + return result == null ? null : Query$SystemSettings.fromJson(result); + } +} + +class Query$SystemSettings$system { + Query$SystemSettings$system({ + required this.settings, + this.$__typename = 'System', + }); + + factory Query$SystemSettings$system.fromJson(Map json) { + final l$settings = json['settings']; + final l$$__typename = json['__typename']; + return Query$SystemSettings$system( + settings: Query$SystemSettings$system$settings.fromJson( + (l$settings as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Query$SystemSettings$system$settings settings; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$settings = settings; + _resultData['settings'] = l$settings.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$settings = settings; + final l$$__typename = $__typename; + return Object.hashAll([ + l$settings, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$SystemSettings$system) || + runtimeType != other.runtimeType) { + return false; + } + final l$settings = settings; + final lOther$settings = other.settings; + if (l$settings != lOther$settings) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$SystemSettings$system + on Query$SystemSettings$system { + CopyWith$Query$SystemSettings$system + get copyWith => CopyWith$Query$SystemSettings$system( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$SystemSettings$system { + factory CopyWith$Query$SystemSettings$system( + Query$SystemSettings$system instance, + TRes Function(Query$SystemSettings$system) then, + ) = _CopyWithImpl$Query$SystemSettings$system; + + factory CopyWith$Query$SystemSettings$system.stub(TRes res) = + _CopyWithStubImpl$Query$SystemSettings$system; + + TRes call({ + Query$SystemSettings$system$settings? settings, + String? $__typename, + }); + CopyWith$Query$SystemSettings$system$settings get settings; +} + +class _CopyWithImpl$Query$SystemSettings$system + implements CopyWith$Query$SystemSettings$system { + _CopyWithImpl$Query$SystemSettings$system( + this._instance, + this._then, + ); + + final Query$SystemSettings$system _instance; + + final TRes Function(Query$SystemSettings$system) _then; + + static const _undefined = {}; + + TRes call({ + Object? settings = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$SystemSettings$system( + settings: settings == _undefined || settings == null + ? _instance.settings + : (settings as Query$SystemSettings$system$settings), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$SystemSettings$system$settings get settings { + final local$settings = _instance.settings; + return CopyWith$Query$SystemSettings$system$settings( + local$settings, (e) => call(settings: e)); + } +} + +class _CopyWithStubImpl$Query$SystemSettings$system + implements CopyWith$Query$SystemSettings$system { + _CopyWithStubImpl$Query$SystemSettings$system(this._res); + + TRes _res; + + call({ + Query$SystemSettings$system$settings? settings, + String? $__typename, + }) => + _res; + CopyWith$Query$SystemSettings$system$settings get settings => + CopyWith$Query$SystemSettings$system$settings.stub(_res); +} + +class Query$SystemSettings$system$settings { + Query$SystemSettings$system$settings({ + required this.autoUpgrade, + required this.ssh, + required this.timezone, + this.$__typename = 'SystemSettings', + }); + + factory Query$SystemSettings$system$settings.fromJson( + Map json) { + final l$autoUpgrade = json['autoUpgrade']; + final l$ssh = json['ssh']; + final l$timezone = json['timezone']; + final l$$__typename = json['__typename']; + return Query$SystemSettings$system$settings( + autoUpgrade: Query$SystemSettings$system$settings$autoUpgrade.fromJson( + (l$autoUpgrade as Map)), + ssh: Query$SystemSettings$system$settings$ssh.fromJson( + (l$ssh as Map)), + timezone: (l$timezone as String), + $__typename: (l$$__typename as String), + ); + } + + final Query$SystemSettings$system$settings$autoUpgrade autoUpgrade; + + final Query$SystemSettings$system$settings$ssh ssh; + + final String timezone; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$autoUpgrade = autoUpgrade; + _resultData['autoUpgrade'] = l$autoUpgrade.toJson(); + final l$ssh = ssh; + _resultData['ssh'] = l$ssh.toJson(); + final l$timezone = timezone; + _resultData['timezone'] = l$timezone; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$autoUpgrade = autoUpgrade; + final l$ssh = ssh; + final l$timezone = timezone; + final l$$__typename = $__typename; + return Object.hashAll([ + l$autoUpgrade, + l$ssh, + l$timezone, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$SystemSettings$system$settings) || + runtimeType != other.runtimeType) { + return false; + } + final l$autoUpgrade = autoUpgrade; + final lOther$autoUpgrade = other.autoUpgrade; + if (l$autoUpgrade != lOther$autoUpgrade) { + return false; + } + final l$ssh = ssh; + final lOther$ssh = other.ssh; + if (l$ssh != lOther$ssh) { + return false; + } + final l$timezone = timezone; + final lOther$timezone = other.timezone; + if (l$timezone != lOther$timezone) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$SystemSettings$system$settings + on Query$SystemSettings$system$settings { + CopyWith$Query$SystemSettings$system$settings< + Query$SystemSettings$system$settings> + get copyWith => CopyWith$Query$SystemSettings$system$settings( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$SystemSettings$system$settings { + factory CopyWith$Query$SystemSettings$system$settings( + Query$SystemSettings$system$settings instance, + TRes Function(Query$SystemSettings$system$settings) then, + ) = _CopyWithImpl$Query$SystemSettings$system$settings; + + factory CopyWith$Query$SystemSettings$system$settings.stub(TRes res) = + _CopyWithStubImpl$Query$SystemSettings$system$settings; + + TRes call({ + Query$SystemSettings$system$settings$autoUpgrade? autoUpgrade, + Query$SystemSettings$system$settings$ssh? ssh, + String? timezone, + String? $__typename, + }); + CopyWith$Query$SystemSettings$system$settings$autoUpgrade + get autoUpgrade; + CopyWith$Query$SystemSettings$system$settings$ssh get ssh; +} + +class _CopyWithImpl$Query$SystemSettings$system$settings + implements CopyWith$Query$SystemSettings$system$settings { + _CopyWithImpl$Query$SystemSettings$system$settings( + this._instance, + this._then, + ); + + final Query$SystemSettings$system$settings _instance; + + final TRes Function(Query$SystemSettings$system$settings) _then; + + static const _undefined = {}; + + TRes call({ + Object? autoUpgrade = _undefined, + Object? ssh = _undefined, + Object? timezone = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$SystemSettings$system$settings( + autoUpgrade: autoUpgrade == _undefined || autoUpgrade == null + ? _instance.autoUpgrade + : (autoUpgrade as Query$SystemSettings$system$settings$autoUpgrade), + ssh: ssh == _undefined || ssh == null + ? _instance.ssh + : (ssh as Query$SystemSettings$system$settings$ssh), + timezone: timezone == _undefined || timezone == null + ? _instance.timezone + : (timezone as String), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$SystemSettings$system$settings$autoUpgrade + get autoUpgrade { + final local$autoUpgrade = _instance.autoUpgrade; + return CopyWith$Query$SystemSettings$system$settings$autoUpgrade( + local$autoUpgrade, (e) => call(autoUpgrade: e)); + } + + CopyWith$Query$SystemSettings$system$settings$ssh get ssh { + final local$ssh = _instance.ssh; + return CopyWith$Query$SystemSettings$system$settings$ssh( + local$ssh, (e) => call(ssh: e)); + } +} + +class _CopyWithStubImpl$Query$SystemSettings$system$settings + implements CopyWith$Query$SystemSettings$system$settings { + _CopyWithStubImpl$Query$SystemSettings$system$settings(this._res); + + TRes _res; + + call({ + Query$SystemSettings$system$settings$autoUpgrade? autoUpgrade, + Query$SystemSettings$system$settings$ssh? ssh, + String? timezone, + String? $__typename, + }) => + _res; + CopyWith$Query$SystemSettings$system$settings$autoUpgrade + get autoUpgrade => + CopyWith$Query$SystemSettings$system$settings$autoUpgrade.stub(_res); + CopyWith$Query$SystemSettings$system$settings$ssh get ssh => + CopyWith$Query$SystemSettings$system$settings$ssh.stub(_res); +} + +class Query$SystemSettings$system$settings$autoUpgrade { + Query$SystemSettings$system$settings$autoUpgrade({ + required this.allowReboot, + required this.enable, + this.$__typename = 'AutoUpgradeOptions', + }); + + factory Query$SystemSettings$system$settings$autoUpgrade.fromJson( + Map json) { + final l$allowReboot = json['allowReboot']; + final l$enable = json['enable']; + final l$$__typename = json['__typename']; + return Query$SystemSettings$system$settings$autoUpgrade( + allowReboot: (l$allowReboot as bool), + enable: (l$enable as bool), + $__typename: (l$$__typename as String), + ); + } + + final bool allowReboot; + + final bool enable; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$allowReboot = allowReboot; + _resultData['allowReboot'] = l$allowReboot; + final l$enable = enable; + _resultData['enable'] = l$enable; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$allowReboot = allowReboot; + final l$enable = enable; + final l$$__typename = $__typename; + return Object.hashAll([ + l$allowReboot, + l$enable, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$SystemSettings$system$settings$autoUpgrade) || + runtimeType != other.runtimeType) { + return false; + } + final l$allowReboot = allowReboot; + final lOther$allowReboot = other.allowReboot; + if (l$allowReboot != lOther$allowReboot) { + return false; + } + final l$enable = enable; + final lOther$enable = other.enable; + if (l$enable != lOther$enable) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$SystemSettings$system$settings$autoUpgrade + on Query$SystemSettings$system$settings$autoUpgrade { + CopyWith$Query$SystemSettings$system$settings$autoUpgrade< + Query$SystemSettings$system$settings$autoUpgrade> + get copyWith => CopyWith$Query$SystemSettings$system$settings$autoUpgrade( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$SystemSettings$system$settings$autoUpgrade { + factory CopyWith$Query$SystemSettings$system$settings$autoUpgrade( + Query$SystemSettings$system$settings$autoUpgrade instance, + TRes Function(Query$SystemSettings$system$settings$autoUpgrade) then, + ) = _CopyWithImpl$Query$SystemSettings$system$settings$autoUpgrade; + + factory CopyWith$Query$SystemSettings$system$settings$autoUpgrade.stub( + TRes res) = + _CopyWithStubImpl$Query$SystemSettings$system$settings$autoUpgrade; + + TRes call({ + bool? allowReboot, + bool? enable, + String? $__typename, + }); +} + +class _CopyWithImpl$Query$SystemSettings$system$settings$autoUpgrade + implements CopyWith$Query$SystemSettings$system$settings$autoUpgrade { + _CopyWithImpl$Query$SystemSettings$system$settings$autoUpgrade( + this._instance, + this._then, + ); + + final Query$SystemSettings$system$settings$autoUpgrade _instance; + + final TRes Function(Query$SystemSettings$system$settings$autoUpgrade) _then; + + static const _undefined = {}; + + TRes call({ + Object? allowReboot = _undefined, + Object? enable = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$SystemSettings$system$settings$autoUpgrade( + allowReboot: allowReboot == _undefined || allowReboot == null + ? _instance.allowReboot + : (allowReboot as bool), + enable: enable == _undefined || enable == null + ? _instance.enable + : (enable as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Query$SystemSettings$system$settings$autoUpgrade + implements CopyWith$Query$SystemSettings$system$settings$autoUpgrade { + _CopyWithStubImpl$Query$SystemSettings$system$settings$autoUpgrade(this._res); + + TRes _res; + + call({ + bool? allowReboot, + bool? enable, + String? $__typename, + }) => + _res; +} + +class Query$SystemSettings$system$settings$ssh { + Query$SystemSettings$system$settings$ssh({ + required this.enable, + required this.passwordAuthentication, + this.$__typename = 'SshSettings', + }); + + factory Query$SystemSettings$system$settings$ssh.fromJson( + Map json) { + final l$enable = json['enable']; + final l$passwordAuthentication = json['passwordAuthentication']; + final l$$__typename = json['__typename']; + return Query$SystemSettings$system$settings$ssh( + enable: (l$enable as bool), + passwordAuthentication: (l$passwordAuthentication as bool), + $__typename: (l$$__typename as String), + ); + } + + final bool enable; + + final bool passwordAuthentication; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$enable = enable; + _resultData['enable'] = l$enable; + final l$passwordAuthentication = passwordAuthentication; + _resultData['passwordAuthentication'] = l$passwordAuthentication; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$enable = enable; + final l$passwordAuthentication = passwordAuthentication; + final l$$__typename = $__typename; + return Object.hashAll([ + l$enable, + l$passwordAuthentication, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$SystemSettings$system$settings$ssh) || + runtimeType != other.runtimeType) { + return false; + } + final l$enable = enable; + final lOther$enable = other.enable; + if (l$enable != lOther$enable) { + return false; + } + final l$passwordAuthentication = passwordAuthentication; + final lOther$passwordAuthentication = other.passwordAuthentication; + if (l$passwordAuthentication != lOther$passwordAuthentication) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$SystemSettings$system$settings$ssh + on Query$SystemSettings$system$settings$ssh { + CopyWith$Query$SystemSettings$system$settings$ssh< + Query$SystemSettings$system$settings$ssh> + get copyWith => CopyWith$Query$SystemSettings$system$settings$ssh( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$SystemSettings$system$settings$ssh { + factory CopyWith$Query$SystemSettings$system$settings$ssh( + Query$SystemSettings$system$settings$ssh instance, + TRes Function(Query$SystemSettings$system$settings$ssh) then, + ) = _CopyWithImpl$Query$SystemSettings$system$settings$ssh; + + factory CopyWith$Query$SystemSettings$system$settings$ssh.stub(TRes res) = + _CopyWithStubImpl$Query$SystemSettings$system$settings$ssh; + + TRes call({ + bool? enable, + bool? passwordAuthentication, + String? $__typename, + }); +} + +class _CopyWithImpl$Query$SystemSettings$system$settings$ssh + implements CopyWith$Query$SystemSettings$system$settings$ssh { + _CopyWithImpl$Query$SystemSettings$system$settings$ssh( + this._instance, + this._then, + ); + + final Query$SystemSettings$system$settings$ssh _instance; + + final TRes Function(Query$SystemSettings$system$settings$ssh) _then; + + static const _undefined = {}; + + TRes call({ + Object? enable = _undefined, + Object? passwordAuthentication = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$SystemSettings$system$settings$ssh( + enable: enable == _undefined || enable == null + ? _instance.enable + : (enable as bool), + passwordAuthentication: passwordAuthentication == _undefined || + passwordAuthentication == null + ? _instance.passwordAuthentication + : (passwordAuthentication as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Query$SystemSettings$system$settings$ssh + implements CopyWith$Query$SystemSettings$system$settings$ssh { + _CopyWithStubImpl$Query$SystemSettings$system$settings$ssh(this._res); + + TRes _res; + + call({ + bool? enable, + bool? passwordAuthentication, + String? $__typename, + }) => + _res; +} + +class Query$SystemIsUsingBinds { + Query$SystemIsUsingBinds({ + required this.system, + this.$__typename = 'Query', + }); + + factory Query$SystemIsUsingBinds.fromJson(Map json) { + final l$system = json['system']; + final l$$__typename = json['__typename']; + return Query$SystemIsUsingBinds( + system: Query$SystemIsUsingBinds$system.fromJson( + (l$system as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Query$SystemIsUsingBinds$system system; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$system = system; + _resultData['system'] = l$system.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$system = system; + final l$$__typename = $__typename; + return Object.hashAll([ + l$system, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$SystemIsUsingBinds) || + runtimeType != other.runtimeType) { + return false; + } + final l$system = system; + final lOther$system = other.system; + if (l$system != lOther$system) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$SystemIsUsingBinds + on Query$SystemIsUsingBinds { + CopyWith$Query$SystemIsUsingBinds get copyWith => + CopyWith$Query$SystemIsUsingBinds( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$SystemIsUsingBinds { + factory CopyWith$Query$SystemIsUsingBinds( + Query$SystemIsUsingBinds instance, + TRes Function(Query$SystemIsUsingBinds) then, + ) = _CopyWithImpl$Query$SystemIsUsingBinds; + + factory CopyWith$Query$SystemIsUsingBinds.stub(TRes res) = + _CopyWithStubImpl$Query$SystemIsUsingBinds; + + TRes call({ + Query$SystemIsUsingBinds$system? system, + String? $__typename, + }); + CopyWith$Query$SystemIsUsingBinds$system get system; +} + +class _CopyWithImpl$Query$SystemIsUsingBinds + implements CopyWith$Query$SystemIsUsingBinds { + _CopyWithImpl$Query$SystemIsUsingBinds( + this._instance, + this._then, + ); + + final Query$SystemIsUsingBinds _instance; + + final TRes Function(Query$SystemIsUsingBinds) _then; + + static const _undefined = {}; + + TRes call({ + Object? system = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$SystemIsUsingBinds( + system: system == _undefined || system == null + ? _instance.system + : (system as Query$SystemIsUsingBinds$system), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$SystemIsUsingBinds$system get system { + final local$system = _instance.system; + return CopyWith$Query$SystemIsUsingBinds$system( + local$system, (e) => call(system: e)); + } +} + +class _CopyWithStubImpl$Query$SystemIsUsingBinds + implements CopyWith$Query$SystemIsUsingBinds { + _CopyWithStubImpl$Query$SystemIsUsingBinds(this._res); + + TRes _res; + + call({ + Query$SystemIsUsingBinds$system? system, + String? $__typename, + }) => + _res; + CopyWith$Query$SystemIsUsingBinds$system get system => + CopyWith$Query$SystemIsUsingBinds$system.stub(_res); +} + +const documentNodeQuerySystemIsUsingBinds = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.query, + name: NameNode(value: 'SystemIsUsingBinds'), + variableDefinitions: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'system'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'info'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'usingBinds'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), +]); +Query$SystemIsUsingBinds _parserFn$Query$SystemIsUsingBinds( + Map data) => + Query$SystemIsUsingBinds.fromJson(data); +typedef OnQueryComplete$Query$SystemIsUsingBinds = FutureOr Function( + Map?, + Query$SystemIsUsingBinds?, +); + +class Options$Query$SystemIsUsingBinds + extends graphql.QueryOptions { + Options$Query$SystemIsUsingBinds({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$SystemIsUsingBinds? typedOptimisticResult, + Duration? pollInterval, + graphql.Context? context, + OnQueryComplete$Query$SystemIsUsingBinds? onComplete, + graphql.OnQueryError? onError, + }) : onCompleteWithParsed = onComplete, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + pollInterval: pollInterval, + context: context, + onComplete: onComplete == null + ? null + : (data) => onComplete( + data, + data == null + ? null + : _parserFn$Query$SystemIsUsingBinds(data), + ), + onError: onError, + document: documentNodeQuerySystemIsUsingBinds, + parserFn: _parserFn$Query$SystemIsUsingBinds, + ); + + final OnQueryComplete$Query$SystemIsUsingBinds? onCompleteWithParsed; + + @override + List get properties => [ + ...super.onComplete == null + ? super.properties + : super.properties.where((property) => property != onComplete), + onCompleteWithParsed, + ]; +} + +class WatchOptions$Query$SystemIsUsingBinds + extends graphql.WatchQueryOptions { + WatchOptions$Query$SystemIsUsingBinds({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$SystemIsUsingBinds? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeQuerySystemIsUsingBinds, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Query$SystemIsUsingBinds, + ); +} + +class FetchMoreOptions$Query$SystemIsUsingBinds + extends graphql.FetchMoreOptions { + FetchMoreOptions$Query$SystemIsUsingBinds( + {required graphql.UpdateQuery updateQuery}) + : super( + updateQuery: updateQuery, + document: documentNodeQuerySystemIsUsingBinds, + ); +} + +extension ClientExtension$Query$SystemIsUsingBinds on graphql.GraphQLClient { + Future> + query$SystemIsUsingBinds( + [Options$Query$SystemIsUsingBinds? options]) async => + await this.query(options ?? Options$Query$SystemIsUsingBinds()); + graphql.ObservableQuery + watchQuery$SystemIsUsingBinds( + [WatchOptions$Query$SystemIsUsingBinds? options]) => + this.watchQuery(options ?? WatchOptions$Query$SystemIsUsingBinds()); + void writeQuery$SystemIsUsingBinds({ + required Query$SystemIsUsingBinds data, + bool broadcast = true, + }) => + this.writeQuery( + graphql.Request( + operation: graphql.Operation( + document: documentNodeQuerySystemIsUsingBinds)), + data: data.toJson(), + broadcast: broadcast, + ); + Query$SystemIsUsingBinds? readQuery$SystemIsUsingBinds( + {bool optimistic = true}) { + final result = this.readQuery( + graphql.Request( + operation: + graphql.Operation(document: documentNodeQuerySystemIsUsingBinds)), + optimistic: optimistic, + ); + return result == null ? null : Query$SystemIsUsingBinds.fromJson(result); + } +} + +class Query$SystemIsUsingBinds$system { + Query$SystemIsUsingBinds$system({ + required this.info, + this.$__typename = 'System', + }); + + factory Query$SystemIsUsingBinds$system.fromJson(Map json) { + final l$info = json['info']; + final l$$__typename = json['__typename']; + return Query$SystemIsUsingBinds$system( + info: Query$SystemIsUsingBinds$system$info.fromJson( + (l$info as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Query$SystemIsUsingBinds$system$info info; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$info = info; + _resultData['info'] = l$info.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$info = info; + final l$$__typename = $__typename; + return Object.hashAll([ + l$info, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$SystemIsUsingBinds$system) || + runtimeType != other.runtimeType) { + return false; + } + final l$info = info; + final lOther$info = other.info; + if (l$info != lOther$info) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$SystemIsUsingBinds$system + on Query$SystemIsUsingBinds$system { + CopyWith$Query$SystemIsUsingBinds$system + get copyWith => CopyWith$Query$SystemIsUsingBinds$system( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$SystemIsUsingBinds$system { + factory CopyWith$Query$SystemIsUsingBinds$system( + Query$SystemIsUsingBinds$system instance, + TRes Function(Query$SystemIsUsingBinds$system) then, + ) = _CopyWithImpl$Query$SystemIsUsingBinds$system; + + factory CopyWith$Query$SystemIsUsingBinds$system.stub(TRes res) = + _CopyWithStubImpl$Query$SystemIsUsingBinds$system; + + TRes call({ + Query$SystemIsUsingBinds$system$info? info, + String? $__typename, + }); + CopyWith$Query$SystemIsUsingBinds$system$info get info; +} + +class _CopyWithImpl$Query$SystemIsUsingBinds$system + implements CopyWith$Query$SystemIsUsingBinds$system { + _CopyWithImpl$Query$SystemIsUsingBinds$system( + this._instance, + this._then, + ); + + final Query$SystemIsUsingBinds$system _instance; + + final TRes Function(Query$SystemIsUsingBinds$system) _then; + + static const _undefined = {}; + + TRes call({ + Object? info = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$SystemIsUsingBinds$system( + info: info == _undefined || info == null + ? _instance.info + : (info as Query$SystemIsUsingBinds$system$info), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$SystemIsUsingBinds$system$info get info { + final local$info = _instance.info; + return CopyWith$Query$SystemIsUsingBinds$system$info( + local$info, (e) => call(info: e)); + } +} + +class _CopyWithStubImpl$Query$SystemIsUsingBinds$system + implements CopyWith$Query$SystemIsUsingBinds$system { + _CopyWithStubImpl$Query$SystemIsUsingBinds$system(this._res); + + TRes _res; + + call({ + Query$SystemIsUsingBinds$system$info? info, + String? $__typename, + }) => + _res; + CopyWith$Query$SystemIsUsingBinds$system$info get info => + CopyWith$Query$SystemIsUsingBinds$system$info.stub(_res); +} + +class Query$SystemIsUsingBinds$system$info { + Query$SystemIsUsingBinds$system$info({ + required this.usingBinds, + this.$__typename = 'SystemInfo', + }); + + factory Query$SystemIsUsingBinds$system$info.fromJson( + Map json) { + final l$usingBinds = json['usingBinds']; + final l$$__typename = json['__typename']; + return Query$SystemIsUsingBinds$system$info( + usingBinds: (l$usingBinds as bool), + $__typename: (l$$__typename as String), + ); + } + + final bool usingBinds; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$usingBinds = usingBinds; + _resultData['usingBinds'] = l$usingBinds; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$usingBinds = usingBinds; + final l$$__typename = $__typename; + return Object.hashAll([ + l$usingBinds, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$SystemIsUsingBinds$system$info) || + runtimeType != other.runtimeType) { + return false; + } + final l$usingBinds = usingBinds; + final lOther$usingBinds = other.usingBinds; + if (l$usingBinds != lOther$usingBinds) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$SystemIsUsingBinds$system$info + on Query$SystemIsUsingBinds$system$info { + CopyWith$Query$SystemIsUsingBinds$system$info< + Query$SystemIsUsingBinds$system$info> + get copyWith => CopyWith$Query$SystemIsUsingBinds$system$info( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$SystemIsUsingBinds$system$info { + factory CopyWith$Query$SystemIsUsingBinds$system$info( + Query$SystemIsUsingBinds$system$info instance, + TRes Function(Query$SystemIsUsingBinds$system$info) then, + ) = _CopyWithImpl$Query$SystemIsUsingBinds$system$info; + + factory CopyWith$Query$SystemIsUsingBinds$system$info.stub(TRes res) = + _CopyWithStubImpl$Query$SystemIsUsingBinds$system$info; + + TRes call({ + bool? usingBinds, + String? $__typename, + }); +} + +class _CopyWithImpl$Query$SystemIsUsingBinds$system$info + implements CopyWith$Query$SystemIsUsingBinds$system$info { + _CopyWithImpl$Query$SystemIsUsingBinds$system$info( + this._instance, + this._then, + ); + + final Query$SystemIsUsingBinds$system$info _instance; + + final TRes Function(Query$SystemIsUsingBinds$system$info) _then; + + static const _undefined = {}; + + TRes call({ + Object? usingBinds = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$SystemIsUsingBinds$system$info( + usingBinds: usingBinds == _undefined || usingBinds == null + ? _instance.usingBinds + : (usingBinds as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Query$SystemIsUsingBinds$system$info + implements CopyWith$Query$SystemIsUsingBinds$system$info { + _CopyWithStubImpl$Query$SystemIsUsingBinds$system$info(this._res); + + TRes _res; + + call({ + bool? usingBinds, + String? $__typename, + }) => + _res; +} + +class Query$DomainInfo { + Query$DomainInfo({ + required this.system, + this.$__typename = 'Query', + }); + + factory Query$DomainInfo.fromJson(Map json) { + final l$system = json['system']; + final l$$__typename = json['__typename']; + return Query$DomainInfo( + system: + Query$DomainInfo$system.fromJson((l$system as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Query$DomainInfo$system system; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$system = system; + _resultData['system'] = l$system.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$system = system; + final l$$__typename = $__typename; + return Object.hashAll([ + l$system, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$DomainInfo) || runtimeType != other.runtimeType) { + return false; + } + final l$system = system; + final lOther$system = other.system; + if (l$system != lOther$system) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$DomainInfo on Query$DomainInfo { + CopyWith$Query$DomainInfo get copyWith => + CopyWith$Query$DomainInfo( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$DomainInfo { + factory CopyWith$Query$DomainInfo( + Query$DomainInfo instance, + TRes Function(Query$DomainInfo) then, + ) = _CopyWithImpl$Query$DomainInfo; + + factory CopyWith$Query$DomainInfo.stub(TRes res) = + _CopyWithStubImpl$Query$DomainInfo; + + TRes call({ + Query$DomainInfo$system? system, + String? $__typename, + }); + CopyWith$Query$DomainInfo$system get system; +} + +class _CopyWithImpl$Query$DomainInfo + implements CopyWith$Query$DomainInfo { + _CopyWithImpl$Query$DomainInfo( + this._instance, + this._then, + ); + + final Query$DomainInfo _instance; + + final TRes Function(Query$DomainInfo) _then; + + static const _undefined = {}; + + TRes call({ + Object? system = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$DomainInfo( + system: system == _undefined || system == null + ? _instance.system + : (system as Query$DomainInfo$system), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$DomainInfo$system get system { + final local$system = _instance.system; + return CopyWith$Query$DomainInfo$system( + local$system, (e) => call(system: e)); + } +} + +class _CopyWithStubImpl$Query$DomainInfo + implements CopyWith$Query$DomainInfo { + _CopyWithStubImpl$Query$DomainInfo(this._res); + + TRes _res; + + call({ + Query$DomainInfo$system? system, + String? $__typename, + }) => + _res; + CopyWith$Query$DomainInfo$system get system => + CopyWith$Query$DomainInfo$system.stub(_res); +} + +const documentNodeQueryDomainInfo = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.query, + name: NameNode(value: 'DomainInfo'), + variableDefinitions: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'system'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'domainInfo'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'domain'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'hostname'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'provider'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'requiredDnsRecords'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'fragmentDnsRecords'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionfragmentDnsRecords, +]); +Query$DomainInfo _parserFn$Query$DomainInfo(Map data) => + Query$DomainInfo.fromJson(data); +typedef OnQueryComplete$Query$DomainInfo = FutureOr Function( + Map?, + Query$DomainInfo?, +); + +class Options$Query$DomainInfo extends graphql.QueryOptions { + Options$Query$DomainInfo({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$DomainInfo? typedOptimisticResult, + Duration? pollInterval, + graphql.Context? context, + OnQueryComplete$Query$DomainInfo? onComplete, + graphql.OnQueryError? onError, + }) : onCompleteWithParsed = onComplete, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + pollInterval: pollInterval, + context: context, + onComplete: onComplete == null + ? null + : (data) => onComplete( + data, + data == null ? null : _parserFn$Query$DomainInfo(data), + ), + onError: onError, + document: documentNodeQueryDomainInfo, + parserFn: _parserFn$Query$DomainInfo, + ); + + final OnQueryComplete$Query$DomainInfo? onCompleteWithParsed; + + @override + List get properties => [ + ...super.onComplete == null + ? super.properties + : super.properties.where((property) => property != onComplete), + onCompleteWithParsed, + ]; +} + +class WatchOptions$Query$DomainInfo + extends graphql.WatchQueryOptions { + WatchOptions$Query$DomainInfo({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$DomainInfo? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeQueryDomainInfo, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Query$DomainInfo, + ); +} + +class FetchMoreOptions$Query$DomainInfo extends graphql.FetchMoreOptions { + FetchMoreOptions$Query$DomainInfo({required graphql.UpdateQuery updateQuery}) + : super( + updateQuery: updateQuery, + document: documentNodeQueryDomainInfo, + ); +} + +extension ClientExtension$Query$DomainInfo on graphql.GraphQLClient { + Future> query$DomainInfo( + [Options$Query$DomainInfo? options]) async => + await this.query(options ?? Options$Query$DomainInfo()); + graphql.ObservableQuery watchQuery$DomainInfo( + [WatchOptions$Query$DomainInfo? options]) => + this.watchQuery(options ?? WatchOptions$Query$DomainInfo()); + void writeQuery$DomainInfo({ + required Query$DomainInfo data, + bool broadcast = true, + }) => + this.writeQuery( + graphql.Request( + operation: + graphql.Operation(document: documentNodeQueryDomainInfo)), + data: data.toJson(), + broadcast: broadcast, + ); + Query$DomainInfo? readQuery$DomainInfo({bool optimistic = true}) { + final result = this.readQuery( + graphql.Request( + operation: graphql.Operation(document: documentNodeQueryDomainInfo)), + optimistic: optimistic, + ); + return result == null ? null : Query$DomainInfo.fromJson(result); + } +} + +class Query$DomainInfo$system { + Query$DomainInfo$system({ + required this.domainInfo, + this.$__typename = 'System', + }); + + factory Query$DomainInfo$system.fromJson(Map json) { + final l$domainInfo = json['domainInfo']; + final l$$__typename = json['__typename']; + return Query$DomainInfo$system( + domainInfo: Query$DomainInfo$system$domainInfo.fromJson( + (l$domainInfo as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Query$DomainInfo$system$domainInfo domainInfo; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$domainInfo = domainInfo; + _resultData['domainInfo'] = l$domainInfo.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$domainInfo = domainInfo; + final l$$__typename = $__typename; + return Object.hashAll([ + l$domainInfo, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$DomainInfo$system) || + runtimeType != other.runtimeType) { + return false; + } + final l$domainInfo = domainInfo; + final lOther$domainInfo = other.domainInfo; + if (l$domainInfo != lOther$domainInfo) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$DomainInfo$system on Query$DomainInfo$system { + CopyWith$Query$DomainInfo$system get copyWith => + CopyWith$Query$DomainInfo$system( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$DomainInfo$system { + factory CopyWith$Query$DomainInfo$system( + Query$DomainInfo$system instance, + TRes Function(Query$DomainInfo$system) then, + ) = _CopyWithImpl$Query$DomainInfo$system; + + factory CopyWith$Query$DomainInfo$system.stub(TRes res) = + _CopyWithStubImpl$Query$DomainInfo$system; + + TRes call({ + Query$DomainInfo$system$domainInfo? domainInfo, + String? $__typename, + }); + CopyWith$Query$DomainInfo$system$domainInfo get domainInfo; +} + +class _CopyWithImpl$Query$DomainInfo$system + implements CopyWith$Query$DomainInfo$system { + _CopyWithImpl$Query$DomainInfo$system( + this._instance, + this._then, + ); + + final Query$DomainInfo$system _instance; + + final TRes Function(Query$DomainInfo$system) _then; + + static const _undefined = {}; + + TRes call({ + Object? domainInfo = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$DomainInfo$system( + domainInfo: domainInfo == _undefined || domainInfo == null + ? _instance.domainInfo + : (domainInfo as Query$DomainInfo$system$domainInfo), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$DomainInfo$system$domainInfo get domainInfo { + final local$domainInfo = _instance.domainInfo; + return CopyWith$Query$DomainInfo$system$domainInfo( + local$domainInfo, (e) => call(domainInfo: e)); + } +} + +class _CopyWithStubImpl$Query$DomainInfo$system + implements CopyWith$Query$DomainInfo$system { + _CopyWithStubImpl$Query$DomainInfo$system(this._res); + + TRes _res; + + call({ + Query$DomainInfo$system$domainInfo? domainInfo, + String? $__typename, + }) => + _res; + CopyWith$Query$DomainInfo$system$domainInfo get domainInfo => + CopyWith$Query$DomainInfo$system$domainInfo.stub(_res); +} + +class Query$DomainInfo$system$domainInfo { + Query$DomainInfo$system$domainInfo({ + required this.domain, + required this.hostname, + required this.provider, + required this.requiredDnsRecords, + this.$__typename = 'SystemDomainInfo', + }); + + factory Query$DomainInfo$system$domainInfo.fromJson( + Map json) { + final l$domain = json['domain']; + final l$hostname = json['hostname']; + final l$provider = json['provider']; + final l$requiredDnsRecords = json['requiredDnsRecords']; + final l$$__typename = json['__typename']; + return Query$DomainInfo$system$domainInfo( + domain: (l$domain as String), + hostname: (l$hostname as String), + provider: fromJson$Enum$DnsProvider((l$provider as String)), + requiredDnsRecords: (l$requiredDnsRecords as List) + .map((e) => + Fragment$fragmentDnsRecords.fromJson((e as Map))) + .toList(), + $__typename: (l$$__typename as String), + ); + } + + final String domain; + + final String hostname; + + final Enum$DnsProvider provider; + + final List requiredDnsRecords; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$domain = domain; + _resultData['domain'] = l$domain; + final l$hostname = hostname; + _resultData['hostname'] = l$hostname; + final l$provider = provider; + _resultData['provider'] = toJson$Enum$DnsProvider(l$provider); + final l$requiredDnsRecords = requiredDnsRecords; + _resultData['requiredDnsRecords'] = + l$requiredDnsRecords.map((e) => e.toJson()).toList(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$domain = domain; + final l$hostname = hostname; + final l$provider = provider; + final l$requiredDnsRecords = requiredDnsRecords; + final l$$__typename = $__typename; + return Object.hashAll([ + l$domain, + l$hostname, + l$provider, + Object.hashAll(l$requiredDnsRecords.map((v) => v)), + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$DomainInfo$system$domainInfo) || + runtimeType != other.runtimeType) { + return false; + } + final l$domain = domain; + final lOther$domain = other.domain; + if (l$domain != lOther$domain) { + return false; + } + final l$hostname = hostname; + final lOther$hostname = other.hostname; + if (l$hostname != lOther$hostname) { + return false; + } + final l$provider = provider; + final lOther$provider = other.provider; + if (l$provider != lOther$provider) { + return false; + } + final l$requiredDnsRecords = requiredDnsRecords; + final lOther$requiredDnsRecords = other.requiredDnsRecords; + if (l$requiredDnsRecords.length != lOther$requiredDnsRecords.length) { + return false; + } + for (int i = 0; i < l$requiredDnsRecords.length; i++) { + final l$requiredDnsRecords$entry = l$requiredDnsRecords[i]; + final lOther$requiredDnsRecords$entry = lOther$requiredDnsRecords[i]; + if (l$requiredDnsRecords$entry != lOther$requiredDnsRecords$entry) { + return false; + } + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$DomainInfo$system$domainInfo + on Query$DomainInfo$system$domainInfo { + CopyWith$Query$DomainInfo$system$domainInfo< + Query$DomainInfo$system$domainInfo> + get copyWith => CopyWith$Query$DomainInfo$system$domainInfo( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$DomainInfo$system$domainInfo { + factory CopyWith$Query$DomainInfo$system$domainInfo( + Query$DomainInfo$system$domainInfo instance, + TRes Function(Query$DomainInfo$system$domainInfo) then, + ) = _CopyWithImpl$Query$DomainInfo$system$domainInfo; + + factory CopyWith$Query$DomainInfo$system$domainInfo.stub(TRes res) = + _CopyWithStubImpl$Query$DomainInfo$system$domainInfo; + + TRes call({ + String? domain, + String? hostname, + Enum$DnsProvider? provider, + List? requiredDnsRecords, + String? $__typename, + }); + TRes requiredDnsRecords( + Iterable Function( + Iterable< + CopyWith$Fragment$fragmentDnsRecords< + Fragment$fragmentDnsRecords>>) + _fn); +} + +class _CopyWithImpl$Query$DomainInfo$system$domainInfo + implements CopyWith$Query$DomainInfo$system$domainInfo { + _CopyWithImpl$Query$DomainInfo$system$domainInfo( + this._instance, + this._then, + ); + + final Query$DomainInfo$system$domainInfo _instance; + + final TRes Function(Query$DomainInfo$system$domainInfo) _then; + + static const _undefined = {}; + + TRes call({ + Object? domain = _undefined, + Object? hostname = _undefined, + Object? provider = _undefined, + Object? requiredDnsRecords = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$DomainInfo$system$domainInfo( + domain: domain == _undefined || domain == null + ? _instance.domain + : (domain as String), + hostname: hostname == _undefined || hostname == null + ? _instance.hostname + : (hostname as String), + provider: provider == _undefined || provider == null + ? _instance.provider + : (provider as Enum$DnsProvider), + requiredDnsRecords: + requiredDnsRecords == _undefined || requiredDnsRecords == null + ? _instance.requiredDnsRecords + : (requiredDnsRecords as List), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + TRes requiredDnsRecords( + Iterable Function( + Iterable< + CopyWith$Fragment$fragmentDnsRecords< + Fragment$fragmentDnsRecords>>) + _fn) => + call( + requiredDnsRecords: _fn(_instance.requiredDnsRecords + .map((e) => CopyWith$Fragment$fragmentDnsRecords( + e, + (i) => i, + ))).toList()); +} + +class _CopyWithStubImpl$Query$DomainInfo$system$domainInfo + implements CopyWith$Query$DomainInfo$system$domainInfo { + _CopyWithStubImpl$Query$DomainInfo$system$domainInfo(this._res); + + TRes _res; + + call({ + String? domain, + String? hostname, + Enum$DnsProvider? provider, + List? requiredDnsRecords, + String? $__typename, + }) => + _res; + requiredDnsRecords(_fn) => _res; +} + +class Variables$Mutation$ChangeTimezone { + factory Variables$Mutation$ChangeTimezone({required String timezone}) => + Variables$Mutation$ChangeTimezone._({ + r'timezone': timezone, + }); + + Variables$Mutation$ChangeTimezone._(this._$data); + + factory Variables$Mutation$ChangeTimezone.fromJson( + Map data) { + final result$data = {}; + final l$timezone = data['timezone']; + result$data['timezone'] = (l$timezone as String); + return Variables$Mutation$ChangeTimezone._(result$data); + } + + Map _$data; + + String get timezone => (_$data['timezone'] as String); + Map toJson() { + final result$data = {}; + final l$timezone = timezone; + result$data['timezone'] = l$timezone; + return result$data; + } + + CopyWith$Variables$Mutation$ChangeTimezone + get copyWith => CopyWith$Variables$Mutation$ChangeTimezone( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$ChangeTimezone) || + runtimeType != other.runtimeType) { + return false; + } + final l$timezone = timezone; + final lOther$timezone = other.timezone; + if (l$timezone != lOther$timezone) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$timezone = timezone; + return Object.hashAll([l$timezone]); + } +} + +abstract class CopyWith$Variables$Mutation$ChangeTimezone { + factory CopyWith$Variables$Mutation$ChangeTimezone( + Variables$Mutation$ChangeTimezone instance, + TRes Function(Variables$Mutation$ChangeTimezone) then, + ) = _CopyWithImpl$Variables$Mutation$ChangeTimezone; + + factory CopyWith$Variables$Mutation$ChangeTimezone.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$ChangeTimezone; + + TRes call({String? timezone}); +} + +class _CopyWithImpl$Variables$Mutation$ChangeTimezone + implements CopyWith$Variables$Mutation$ChangeTimezone { + _CopyWithImpl$Variables$Mutation$ChangeTimezone( + this._instance, + this._then, + ); + + final Variables$Mutation$ChangeTimezone _instance; + + final TRes Function(Variables$Mutation$ChangeTimezone) _then; + + static const _undefined = {}; + + TRes call({Object? timezone = _undefined}) => + _then(Variables$Mutation$ChangeTimezone._({ + ..._instance._$data, + if (timezone != _undefined && timezone != null) + 'timezone': (timezone as String), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$ChangeTimezone + implements CopyWith$Variables$Mutation$ChangeTimezone { + _CopyWithStubImpl$Variables$Mutation$ChangeTimezone(this._res); + + TRes _res; + + call({String? timezone}) => _res; +} + +class Mutation$ChangeTimezone { + Mutation$ChangeTimezone({ + required this.changeTimezone, + this.$__typename = 'Mutation', + }); + + factory Mutation$ChangeTimezone.fromJson(Map json) { + final l$changeTimezone = json['changeTimezone']; + final l$$__typename = json['__typename']; + return Mutation$ChangeTimezone( + changeTimezone: Mutation$ChangeTimezone$changeTimezone.fromJson( + (l$changeTimezone as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$ChangeTimezone$changeTimezone changeTimezone; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$changeTimezone = changeTimezone; + _resultData['changeTimezone'] = l$changeTimezone.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$changeTimezone = changeTimezone; + final l$$__typename = $__typename; + return Object.hashAll([ + l$changeTimezone, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$ChangeTimezone) || + runtimeType != other.runtimeType) { + return false; + } + final l$changeTimezone = changeTimezone; + final lOther$changeTimezone = other.changeTimezone; + if (l$changeTimezone != lOther$changeTimezone) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$ChangeTimezone on Mutation$ChangeTimezone { + CopyWith$Mutation$ChangeTimezone get copyWith => + CopyWith$Mutation$ChangeTimezone( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$ChangeTimezone { + factory CopyWith$Mutation$ChangeTimezone( + Mutation$ChangeTimezone instance, + TRes Function(Mutation$ChangeTimezone) then, + ) = _CopyWithImpl$Mutation$ChangeTimezone; + + factory CopyWith$Mutation$ChangeTimezone.stub(TRes res) = + _CopyWithStubImpl$Mutation$ChangeTimezone; + + TRes call({ + Mutation$ChangeTimezone$changeTimezone? changeTimezone, + String? $__typename, + }); + CopyWith$Mutation$ChangeTimezone$changeTimezone get changeTimezone; +} + +class _CopyWithImpl$Mutation$ChangeTimezone + implements CopyWith$Mutation$ChangeTimezone { + _CopyWithImpl$Mutation$ChangeTimezone( + this._instance, + this._then, + ); + + final Mutation$ChangeTimezone _instance; + + final TRes Function(Mutation$ChangeTimezone) _then; + + static const _undefined = {}; + + TRes call({ + Object? changeTimezone = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$ChangeTimezone( + changeTimezone: changeTimezone == _undefined || changeTimezone == null + ? _instance.changeTimezone + : (changeTimezone as Mutation$ChangeTimezone$changeTimezone), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$ChangeTimezone$changeTimezone get changeTimezone { + final local$changeTimezone = _instance.changeTimezone; + return CopyWith$Mutation$ChangeTimezone$changeTimezone( + local$changeTimezone, (e) => call(changeTimezone: e)); + } +} + +class _CopyWithStubImpl$Mutation$ChangeTimezone + implements CopyWith$Mutation$ChangeTimezone { + _CopyWithStubImpl$Mutation$ChangeTimezone(this._res); + + TRes _res; + + call({ + Mutation$ChangeTimezone$changeTimezone? changeTimezone, + String? $__typename, + }) => + _res; + CopyWith$Mutation$ChangeTimezone$changeTimezone get changeTimezone => + CopyWith$Mutation$ChangeTimezone$changeTimezone.stub(_res); +} + +const documentNodeMutationChangeTimezone = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'ChangeTimezone'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'timezone')), + type: NamedTypeNode( + name: NameNode(value: 'String'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'changeTimezone'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'timezone'), + value: VariableNode(name: NameNode(value: 'timezone')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: 'timezone'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, +]); +Mutation$ChangeTimezone _parserFn$Mutation$ChangeTimezone( + Map data) => + Mutation$ChangeTimezone.fromJson(data); +typedef OnMutationCompleted$Mutation$ChangeTimezone = FutureOr Function( + Map?, + Mutation$ChangeTimezone?, +); + +class Options$Mutation$ChangeTimezone + extends graphql.MutationOptions { + Options$Mutation$ChangeTimezone({ + String? operationName, + required Variables$Mutation$ChangeTimezone variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$ChangeTimezone? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$ChangeTimezone? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$ChangeTimezone(data), + ), + update: update, + onError: onError, + document: documentNodeMutationChangeTimezone, + parserFn: _parserFn$Mutation$ChangeTimezone, + ); + + final OnMutationCompleted$Mutation$ChangeTimezone? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$ChangeTimezone + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$ChangeTimezone({ + String? operationName, + required Variables$Mutation$ChangeTimezone variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$ChangeTimezone? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationChangeTimezone, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$ChangeTimezone, + ); +} + +extension ClientExtension$Mutation$ChangeTimezone on graphql.GraphQLClient { + Future> mutate$ChangeTimezone( + Options$Mutation$ChangeTimezone options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$ChangeTimezone( + WatchOptions$Mutation$ChangeTimezone options) => + this.watchMutation(options); +} + +class Mutation$ChangeTimezone$changeTimezone + implements Fragment$basicMutationReturnFields$$TimezoneMutationReturn { + Mutation$ChangeTimezone$changeTimezone({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'TimezoneMutationReturn', + this.timezone, + }); + + factory Mutation$ChangeTimezone$changeTimezone.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + final l$timezone = json['timezone']; + return Mutation$ChangeTimezone$changeTimezone( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + timezone: (l$timezone as String?), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + final String? timezone; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + final l$timezone = timezone; + _resultData['timezone'] = l$timezone; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + final l$timezone = timezone; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + l$timezone, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$ChangeTimezone$changeTimezone) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + final l$timezone = timezone; + final lOther$timezone = other.timezone; + if (l$timezone != lOther$timezone) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$ChangeTimezone$changeTimezone + on Mutation$ChangeTimezone$changeTimezone { + CopyWith$Mutation$ChangeTimezone$changeTimezone< + Mutation$ChangeTimezone$changeTimezone> + get copyWith => CopyWith$Mutation$ChangeTimezone$changeTimezone( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$ChangeTimezone$changeTimezone { + factory CopyWith$Mutation$ChangeTimezone$changeTimezone( + Mutation$ChangeTimezone$changeTimezone instance, + TRes Function(Mutation$ChangeTimezone$changeTimezone) then, + ) = _CopyWithImpl$Mutation$ChangeTimezone$changeTimezone; + + factory CopyWith$Mutation$ChangeTimezone$changeTimezone.stub(TRes res) = + _CopyWithStubImpl$Mutation$ChangeTimezone$changeTimezone; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + String? timezone, + }); +} + +class _CopyWithImpl$Mutation$ChangeTimezone$changeTimezone + implements CopyWith$Mutation$ChangeTimezone$changeTimezone { + _CopyWithImpl$Mutation$ChangeTimezone$changeTimezone( + this._instance, + this._then, + ); + + final Mutation$ChangeTimezone$changeTimezone _instance; + + final TRes Function(Mutation$ChangeTimezone$changeTimezone) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + Object? timezone = _undefined, + }) => + _then(Mutation$ChangeTimezone$changeTimezone( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + timezone: + timezone == _undefined ? _instance.timezone : (timezone as String?), + )); +} + +class _CopyWithStubImpl$Mutation$ChangeTimezone$changeTimezone + implements CopyWith$Mutation$ChangeTimezone$changeTimezone { + _CopyWithStubImpl$Mutation$ChangeTimezone$changeTimezone(this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + String? timezone, + }) => + _res; +} + +class Variables$Mutation$ChangeAutoUpgradeSettings { + factory Variables$Mutation$ChangeAutoUpgradeSettings( + {required Input$AutoUpgradeSettingsInput settings}) => + Variables$Mutation$ChangeAutoUpgradeSettings._({ + r'settings': settings, + }); + + Variables$Mutation$ChangeAutoUpgradeSettings._(this._$data); + + factory Variables$Mutation$ChangeAutoUpgradeSettings.fromJson( + Map data) { + final result$data = {}; + final l$settings = data['settings']; + result$data['settings'] = Input$AutoUpgradeSettingsInput.fromJson( + (l$settings as Map)); + return Variables$Mutation$ChangeAutoUpgradeSettings._(result$data); + } + + Map _$data; + + Input$AutoUpgradeSettingsInput get settings => + (_$data['settings'] as Input$AutoUpgradeSettingsInput); + Map toJson() { + final result$data = {}; + final l$settings = settings; + result$data['settings'] = l$settings.toJson(); + return result$data; + } + + CopyWith$Variables$Mutation$ChangeAutoUpgradeSettings< + Variables$Mutation$ChangeAutoUpgradeSettings> + get copyWith => CopyWith$Variables$Mutation$ChangeAutoUpgradeSettings( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$ChangeAutoUpgradeSettings) || + runtimeType != other.runtimeType) { + return false; + } + final l$settings = settings; + final lOther$settings = other.settings; + if (l$settings != lOther$settings) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$settings = settings; + return Object.hashAll([l$settings]); + } +} + +abstract class CopyWith$Variables$Mutation$ChangeAutoUpgradeSettings { + factory CopyWith$Variables$Mutation$ChangeAutoUpgradeSettings( + Variables$Mutation$ChangeAutoUpgradeSettings instance, + TRes Function(Variables$Mutation$ChangeAutoUpgradeSettings) then, + ) = _CopyWithImpl$Variables$Mutation$ChangeAutoUpgradeSettings; + + factory CopyWith$Variables$Mutation$ChangeAutoUpgradeSettings.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$ChangeAutoUpgradeSettings; + + TRes call({Input$AutoUpgradeSettingsInput? settings}); +} + +class _CopyWithImpl$Variables$Mutation$ChangeAutoUpgradeSettings + implements CopyWith$Variables$Mutation$ChangeAutoUpgradeSettings { + _CopyWithImpl$Variables$Mutation$ChangeAutoUpgradeSettings( + this._instance, + this._then, + ); + + final Variables$Mutation$ChangeAutoUpgradeSettings _instance; + + final TRes Function(Variables$Mutation$ChangeAutoUpgradeSettings) _then; + + static const _undefined = {}; + + TRes call({Object? settings = _undefined}) => + _then(Variables$Mutation$ChangeAutoUpgradeSettings._({ + ..._instance._$data, + if (settings != _undefined && settings != null) + 'settings': (settings as Input$AutoUpgradeSettingsInput), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$ChangeAutoUpgradeSettings + implements CopyWith$Variables$Mutation$ChangeAutoUpgradeSettings { + _CopyWithStubImpl$Variables$Mutation$ChangeAutoUpgradeSettings(this._res); + + TRes _res; + + call({Input$AutoUpgradeSettingsInput? settings}) => _res; +} + +class Mutation$ChangeAutoUpgradeSettings { + Mutation$ChangeAutoUpgradeSettings({ + required this.changeAutoUpgradeSettings, + this.$__typename = 'Mutation', + }); + + factory Mutation$ChangeAutoUpgradeSettings.fromJson( + Map json) { + final l$changeAutoUpgradeSettings = json['changeAutoUpgradeSettings']; + final l$$__typename = json['__typename']; + return Mutation$ChangeAutoUpgradeSettings( + changeAutoUpgradeSettings: + Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings.fromJson( + (l$changeAutoUpgradeSettings as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings + changeAutoUpgradeSettings; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$changeAutoUpgradeSettings = changeAutoUpgradeSettings; + _resultData['changeAutoUpgradeSettings'] = + l$changeAutoUpgradeSettings.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$changeAutoUpgradeSettings = changeAutoUpgradeSettings; + final l$$__typename = $__typename; + return Object.hashAll([ + l$changeAutoUpgradeSettings, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$ChangeAutoUpgradeSettings) || + runtimeType != other.runtimeType) { + return false; + } + final l$changeAutoUpgradeSettings = changeAutoUpgradeSettings; + final lOther$changeAutoUpgradeSettings = other.changeAutoUpgradeSettings; + if (l$changeAutoUpgradeSettings != lOther$changeAutoUpgradeSettings) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$ChangeAutoUpgradeSettings + on Mutation$ChangeAutoUpgradeSettings { + CopyWith$Mutation$ChangeAutoUpgradeSettings< + Mutation$ChangeAutoUpgradeSettings> + get copyWith => CopyWith$Mutation$ChangeAutoUpgradeSettings( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$ChangeAutoUpgradeSettings { + factory CopyWith$Mutation$ChangeAutoUpgradeSettings( + Mutation$ChangeAutoUpgradeSettings instance, + TRes Function(Mutation$ChangeAutoUpgradeSettings) then, + ) = _CopyWithImpl$Mutation$ChangeAutoUpgradeSettings; + + factory CopyWith$Mutation$ChangeAutoUpgradeSettings.stub(TRes res) = + _CopyWithStubImpl$Mutation$ChangeAutoUpgradeSettings; + + TRes call({ + Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings? + changeAutoUpgradeSettings, + String? $__typename, + }); + CopyWith$Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings + get changeAutoUpgradeSettings; +} + +class _CopyWithImpl$Mutation$ChangeAutoUpgradeSettings + implements CopyWith$Mutation$ChangeAutoUpgradeSettings { + _CopyWithImpl$Mutation$ChangeAutoUpgradeSettings( + this._instance, + this._then, + ); + + final Mutation$ChangeAutoUpgradeSettings _instance; + + final TRes Function(Mutation$ChangeAutoUpgradeSettings) _then; + + static const _undefined = {}; + + TRes call({ + Object? changeAutoUpgradeSettings = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$ChangeAutoUpgradeSettings( + changeAutoUpgradeSettings: changeAutoUpgradeSettings == _undefined || + changeAutoUpgradeSettings == null + ? _instance.changeAutoUpgradeSettings + : (changeAutoUpgradeSettings + as Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings + get changeAutoUpgradeSettings { + final local$changeAutoUpgradeSettings = _instance.changeAutoUpgradeSettings; + return CopyWith$Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings( + local$changeAutoUpgradeSettings, + (e) => call(changeAutoUpgradeSettings: e)); + } +} + +class _CopyWithStubImpl$Mutation$ChangeAutoUpgradeSettings + implements CopyWith$Mutation$ChangeAutoUpgradeSettings { + _CopyWithStubImpl$Mutation$ChangeAutoUpgradeSettings(this._res); + + TRes _res; + + call({ + Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings? + changeAutoUpgradeSettings, + String? $__typename, + }) => + _res; + CopyWith$Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings + get changeAutoUpgradeSettings => + CopyWith$Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings + .stub(_res); +} + +const documentNodeMutationChangeAutoUpgradeSettings = + DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'ChangeAutoUpgradeSettings'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'settings')), + type: NamedTypeNode( + name: NameNode(value: 'AutoUpgradeSettingsInput'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'changeAutoUpgradeSettings'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'settings'), + value: VariableNode(name: NameNode(value: 'settings')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: 'allowReboot'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'enableAutoUpgrade'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, +]); +Mutation$ChangeAutoUpgradeSettings _parserFn$Mutation$ChangeAutoUpgradeSettings( + Map data) => + Mutation$ChangeAutoUpgradeSettings.fromJson(data); +typedef OnMutationCompleted$Mutation$ChangeAutoUpgradeSettings = FutureOr + Function( + Map?, + Mutation$ChangeAutoUpgradeSettings?, +); + +class Options$Mutation$ChangeAutoUpgradeSettings + extends graphql.MutationOptions { + Options$Mutation$ChangeAutoUpgradeSettings({ + String? operationName, + required Variables$Mutation$ChangeAutoUpgradeSettings variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$ChangeAutoUpgradeSettings? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$ChangeAutoUpgradeSettings? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$ChangeAutoUpgradeSettings(data), + ), + update: update, + onError: onError, + document: documentNodeMutationChangeAutoUpgradeSettings, + parserFn: _parserFn$Mutation$ChangeAutoUpgradeSettings, + ); + + final OnMutationCompleted$Mutation$ChangeAutoUpgradeSettings? + onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$ChangeAutoUpgradeSettings + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$ChangeAutoUpgradeSettings({ + String? operationName, + required Variables$Mutation$ChangeAutoUpgradeSettings variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$ChangeAutoUpgradeSettings? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationChangeAutoUpgradeSettings, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$ChangeAutoUpgradeSettings, + ); +} + +extension ClientExtension$Mutation$ChangeAutoUpgradeSettings + on graphql.GraphQLClient { + Future> + mutate$ChangeAutoUpgradeSettings( + Options$Mutation$ChangeAutoUpgradeSettings options) async => + await this.mutate(options); + graphql.ObservableQuery + watchMutation$ChangeAutoUpgradeSettings( + WatchOptions$Mutation$ChangeAutoUpgradeSettings options) => + this.watchMutation(options); +} + +class Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings + implements + Fragment$basicMutationReturnFields$$AutoUpgradeSettingsMutationReturn { + Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'AutoUpgradeSettingsMutationReturn', + required this.allowReboot, + required this.enableAutoUpgrade, + }); + + factory Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + final l$allowReboot = json['allowReboot']; + final l$enableAutoUpgrade = json['enableAutoUpgrade']; + return Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + allowReboot: (l$allowReboot as bool), + enableAutoUpgrade: (l$enableAutoUpgrade as bool), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + final bool allowReboot; + + final bool enableAutoUpgrade; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + final l$allowReboot = allowReboot; + _resultData['allowReboot'] = l$allowReboot; + final l$enableAutoUpgrade = enableAutoUpgrade; + _resultData['enableAutoUpgrade'] = l$enableAutoUpgrade; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + final l$allowReboot = allowReboot; + final l$enableAutoUpgrade = enableAutoUpgrade; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + l$allowReboot, + l$enableAutoUpgrade, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other + is Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + final l$allowReboot = allowReboot; + final lOther$allowReboot = other.allowReboot; + if (l$allowReboot != lOther$allowReboot) { + return false; + } + final l$enableAutoUpgrade = enableAutoUpgrade; + final lOther$enableAutoUpgrade = other.enableAutoUpgrade; + if (l$enableAutoUpgrade != lOther$enableAutoUpgrade) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings + on Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings { + CopyWith$Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings< + Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings> + get copyWith => + CopyWith$Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings< + TRes> { + factory CopyWith$Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings( + Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings instance, + TRes Function(Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings) + then, + ) = _CopyWithImpl$Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings; + + factory CopyWith$Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings.stub( + TRes res) = + _CopyWithStubImpl$Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + bool? allowReboot, + bool? enableAutoUpgrade, + }); +} + +class _CopyWithImpl$Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings< + TRes> + implements + CopyWith$Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings< + TRes> { + _CopyWithImpl$Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings( + this._instance, + this._then, + ); + + final Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings _instance; + + final TRes Function( + Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + Object? allowReboot = _undefined, + Object? enableAutoUpgrade = _undefined, + }) => + _then(Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + allowReboot: allowReboot == _undefined || allowReboot == null + ? _instance.allowReboot + : (allowReboot as bool), + enableAutoUpgrade: + enableAutoUpgrade == _undefined || enableAutoUpgrade == null + ? _instance.enableAutoUpgrade + : (enableAutoUpgrade as bool), + )); +} + +class _CopyWithStubImpl$Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings< + TRes> + implements + CopyWith$Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings< + TRes> { + _CopyWithStubImpl$Mutation$ChangeAutoUpgradeSettings$changeAutoUpgradeSettings( + this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + bool? allowReboot, + bool? enableAutoUpgrade, + }) => + _res; +} diff --git a/lib/logic/api_maps/graphql_maps/schema/services.graphql b/lib/logic/api_maps/graphql_maps/schema/services.graphql new file mode 100644 index 00000000..a2430dcf --- /dev/null +++ b/lib/logic/api_maps/graphql_maps/schema/services.graphql @@ -0,0 +1,66 @@ +query AllServices { + services { + allServices { + description + displayName + dnsRecords { + ...fragmentDnsRecords + } + id + isEnabled + isMovable + isRequired + canBeBackedUp + backupDescription + status + storageUsage { + title + usedSpace + volume { + name + } + } + svgIcon + url + } + } +} + +mutation EnableService($serviceId: String!) { + enableService(serviceId: $serviceId) { + ...basicMutationReturnFields + } +} + +mutation DisableService($serviceId: String!) { + disableService(serviceId: $serviceId) { + ...basicMutationReturnFields + } +} + +mutation StopService($serviceId: String!) { + stopService(serviceId: $serviceId) { + ...basicMutationReturnFields + } +} + +mutation StartService($serviceId: String!) { + startService(serviceId: $serviceId) { + ...basicMutationReturnFields + } +} + +mutation RestartService($serviceId: String!) { + restartService(serviceId: $serviceId) { + ...basicMutationReturnFields + } +} + +mutation MoveService($input: MoveServiceInput!) { + moveService(input: $input) { + ...basicMutationReturnFields + job { + ...basicApiJobsFields + } + } +} diff --git a/lib/logic/api_maps/graphql_maps/schema/services.graphql.dart b/lib/logic/api_maps/graphql_maps/schema/services.graphql.dart new file mode 100644 index 00000000..798167fd --- /dev/null +++ b/lib/logic/api_maps/graphql_maps/schema/services.graphql.dart @@ -0,0 +1,4664 @@ +import 'dart:async'; +import 'package:gql/ast.dart'; +import 'package:graphql/client.dart' as graphql; +import 'schema.graphql.dart'; +import 'server_api.graphql.dart'; +import 'server_settings.graphql.dart'; + +class Query$AllServices { + Query$AllServices({ + required this.services, + this.$__typename = 'Query', + }); + + factory Query$AllServices.fromJson(Map json) { + final l$services = json['services']; + final l$$__typename = json['__typename']; + return Query$AllServices( + services: Query$AllServices$services.fromJson( + (l$services as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Query$AllServices$services services; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$services = services; + _resultData['services'] = l$services.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$services = services; + final l$$__typename = $__typename; + return Object.hashAll([ + l$services, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$AllServices) || runtimeType != other.runtimeType) { + return false; + } + final l$services = services; + final lOther$services = other.services; + if (l$services != lOther$services) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$AllServices on Query$AllServices { + CopyWith$Query$AllServices get copyWith => + CopyWith$Query$AllServices( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$AllServices { + factory CopyWith$Query$AllServices( + Query$AllServices instance, + TRes Function(Query$AllServices) then, + ) = _CopyWithImpl$Query$AllServices; + + factory CopyWith$Query$AllServices.stub(TRes res) = + _CopyWithStubImpl$Query$AllServices; + + TRes call({ + Query$AllServices$services? services, + String? $__typename, + }); + CopyWith$Query$AllServices$services get services; +} + +class _CopyWithImpl$Query$AllServices + implements CopyWith$Query$AllServices { + _CopyWithImpl$Query$AllServices( + this._instance, + this._then, + ); + + final Query$AllServices _instance; + + final TRes Function(Query$AllServices) _then; + + static const _undefined = {}; + + TRes call({ + Object? services = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$AllServices( + services: services == _undefined || services == null + ? _instance.services + : (services as Query$AllServices$services), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$AllServices$services get services { + final local$services = _instance.services; + return CopyWith$Query$AllServices$services( + local$services, (e) => call(services: e)); + } +} + +class _CopyWithStubImpl$Query$AllServices + implements CopyWith$Query$AllServices { + _CopyWithStubImpl$Query$AllServices(this._res); + + TRes _res; + + call({ + Query$AllServices$services? services, + String? $__typename, + }) => + _res; + CopyWith$Query$AllServices$services get services => + CopyWith$Query$AllServices$services.stub(_res); +} + +const documentNodeQueryAllServices = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.query, + name: NameNode(value: 'AllServices'), + variableDefinitions: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'services'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'allServices'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'description'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'displayName'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'dnsRecords'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'fragmentDnsRecords'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: 'id'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'isEnabled'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'isMovable'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'isRequired'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'canBeBackedUp'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'backupDescription'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'status'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'storageUsage'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'title'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'usedSpace'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'volume'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'name'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: 'svgIcon'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'url'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionfragmentDnsRecords, +]); +Query$AllServices _parserFn$Query$AllServices(Map data) => + Query$AllServices.fromJson(data); +typedef OnQueryComplete$Query$AllServices = FutureOr Function( + Map?, + Query$AllServices?, +); + +class Options$Query$AllServices + extends graphql.QueryOptions { + Options$Query$AllServices({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$AllServices? typedOptimisticResult, + Duration? pollInterval, + graphql.Context? context, + OnQueryComplete$Query$AllServices? onComplete, + graphql.OnQueryError? onError, + }) : onCompleteWithParsed = onComplete, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + pollInterval: pollInterval, + context: context, + onComplete: onComplete == null + ? null + : (data) => onComplete( + data, + data == null ? null : _parserFn$Query$AllServices(data), + ), + onError: onError, + document: documentNodeQueryAllServices, + parserFn: _parserFn$Query$AllServices, + ); + + final OnQueryComplete$Query$AllServices? onCompleteWithParsed; + + @override + List get properties => [ + ...super.onComplete == null + ? super.properties + : super.properties.where((property) => property != onComplete), + onCompleteWithParsed, + ]; +} + +class WatchOptions$Query$AllServices + extends graphql.WatchQueryOptions { + WatchOptions$Query$AllServices({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$AllServices? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeQueryAllServices, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Query$AllServices, + ); +} + +class FetchMoreOptions$Query$AllServices extends graphql.FetchMoreOptions { + FetchMoreOptions$Query$AllServices({required graphql.UpdateQuery updateQuery}) + : super( + updateQuery: updateQuery, + document: documentNodeQueryAllServices, + ); +} + +extension ClientExtension$Query$AllServices on graphql.GraphQLClient { + Future> query$AllServices( + [Options$Query$AllServices? options]) async => + await this.query(options ?? Options$Query$AllServices()); + graphql.ObservableQuery watchQuery$AllServices( + [WatchOptions$Query$AllServices? options]) => + this.watchQuery(options ?? WatchOptions$Query$AllServices()); + void writeQuery$AllServices({ + required Query$AllServices data, + bool broadcast = true, + }) => + this.writeQuery( + graphql.Request( + operation: + graphql.Operation(document: documentNodeQueryAllServices)), + data: data.toJson(), + broadcast: broadcast, + ); + Query$AllServices? readQuery$AllServices({bool optimistic = true}) { + final result = this.readQuery( + graphql.Request( + operation: graphql.Operation(document: documentNodeQueryAllServices)), + optimistic: optimistic, + ); + return result == null ? null : Query$AllServices.fromJson(result); + } +} + +class Query$AllServices$services { + Query$AllServices$services({ + required this.allServices, + this.$__typename = 'Services', + }); + + factory Query$AllServices$services.fromJson(Map json) { + final l$allServices = json['allServices']; + final l$$__typename = json['__typename']; + return Query$AllServices$services( + allServices: (l$allServices as List) + .map((e) => Query$AllServices$services$allServices.fromJson( + (e as Map))) + .toList(), + $__typename: (l$$__typename as String), + ); + } + + final List allServices; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$allServices = allServices; + _resultData['allServices'] = l$allServices.map((e) => e.toJson()).toList(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$allServices = allServices; + final l$$__typename = $__typename; + return Object.hashAll([ + Object.hashAll(l$allServices.map((v) => v)), + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$AllServices$services) || + runtimeType != other.runtimeType) { + return false; + } + final l$allServices = allServices; + final lOther$allServices = other.allServices; + if (l$allServices.length != lOther$allServices.length) { + return false; + } + for (int i = 0; i < l$allServices.length; i++) { + final l$allServices$entry = l$allServices[i]; + final lOther$allServices$entry = lOther$allServices[i]; + if (l$allServices$entry != lOther$allServices$entry) { + return false; + } + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$AllServices$services + on Query$AllServices$services { + CopyWith$Query$AllServices$services + get copyWith => CopyWith$Query$AllServices$services( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$AllServices$services { + factory CopyWith$Query$AllServices$services( + Query$AllServices$services instance, + TRes Function(Query$AllServices$services) then, + ) = _CopyWithImpl$Query$AllServices$services; + + factory CopyWith$Query$AllServices$services.stub(TRes res) = + _CopyWithStubImpl$Query$AllServices$services; + + TRes call({ + List? allServices, + String? $__typename, + }); + TRes allServices( + Iterable Function( + Iterable< + CopyWith$Query$AllServices$services$allServices< + Query$AllServices$services$allServices>>) + _fn); +} + +class _CopyWithImpl$Query$AllServices$services + implements CopyWith$Query$AllServices$services { + _CopyWithImpl$Query$AllServices$services( + this._instance, + this._then, + ); + + final Query$AllServices$services _instance; + + final TRes Function(Query$AllServices$services) _then; + + static const _undefined = {}; + + TRes call({ + Object? allServices = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$AllServices$services( + allServices: allServices == _undefined || allServices == null + ? _instance.allServices + : (allServices as List), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + TRes allServices( + Iterable Function( + Iterable< + CopyWith$Query$AllServices$services$allServices< + Query$AllServices$services$allServices>>) + _fn) => + call( + allServices: _fn(_instance.allServices + .map((e) => CopyWith$Query$AllServices$services$allServices( + e, + (i) => i, + ))).toList()); +} + +class _CopyWithStubImpl$Query$AllServices$services + implements CopyWith$Query$AllServices$services { + _CopyWithStubImpl$Query$AllServices$services(this._res); + + TRes _res; + + call({ + List? allServices, + String? $__typename, + }) => + _res; + allServices(_fn) => _res; +} + +class Query$AllServices$services$allServices { + Query$AllServices$services$allServices({ + required this.description, + required this.displayName, + this.dnsRecords, + required this.id, + required this.isEnabled, + required this.isMovable, + required this.isRequired, + required this.canBeBackedUp, + required this.backupDescription, + required this.status, + required this.storageUsage, + required this.svgIcon, + this.url, + this.$__typename = 'Service', + }); + + factory Query$AllServices$services$allServices.fromJson( + Map json) { + final l$description = json['description']; + final l$displayName = json['displayName']; + final l$dnsRecords = json['dnsRecords']; + final l$id = json['id']; + final l$isEnabled = json['isEnabled']; + final l$isMovable = json['isMovable']; + final l$isRequired = json['isRequired']; + final l$canBeBackedUp = json['canBeBackedUp']; + final l$backupDescription = json['backupDescription']; + final l$status = json['status']; + final l$storageUsage = json['storageUsage']; + final l$svgIcon = json['svgIcon']; + final l$url = json['url']; + final l$$__typename = json['__typename']; + return Query$AllServices$services$allServices( + description: (l$description as String), + displayName: (l$displayName as String), + dnsRecords: (l$dnsRecords as List?) + ?.map((e) => + Fragment$fragmentDnsRecords.fromJson((e as Map))) + .toList(), + id: (l$id as String), + isEnabled: (l$isEnabled as bool), + isMovable: (l$isMovable as bool), + isRequired: (l$isRequired as bool), + canBeBackedUp: (l$canBeBackedUp as bool), + backupDescription: (l$backupDescription as String), + status: fromJson$Enum$ServiceStatusEnum((l$status as String)), + storageUsage: + Query$AllServices$services$allServices$storageUsage.fromJson( + (l$storageUsage as Map)), + svgIcon: (l$svgIcon as String), + url: (l$url as String?), + $__typename: (l$$__typename as String), + ); + } + + final String description; + + final String displayName; + + final List? dnsRecords; + + final String id; + + final bool isEnabled; + + final bool isMovable; + + final bool isRequired; + + final bool canBeBackedUp; + + final String backupDescription; + + final Enum$ServiceStatusEnum status; + + final Query$AllServices$services$allServices$storageUsage storageUsage; + + final String svgIcon; + + final String? url; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$description = description; + _resultData['description'] = l$description; + final l$displayName = displayName; + _resultData['displayName'] = l$displayName; + final l$dnsRecords = dnsRecords; + _resultData['dnsRecords'] = l$dnsRecords?.map((e) => e.toJson()).toList(); + final l$id = id; + _resultData['id'] = l$id; + final l$isEnabled = isEnabled; + _resultData['isEnabled'] = l$isEnabled; + final l$isMovable = isMovable; + _resultData['isMovable'] = l$isMovable; + final l$isRequired = isRequired; + _resultData['isRequired'] = l$isRequired; + final l$canBeBackedUp = canBeBackedUp; + _resultData['canBeBackedUp'] = l$canBeBackedUp; + final l$backupDescription = backupDescription; + _resultData['backupDescription'] = l$backupDescription; + final l$status = status; + _resultData['status'] = toJson$Enum$ServiceStatusEnum(l$status); + final l$storageUsage = storageUsage; + _resultData['storageUsage'] = l$storageUsage.toJson(); + final l$svgIcon = svgIcon; + _resultData['svgIcon'] = l$svgIcon; + final l$url = url; + _resultData['url'] = l$url; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$description = description; + final l$displayName = displayName; + final l$dnsRecords = dnsRecords; + final l$id = id; + final l$isEnabled = isEnabled; + final l$isMovable = isMovable; + final l$isRequired = isRequired; + final l$canBeBackedUp = canBeBackedUp; + final l$backupDescription = backupDescription; + final l$status = status; + final l$storageUsage = storageUsage; + final l$svgIcon = svgIcon; + final l$url = url; + final l$$__typename = $__typename; + return Object.hashAll([ + l$description, + l$displayName, + l$dnsRecords == null ? null : Object.hashAll(l$dnsRecords.map((v) => v)), + l$id, + l$isEnabled, + l$isMovable, + l$isRequired, + l$canBeBackedUp, + l$backupDescription, + l$status, + l$storageUsage, + l$svgIcon, + l$url, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$AllServices$services$allServices) || + runtimeType != other.runtimeType) { + return false; + } + final l$description = description; + final lOther$description = other.description; + if (l$description != lOther$description) { + return false; + } + final l$displayName = displayName; + final lOther$displayName = other.displayName; + if (l$displayName != lOther$displayName) { + return false; + } + final l$dnsRecords = dnsRecords; + final lOther$dnsRecords = other.dnsRecords; + if (l$dnsRecords != null && lOther$dnsRecords != null) { + if (l$dnsRecords.length != lOther$dnsRecords.length) { + return false; + } + for (int i = 0; i < l$dnsRecords.length; i++) { + final l$dnsRecords$entry = l$dnsRecords[i]; + final lOther$dnsRecords$entry = lOther$dnsRecords[i]; + if (l$dnsRecords$entry != lOther$dnsRecords$entry) { + return false; + } + } + } else if (l$dnsRecords != lOther$dnsRecords) { + return false; + } + final l$id = id; + final lOther$id = other.id; + if (l$id != lOther$id) { + return false; + } + final l$isEnabled = isEnabled; + final lOther$isEnabled = other.isEnabled; + if (l$isEnabled != lOther$isEnabled) { + return false; + } + final l$isMovable = isMovable; + final lOther$isMovable = other.isMovable; + if (l$isMovable != lOther$isMovable) { + return false; + } + final l$isRequired = isRequired; + final lOther$isRequired = other.isRequired; + if (l$isRequired != lOther$isRequired) { + return false; + } + final l$canBeBackedUp = canBeBackedUp; + final lOther$canBeBackedUp = other.canBeBackedUp; + if (l$canBeBackedUp != lOther$canBeBackedUp) { + return false; + } + final l$backupDescription = backupDescription; + final lOther$backupDescription = other.backupDescription; + if (l$backupDescription != lOther$backupDescription) { + return false; + } + final l$status = status; + final lOther$status = other.status; + if (l$status != lOther$status) { + return false; + } + final l$storageUsage = storageUsage; + final lOther$storageUsage = other.storageUsage; + if (l$storageUsage != lOther$storageUsage) { + return false; + } + final l$svgIcon = svgIcon; + final lOther$svgIcon = other.svgIcon; + if (l$svgIcon != lOther$svgIcon) { + return false; + } + final l$url = url; + final lOther$url = other.url; + if (l$url != lOther$url) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$AllServices$services$allServices + on Query$AllServices$services$allServices { + CopyWith$Query$AllServices$services$allServices< + Query$AllServices$services$allServices> + get copyWith => CopyWith$Query$AllServices$services$allServices( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$AllServices$services$allServices { + factory CopyWith$Query$AllServices$services$allServices( + Query$AllServices$services$allServices instance, + TRes Function(Query$AllServices$services$allServices) then, + ) = _CopyWithImpl$Query$AllServices$services$allServices; + + factory CopyWith$Query$AllServices$services$allServices.stub(TRes res) = + _CopyWithStubImpl$Query$AllServices$services$allServices; + + TRes call({ + String? description, + String? displayName, + List? dnsRecords, + String? id, + bool? isEnabled, + bool? isMovable, + bool? isRequired, + bool? canBeBackedUp, + String? backupDescription, + Enum$ServiceStatusEnum? status, + Query$AllServices$services$allServices$storageUsage? storageUsage, + String? svgIcon, + String? url, + String? $__typename, + }); + TRes dnsRecords( + Iterable? Function( + Iterable< + CopyWith$Fragment$fragmentDnsRecords< + Fragment$fragmentDnsRecords>>?) + _fn); + CopyWith$Query$AllServices$services$allServices$storageUsage + get storageUsage; +} + +class _CopyWithImpl$Query$AllServices$services$allServices + implements CopyWith$Query$AllServices$services$allServices { + _CopyWithImpl$Query$AllServices$services$allServices( + this._instance, + this._then, + ); + + final Query$AllServices$services$allServices _instance; + + final TRes Function(Query$AllServices$services$allServices) _then; + + static const _undefined = {}; + + TRes call({ + Object? description = _undefined, + Object? displayName = _undefined, + Object? dnsRecords = _undefined, + Object? id = _undefined, + Object? isEnabled = _undefined, + Object? isMovable = _undefined, + Object? isRequired = _undefined, + Object? canBeBackedUp = _undefined, + Object? backupDescription = _undefined, + Object? status = _undefined, + Object? storageUsage = _undefined, + Object? svgIcon = _undefined, + Object? url = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$AllServices$services$allServices( + description: description == _undefined || description == null + ? _instance.description + : (description as String), + displayName: displayName == _undefined || displayName == null + ? _instance.displayName + : (displayName as String), + dnsRecords: dnsRecords == _undefined + ? _instance.dnsRecords + : (dnsRecords as List?), + id: id == _undefined || id == null ? _instance.id : (id as String), + isEnabled: isEnabled == _undefined || isEnabled == null + ? _instance.isEnabled + : (isEnabled as bool), + isMovable: isMovable == _undefined || isMovable == null + ? _instance.isMovable + : (isMovable as bool), + isRequired: isRequired == _undefined || isRequired == null + ? _instance.isRequired + : (isRequired as bool), + canBeBackedUp: canBeBackedUp == _undefined || canBeBackedUp == null + ? _instance.canBeBackedUp + : (canBeBackedUp as bool), + backupDescription: + backupDescription == _undefined || backupDescription == null + ? _instance.backupDescription + : (backupDescription as String), + status: status == _undefined || status == null + ? _instance.status + : (status as Enum$ServiceStatusEnum), + storageUsage: storageUsage == _undefined || storageUsage == null + ? _instance.storageUsage + : (storageUsage + as Query$AllServices$services$allServices$storageUsage), + svgIcon: svgIcon == _undefined || svgIcon == null + ? _instance.svgIcon + : (svgIcon as String), + url: url == _undefined ? _instance.url : (url as String?), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + TRes dnsRecords( + Iterable? Function( + Iterable< + CopyWith$Fragment$fragmentDnsRecords< + Fragment$fragmentDnsRecords>>?) + _fn) => + call( + dnsRecords: _fn(_instance.dnsRecords + ?.map((e) => CopyWith$Fragment$fragmentDnsRecords( + e, + (i) => i, + )))?.toList()); + CopyWith$Query$AllServices$services$allServices$storageUsage + get storageUsage { + final local$storageUsage = _instance.storageUsage; + return CopyWith$Query$AllServices$services$allServices$storageUsage( + local$storageUsage, (e) => call(storageUsage: e)); + } +} + +class _CopyWithStubImpl$Query$AllServices$services$allServices + implements CopyWith$Query$AllServices$services$allServices { + _CopyWithStubImpl$Query$AllServices$services$allServices(this._res); + + TRes _res; + + call({ + String? description, + String? displayName, + List? dnsRecords, + String? id, + bool? isEnabled, + bool? isMovable, + bool? isRequired, + bool? canBeBackedUp, + String? backupDescription, + Enum$ServiceStatusEnum? status, + Query$AllServices$services$allServices$storageUsage? storageUsage, + String? svgIcon, + String? url, + String? $__typename, + }) => + _res; + dnsRecords(_fn) => _res; + CopyWith$Query$AllServices$services$allServices$storageUsage + get storageUsage => + CopyWith$Query$AllServices$services$allServices$storageUsage.stub( + _res); +} + +class Query$AllServices$services$allServices$storageUsage { + Query$AllServices$services$allServices$storageUsage({ + required this.title, + required this.usedSpace, + this.volume, + this.$__typename = 'ServiceStorageUsage', + }); + + factory Query$AllServices$services$allServices$storageUsage.fromJson( + Map json) { + final l$title = json['title']; + final l$usedSpace = json['usedSpace']; + final l$volume = json['volume']; + final l$$__typename = json['__typename']; + return Query$AllServices$services$allServices$storageUsage( + title: (l$title as String), + usedSpace: (l$usedSpace as String), + volume: l$volume == null + ? null + : Query$AllServices$services$allServices$storageUsage$volume.fromJson( + (l$volume as Map)), + $__typename: (l$$__typename as String), + ); + } + + final String title; + + final String usedSpace; + + final Query$AllServices$services$allServices$storageUsage$volume? volume; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$title = title; + _resultData['title'] = l$title; + final l$usedSpace = usedSpace; + _resultData['usedSpace'] = l$usedSpace; + final l$volume = volume; + _resultData['volume'] = l$volume?.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$title = title; + final l$usedSpace = usedSpace; + final l$volume = volume; + final l$$__typename = $__typename; + return Object.hashAll([ + l$title, + l$usedSpace, + l$volume, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$AllServices$services$allServices$storageUsage) || + runtimeType != other.runtimeType) { + return false; + } + final l$title = title; + final lOther$title = other.title; + if (l$title != lOther$title) { + return false; + } + final l$usedSpace = usedSpace; + final lOther$usedSpace = other.usedSpace; + if (l$usedSpace != lOther$usedSpace) { + return false; + } + final l$volume = volume; + final lOther$volume = other.volume; + if (l$volume != lOther$volume) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$AllServices$services$allServices$storageUsage + on Query$AllServices$services$allServices$storageUsage { + CopyWith$Query$AllServices$services$allServices$storageUsage< + Query$AllServices$services$allServices$storageUsage> + get copyWith => + CopyWith$Query$AllServices$services$allServices$storageUsage( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$AllServices$services$allServices$storageUsage< + TRes> { + factory CopyWith$Query$AllServices$services$allServices$storageUsage( + Query$AllServices$services$allServices$storageUsage instance, + TRes Function(Query$AllServices$services$allServices$storageUsage) then, + ) = _CopyWithImpl$Query$AllServices$services$allServices$storageUsage; + + factory CopyWith$Query$AllServices$services$allServices$storageUsage.stub( + TRes res) = + _CopyWithStubImpl$Query$AllServices$services$allServices$storageUsage; + + TRes call({ + String? title, + String? usedSpace, + Query$AllServices$services$allServices$storageUsage$volume? volume, + String? $__typename, + }); + CopyWith$Query$AllServices$services$allServices$storageUsage$volume + get volume; +} + +class _CopyWithImpl$Query$AllServices$services$allServices$storageUsage + implements + CopyWith$Query$AllServices$services$allServices$storageUsage { + _CopyWithImpl$Query$AllServices$services$allServices$storageUsage( + this._instance, + this._then, + ); + + final Query$AllServices$services$allServices$storageUsage _instance; + + final TRes Function(Query$AllServices$services$allServices$storageUsage) + _then; + + static const _undefined = {}; + + TRes call({ + Object? title = _undefined, + Object? usedSpace = _undefined, + Object? volume = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$AllServices$services$allServices$storageUsage( + title: title == _undefined || title == null + ? _instance.title + : (title as String), + usedSpace: usedSpace == _undefined || usedSpace == null + ? _instance.usedSpace + : (usedSpace as String), + volume: volume == _undefined + ? _instance.volume + : (volume + as Query$AllServices$services$allServices$storageUsage$volume?), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$AllServices$services$allServices$storageUsage$volume + get volume { + final local$volume = _instance.volume; + return local$volume == null + ? CopyWith$Query$AllServices$services$allServices$storageUsage$volume + .stub(_then(_instance)) + : CopyWith$Query$AllServices$services$allServices$storageUsage$volume( + local$volume, (e) => call(volume: e)); + } +} + +class _CopyWithStubImpl$Query$AllServices$services$allServices$storageUsage< + TRes> + implements + CopyWith$Query$AllServices$services$allServices$storageUsage { + _CopyWithStubImpl$Query$AllServices$services$allServices$storageUsage( + this._res); + + TRes _res; + + call({ + String? title, + String? usedSpace, + Query$AllServices$services$allServices$storageUsage$volume? volume, + String? $__typename, + }) => + _res; + CopyWith$Query$AllServices$services$allServices$storageUsage$volume + get volume => + CopyWith$Query$AllServices$services$allServices$storageUsage$volume + .stub(_res); +} + +class Query$AllServices$services$allServices$storageUsage$volume { + Query$AllServices$services$allServices$storageUsage$volume({ + required this.name, + this.$__typename = 'StorageVolume', + }); + + factory Query$AllServices$services$allServices$storageUsage$volume.fromJson( + Map json) { + final l$name = json['name']; + final l$$__typename = json['__typename']; + return Query$AllServices$services$allServices$storageUsage$volume( + name: (l$name as String), + $__typename: (l$$__typename as String), + ); + } + + final String name; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$name = name; + _resultData['name'] = l$name; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$name = name; + final l$$__typename = $__typename; + return Object.hashAll([ + l$name, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other + is Query$AllServices$services$allServices$storageUsage$volume) || + runtimeType != other.runtimeType) { + return false; + } + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$AllServices$services$allServices$storageUsage$volume + on Query$AllServices$services$allServices$storageUsage$volume { + CopyWith$Query$AllServices$services$allServices$storageUsage$volume< + Query$AllServices$services$allServices$storageUsage$volume> + get copyWith => + CopyWith$Query$AllServices$services$allServices$storageUsage$volume( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$AllServices$services$allServices$storageUsage$volume< + TRes> { + factory CopyWith$Query$AllServices$services$allServices$storageUsage$volume( + Query$AllServices$services$allServices$storageUsage$volume instance, + TRes Function(Query$AllServices$services$allServices$storageUsage$volume) + then, + ) = _CopyWithImpl$Query$AllServices$services$allServices$storageUsage$volume; + + factory CopyWith$Query$AllServices$services$allServices$storageUsage$volume.stub( + TRes res) = + _CopyWithStubImpl$Query$AllServices$services$allServices$storageUsage$volume; + + TRes call({ + String? name, + String? $__typename, + }); +} + +class _CopyWithImpl$Query$AllServices$services$allServices$storageUsage$volume< + TRes> + implements + CopyWith$Query$AllServices$services$allServices$storageUsage$volume< + TRes> { + _CopyWithImpl$Query$AllServices$services$allServices$storageUsage$volume( + this._instance, + this._then, + ); + + final Query$AllServices$services$allServices$storageUsage$volume _instance; + + final TRes Function( + Query$AllServices$services$allServices$storageUsage$volume) _then; + + static const _undefined = {}; + + TRes call({ + Object? name = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$AllServices$services$allServices$storageUsage$volume( + name: name == _undefined || name == null + ? _instance.name + : (name as String), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Query$AllServices$services$allServices$storageUsage$volume< + TRes> + implements + CopyWith$Query$AllServices$services$allServices$storageUsage$volume< + TRes> { + _CopyWithStubImpl$Query$AllServices$services$allServices$storageUsage$volume( + this._res); + + TRes _res; + + call({ + String? name, + String? $__typename, + }) => + _res; +} + +class Variables$Mutation$EnableService { + factory Variables$Mutation$EnableService({required String serviceId}) => + Variables$Mutation$EnableService._({ + r'serviceId': serviceId, + }); + + Variables$Mutation$EnableService._(this._$data); + + factory Variables$Mutation$EnableService.fromJson(Map data) { + final result$data = {}; + final l$serviceId = data['serviceId']; + result$data['serviceId'] = (l$serviceId as String); + return Variables$Mutation$EnableService._(result$data); + } + + Map _$data; + + String get serviceId => (_$data['serviceId'] as String); + Map toJson() { + final result$data = {}; + final l$serviceId = serviceId; + result$data['serviceId'] = l$serviceId; + return result$data; + } + + CopyWith$Variables$Mutation$EnableService + get copyWith => CopyWith$Variables$Mutation$EnableService( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$EnableService) || + runtimeType != other.runtimeType) { + return false; + } + final l$serviceId = serviceId; + final lOther$serviceId = other.serviceId; + if (l$serviceId != lOther$serviceId) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$serviceId = serviceId; + return Object.hashAll([l$serviceId]); + } +} + +abstract class CopyWith$Variables$Mutation$EnableService { + factory CopyWith$Variables$Mutation$EnableService( + Variables$Mutation$EnableService instance, + TRes Function(Variables$Mutation$EnableService) then, + ) = _CopyWithImpl$Variables$Mutation$EnableService; + + factory CopyWith$Variables$Mutation$EnableService.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$EnableService; + + TRes call({String? serviceId}); +} + +class _CopyWithImpl$Variables$Mutation$EnableService + implements CopyWith$Variables$Mutation$EnableService { + _CopyWithImpl$Variables$Mutation$EnableService( + this._instance, + this._then, + ); + + final Variables$Mutation$EnableService _instance; + + final TRes Function(Variables$Mutation$EnableService) _then; + + static const _undefined = {}; + + TRes call({Object? serviceId = _undefined}) => + _then(Variables$Mutation$EnableService._({ + ..._instance._$data, + if (serviceId != _undefined && serviceId != null) + 'serviceId': (serviceId as String), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$EnableService + implements CopyWith$Variables$Mutation$EnableService { + _CopyWithStubImpl$Variables$Mutation$EnableService(this._res); + + TRes _res; + + call({String? serviceId}) => _res; +} + +class Mutation$EnableService { + Mutation$EnableService({ + required this.enableService, + this.$__typename = 'Mutation', + }); + + factory Mutation$EnableService.fromJson(Map json) { + final l$enableService = json['enableService']; + final l$$__typename = json['__typename']; + return Mutation$EnableService( + enableService: Mutation$EnableService$enableService.fromJson( + (l$enableService as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$EnableService$enableService enableService; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$enableService = enableService; + _resultData['enableService'] = l$enableService.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$enableService = enableService; + final l$$__typename = $__typename; + return Object.hashAll([ + l$enableService, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$EnableService) || + runtimeType != other.runtimeType) { + return false; + } + final l$enableService = enableService; + final lOther$enableService = other.enableService; + if (l$enableService != lOther$enableService) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$EnableService on Mutation$EnableService { + CopyWith$Mutation$EnableService get copyWith => + CopyWith$Mutation$EnableService( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$EnableService { + factory CopyWith$Mutation$EnableService( + Mutation$EnableService instance, + TRes Function(Mutation$EnableService) then, + ) = _CopyWithImpl$Mutation$EnableService; + + factory CopyWith$Mutation$EnableService.stub(TRes res) = + _CopyWithStubImpl$Mutation$EnableService; + + TRes call({ + Mutation$EnableService$enableService? enableService, + String? $__typename, + }); + CopyWith$Mutation$EnableService$enableService get enableService; +} + +class _CopyWithImpl$Mutation$EnableService + implements CopyWith$Mutation$EnableService { + _CopyWithImpl$Mutation$EnableService( + this._instance, + this._then, + ); + + final Mutation$EnableService _instance; + + final TRes Function(Mutation$EnableService) _then; + + static const _undefined = {}; + + TRes call({ + Object? enableService = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$EnableService( + enableService: enableService == _undefined || enableService == null + ? _instance.enableService + : (enableService as Mutation$EnableService$enableService), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$EnableService$enableService get enableService { + final local$enableService = _instance.enableService; + return CopyWith$Mutation$EnableService$enableService( + local$enableService, (e) => call(enableService: e)); + } +} + +class _CopyWithStubImpl$Mutation$EnableService + implements CopyWith$Mutation$EnableService { + _CopyWithStubImpl$Mutation$EnableService(this._res); + + TRes _res; + + call({ + Mutation$EnableService$enableService? enableService, + String? $__typename, + }) => + _res; + CopyWith$Mutation$EnableService$enableService get enableService => + CopyWith$Mutation$EnableService$enableService.stub(_res); +} + +const documentNodeMutationEnableService = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'EnableService'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'serviceId')), + type: NamedTypeNode( + name: NameNode(value: 'String'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'enableService'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'serviceId'), + value: VariableNode(name: NameNode(value: 'serviceId')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, +]); +Mutation$EnableService _parserFn$Mutation$EnableService( + Map data) => + Mutation$EnableService.fromJson(data); +typedef OnMutationCompleted$Mutation$EnableService = FutureOr Function( + Map?, + Mutation$EnableService?, +); + +class Options$Mutation$EnableService + extends graphql.MutationOptions { + Options$Mutation$EnableService({ + String? operationName, + required Variables$Mutation$EnableService variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$EnableService? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$EnableService? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$EnableService(data), + ), + update: update, + onError: onError, + document: documentNodeMutationEnableService, + parserFn: _parserFn$Mutation$EnableService, + ); + + final OnMutationCompleted$Mutation$EnableService? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$EnableService + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$EnableService({ + String? operationName, + required Variables$Mutation$EnableService variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$EnableService? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationEnableService, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$EnableService, + ); +} + +extension ClientExtension$Mutation$EnableService on graphql.GraphQLClient { + Future> mutate$EnableService( + Options$Mutation$EnableService options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$EnableService( + WatchOptions$Mutation$EnableService options) => + this.watchMutation(options); +} + +class Mutation$EnableService$enableService + implements Fragment$basicMutationReturnFields$$ServiceMutationReturn { + Mutation$EnableService$enableService({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'ServiceMutationReturn', + }); + + factory Mutation$EnableService$enableService.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Mutation$EnableService$enableService( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$EnableService$enableService) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$EnableService$enableService + on Mutation$EnableService$enableService { + CopyWith$Mutation$EnableService$enableService< + Mutation$EnableService$enableService> + get copyWith => CopyWith$Mutation$EnableService$enableService( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$EnableService$enableService { + factory CopyWith$Mutation$EnableService$enableService( + Mutation$EnableService$enableService instance, + TRes Function(Mutation$EnableService$enableService) then, + ) = _CopyWithImpl$Mutation$EnableService$enableService; + + factory CopyWith$Mutation$EnableService$enableService.stub(TRes res) = + _CopyWithStubImpl$Mutation$EnableService$enableService; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$EnableService$enableService + implements CopyWith$Mutation$EnableService$enableService { + _CopyWithImpl$Mutation$EnableService$enableService( + this._instance, + this._then, + ); + + final Mutation$EnableService$enableService _instance; + + final TRes Function(Mutation$EnableService$enableService) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$EnableService$enableService( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$EnableService$enableService + implements CopyWith$Mutation$EnableService$enableService { + _CopyWithStubImpl$Mutation$EnableService$enableService(this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +class Variables$Mutation$DisableService { + factory Variables$Mutation$DisableService({required String serviceId}) => + Variables$Mutation$DisableService._({ + r'serviceId': serviceId, + }); + + Variables$Mutation$DisableService._(this._$data); + + factory Variables$Mutation$DisableService.fromJson( + Map data) { + final result$data = {}; + final l$serviceId = data['serviceId']; + result$data['serviceId'] = (l$serviceId as String); + return Variables$Mutation$DisableService._(result$data); + } + + Map _$data; + + String get serviceId => (_$data['serviceId'] as String); + Map toJson() { + final result$data = {}; + final l$serviceId = serviceId; + result$data['serviceId'] = l$serviceId; + return result$data; + } + + CopyWith$Variables$Mutation$DisableService + get copyWith => CopyWith$Variables$Mutation$DisableService( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$DisableService) || + runtimeType != other.runtimeType) { + return false; + } + final l$serviceId = serviceId; + final lOther$serviceId = other.serviceId; + if (l$serviceId != lOther$serviceId) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$serviceId = serviceId; + return Object.hashAll([l$serviceId]); + } +} + +abstract class CopyWith$Variables$Mutation$DisableService { + factory CopyWith$Variables$Mutation$DisableService( + Variables$Mutation$DisableService instance, + TRes Function(Variables$Mutation$DisableService) then, + ) = _CopyWithImpl$Variables$Mutation$DisableService; + + factory CopyWith$Variables$Mutation$DisableService.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$DisableService; + + TRes call({String? serviceId}); +} + +class _CopyWithImpl$Variables$Mutation$DisableService + implements CopyWith$Variables$Mutation$DisableService { + _CopyWithImpl$Variables$Mutation$DisableService( + this._instance, + this._then, + ); + + final Variables$Mutation$DisableService _instance; + + final TRes Function(Variables$Mutation$DisableService) _then; + + static const _undefined = {}; + + TRes call({Object? serviceId = _undefined}) => + _then(Variables$Mutation$DisableService._({ + ..._instance._$data, + if (serviceId != _undefined && serviceId != null) + 'serviceId': (serviceId as String), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$DisableService + implements CopyWith$Variables$Mutation$DisableService { + _CopyWithStubImpl$Variables$Mutation$DisableService(this._res); + + TRes _res; + + call({String? serviceId}) => _res; +} + +class Mutation$DisableService { + Mutation$DisableService({ + required this.disableService, + this.$__typename = 'Mutation', + }); + + factory Mutation$DisableService.fromJson(Map json) { + final l$disableService = json['disableService']; + final l$$__typename = json['__typename']; + return Mutation$DisableService( + disableService: Mutation$DisableService$disableService.fromJson( + (l$disableService as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$DisableService$disableService disableService; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$disableService = disableService; + _resultData['disableService'] = l$disableService.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$disableService = disableService; + final l$$__typename = $__typename; + return Object.hashAll([ + l$disableService, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$DisableService) || + runtimeType != other.runtimeType) { + return false; + } + final l$disableService = disableService; + final lOther$disableService = other.disableService; + if (l$disableService != lOther$disableService) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$DisableService on Mutation$DisableService { + CopyWith$Mutation$DisableService get copyWith => + CopyWith$Mutation$DisableService( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$DisableService { + factory CopyWith$Mutation$DisableService( + Mutation$DisableService instance, + TRes Function(Mutation$DisableService) then, + ) = _CopyWithImpl$Mutation$DisableService; + + factory CopyWith$Mutation$DisableService.stub(TRes res) = + _CopyWithStubImpl$Mutation$DisableService; + + TRes call({ + Mutation$DisableService$disableService? disableService, + String? $__typename, + }); + CopyWith$Mutation$DisableService$disableService get disableService; +} + +class _CopyWithImpl$Mutation$DisableService + implements CopyWith$Mutation$DisableService { + _CopyWithImpl$Mutation$DisableService( + this._instance, + this._then, + ); + + final Mutation$DisableService _instance; + + final TRes Function(Mutation$DisableService) _then; + + static const _undefined = {}; + + TRes call({ + Object? disableService = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$DisableService( + disableService: disableService == _undefined || disableService == null + ? _instance.disableService + : (disableService as Mutation$DisableService$disableService), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$DisableService$disableService get disableService { + final local$disableService = _instance.disableService; + return CopyWith$Mutation$DisableService$disableService( + local$disableService, (e) => call(disableService: e)); + } +} + +class _CopyWithStubImpl$Mutation$DisableService + implements CopyWith$Mutation$DisableService { + _CopyWithStubImpl$Mutation$DisableService(this._res); + + TRes _res; + + call({ + Mutation$DisableService$disableService? disableService, + String? $__typename, + }) => + _res; + CopyWith$Mutation$DisableService$disableService get disableService => + CopyWith$Mutation$DisableService$disableService.stub(_res); +} + +const documentNodeMutationDisableService = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'DisableService'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'serviceId')), + type: NamedTypeNode( + name: NameNode(value: 'String'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'disableService'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'serviceId'), + value: VariableNode(name: NameNode(value: 'serviceId')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, +]); +Mutation$DisableService _parserFn$Mutation$DisableService( + Map data) => + Mutation$DisableService.fromJson(data); +typedef OnMutationCompleted$Mutation$DisableService = FutureOr Function( + Map?, + Mutation$DisableService?, +); + +class Options$Mutation$DisableService + extends graphql.MutationOptions { + Options$Mutation$DisableService({ + String? operationName, + required Variables$Mutation$DisableService variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$DisableService? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$DisableService? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$DisableService(data), + ), + update: update, + onError: onError, + document: documentNodeMutationDisableService, + parserFn: _parserFn$Mutation$DisableService, + ); + + final OnMutationCompleted$Mutation$DisableService? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$DisableService + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$DisableService({ + String? operationName, + required Variables$Mutation$DisableService variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$DisableService? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationDisableService, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$DisableService, + ); +} + +extension ClientExtension$Mutation$DisableService on graphql.GraphQLClient { + Future> mutate$DisableService( + Options$Mutation$DisableService options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$DisableService( + WatchOptions$Mutation$DisableService options) => + this.watchMutation(options); +} + +class Mutation$DisableService$disableService + implements Fragment$basicMutationReturnFields$$ServiceMutationReturn { + Mutation$DisableService$disableService({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'ServiceMutationReturn', + }); + + factory Mutation$DisableService$disableService.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Mutation$DisableService$disableService( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$DisableService$disableService) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$DisableService$disableService + on Mutation$DisableService$disableService { + CopyWith$Mutation$DisableService$disableService< + Mutation$DisableService$disableService> + get copyWith => CopyWith$Mutation$DisableService$disableService( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$DisableService$disableService { + factory CopyWith$Mutation$DisableService$disableService( + Mutation$DisableService$disableService instance, + TRes Function(Mutation$DisableService$disableService) then, + ) = _CopyWithImpl$Mutation$DisableService$disableService; + + factory CopyWith$Mutation$DisableService$disableService.stub(TRes res) = + _CopyWithStubImpl$Mutation$DisableService$disableService; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$DisableService$disableService + implements CopyWith$Mutation$DisableService$disableService { + _CopyWithImpl$Mutation$DisableService$disableService( + this._instance, + this._then, + ); + + final Mutation$DisableService$disableService _instance; + + final TRes Function(Mutation$DisableService$disableService) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$DisableService$disableService( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$DisableService$disableService + implements CopyWith$Mutation$DisableService$disableService { + _CopyWithStubImpl$Mutation$DisableService$disableService(this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +class Variables$Mutation$StopService { + factory Variables$Mutation$StopService({required String serviceId}) => + Variables$Mutation$StopService._({ + r'serviceId': serviceId, + }); + + Variables$Mutation$StopService._(this._$data); + + factory Variables$Mutation$StopService.fromJson(Map data) { + final result$data = {}; + final l$serviceId = data['serviceId']; + result$data['serviceId'] = (l$serviceId as String); + return Variables$Mutation$StopService._(result$data); + } + + Map _$data; + + String get serviceId => (_$data['serviceId'] as String); + Map toJson() { + final result$data = {}; + final l$serviceId = serviceId; + result$data['serviceId'] = l$serviceId; + return result$data; + } + + CopyWith$Variables$Mutation$StopService + get copyWith => CopyWith$Variables$Mutation$StopService( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$StopService) || + runtimeType != other.runtimeType) { + return false; + } + final l$serviceId = serviceId; + final lOther$serviceId = other.serviceId; + if (l$serviceId != lOther$serviceId) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$serviceId = serviceId; + return Object.hashAll([l$serviceId]); + } +} + +abstract class CopyWith$Variables$Mutation$StopService { + factory CopyWith$Variables$Mutation$StopService( + Variables$Mutation$StopService instance, + TRes Function(Variables$Mutation$StopService) then, + ) = _CopyWithImpl$Variables$Mutation$StopService; + + factory CopyWith$Variables$Mutation$StopService.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$StopService; + + TRes call({String? serviceId}); +} + +class _CopyWithImpl$Variables$Mutation$StopService + implements CopyWith$Variables$Mutation$StopService { + _CopyWithImpl$Variables$Mutation$StopService( + this._instance, + this._then, + ); + + final Variables$Mutation$StopService _instance; + + final TRes Function(Variables$Mutation$StopService) _then; + + static const _undefined = {}; + + TRes call({Object? serviceId = _undefined}) => + _then(Variables$Mutation$StopService._({ + ..._instance._$data, + if (serviceId != _undefined && serviceId != null) + 'serviceId': (serviceId as String), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$StopService + implements CopyWith$Variables$Mutation$StopService { + _CopyWithStubImpl$Variables$Mutation$StopService(this._res); + + TRes _res; + + call({String? serviceId}) => _res; +} + +class Mutation$StopService { + Mutation$StopService({ + required this.stopService, + this.$__typename = 'Mutation', + }); + + factory Mutation$StopService.fromJson(Map json) { + final l$stopService = json['stopService']; + final l$$__typename = json['__typename']; + return Mutation$StopService( + stopService: Mutation$StopService$stopService.fromJson( + (l$stopService as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$StopService$stopService stopService; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$stopService = stopService; + _resultData['stopService'] = l$stopService.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$stopService = stopService; + final l$$__typename = $__typename; + return Object.hashAll([ + l$stopService, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$StopService) || runtimeType != other.runtimeType) { + return false; + } + final l$stopService = stopService; + final lOther$stopService = other.stopService; + if (l$stopService != lOther$stopService) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$StopService on Mutation$StopService { + CopyWith$Mutation$StopService get copyWith => + CopyWith$Mutation$StopService( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$StopService { + factory CopyWith$Mutation$StopService( + Mutation$StopService instance, + TRes Function(Mutation$StopService) then, + ) = _CopyWithImpl$Mutation$StopService; + + factory CopyWith$Mutation$StopService.stub(TRes res) = + _CopyWithStubImpl$Mutation$StopService; + + TRes call({ + Mutation$StopService$stopService? stopService, + String? $__typename, + }); + CopyWith$Mutation$StopService$stopService get stopService; +} + +class _CopyWithImpl$Mutation$StopService + implements CopyWith$Mutation$StopService { + _CopyWithImpl$Mutation$StopService( + this._instance, + this._then, + ); + + final Mutation$StopService _instance; + + final TRes Function(Mutation$StopService) _then; + + static const _undefined = {}; + + TRes call({ + Object? stopService = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$StopService( + stopService: stopService == _undefined || stopService == null + ? _instance.stopService + : (stopService as Mutation$StopService$stopService), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$StopService$stopService get stopService { + final local$stopService = _instance.stopService; + return CopyWith$Mutation$StopService$stopService( + local$stopService, (e) => call(stopService: e)); + } +} + +class _CopyWithStubImpl$Mutation$StopService + implements CopyWith$Mutation$StopService { + _CopyWithStubImpl$Mutation$StopService(this._res); + + TRes _res; + + call({ + Mutation$StopService$stopService? stopService, + String? $__typename, + }) => + _res; + CopyWith$Mutation$StopService$stopService get stopService => + CopyWith$Mutation$StopService$stopService.stub(_res); +} + +const documentNodeMutationStopService = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'StopService'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'serviceId')), + type: NamedTypeNode( + name: NameNode(value: 'String'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'stopService'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'serviceId'), + value: VariableNode(name: NameNode(value: 'serviceId')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, +]); +Mutation$StopService _parserFn$Mutation$StopService( + Map data) => + Mutation$StopService.fromJson(data); +typedef OnMutationCompleted$Mutation$StopService = FutureOr Function( + Map?, + Mutation$StopService?, +); + +class Options$Mutation$StopService + extends graphql.MutationOptions { + Options$Mutation$StopService({ + String? operationName, + required Variables$Mutation$StopService variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$StopService? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$StopService? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null ? null : _parserFn$Mutation$StopService(data), + ), + update: update, + onError: onError, + document: documentNodeMutationStopService, + parserFn: _parserFn$Mutation$StopService, + ); + + final OnMutationCompleted$Mutation$StopService? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$StopService + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$StopService({ + String? operationName, + required Variables$Mutation$StopService variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$StopService? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationStopService, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$StopService, + ); +} + +extension ClientExtension$Mutation$StopService on graphql.GraphQLClient { + Future> mutate$StopService( + Options$Mutation$StopService options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$StopService( + WatchOptions$Mutation$StopService options) => + this.watchMutation(options); +} + +class Mutation$StopService$stopService + implements Fragment$basicMutationReturnFields$$ServiceMutationReturn { + Mutation$StopService$stopService({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'ServiceMutationReturn', + }); + + factory Mutation$StopService$stopService.fromJson(Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Mutation$StopService$stopService( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$StopService$stopService) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$StopService$stopService + on Mutation$StopService$stopService { + CopyWith$Mutation$StopService$stopService + get copyWith => CopyWith$Mutation$StopService$stopService( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$StopService$stopService { + factory CopyWith$Mutation$StopService$stopService( + Mutation$StopService$stopService instance, + TRes Function(Mutation$StopService$stopService) then, + ) = _CopyWithImpl$Mutation$StopService$stopService; + + factory CopyWith$Mutation$StopService$stopService.stub(TRes res) = + _CopyWithStubImpl$Mutation$StopService$stopService; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$StopService$stopService + implements CopyWith$Mutation$StopService$stopService { + _CopyWithImpl$Mutation$StopService$stopService( + this._instance, + this._then, + ); + + final Mutation$StopService$stopService _instance; + + final TRes Function(Mutation$StopService$stopService) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$StopService$stopService( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$StopService$stopService + implements CopyWith$Mutation$StopService$stopService { + _CopyWithStubImpl$Mutation$StopService$stopService(this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +class Variables$Mutation$StartService { + factory Variables$Mutation$StartService({required String serviceId}) => + Variables$Mutation$StartService._({ + r'serviceId': serviceId, + }); + + Variables$Mutation$StartService._(this._$data); + + factory Variables$Mutation$StartService.fromJson(Map data) { + final result$data = {}; + final l$serviceId = data['serviceId']; + result$data['serviceId'] = (l$serviceId as String); + return Variables$Mutation$StartService._(result$data); + } + + Map _$data; + + String get serviceId => (_$data['serviceId'] as String); + Map toJson() { + final result$data = {}; + final l$serviceId = serviceId; + result$data['serviceId'] = l$serviceId; + return result$data; + } + + CopyWith$Variables$Mutation$StartService + get copyWith => CopyWith$Variables$Mutation$StartService( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$StartService) || + runtimeType != other.runtimeType) { + return false; + } + final l$serviceId = serviceId; + final lOther$serviceId = other.serviceId; + if (l$serviceId != lOther$serviceId) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$serviceId = serviceId; + return Object.hashAll([l$serviceId]); + } +} + +abstract class CopyWith$Variables$Mutation$StartService { + factory CopyWith$Variables$Mutation$StartService( + Variables$Mutation$StartService instance, + TRes Function(Variables$Mutation$StartService) then, + ) = _CopyWithImpl$Variables$Mutation$StartService; + + factory CopyWith$Variables$Mutation$StartService.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$StartService; + + TRes call({String? serviceId}); +} + +class _CopyWithImpl$Variables$Mutation$StartService + implements CopyWith$Variables$Mutation$StartService { + _CopyWithImpl$Variables$Mutation$StartService( + this._instance, + this._then, + ); + + final Variables$Mutation$StartService _instance; + + final TRes Function(Variables$Mutation$StartService) _then; + + static const _undefined = {}; + + TRes call({Object? serviceId = _undefined}) => + _then(Variables$Mutation$StartService._({ + ..._instance._$data, + if (serviceId != _undefined && serviceId != null) + 'serviceId': (serviceId as String), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$StartService + implements CopyWith$Variables$Mutation$StartService { + _CopyWithStubImpl$Variables$Mutation$StartService(this._res); + + TRes _res; + + call({String? serviceId}) => _res; +} + +class Mutation$StartService { + Mutation$StartService({ + required this.startService, + this.$__typename = 'Mutation', + }); + + factory Mutation$StartService.fromJson(Map json) { + final l$startService = json['startService']; + final l$$__typename = json['__typename']; + return Mutation$StartService( + startService: Mutation$StartService$startService.fromJson( + (l$startService as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$StartService$startService startService; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$startService = startService; + _resultData['startService'] = l$startService.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$startService = startService; + final l$$__typename = $__typename; + return Object.hashAll([ + l$startService, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$StartService) || runtimeType != other.runtimeType) { + return false; + } + final l$startService = startService; + final lOther$startService = other.startService; + if (l$startService != lOther$startService) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$StartService on Mutation$StartService { + CopyWith$Mutation$StartService get copyWith => + CopyWith$Mutation$StartService( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$StartService { + factory CopyWith$Mutation$StartService( + Mutation$StartService instance, + TRes Function(Mutation$StartService) then, + ) = _CopyWithImpl$Mutation$StartService; + + factory CopyWith$Mutation$StartService.stub(TRes res) = + _CopyWithStubImpl$Mutation$StartService; + + TRes call({ + Mutation$StartService$startService? startService, + String? $__typename, + }); + CopyWith$Mutation$StartService$startService get startService; +} + +class _CopyWithImpl$Mutation$StartService + implements CopyWith$Mutation$StartService { + _CopyWithImpl$Mutation$StartService( + this._instance, + this._then, + ); + + final Mutation$StartService _instance; + + final TRes Function(Mutation$StartService) _then; + + static const _undefined = {}; + + TRes call({ + Object? startService = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$StartService( + startService: startService == _undefined || startService == null + ? _instance.startService + : (startService as Mutation$StartService$startService), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$StartService$startService get startService { + final local$startService = _instance.startService; + return CopyWith$Mutation$StartService$startService( + local$startService, (e) => call(startService: e)); + } +} + +class _CopyWithStubImpl$Mutation$StartService + implements CopyWith$Mutation$StartService { + _CopyWithStubImpl$Mutation$StartService(this._res); + + TRes _res; + + call({ + Mutation$StartService$startService? startService, + String? $__typename, + }) => + _res; + CopyWith$Mutation$StartService$startService get startService => + CopyWith$Mutation$StartService$startService.stub(_res); +} + +const documentNodeMutationStartService = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'StartService'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'serviceId')), + type: NamedTypeNode( + name: NameNode(value: 'String'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'startService'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'serviceId'), + value: VariableNode(name: NameNode(value: 'serviceId')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, +]); +Mutation$StartService _parserFn$Mutation$StartService( + Map data) => + Mutation$StartService.fromJson(data); +typedef OnMutationCompleted$Mutation$StartService = FutureOr Function( + Map?, + Mutation$StartService?, +); + +class Options$Mutation$StartService + extends graphql.MutationOptions { + Options$Mutation$StartService({ + String? operationName, + required Variables$Mutation$StartService variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$StartService? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$StartService? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null ? null : _parserFn$Mutation$StartService(data), + ), + update: update, + onError: onError, + document: documentNodeMutationStartService, + parserFn: _parserFn$Mutation$StartService, + ); + + final OnMutationCompleted$Mutation$StartService? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$StartService + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$StartService({ + String? operationName, + required Variables$Mutation$StartService variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$StartService? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationStartService, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$StartService, + ); +} + +extension ClientExtension$Mutation$StartService on graphql.GraphQLClient { + Future> mutate$StartService( + Options$Mutation$StartService options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$StartService( + WatchOptions$Mutation$StartService options) => + this.watchMutation(options); +} + +class Mutation$StartService$startService + implements Fragment$basicMutationReturnFields$$ServiceMutationReturn { + Mutation$StartService$startService({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'ServiceMutationReturn', + }); + + factory Mutation$StartService$startService.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Mutation$StartService$startService( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$StartService$startService) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$StartService$startService + on Mutation$StartService$startService { + CopyWith$Mutation$StartService$startService< + Mutation$StartService$startService> + get copyWith => CopyWith$Mutation$StartService$startService( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$StartService$startService { + factory CopyWith$Mutation$StartService$startService( + Mutation$StartService$startService instance, + TRes Function(Mutation$StartService$startService) then, + ) = _CopyWithImpl$Mutation$StartService$startService; + + factory CopyWith$Mutation$StartService$startService.stub(TRes res) = + _CopyWithStubImpl$Mutation$StartService$startService; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$StartService$startService + implements CopyWith$Mutation$StartService$startService { + _CopyWithImpl$Mutation$StartService$startService( + this._instance, + this._then, + ); + + final Mutation$StartService$startService _instance; + + final TRes Function(Mutation$StartService$startService) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$StartService$startService( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$StartService$startService + implements CopyWith$Mutation$StartService$startService { + _CopyWithStubImpl$Mutation$StartService$startService(this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +class Variables$Mutation$RestartService { + factory Variables$Mutation$RestartService({required String serviceId}) => + Variables$Mutation$RestartService._({ + r'serviceId': serviceId, + }); + + Variables$Mutation$RestartService._(this._$data); + + factory Variables$Mutation$RestartService.fromJson( + Map data) { + final result$data = {}; + final l$serviceId = data['serviceId']; + result$data['serviceId'] = (l$serviceId as String); + return Variables$Mutation$RestartService._(result$data); + } + + Map _$data; + + String get serviceId => (_$data['serviceId'] as String); + Map toJson() { + final result$data = {}; + final l$serviceId = serviceId; + result$data['serviceId'] = l$serviceId; + return result$data; + } + + CopyWith$Variables$Mutation$RestartService + get copyWith => CopyWith$Variables$Mutation$RestartService( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$RestartService) || + runtimeType != other.runtimeType) { + return false; + } + final l$serviceId = serviceId; + final lOther$serviceId = other.serviceId; + if (l$serviceId != lOther$serviceId) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$serviceId = serviceId; + return Object.hashAll([l$serviceId]); + } +} + +abstract class CopyWith$Variables$Mutation$RestartService { + factory CopyWith$Variables$Mutation$RestartService( + Variables$Mutation$RestartService instance, + TRes Function(Variables$Mutation$RestartService) then, + ) = _CopyWithImpl$Variables$Mutation$RestartService; + + factory CopyWith$Variables$Mutation$RestartService.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$RestartService; + + TRes call({String? serviceId}); +} + +class _CopyWithImpl$Variables$Mutation$RestartService + implements CopyWith$Variables$Mutation$RestartService { + _CopyWithImpl$Variables$Mutation$RestartService( + this._instance, + this._then, + ); + + final Variables$Mutation$RestartService _instance; + + final TRes Function(Variables$Mutation$RestartService) _then; + + static const _undefined = {}; + + TRes call({Object? serviceId = _undefined}) => + _then(Variables$Mutation$RestartService._({ + ..._instance._$data, + if (serviceId != _undefined && serviceId != null) + 'serviceId': (serviceId as String), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$RestartService + implements CopyWith$Variables$Mutation$RestartService { + _CopyWithStubImpl$Variables$Mutation$RestartService(this._res); + + TRes _res; + + call({String? serviceId}) => _res; +} + +class Mutation$RestartService { + Mutation$RestartService({ + required this.restartService, + this.$__typename = 'Mutation', + }); + + factory Mutation$RestartService.fromJson(Map json) { + final l$restartService = json['restartService']; + final l$$__typename = json['__typename']; + return Mutation$RestartService( + restartService: Mutation$RestartService$restartService.fromJson( + (l$restartService as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$RestartService$restartService restartService; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$restartService = restartService; + _resultData['restartService'] = l$restartService.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$restartService = restartService; + final l$$__typename = $__typename; + return Object.hashAll([ + l$restartService, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$RestartService) || + runtimeType != other.runtimeType) { + return false; + } + final l$restartService = restartService; + final lOther$restartService = other.restartService; + if (l$restartService != lOther$restartService) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$RestartService on Mutation$RestartService { + CopyWith$Mutation$RestartService get copyWith => + CopyWith$Mutation$RestartService( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$RestartService { + factory CopyWith$Mutation$RestartService( + Mutation$RestartService instance, + TRes Function(Mutation$RestartService) then, + ) = _CopyWithImpl$Mutation$RestartService; + + factory CopyWith$Mutation$RestartService.stub(TRes res) = + _CopyWithStubImpl$Mutation$RestartService; + + TRes call({ + Mutation$RestartService$restartService? restartService, + String? $__typename, + }); + CopyWith$Mutation$RestartService$restartService get restartService; +} + +class _CopyWithImpl$Mutation$RestartService + implements CopyWith$Mutation$RestartService { + _CopyWithImpl$Mutation$RestartService( + this._instance, + this._then, + ); + + final Mutation$RestartService _instance; + + final TRes Function(Mutation$RestartService) _then; + + static const _undefined = {}; + + TRes call({ + Object? restartService = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$RestartService( + restartService: restartService == _undefined || restartService == null + ? _instance.restartService + : (restartService as Mutation$RestartService$restartService), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$RestartService$restartService get restartService { + final local$restartService = _instance.restartService; + return CopyWith$Mutation$RestartService$restartService( + local$restartService, (e) => call(restartService: e)); + } +} + +class _CopyWithStubImpl$Mutation$RestartService + implements CopyWith$Mutation$RestartService { + _CopyWithStubImpl$Mutation$RestartService(this._res); + + TRes _res; + + call({ + Mutation$RestartService$restartService? restartService, + String? $__typename, + }) => + _res; + CopyWith$Mutation$RestartService$restartService get restartService => + CopyWith$Mutation$RestartService$restartService.stub(_res); +} + +const documentNodeMutationRestartService = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'RestartService'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'serviceId')), + type: NamedTypeNode( + name: NameNode(value: 'String'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'restartService'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'serviceId'), + value: VariableNode(name: NameNode(value: 'serviceId')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, +]); +Mutation$RestartService _parserFn$Mutation$RestartService( + Map data) => + Mutation$RestartService.fromJson(data); +typedef OnMutationCompleted$Mutation$RestartService = FutureOr Function( + Map?, + Mutation$RestartService?, +); + +class Options$Mutation$RestartService + extends graphql.MutationOptions { + Options$Mutation$RestartService({ + String? operationName, + required Variables$Mutation$RestartService variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RestartService? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$RestartService? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null + ? null + : _parserFn$Mutation$RestartService(data), + ), + update: update, + onError: onError, + document: documentNodeMutationRestartService, + parserFn: _parserFn$Mutation$RestartService, + ); + + final OnMutationCompleted$Mutation$RestartService? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$RestartService + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$RestartService({ + String? operationName, + required Variables$Mutation$RestartService variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RestartService? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationRestartService, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$RestartService, + ); +} + +extension ClientExtension$Mutation$RestartService on graphql.GraphQLClient { + Future> mutate$RestartService( + Options$Mutation$RestartService options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$RestartService( + WatchOptions$Mutation$RestartService options) => + this.watchMutation(options); +} + +class Mutation$RestartService$restartService + implements Fragment$basicMutationReturnFields$$ServiceMutationReturn { + Mutation$RestartService$restartService({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'ServiceMutationReturn', + }); + + factory Mutation$RestartService$restartService.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Mutation$RestartService$restartService( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$RestartService$restartService) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$RestartService$restartService + on Mutation$RestartService$restartService { + CopyWith$Mutation$RestartService$restartService< + Mutation$RestartService$restartService> + get copyWith => CopyWith$Mutation$RestartService$restartService( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$RestartService$restartService { + factory CopyWith$Mutation$RestartService$restartService( + Mutation$RestartService$restartService instance, + TRes Function(Mutation$RestartService$restartService) then, + ) = _CopyWithImpl$Mutation$RestartService$restartService; + + factory CopyWith$Mutation$RestartService$restartService.stub(TRes res) = + _CopyWithStubImpl$Mutation$RestartService$restartService; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$RestartService$restartService + implements CopyWith$Mutation$RestartService$restartService { + _CopyWithImpl$Mutation$RestartService$restartService( + this._instance, + this._then, + ); + + final Mutation$RestartService$restartService _instance; + + final TRes Function(Mutation$RestartService$restartService) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$RestartService$restartService( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$RestartService$restartService + implements CopyWith$Mutation$RestartService$restartService { + _CopyWithStubImpl$Mutation$RestartService$restartService(this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +class Variables$Mutation$MoveService { + factory Variables$Mutation$MoveService( + {required Input$MoveServiceInput input}) => + Variables$Mutation$MoveService._({ + r'input': input, + }); + + Variables$Mutation$MoveService._(this._$data); + + factory Variables$Mutation$MoveService.fromJson(Map data) { + final result$data = {}; + final l$input = data['input']; + result$data['input'] = + Input$MoveServiceInput.fromJson((l$input as Map)); + return Variables$Mutation$MoveService._(result$data); + } + + Map _$data; + + Input$MoveServiceInput get input => + (_$data['input'] as Input$MoveServiceInput); + Map toJson() { + final result$data = {}; + final l$input = input; + result$data['input'] = l$input.toJson(); + return result$data; + } + + CopyWith$Variables$Mutation$MoveService + get copyWith => CopyWith$Variables$Mutation$MoveService( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$MoveService) || + runtimeType != other.runtimeType) { + return false; + } + final l$input = input; + final lOther$input = other.input; + if (l$input != lOther$input) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$input = input; + return Object.hashAll([l$input]); + } +} + +abstract class CopyWith$Variables$Mutation$MoveService { + factory CopyWith$Variables$Mutation$MoveService( + Variables$Mutation$MoveService instance, + TRes Function(Variables$Mutation$MoveService) then, + ) = _CopyWithImpl$Variables$Mutation$MoveService; + + factory CopyWith$Variables$Mutation$MoveService.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$MoveService; + + TRes call({Input$MoveServiceInput? input}); +} + +class _CopyWithImpl$Variables$Mutation$MoveService + implements CopyWith$Variables$Mutation$MoveService { + _CopyWithImpl$Variables$Mutation$MoveService( + this._instance, + this._then, + ); + + final Variables$Mutation$MoveService _instance; + + final TRes Function(Variables$Mutation$MoveService) _then; + + static const _undefined = {}; + + TRes call({Object? input = _undefined}) => + _then(Variables$Mutation$MoveService._({ + ..._instance._$data, + if (input != _undefined && input != null) + 'input': (input as Input$MoveServiceInput), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$MoveService + implements CopyWith$Variables$Mutation$MoveService { + _CopyWithStubImpl$Variables$Mutation$MoveService(this._res); + + TRes _res; + + call({Input$MoveServiceInput? input}) => _res; +} + +class Mutation$MoveService { + Mutation$MoveService({ + required this.moveService, + this.$__typename = 'Mutation', + }); + + factory Mutation$MoveService.fromJson(Map json) { + final l$moveService = json['moveService']; + final l$$__typename = json['__typename']; + return Mutation$MoveService( + moveService: Mutation$MoveService$moveService.fromJson( + (l$moveService as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$MoveService$moveService moveService; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$moveService = moveService; + _resultData['moveService'] = l$moveService.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$moveService = moveService; + final l$$__typename = $__typename; + return Object.hashAll([ + l$moveService, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$MoveService) || runtimeType != other.runtimeType) { + return false; + } + final l$moveService = moveService; + final lOther$moveService = other.moveService; + if (l$moveService != lOther$moveService) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$MoveService on Mutation$MoveService { + CopyWith$Mutation$MoveService get copyWith => + CopyWith$Mutation$MoveService( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$MoveService { + factory CopyWith$Mutation$MoveService( + Mutation$MoveService instance, + TRes Function(Mutation$MoveService) then, + ) = _CopyWithImpl$Mutation$MoveService; + + factory CopyWith$Mutation$MoveService.stub(TRes res) = + _CopyWithStubImpl$Mutation$MoveService; + + TRes call({ + Mutation$MoveService$moveService? moveService, + String? $__typename, + }); + CopyWith$Mutation$MoveService$moveService get moveService; +} + +class _CopyWithImpl$Mutation$MoveService + implements CopyWith$Mutation$MoveService { + _CopyWithImpl$Mutation$MoveService( + this._instance, + this._then, + ); + + final Mutation$MoveService _instance; + + final TRes Function(Mutation$MoveService) _then; + + static const _undefined = {}; + + TRes call({ + Object? moveService = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$MoveService( + moveService: moveService == _undefined || moveService == null + ? _instance.moveService + : (moveService as Mutation$MoveService$moveService), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$MoveService$moveService get moveService { + final local$moveService = _instance.moveService; + return CopyWith$Mutation$MoveService$moveService( + local$moveService, (e) => call(moveService: e)); + } +} + +class _CopyWithStubImpl$Mutation$MoveService + implements CopyWith$Mutation$MoveService { + _CopyWithStubImpl$Mutation$MoveService(this._res); + + TRes _res; + + call({ + Mutation$MoveService$moveService? moveService, + String? $__typename, + }) => + _res; + CopyWith$Mutation$MoveService$moveService get moveService => + CopyWith$Mutation$MoveService$moveService.stub(_res); +} + +const documentNodeMutationMoveService = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'MoveService'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'input')), + type: NamedTypeNode( + name: NameNode(value: 'MoveServiceInput'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'moveService'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'input'), + value: VariableNode(name: NameNode(value: 'input')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: 'job'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicApiJobsFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, + fragmentDefinitionbasicApiJobsFields, +]); +Mutation$MoveService _parserFn$Mutation$MoveService( + Map data) => + Mutation$MoveService.fromJson(data); +typedef OnMutationCompleted$Mutation$MoveService = FutureOr Function( + Map?, + Mutation$MoveService?, +); + +class Options$Mutation$MoveService + extends graphql.MutationOptions { + Options$Mutation$MoveService({ + String? operationName, + required Variables$Mutation$MoveService variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$MoveService? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$MoveService? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null ? null : _parserFn$Mutation$MoveService(data), + ), + update: update, + onError: onError, + document: documentNodeMutationMoveService, + parserFn: _parserFn$Mutation$MoveService, + ); + + final OnMutationCompleted$Mutation$MoveService? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$MoveService + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$MoveService({ + String? operationName, + required Variables$Mutation$MoveService variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$MoveService? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationMoveService, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$MoveService, + ); +} + +extension ClientExtension$Mutation$MoveService on graphql.GraphQLClient { + Future> mutate$MoveService( + Options$Mutation$MoveService options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$MoveService( + WatchOptions$Mutation$MoveService options) => + this.watchMutation(options); +} + +class Mutation$MoveService$moveService + implements Fragment$basicMutationReturnFields$$ServiceJobMutationReturn { + Mutation$MoveService$moveService({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'ServiceJobMutationReturn', + this.job, + }); + + factory Mutation$MoveService$moveService.fromJson(Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + final l$job = json['job']; + return Mutation$MoveService$moveService( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + job: l$job == null + ? null + : Fragment$basicApiJobsFields.fromJson( + (l$job as Map)), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + final Fragment$basicApiJobsFields? job; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + final l$job = job; + _resultData['job'] = l$job?.toJson(); + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + final l$job = job; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + l$job, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$MoveService$moveService) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + final l$job = job; + final lOther$job = other.job; + if (l$job != lOther$job) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$MoveService$moveService + on Mutation$MoveService$moveService { + CopyWith$Mutation$MoveService$moveService + get copyWith => CopyWith$Mutation$MoveService$moveService( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$MoveService$moveService { + factory CopyWith$Mutation$MoveService$moveService( + Mutation$MoveService$moveService instance, + TRes Function(Mutation$MoveService$moveService) then, + ) = _CopyWithImpl$Mutation$MoveService$moveService; + + factory CopyWith$Mutation$MoveService$moveService.stub(TRes res) = + _CopyWithStubImpl$Mutation$MoveService$moveService; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + Fragment$basicApiJobsFields? job, + }); + CopyWith$Fragment$basicApiJobsFields get job; +} + +class _CopyWithImpl$Mutation$MoveService$moveService + implements CopyWith$Mutation$MoveService$moveService { + _CopyWithImpl$Mutation$MoveService$moveService( + this._instance, + this._then, + ); + + final Mutation$MoveService$moveService _instance; + + final TRes Function(Mutation$MoveService$moveService) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + Object? job = _undefined, + }) => + _then(Mutation$MoveService$moveService( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + job: job == _undefined + ? _instance.job + : (job as Fragment$basicApiJobsFields?), + )); + CopyWith$Fragment$basicApiJobsFields get job { + final local$job = _instance.job; + return local$job == null + ? CopyWith$Fragment$basicApiJobsFields.stub(_then(_instance)) + : CopyWith$Fragment$basicApiJobsFields(local$job, (e) => call(job: e)); + } +} + +class _CopyWithStubImpl$Mutation$MoveService$moveService + implements CopyWith$Mutation$MoveService$moveService { + _CopyWithStubImpl$Mutation$MoveService$moveService(this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + Fragment$basicApiJobsFields? job, + }) => + _res; + CopyWith$Fragment$basicApiJobsFields get job => + CopyWith$Fragment$basicApiJobsFields.stub(_res); +} diff --git a/lib/logic/api_maps/graphql_maps/schema/users.graphql b/lib/logic/api_maps/graphql_maps/schema/users.graphql new file mode 100644 index 00000000..7ce64f8a --- /dev/null +++ b/lib/logic/api_maps/graphql_maps/schema/users.graphql @@ -0,0 +1,66 @@ +fragment userFields on User{ + username + userType + sshKeys +} + +query AllUsers { + users { + allUsers { + ...userFields + } + rootUser: getUser(username: "root") { + ...userFields + } + } +} + +query GetUser($username: String!) { + users { + getUser(username: $username) { + ...userFields + } + } +} + +mutation CreateUser($user: UserMutationInput!) { + createUser(user: $user) { + ...basicMutationReturnFields + user { + ...userFields + } + } +} + +mutation DeleteUser($username: String!) { + deleteUser(username: $username) { + ...basicMutationReturnFields + } +} + +mutation UpdateUser($user: UserMutationInput!) { + updateUser(user: $user) { + ...basicMutationReturnFields + user { + ...userFields + } + } +} + +mutation AddSshKey($sshInput: SshMutationInput!) { + addSshKey(sshInput: $sshInput) { + ...basicMutationReturnFields + user { + ...userFields + } + } +} + +mutation RemoveSshKey($sshInput: SshMutationInput!) { + removeSshKey(sshInput: $sshInput) { + ...basicMutationReturnFields + user { + ...userFields + } + } +} diff --git a/lib/logic/api_maps/graphql_maps/schema/users.graphql.dart b/lib/logic/api_maps/graphql_maps/schema/users.graphql.dart new file mode 100644 index 00000000..5d86dc05 --- /dev/null +++ b/lib/logic/api_maps/graphql_maps/schema/users.graphql.dart @@ -0,0 +1,4226 @@ +import 'dart:async'; +import 'package:gql/ast.dart'; +import 'package:graphql/client.dart' as graphql; +import 'schema.graphql.dart'; +import 'server_api.graphql.dart'; + +class Fragment$userFields { + Fragment$userFields({ + required this.username, + required this.userType, + required this.sshKeys, + this.$__typename = 'User', + }); + + factory Fragment$userFields.fromJson(Map json) { + final l$username = json['username']; + final l$userType = json['userType']; + final l$sshKeys = json['sshKeys']; + final l$$__typename = json['__typename']; + return Fragment$userFields( + username: (l$username as String), + userType: fromJson$Enum$UserType((l$userType as String)), + sshKeys: (l$sshKeys as List).map((e) => (e as String)).toList(), + $__typename: (l$$__typename as String), + ); + } + + final String username; + + final Enum$UserType userType; + + final List sshKeys; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$username = username; + _resultData['username'] = l$username; + final l$userType = userType; + _resultData['userType'] = toJson$Enum$UserType(l$userType); + final l$sshKeys = sshKeys; + _resultData['sshKeys'] = l$sshKeys.map((e) => e).toList(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$username = username; + final l$userType = userType; + final l$sshKeys = sshKeys; + final l$$__typename = $__typename; + return Object.hashAll([ + l$username, + l$userType, + Object.hashAll(l$sshKeys.map((v) => v)), + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Fragment$userFields) || runtimeType != other.runtimeType) { + return false; + } + final l$username = username; + final lOther$username = other.username; + if (l$username != lOther$username) { + return false; + } + final l$userType = userType; + final lOther$userType = other.userType; + if (l$userType != lOther$userType) { + return false; + } + final l$sshKeys = sshKeys; + final lOther$sshKeys = other.sshKeys; + if (l$sshKeys.length != lOther$sshKeys.length) { + return false; + } + for (int i = 0; i < l$sshKeys.length; i++) { + final l$sshKeys$entry = l$sshKeys[i]; + final lOther$sshKeys$entry = lOther$sshKeys[i]; + if (l$sshKeys$entry != lOther$sshKeys$entry) { + return false; + } + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Fragment$userFields on Fragment$userFields { + CopyWith$Fragment$userFields get copyWith => + CopyWith$Fragment$userFields( + this, + (i) => i, + ); +} + +abstract class CopyWith$Fragment$userFields { + factory CopyWith$Fragment$userFields( + Fragment$userFields instance, + TRes Function(Fragment$userFields) then, + ) = _CopyWithImpl$Fragment$userFields; + + factory CopyWith$Fragment$userFields.stub(TRes res) = + _CopyWithStubImpl$Fragment$userFields; + + TRes call({ + String? username, + Enum$UserType? userType, + List? sshKeys, + String? $__typename, + }); +} + +class _CopyWithImpl$Fragment$userFields + implements CopyWith$Fragment$userFields { + _CopyWithImpl$Fragment$userFields( + this._instance, + this._then, + ); + + final Fragment$userFields _instance; + + final TRes Function(Fragment$userFields) _then; + + static const _undefined = {}; + + TRes call({ + Object? username = _undefined, + Object? userType = _undefined, + Object? sshKeys = _undefined, + Object? $__typename = _undefined, + }) => + _then(Fragment$userFields( + username: username == _undefined || username == null + ? _instance.username + : (username as String), + userType: userType == _undefined || userType == null + ? _instance.userType + : (userType as Enum$UserType), + sshKeys: sshKeys == _undefined || sshKeys == null + ? _instance.sshKeys + : (sshKeys as List), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Fragment$userFields + implements CopyWith$Fragment$userFields { + _CopyWithStubImpl$Fragment$userFields(this._res); + + TRes _res; + + call({ + String? username, + Enum$UserType? userType, + List? sshKeys, + String? $__typename, + }) => + _res; +} + +const fragmentDefinitionuserFields = FragmentDefinitionNode( + name: NameNode(value: 'userFields'), + typeCondition: TypeConditionNode( + on: NamedTypeNode( + name: NameNode(value: 'User'), + isNonNull: false, + )), + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'username'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'userType'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: 'sshKeys'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), +); +const documentNodeFragmentuserFields = DocumentNode(definitions: [ + fragmentDefinitionuserFields, +]); + +extension ClientExtension$Fragment$userFields on graphql.GraphQLClient { + void writeFragment$userFields({ + required Fragment$userFields data, + required Map idFields, + bool broadcast = true, + }) => + this.writeFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'userFields', + document: documentNodeFragmentuserFields, + ), + ), + data: data.toJson(), + broadcast: broadcast, + ); + Fragment$userFields? readFragment$userFields({ + required Map idFields, + bool optimistic = true, + }) { + final result = this.readFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'userFields', + document: documentNodeFragmentuserFields, + ), + ), + optimistic: optimistic, + ); + return result == null ? null : Fragment$userFields.fromJson(result); + } +} + +class Query$AllUsers { + Query$AllUsers({ + required this.users, + this.$__typename = 'Query', + }); + + factory Query$AllUsers.fromJson(Map json) { + final l$users = json['users']; + final l$$__typename = json['__typename']; + return Query$AllUsers( + users: Query$AllUsers$users.fromJson((l$users as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Query$AllUsers$users users; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$users = users; + _resultData['users'] = l$users.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$users = users; + final l$$__typename = $__typename; + return Object.hashAll([ + l$users, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$AllUsers) || runtimeType != other.runtimeType) { + return false; + } + final l$users = users; + final lOther$users = other.users; + if (l$users != lOther$users) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$AllUsers on Query$AllUsers { + CopyWith$Query$AllUsers get copyWith => + CopyWith$Query$AllUsers( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$AllUsers { + factory CopyWith$Query$AllUsers( + Query$AllUsers instance, + TRes Function(Query$AllUsers) then, + ) = _CopyWithImpl$Query$AllUsers; + + factory CopyWith$Query$AllUsers.stub(TRes res) = + _CopyWithStubImpl$Query$AllUsers; + + TRes call({ + Query$AllUsers$users? users, + String? $__typename, + }); + CopyWith$Query$AllUsers$users get users; +} + +class _CopyWithImpl$Query$AllUsers + implements CopyWith$Query$AllUsers { + _CopyWithImpl$Query$AllUsers( + this._instance, + this._then, + ); + + final Query$AllUsers _instance; + + final TRes Function(Query$AllUsers) _then; + + static const _undefined = {}; + + TRes call({ + Object? users = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$AllUsers( + users: users == _undefined || users == null + ? _instance.users + : (users as Query$AllUsers$users), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$AllUsers$users get users { + final local$users = _instance.users; + return CopyWith$Query$AllUsers$users(local$users, (e) => call(users: e)); + } +} + +class _CopyWithStubImpl$Query$AllUsers + implements CopyWith$Query$AllUsers { + _CopyWithStubImpl$Query$AllUsers(this._res); + + TRes _res; + + call({ + Query$AllUsers$users? users, + String? $__typename, + }) => + _res; + CopyWith$Query$AllUsers$users get users => + CopyWith$Query$AllUsers$users.stub(_res); +} + +const documentNodeQueryAllUsers = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.query, + name: NameNode(value: 'AllUsers'), + variableDefinitions: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'users'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'allUsers'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'userFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: 'getUser'), + alias: NameNode(value: 'rootUser'), + arguments: [ + ArgumentNode( + name: NameNode(value: 'username'), + value: StringValueNode( + value: 'root', + isBlock: false, + ), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'userFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionuserFields, +]); +Query$AllUsers _parserFn$Query$AllUsers(Map data) => + Query$AllUsers.fromJson(data); +typedef OnQueryComplete$Query$AllUsers = FutureOr Function( + Map?, + Query$AllUsers?, +); + +class Options$Query$AllUsers extends graphql.QueryOptions { + Options$Query$AllUsers({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$AllUsers? typedOptimisticResult, + Duration? pollInterval, + graphql.Context? context, + OnQueryComplete$Query$AllUsers? onComplete, + graphql.OnQueryError? onError, + }) : onCompleteWithParsed = onComplete, + super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + pollInterval: pollInterval, + context: context, + onComplete: onComplete == null + ? null + : (data) => onComplete( + data, + data == null ? null : _parserFn$Query$AllUsers(data), + ), + onError: onError, + document: documentNodeQueryAllUsers, + parserFn: _parserFn$Query$AllUsers, + ); + + final OnQueryComplete$Query$AllUsers? onCompleteWithParsed; + + @override + List get properties => [ + ...super.onComplete == null + ? super.properties + : super.properties.where((property) => property != onComplete), + onCompleteWithParsed, + ]; +} + +class WatchOptions$Query$AllUsers + extends graphql.WatchQueryOptions { + WatchOptions$Query$AllUsers({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$AllUsers? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeQueryAllUsers, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Query$AllUsers, + ); +} + +class FetchMoreOptions$Query$AllUsers extends graphql.FetchMoreOptions { + FetchMoreOptions$Query$AllUsers({required graphql.UpdateQuery updateQuery}) + : super( + updateQuery: updateQuery, + document: documentNodeQueryAllUsers, + ); +} + +extension ClientExtension$Query$AllUsers on graphql.GraphQLClient { + Future> query$AllUsers( + [Options$Query$AllUsers? options]) async => + await this.query(options ?? Options$Query$AllUsers()); + graphql.ObservableQuery watchQuery$AllUsers( + [WatchOptions$Query$AllUsers? options]) => + this.watchQuery(options ?? WatchOptions$Query$AllUsers()); + void writeQuery$AllUsers({ + required Query$AllUsers data, + bool broadcast = true, + }) => + this.writeQuery( + graphql.Request( + operation: graphql.Operation(document: documentNodeQueryAllUsers)), + data: data.toJson(), + broadcast: broadcast, + ); + Query$AllUsers? readQuery$AllUsers({bool optimistic = true}) { + final result = this.readQuery( + graphql.Request( + operation: graphql.Operation(document: documentNodeQueryAllUsers)), + optimistic: optimistic, + ); + return result == null ? null : Query$AllUsers.fromJson(result); + } +} + +class Query$AllUsers$users { + Query$AllUsers$users({ + required this.allUsers, + this.rootUser, + this.$__typename = 'Users', + }); + + factory Query$AllUsers$users.fromJson(Map json) { + final l$allUsers = json['allUsers']; + final l$rootUser = json['rootUser']; + final l$$__typename = json['__typename']; + return Query$AllUsers$users( + allUsers: (l$allUsers as List) + .map((e) => Fragment$userFields.fromJson((e as Map))) + .toList(), + rootUser: l$rootUser == null + ? null + : Fragment$userFields.fromJson((l$rootUser as Map)), + $__typename: (l$$__typename as String), + ); + } + + final List allUsers; + + final Fragment$userFields? rootUser; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$allUsers = allUsers; + _resultData['allUsers'] = l$allUsers.map((e) => e.toJson()).toList(); + final l$rootUser = rootUser; + _resultData['rootUser'] = l$rootUser?.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$allUsers = allUsers; + final l$rootUser = rootUser; + final l$$__typename = $__typename; + return Object.hashAll([ + Object.hashAll(l$allUsers.map((v) => v)), + l$rootUser, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$AllUsers$users) || runtimeType != other.runtimeType) { + return false; + } + final l$allUsers = allUsers; + final lOther$allUsers = other.allUsers; + if (l$allUsers.length != lOther$allUsers.length) { + return false; + } + for (int i = 0; i < l$allUsers.length; i++) { + final l$allUsers$entry = l$allUsers[i]; + final lOther$allUsers$entry = lOther$allUsers[i]; + if (l$allUsers$entry != lOther$allUsers$entry) { + return false; + } + } + final l$rootUser = rootUser; + final lOther$rootUser = other.rootUser; + if (l$rootUser != lOther$rootUser) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$AllUsers$users on Query$AllUsers$users { + CopyWith$Query$AllUsers$users get copyWith => + CopyWith$Query$AllUsers$users( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$AllUsers$users { + factory CopyWith$Query$AllUsers$users( + Query$AllUsers$users instance, + TRes Function(Query$AllUsers$users) then, + ) = _CopyWithImpl$Query$AllUsers$users; + + factory CopyWith$Query$AllUsers$users.stub(TRes res) = + _CopyWithStubImpl$Query$AllUsers$users; + + TRes call({ + List? allUsers, + Fragment$userFields? rootUser, + String? $__typename, + }); + TRes allUsers( + Iterable Function( + Iterable>) + _fn); + CopyWith$Fragment$userFields get rootUser; +} + +class _CopyWithImpl$Query$AllUsers$users + implements CopyWith$Query$AllUsers$users { + _CopyWithImpl$Query$AllUsers$users( + this._instance, + this._then, + ); + + final Query$AllUsers$users _instance; + + final TRes Function(Query$AllUsers$users) _then; + + static const _undefined = {}; + + TRes call({ + Object? allUsers = _undefined, + Object? rootUser = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$AllUsers$users( + allUsers: allUsers == _undefined || allUsers == null + ? _instance.allUsers + : (allUsers as List), + rootUser: rootUser == _undefined + ? _instance.rootUser + : (rootUser as Fragment$userFields?), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + TRes allUsers( + Iterable Function( + Iterable>) + _fn) => + call( + allUsers: + _fn(_instance.allUsers.map((e) => CopyWith$Fragment$userFields( + e, + (i) => i, + ))).toList()); + CopyWith$Fragment$userFields get rootUser { + final local$rootUser = _instance.rootUser; + return local$rootUser == null + ? CopyWith$Fragment$userFields.stub(_then(_instance)) + : CopyWith$Fragment$userFields( + local$rootUser, (e) => call(rootUser: e)); + } +} + +class _CopyWithStubImpl$Query$AllUsers$users + implements CopyWith$Query$AllUsers$users { + _CopyWithStubImpl$Query$AllUsers$users(this._res); + + TRes _res; + + call({ + List? allUsers, + Fragment$userFields? rootUser, + String? $__typename, + }) => + _res; + allUsers(_fn) => _res; + CopyWith$Fragment$userFields get rootUser => + CopyWith$Fragment$userFields.stub(_res); +} + +class Variables$Query$GetUser { + factory Variables$Query$GetUser({required String username}) => + Variables$Query$GetUser._({ + r'username': username, + }); + + Variables$Query$GetUser._(this._$data); + + factory Variables$Query$GetUser.fromJson(Map data) { + final result$data = {}; + final l$username = data['username']; + result$data['username'] = (l$username as String); + return Variables$Query$GetUser._(result$data); + } + + Map _$data; + + String get username => (_$data['username'] as String); + Map toJson() { + final result$data = {}; + final l$username = username; + result$data['username'] = l$username; + return result$data; + } + + CopyWith$Variables$Query$GetUser get copyWith => + CopyWith$Variables$Query$GetUser( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Query$GetUser) || + runtimeType != other.runtimeType) { + return false; + } + final l$username = username; + final lOther$username = other.username; + if (l$username != lOther$username) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$username = username; + return Object.hashAll([l$username]); + } +} + +abstract class CopyWith$Variables$Query$GetUser { + factory CopyWith$Variables$Query$GetUser( + Variables$Query$GetUser instance, + TRes Function(Variables$Query$GetUser) then, + ) = _CopyWithImpl$Variables$Query$GetUser; + + factory CopyWith$Variables$Query$GetUser.stub(TRes res) = + _CopyWithStubImpl$Variables$Query$GetUser; + + TRes call({String? username}); +} + +class _CopyWithImpl$Variables$Query$GetUser + implements CopyWith$Variables$Query$GetUser { + _CopyWithImpl$Variables$Query$GetUser( + this._instance, + this._then, + ); + + final Variables$Query$GetUser _instance; + + final TRes Function(Variables$Query$GetUser) _then; + + static const _undefined = {}; + + TRes call({Object? username = _undefined}) => + _then(Variables$Query$GetUser._({ + ..._instance._$data, + if (username != _undefined && username != null) + 'username': (username as String), + })); +} + +class _CopyWithStubImpl$Variables$Query$GetUser + implements CopyWith$Variables$Query$GetUser { + _CopyWithStubImpl$Variables$Query$GetUser(this._res); + + TRes _res; + + call({String? username}) => _res; +} + +class Query$GetUser { + Query$GetUser({ + required this.users, + this.$__typename = 'Query', + }); + + factory Query$GetUser.fromJson(Map json) { + final l$users = json['users']; + final l$$__typename = json['__typename']; + return Query$GetUser( + users: Query$GetUser$users.fromJson((l$users as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Query$GetUser$users users; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$users = users; + _resultData['users'] = l$users.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$users = users; + final l$$__typename = $__typename; + return Object.hashAll([ + l$users, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$GetUser) || runtimeType != other.runtimeType) { + return false; + } + final l$users = users; + final lOther$users = other.users; + if (l$users != lOther$users) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$GetUser on Query$GetUser { + CopyWith$Query$GetUser get copyWith => CopyWith$Query$GetUser( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$GetUser { + factory CopyWith$Query$GetUser( + Query$GetUser instance, + TRes Function(Query$GetUser) then, + ) = _CopyWithImpl$Query$GetUser; + + factory CopyWith$Query$GetUser.stub(TRes res) = + _CopyWithStubImpl$Query$GetUser; + + TRes call({ + Query$GetUser$users? users, + String? $__typename, + }); + CopyWith$Query$GetUser$users get users; +} + +class _CopyWithImpl$Query$GetUser + implements CopyWith$Query$GetUser { + _CopyWithImpl$Query$GetUser( + this._instance, + this._then, + ); + + final Query$GetUser _instance; + + final TRes Function(Query$GetUser) _then; + + static const _undefined = {}; + + TRes call({ + Object? users = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$GetUser( + users: users == _undefined || users == null + ? _instance.users + : (users as Query$GetUser$users), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$GetUser$users get users { + final local$users = _instance.users; + return CopyWith$Query$GetUser$users(local$users, (e) => call(users: e)); + } +} + +class _CopyWithStubImpl$Query$GetUser + implements CopyWith$Query$GetUser { + _CopyWithStubImpl$Query$GetUser(this._res); + + TRes _res; + + call({ + Query$GetUser$users? users, + String? $__typename, + }) => + _res; + CopyWith$Query$GetUser$users get users => + CopyWith$Query$GetUser$users.stub(_res); +} + +const documentNodeQueryGetUser = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.query, + name: NameNode(value: 'GetUser'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'username')), + type: NamedTypeNode( + name: NameNode(value: 'String'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'users'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'getUser'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'username'), + value: VariableNode(name: NameNode(value: 'username')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'userFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionuserFields, +]); +Query$GetUser _parserFn$Query$GetUser(Map data) => + Query$GetUser.fromJson(data); +typedef OnQueryComplete$Query$GetUser = FutureOr Function( + Map?, + Query$GetUser?, +); + +class Options$Query$GetUser extends graphql.QueryOptions { + Options$Query$GetUser({ + String? operationName, + required Variables$Query$GetUser variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$GetUser? typedOptimisticResult, + Duration? pollInterval, + graphql.Context? context, + OnQueryComplete$Query$GetUser? onComplete, + graphql.OnQueryError? onError, + }) : onCompleteWithParsed = onComplete, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + pollInterval: pollInterval, + context: context, + onComplete: onComplete == null + ? null + : (data) => onComplete( + data, + data == null ? null : _parserFn$Query$GetUser(data), + ), + onError: onError, + document: documentNodeQueryGetUser, + parserFn: _parserFn$Query$GetUser, + ); + + final OnQueryComplete$Query$GetUser? onCompleteWithParsed; + + @override + List get properties => [ + ...super.onComplete == null + ? super.properties + : super.properties.where((property) => property != onComplete), + onCompleteWithParsed, + ]; +} + +class WatchOptions$Query$GetUser + extends graphql.WatchQueryOptions { + WatchOptions$Query$GetUser({ + String? operationName, + required Variables$Query$GetUser variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Query$GetUser? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeQueryGetUser, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Query$GetUser, + ); +} + +class FetchMoreOptions$Query$GetUser extends graphql.FetchMoreOptions { + FetchMoreOptions$Query$GetUser({ + required graphql.UpdateQuery updateQuery, + required Variables$Query$GetUser variables, + }) : super( + updateQuery: updateQuery, + variables: variables.toJson(), + document: documentNodeQueryGetUser, + ); +} + +extension ClientExtension$Query$GetUser on graphql.GraphQLClient { + Future> query$GetUser( + Options$Query$GetUser options) async => + await this.query(options); + graphql.ObservableQuery watchQuery$GetUser( + WatchOptions$Query$GetUser options) => + this.watchQuery(options); + void writeQuery$GetUser({ + required Query$GetUser data, + required Variables$Query$GetUser variables, + bool broadcast = true, + }) => + this.writeQuery( + graphql.Request( + operation: graphql.Operation(document: documentNodeQueryGetUser), + variables: variables.toJson(), + ), + data: data.toJson(), + broadcast: broadcast, + ); + Query$GetUser? readQuery$GetUser({ + required Variables$Query$GetUser variables, + bool optimistic = true, + }) { + final result = this.readQuery( + graphql.Request( + operation: graphql.Operation(document: documentNodeQueryGetUser), + variables: variables.toJson(), + ), + optimistic: optimistic, + ); + return result == null ? null : Query$GetUser.fromJson(result); + } +} + +class Query$GetUser$users { + Query$GetUser$users({ + this.getUser, + this.$__typename = 'Users', + }); + + factory Query$GetUser$users.fromJson(Map json) { + final l$getUser = json['getUser']; + final l$$__typename = json['__typename']; + return Query$GetUser$users( + getUser: l$getUser == null + ? null + : Fragment$userFields.fromJson((l$getUser as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Fragment$userFields? getUser; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$getUser = getUser; + _resultData['getUser'] = l$getUser?.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$getUser = getUser; + final l$$__typename = $__typename; + return Object.hashAll([ + l$getUser, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$GetUser$users) || runtimeType != other.runtimeType) { + return false; + } + final l$getUser = getUser; + final lOther$getUser = other.getUser; + if (l$getUser != lOther$getUser) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$GetUser$users on Query$GetUser$users { + CopyWith$Query$GetUser$users get copyWith => + CopyWith$Query$GetUser$users( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$GetUser$users { + factory CopyWith$Query$GetUser$users( + Query$GetUser$users instance, + TRes Function(Query$GetUser$users) then, + ) = _CopyWithImpl$Query$GetUser$users; + + factory CopyWith$Query$GetUser$users.stub(TRes res) = + _CopyWithStubImpl$Query$GetUser$users; + + TRes call({ + Fragment$userFields? getUser, + String? $__typename, + }); + CopyWith$Fragment$userFields get getUser; +} + +class _CopyWithImpl$Query$GetUser$users + implements CopyWith$Query$GetUser$users { + _CopyWithImpl$Query$GetUser$users( + this._instance, + this._then, + ); + + final Query$GetUser$users _instance; + + final TRes Function(Query$GetUser$users) _then; + + static const _undefined = {}; + + TRes call({ + Object? getUser = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$GetUser$users( + getUser: getUser == _undefined + ? _instance.getUser + : (getUser as Fragment$userFields?), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Fragment$userFields get getUser { + final local$getUser = _instance.getUser; + return local$getUser == null + ? CopyWith$Fragment$userFields.stub(_then(_instance)) + : CopyWith$Fragment$userFields(local$getUser, (e) => call(getUser: e)); + } +} + +class _CopyWithStubImpl$Query$GetUser$users + implements CopyWith$Query$GetUser$users { + _CopyWithStubImpl$Query$GetUser$users(this._res); + + TRes _res; + + call({ + Fragment$userFields? getUser, + String? $__typename, + }) => + _res; + CopyWith$Fragment$userFields get getUser => + CopyWith$Fragment$userFields.stub(_res); +} + +class Variables$Mutation$CreateUser { + factory Variables$Mutation$CreateUser( + {required Input$UserMutationInput user}) => + Variables$Mutation$CreateUser._({ + r'user': user, + }); + + Variables$Mutation$CreateUser._(this._$data); + + factory Variables$Mutation$CreateUser.fromJson(Map data) { + final result$data = {}; + final l$user = data['user']; + result$data['user'] = + Input$UserMutationInput.fromJson((l$user as Map)); + return Variables$Mutation$CreateUser._(result$data); + } + + Map _$data; + + Input$UserMutationInput get user => + (_$data['user'] as Input$UserMutationInput); + Map toJson() { + final result$data = {}; + final l$user = user; + result$data['user'] = l$user.toJson(); + return result$data; + } + + CopyWith$Variables$Mutation$CreateUser + get copyWith => CopyWith$Variables$Mutation$CreateUser( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$CreateUser) || + runtimeType != other.runtimeType) { + return false; + } + final l$user = user; + final lOther$user = other.user; + if (l$user != lOther$user) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$user = user; + return Object.hashAll([l$user]); + } +} + +abstract class CopyWith$Variables$Mutation$CreateUser { + factory CopyWith$Variables$Mutation$CreateUser( + Variables$Mutation$CreateUser instance, + TRes Function(Variables$Mutation$CreateUser) then, + ) = _CopyWithImpl$Variables$Mutation$CreateUser; + + factory CopyWith$Variables$Mutation$CreateUser.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$CreateUser; + + TRes call({Input$UserMutationInput? user}); +} + +class _CopyWithImpl$Variables$Mutation$CreateUser + implements CopyWith$Variables$Mutation$CreateUser { + _CopyWithImpl$Variables$Mutation$CreateUser( + this._instance, + this._then, + ); + + final Variables$Mutation$CreateUser _instance; + + final TRes Function(Variables$Mutation$CreateUser) _then; + + static const _undefined = {}; + + TRes call({Object? user = _undefined}) => + _then(Variables$Mutation$CreateUser._({ + ..._instance._$data, + if (user != _undefined && user != null) + 'user': (user as Input$UserMutationInput), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$CreateUser + implements CopyWith$Variables$Mutation$CreateUser { + _CopyWithStubImpl$Variables$Mutation$CreateUser(this._res); + + TRes _res; + + call({Input$UserMutationInput? user}) => _res; +} + +class Mutation$CreateUser { + Mutation$CreateUser({ + required this.createUser, + this.$__typename = 'Mutation', + }); + + factory Mutation$CreateUser.fromJson(Map json) { + final l$createUser = json['createUser']; + final l$$__typename = json['__typename']; + return Mutation$CreateUser( + createUser: Mutation$CreateUser$createUser.fromJson( + (l$createUser as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$CreateUser$createUser createUser; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$createUser = createUser; + _resultData['createUser'] = l$createUser.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$createUser = createUser; + final l$$__typename = $__typename; + return Object.hashAll([ + l$createUser, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$CreateUser) || runtimeType != other.runtimeType) { + return false; + } + final l$createUser = createUser; + final lOther$createUser = other.createUser; + if (l$createUser != lOther$createUser) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$CreateUser on Mutation$CreateUser { + CopyWith$Mutation$CreateUser get copyWith => + CopyWith$Mutation$CreateUser( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$CreateUser { + factory CopyWith$Mutation$CreateUser( + Mutation$CreateUser instance, + TRes Function(Mutation$CreateUser) then, + ) = _CopyWithImpl$Mutation$CreateUser; + + factory CopyWith$Mutation$CreateUser.stub(TRes res) = + _CopyWithStubImpl$Mutation$CreateUser; + + TRes call({ + Mutation$CreateUser$createUser? createUser, + String? $__typename, + }); + CopyWith$Mutation$CreateUser$createUser get createUser; +} + +class _CopyWithImpl$Mutation$CreateUser + implements CopyWith$Mutation$CreateUser { + _CopyWithImpl$Mutation$CreateUser( + this._instance, + this._then, + ); + + final Mutation$CreateUser _instance; + + final TRes Function(Mutation$CreateUser) _then; + + static const _undefined = {}; + + TRes call({ + Object? createUser = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$CreateUser( + createUser: createUser == _undefined || createUser == null + ? _instance.createUser + : (createUser as Mutation$CreateUser$createUser), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$CreateUser$createUser get createUser { + final local$createUser = _instance.createUser; + return CopyWith$Mutation$CreateUser$createUser( + local$createUser, (e) => call(createUser: e)); + } +} + +class _CopyWithStubImpl$Mutation$CreateUser + implements CopyWith$Mutation$CreateUser { + _CopyWithStubImpl$Mutation$CreateUser(this._res); + + TRes _res; + + call({ + Mutation$CreateUser$createUser? createUser, + String? $__typename, + }) => + _res; + CopyWith$Mutation$CreateUser$createUser get createUser => + CopyWith$Mutation$CreateUser$createUser.stub(_res); +} + +const documentNodeMutationCreateUser = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'CreateUser'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'user')), + type: NamedTypeNode( + name: NameNode(value: 'UserMutationInput'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'createUser'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'user'), + value: VariableNode(name: NameNode(value: 'user')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: 'user'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'userFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, + fragmentDefinitionuserFields, +]); +Mutation$CreateUser _parserFn$Mutation$CreateUser(Map data) => + Mutation$CreateUser.fromJson(data); +typedef OnMutationCompleted$Mutation$CreateUser = FutureOr Function( + Map?, + Mutation$CreateUser?, +); + +class Options$Mutation$CreateUser + extends graphql.MutationOptions { + Options$Mutation$CreateUser({ + String? operationName, + required Variables$Mutation$CreateUser variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$CreateUser? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$CreateUser? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null ? null : _parserFn$Mutation$CreateUser(data), + ), + update: update, + onError: onError, + document: documentNodeMutationCreateUser, + parserFn: _parserFn$Mutation$CreateUser, + ); + + final OnMutationCompleted$Mutation$CreateUser? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$CreateUser + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$CreateUser({ + String? operationName, + required Variables$Mutation$CreateUser variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$CreateUser? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationCreateUser, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$CreateUser, + ); +} + +extension ClientExtension$Mutation$CreateUser on graphql.GraphQLClient { + Future> mutate$CreateUser( + Options$Mutation$CreateUser options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$CreateUser( + WatchOptions$Mutation$CreateUser options) => + this.watchMutation(options); +} + +class Mutation$CreateUser$createUser + implements Fragment$basicMutationReturnFields$$UserMutationReturn { + Mutation$CreateUser$createUser({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'UserMutationReturn', + this.user, + }); + + factory Mutation$CreateUser$createUser.fromJson(Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + final l$user = json['user']; + return Mutation$CreateUser$createUser( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + user: l$user == null + ? null + : Fragment$userFields.fromJson((l$user as Map)), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + final Fragment$userFields? user; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + final l$user = user; + _resultData['user'] = l$user?.toJson(); + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + final l$user = user; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + l$user, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$CreateUser$createUser) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + final l$user = user; + final lOther$user = other.user; + if (l$user != lOther$user) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$CreateUser$createUser + on Mutation$CreateUser$createUser { + CopyWith$Mutation$CreateUser$createUser + get copyWith => CopyWith$Mutation$CreateUser$createUser( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$CreateUser$createUser { + factory CopyWith$Mutation$CreateUser$createUser( + Mutation$CreateUser$createUser instance, + TRes Function(Mutation$CreateUser$createUser) then, + ) = _CopyWithImpl$Mutation$CreateUser$createUser; + + factory CopyWith$Mutation$CreateUser$createUser.stub(TRes res) = + _CopyWithStubImpl$Mutation$CreateUser$createUser; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + Fragment$userFields? user, + }); + CopyWith$Fragment$userFields get user; +} + +class _CopyWithImpl$Mutation$CreateUser$createUser + implements CopyWith$Mutation$CreateUser$createUser { + _CopyWithImpl$Mutation$CreateUser$createUser( + this._instance, + this._then, + ); + + final Mutation$CreateUser$createUser _instance; + + final TRes Function(Mutation$CreateUser$createUser) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + Object? user = _undefined, + }) => + _then(Mutation$CreateUser$createUser( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + user: user == _undefined + ? _instance.user + : (user as Fragment$userFields?), + )); + CopyWith$Fragment$userFields get user { + final local$user = _instance.user; + return local$user == null + ? CopyWith$Fragment$userFields.stub(_then(_instance)) + : CopyWith$Fragment$userFields(local$user, (e) => call(user: e)); + } +} + +class _CopyWithStubImpl$Mutation$CreateUser$createUser + implements CopyWith$Mutation$CreateUser$createUser { + _CopyWithStubImpl$Mutation$CreateUser$createUser(this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + Fragment$userFields? user, + }) => + _res; + CopyWith$Fragment$userFields get user => + CopyWith$Fragment$userFields.stub(_res); +} + +class Variables$Mutation$DeleteUser { + factory Variables$Mutation$DeleteUser({required String username}) => + Variables$Mutation$DeleteUser._({ + r'username': username, + }); + + Variables$Mutation$DeleteUser._(this._$data); + + factory Variables$Mutation$DeleteUser.fromJson(Map data) { + final result$data = {}; + final l$username = data['username']; + result$data['username'] = (l$username as String); + return Variables$Mutation$DeleteUser._(result$data); + } + + Map _$data; + + String get username => (_$data['username'] as String); + Map toJson() { + final result$data = {}; + final l$username = username; + result$data['username'] = l$username; + return result$data; + } + + CopyWith$Variables$Mutation$DeleteUser + get copyWith => CopyWith$Variables$Mutation$DeleteUser( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$DeleteUser) || + runtimeType != other.runtimeType) { + return false; + } + final l$username = username; + final lOther$username = other.username; + if (l$username != lOther$username) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$username = username; + return Object.hashAll([l$username]); + } +} + +abstract class CopyWith$Variables$Mutation$DeleteUser { + factory CopyWith$Variables$Mutation$DeleteUser( + Variables$Mutation$DeleteUser instance, + TRes Function(Variables$Mutation$DeleteUser) then, + ) = _CopyWithImpl$Variables$Mutation$DeleteUser; + + factory CopyWith$Variables$Mutation$DeleteUser.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$DeleteUser; + + TRes call({String? username}); +} + +class _CopyWithImpl$Variables$Mutation$DeleteUser + implements CopyWith$Variables$Mutation$DeleteUser { + _CopyWithImpl$Variables$Mutation$DeleteUser( + this._instance, + this._then, + ); + + final Variables$Mutation$DeleteUser _instance; + + final TRes Function(Variables$Mutation$DeleteUser) _then; + + static const _undefined = {}; + + TRes call({Object? username = _undefined}) => + _then(Variables$Mutation$DeleteUser._({ + ..._instance._$data, + if (username != _undefined && username != null) + 'username': (username as String), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$DeleteUser + implements CopyWith$Variables$Mutation$DeleteUser { + _CopyWithStubImpl$Variables$Mutation$DeleteUser(this._res); + + TRes _res; + + call({String? username}) => _res; +} + +class Mutation$DeleteUser { + Mutation$DeleteUser({ + required this.deleteUser, + this.$__typename = 'Mutation', + }); + + factory Mutation$DeleteUser.fromJson(Map json) { + final l$deleteUser = json['deleteUser']; + final l$$__typename = json['__typename']; + return Mutation$DeleteUser( + deleteUser: Mutation$DeleteUser$deleteUser.fromJson( + (l$deleteUser as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$DeleteUser$deleteUser deleteUser; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$deleteUser = deleteUser; + _resultData['deleteUser'] = l$deleteUser.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$deleteUser = deleteUser; + final l$$__typename = $__typename; + return Object.hashAll([ + l$deleteUser, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$DeleteUser) || runtimeType != other.runtimeType) { + return false; + } + final l$deleteUser = deleteUser; + final lOther$deleteUser = other.deleteUser; + if (l$deleteUser != lOther$deleteUser) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$DeleteUser on Mutation$DeleteUser { + CopyWith$Mutation$DeleteUser get copyWith => + CopyWith$Mutation$DeleteUser( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$DeleteUser { + factory CopyWith$Mutation$DeleteUser( + Mutation$DeleteUser instance, + TRes Function(Mutation$DeleteUser) then, + ) = _CopyWithImpl$Mutation$DeleteUser; + + factory CopyWith$Mutation$DeleteUser.stub(TRes res) = + _CopyWithStubImpl$Mutation$DeleteUser; + + TRes call({ + Mutation$DeleteUser$deleteUser? deleteUser, + String? $__typename, + }); + CopyWith$Mutation$DeleteUser$deleteUser get deleteUser; +} + +class _CopyWithImpl$Mutation$DeleteUser + implements CopyWith$Mutation$DeleteUser { + _CopyWithImpl$Mutation$DeleteUser( + this._instance, + this._then, + ); + + final Mutation$DeleteUser _instance; + + final TRes Function(Mutation$DeleteUser) _then; + + static const _undefined = {}; + + TRes call({ + Object? deleteUser = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$DeleteUser( + deleteUser: deleteUser == _undefined || deleteUser == null + ? _instance.deleteUser + : (deleteUser as Mutation$DeleteUser$deleteUser), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$DeleteUser$deleteUser get deleteUser { + final local$deleteUser = _instance.deleteUser; + return CopyWith$Mutation$DeleteUser$deleteUser( + local$deleteUser, (e) => call(deleteUser: e)); + } +} + +class _CopyWithStubImpl$Mutation$DeleteUser + implements CopyWith$Mutation$DeleteUser { + _CopyWithStubImpl$Mutation$DeleteUser(this._res); + + TRes _res; + + call({ + Mutation$DeleteUser$deleteUser? deleteUser, + String? $__typename, + }) => + _res; + CopyWith$Mutation$DeleteUser$deleteUser get deleteUser => + CopyWith$Mutation$DeleteUser$deleteUser.stub(_res); +} + +const documentNodeMutationDeleteUser = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'DeleteUser'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'username')), + type: NamedTypeNode( + name: NameNode(value: 'String'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'deleteUser'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'username'), + value: VariableNode(name: NameNode(value: 'username')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, +]); +Mutation$DeleteUser _parserFn$Mutation$DeleteUser(Map data) => + Mutation$DeleteUser.fromJson(data); +typedef OnMutationCompleted$Mutation$DeleteUser = FutureOr Function( + Map?, + Mutation$DeleteUser?, +); + +class Options$Mutation$DeleteUser + extends graphql.MutationOptions { + Options$Mutation$DeleteUser({ + String? operationName, + required Variables$Mutation$DeleteUser variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$DeleteUser? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$DeleteUser? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null ? null : _parserFn$Mutation$DeleteUser(data), + ), + update: update, + onError: onError, + document: documentNodeMutationDeleteUser, + parserFn: _parserFn$Mutation$DeleteUser, + ); + + final OnMutationCompleted$Mutation$DeleteUser? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$DeleteUser + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$DeleteUser({ + String? operationName, + required Variables$Mutation$DeleteUser variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$DeleteUser? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationDeleteUser, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$DeleteUser, + ); +} + +extension ClientExtension$Mutation$DeleteUser on graphql.GraphQLClient { + Future> mutate$DeleteUser( + Options$Mutation$DeleteUser options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$DeleteUser( + WatchOptions$Mutation$DeleteUser options) => + this.watchMutation(options); +} + +class Mutation$DeleteUser$deleteUser + implements Fragment$basicMutationReturnFields$$GenericMutationReturn { + Mutation$DeleteUser$deleteUser({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'GenericMutationReturn', + }); + + factory Mutation$DeleteUser$deleteUser.fromJson(Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + return Mutation$DeleteUser$deleteUser( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$DeleteUser$deleteUser) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$DeleteUser$deleteUser + on Mutation$DeleteUser$deleteUser { + CopyWith$Mutation$DeleteUser$deleteUser + get copyWith => CopyWith$Mutation$DeleteUser$deleteUser( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$DeleteUser$deleteUser { + factory CopyWith$Mutation$DeleteUser$deleteUser( + Mutation$DeleteUser$deleteUser instance, + TRes Function(Mutation$DeleteUser$deleteUser) then, + ) = _CopyWithImpl$Mutation$DeleteUser$deleteUser; + + factory CopyWith$Mutation$DeleteUser$deleteUser.stub(TRes res) = + _CopyWithStubImpl$Mutation$DeleteUser$deleteUser; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + }); +} + +class _CopyWithImpl$Mutation$DeleteUser$deleteUser + implements CopyWith$Mutation$DeleteUser$deleteUser { + _CopyWithImpl$Mutation$DeleteUser$deleteUser( + this._instance, + this._then, + ); + + final Mutation$DeleteUser$deleteUser _instance; + + final TRes Function(Mutation$DeleteUser$deleteUser) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$DeleteUser$deleteUser( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Mutation$DeleteUser$deleteUser + implements CopyWith$Mutation$DeleteUser$deleteUser { + _CopyWithStubImpl$Mutation$DeleteUser$deleteUser(this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + }) => + _res; +} + +class Variables$Mutation$UpdateUser { + factory Variables$Mutation$UpdateUser( + {required Input$UserMutationInput user}) => + Variables$Mutation$UpdateUser._({ + r'user': user, + }); + + Variables$Mutation$UpdateUser._(this._$data); + + factory Variables$Mutation$UpdateUser.fromJson(Map data) { + final result$data = {}; + final l$user = data['user']; + result$data['user'] = + Input$UserMutationInput.fromJson((l$user as Map)); + return Variables$Mutation$UpdateUser._(result$data); + } + + Map _$data; + + Input$UserMutationInput get user => + (_$data['user'] as Input$UserMutationInput); + Map toJson() { + final result$data = {}; + final l$user = user; + result$data['user'] = l$user.toJson(); + return result$data; + } + + CopyWith$Variables$Mutation$UpdateUser + get copyWith => CopyWith$Variables$Mutation$UpdateUser( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$UpdateUser) || + runtimeType != other.runtimeType) { + return false; + } + final l$user = user; + final lOther$user = other.user; + if (l$user != lOther$user) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$user = user; + return Object.hashAll([l$user]); + } +} + +abstract class CopyWith$Variables$Mutation$UpdateUser { + factory CopyWith$Variables$Mutation$UpdateUser( + Variables$Mutation$UpdateUser instance, + TRes Function(Variables$Mutation$UpdateUser) then, + ) = _CopyWithImpl$Variables$Mutation$UpdateUser; + + factory CopyWith$Variables$Mutation$UpdateUser.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$UpdateUser; + + TRes call({Input$UserMutationInput? user}); +} + +class _CopyWithImpl$Variables$Mutation$UpdateUser + implements CopyWith$Variables$Mutation$UpdateUser { + _CopyWithImpl$Variables$Mutation$UpdateUser( + this._instance, + this._then, + ); + + final Variables$Mutation$UpdateUser _instance; + + final TRes Function(Variables$Mutation$UpdateUser) _then; + + static const _undefined = {}; + + TRes call({Object? user = _undefined}) => + _then(Variables$Mutation$UpdateUser._({ + ..._instance._$data, + if (user != _undefined && user != null) + 'user': (user as Input$UserMutationInput), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$UpdateUser + implements CopyWith$Variables$Mutation$UpdateUser { + _CopyWithStubImpl$Variables$Mutation$UpdateUser(this._res); + + TRes _res; + + call({Input$UserMutationInput? user}) => _res; +} + +class Mutation$UpdateUser { + Mutation$UpdateUser({ + required this.updateUser, + this.$__typename = 'Mutation', + }); + + factory Mutation$UpdateUser.fromJson(Map json) { + final l$updateUser = json['updateUser']; + final l$$__typename = json['__typename']; + return Mutation$UpdateUser( + updateUser: Mutation$UpdateUser$updateUser.fromJson( + (l$updateUser as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$UpdateUser$updateUser updateUser; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$updateUser = updateUser; + _resultData['updateUser'] = l$updateUser.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$updateUser = updateUser; + final l$$__typename = $__typename; + return Object.hashAll([ + l$updateUser, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$UpdateUser) || runtimeType != other.runtimeType) { + return false; + } + final l$updateUser = updateUser; + final lOther$updateUser = other.updateUser; + if (l$updateUser != lOther$updateUser) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$UpdateUser on Mutation$UpdateUser { + CopyWith$Mutation$UpdateUser get copyWith => + CopyWith$Mutation$UpdateUser( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$UpdateUser { + factory CopyWith$Mutation$UpdateUser( + Mutation$UpdateUser instance, + TRes Function(Mutation$UpdateUser) then, + ) = _CopyWithImpl$Mutation$UpdateUser; + + factory CopyWith$Mutation$UpdateUser.stub(TRes res) = + _CopyWithStubImpl$Mutation$UpdateUser; + + TRes call({ + Mutation$UpdateUser$updateUser? updateUser, + String? $__typename, + }); + CopyWith$Mutation$UpdateUser$updateUser get updateUser; +} + +class _CopyWithImpl$Mutation$UpdateUser + implements CopyWith$Mutation$UpdateUser { + _CopyWithImpl$Mutation$UpdateUser( + this._instance, + this._then, + ); + + final Mutation$UpdateUser _instance; + + final TRes Function(Mutation$UpdateUser) _then; + + static const _undefined = {}; + + TRes call({ + Object? updateUser = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$UpdateUser( + updateUser: updateUser == _undefined || updateUser == null + ? _instance.updateUser + : (updateUser as Mutation$UpdateUser$updateUser), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$UpdateUser$updateUser get updateUser { + final local$updateUser = _instance.updateUser; + return CopyWith$Mutation$UpdateUser$updateUser( + local$updateUser, (e) => call(updateUser: e)); + } +} + +class _CopyWithStubImpl$Mutation$UpdateUser + implements CopyWith$Mutation$UpdateUser { + _CopyWithStubImpl$Mutation$UpdateUser(this._res); + + TRes _res; + + call({ + Mutation$UpdateUser$updateUser? updateUser, + String? $__typename, + }) => + _res; + CopyWith$Mutation$UpdateUser$updateUser get updateUser => + CopyWith$Mutation$UpdateUser$updateUser.stub(_res); +} + +const documentNodeMutationUpdateUser = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'UpdateUser'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'user')), + type: NamedTypeNode( + name: NameNode(value: 'UserMutationInput'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'updateUser'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'user'), + value: VariableNode(name: NameNode(value: 'user')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: 'user'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'userFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, + fragmentDefinitionuserFields, +]); +Mutation$UpdateUser _parserFn$Mutation$UpdateUser(Map data) => + Mutation$UpdateUser.fromJson(data); +typedef OnMutationCompleted$Mutation$UpdateUser = FutureOr Function( + Map?, + Mutation$UpdateUser?, +); + +class Options$Mutation$UpdateUser + extends graphql.MutationOptions { + Options$Mutation$UpdateUser({ + String? operationName, + required Variables$Mutation$UpdateUser variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$UpdateUser? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$UpdateUser? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null ? null : _parserFn$Mutation$UpdateUser(data), + ), + update: update, + onError: onError, + document: documentNodeMutationUpdateUser, + parserFn: _parserFn$Mutation$UpdateUser, + ); + + final OnMutationCompleted$Mutation$UpdateUser? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$UpdateUser + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$UpdateUser({ + String? operationName, + required Variables$Mutation$UpdateUser variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$UpdateUser? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationUpdateUser, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$UpdateUser, + ); +} + +extension ClientExtension$Mutation$UpdateUser on graphql.GraphQLClient { + Future> mutate$UpdateUser( + Options$Mutation$UpdateUser options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$UpdateUser( + WatchOptions$Mutation$UpdateUser options) => + this.watchMutation(options); +} + +class Mutation$UpdateUser$updateUser + implements Fragment$basicMutationReturnFields$$UserMutationReturn { + Mutation$UpdateUser$updateUser({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'UserMutationReturn', + this.user, + }); + + factory Mutation$UpdateUser$updateUser.fromJson(Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + final l$user = json['user']; + return Mutation$UpdateUser$updateUser( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + user: l$user == null + ? null + : Fragment$userFields.fromJson((l$user as Map)), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + final Fragment$userFields? user; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + final l$user = user; + _resultData['user'] = l$user?.toJson(); + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + final l$user = user; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + l$user, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$UpdateUser$updateUser) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + final l$user = user; + final lOther$user = other.user; + if (l$user != lOther$user) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$UpdateUser$updateUser + on Mutation$UpdateUser$updateUser { + CopyWith$Mutation$UpdateUser$updateUser + get copyWith => CopyWith$Mutation$UpdateUser$updateUser( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$UpdateUser$updateUser { + factory CopyWith$Mutation$UpdateUser$updateUser( + Mutation$UpdateUser$updateUser instance, + TRes Function(Mutation$UpdateUser$updateUser) then, + ) = _CopyWithImpl$Mutation$UpdateUser$updateUser; + + factory CopyWith$Mutation$UpdateUser$updateUser.stub(TRes res) = + _CopyWithStubImpl$Mutation$UpdateUser$updateUser; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + Fragment$userFields? user, + }); + CopyWith$Fragment$userFields get user; +} + +class _CopyWithImpl$Mutation$UpdateUser$updateUser + implements CopyWith$Mutation$UpdateUser$updateUser { + _CopyWithImpl$Mutation$UpdateUser$updateUser( + this._instance, + this._then, + ); + + final Mutation$UpdateUser$updateUser _instance; + + final TRes Function(Mutation$UpdateUser$updateUser) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + Object? user = _undefined, + }) => + _then(Mutation$UpdateUser$updateUser( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + user: user == _undefined + ? _instance.user + : (user as Fragment$userFields?), + )); + CopyWith$Fragment$userFields get user { + final local$user = _instance.user; + return local$user == null + ? CopyWith$Fragment$userFields.stub(_then(_instance)) + : CopyWith$Fragment$userFields(local$user, (e) => call(user: e)); + } +} + +class _CopyWithStubImpl$Mutation$UpdateUser$updateUser + implements CopyWith$Mutation$UpdateUser$updateUser { + _CopyWithStubImpl$Mutation$UpdateUser$updateUser(this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + Fragment$userFields? user, + }) => + _res; + CopyWith$Fragment$userFields get user => + CopyWith$Fragment$userFields.stub(_res); +} + +class Variables$Mutation$AddSshKey { + factory Variables$Mutation$AddSshKey( + {required Input$SshMutationInput sshInput}) => + Variables$Mutation$AddSshKey._({ + r'sshInput': sshInput, + }); + + Variables$Mutation$AddSshKey._(this._$data); + + factory Variables$Mutation$AddSshKey.fromJson(Map data) { + final result$data = {}; + final l$sshInput = data['sshInput']; + result$data['sshInput'] = + Input$SshMutationInput.fromJson((l$sshInput as Map)); + return Variables$Mutation$AddSshKey._(result$data); + } + + Map _$data; + + Input$SshMutationInput get sshInput => + (_$data['sshInput'] as Input$SshMutationInput); + Map toJson() { + final result$data = {}; + final l$sshInput = sshInput; + result$data['sshInput'] = l$sshInput.toJson(); + return result$data; + } + + CopyWith$Variables$Mutation$AddSshKey + get copyWith => CopyWith$Variables$Mutation$AddSshKey( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$AddSshKey) || + runtimeType != other.runtimeType) { + return false; + } + final l$sshInput = sshInput; + final lOther$sshInput = other.sshInput; + if (l$sshInput != lOther$sshInput) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$sshInput = sshInput; + return Object.hashAll([l$sshInput]); + } +} + +abstract class CopyWith$Variables$Mutation$AddSshKey { + factory CopyWith$Variables$Mutation$AddSshKey( + Variables$Mutation$AddSshKey instance, + TRes Function(Variables$Mutation$AddSshKey) then, + ) = _CopyWithImpl$Variables$Mutation$AddSshKey; + + factory CopyWith$Variables$Mutation$AddSshKey.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$AddSshKey; + + TRes call({Input$SshMutationInput? sshInput}); +} + +class _CopyWithImpl$Variables$Mutation$AddSshKey + implements CopyWith$Variables$Mutation$AddSshKey { + _CopyWithImpl$Variables$Mutation$AddSshKey( + this._instance, + this._then, + ); + + final Variables$Mutation$AddSshKey _instance; + + final TRes Function(Variables$Mutation$AddSshKey) _then; + + static const _undefined = {}; + + TRes call({Object? sshInput = _undefined}) => + _then(Variables$Mutation$AddSshKey._({ + ..._instance._$data, + if (sshInput != _undefined && sshInput != null) + 'sshInput': (sshInput as Input$SshMutationInput), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$AddSshKey + implements CopyWith$Variables$Mutation$AddSshKey { + _CopyWithStubImpl$Variables$Mutation$AddSshKey(this._res); + + TRes _res; + + call({Input$SshMutationInput? sshInput}) => _res; +} + +class Mutation$AddSshKey { + Mutation$AddSshKey({ + required this.addSshKey, + this.$__typename = 'Mutation', + }); + + factory Mutation$AddSshKey.fromJson(Map json) { + final l$addSshKey = json['addSshKey']; + final l$$__typename = json['__typename']; + return Mutation$AddSshKey( + addSshKey: Mutation$AddSshKey$addSshKey.fromJson( + (l$addSshKey as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$AddSshKey$addSshKey addSshKey; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$addSshKey = addSshKey; + _resultData['addSshKey'] = l$addSshKey.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$addSshKey = addSshKey; + final l$$__typename = $__typename; + return Object.hashAll([ + l$addSshKey, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$AddSshKey) || runtimeType != other.runtimeType) { + return false; + } + final l$addSshKey = addSshKey; + final lOther$addSshKey = other.addSshKey; + if (l$addSshKey != lOther$addSshKey) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$AddSshKey on Mutation$AddSshKey { + CopyWith$Mutation$AddSshKey get copyWith => + CopyWith$Mutation$AddSshKey( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$AddSshKey { + factory CopyWith$Mutation$AddSshKey( + Mutation$AddSshKey instance, + TRes Function(Mutation$AddSshKey) then, + ) = _CopyWithImpl$Mutation$AddSshKey; + + factory CopyWith$Mutation$AddSshKey.stub(TRes res) = + _CopyWithStubImpl$Mutation$AddSshKey; + + TRes call({ + Mutation$AddSshKey$addSshKey? addSshKey, + String? $__typename, + }); + CopyWith$Mutation$AddSshKey$addSshKey get addSshKey; +} + +class _CopyWithImpl$Mutation$AddSshKey + implements CopyWith$Mutation$AddSshKey { + _CopyWithImpl$Mutation$AddSshKey( + this._instance, + this._then, + ); + + final Mutation$AddSshKey _instance; + + final TRes Function(Mutation$AddSshKey) _then; + + static const _undefined = {}; + + TRes call({ + Object? addSshKey = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$AddSshKey( + addSshKey: addSshKey == _undefined || addSshKey == null + ? _instance.addSshKey + : (addSshKey as Mutation$AddSshKey$addSshKey), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$AddSshKey$addSshKey get addSshKey { + final local$addSshKey = _instance.addSshKey; + return CopyWith$Mutation$AddSshKey$addSshKey( + local$addSshKey, (e) => call(addSshKey: e)); + } +} + +class _CopyWithStubImpl$Mutation$AddSshKey + implements CopyWith$Mutation$AddSshKey { + _CopyWithStubImpl$Mutation$AddSshKey(this._res); + + TRes _res; + + call({ + Mutation$AddSshKey$addSshKey? addSshKey, + String? $__typename, + }) => + _res; + CopyWith$Mutation$AddSshKey$addSshKey get addSshKey => + CopyWith$Mutation$AddSshKey$addSshKey.stub(_res); +} + +const documentNodeMutationAddSshKey = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'AddSshKey'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'sshInput')), + type: NamedTypeNode( + name: NameNode(value: 'SshMutationInput'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'addSshKey'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'sshInput'), + value: VariableNode(name: NameNode(value: 'sshInput')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: 'user'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'userFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, + fragmentDefinitionuserFields, +]); +Mutation$AddSshKey _parserFn$Mutation$AddSshKey(Map data) => + Mutation$AddSshKey.fromJson(data); +typedef OnMutationCompleted$Mutation$AddSshKey = FutureOr Function( + Map?, + Mutation$AddSshKey?, +); + +class Options$Mutation$AddSshKey + extends graphql.MutationOptions { + Options$Mutation$AddSshKey({ + String? operationName, + required Variables$Mutation$AddSshKey variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$AddSshKey? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$AddSshKey? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null ? null : _parserFn$Mutation$AddSshKey(data), + ), + update: update, + onError: onError, + document: documentNodeMutationAddSshKey, + parserFn: _parserFn$Mutation$AddSshKey, + ); + + final OnMutationCompleted$Mutation$AddSshKey? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$AddSshKey + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$AddSshKey({ + String? operationName, + required Variables$Mutation$AddSshKey variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$AddSshKey? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationAddSshKey, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$AddSshKey, + ); +} + +extension ClientExtension$Mutation$AddSshKey on graphql.GraphQLClient { + Future> mutate$AddSshKey( + Options$Mutation$AddSshKey options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$AddSshKey( + WatchOptions$Mutation$AddSshKey options) => + this.watchMutation(options); +} + +class Mutation$AddSshKey$addSshKey + implements Fragment$basicMutationReturnFields$$UserMutationReturn { + Mutation$AddSshKey$addSshKey({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'UserMutationReturn', + this.user, + }); + + factory Mutation$AddSshKey$addSshKey.fromJson(Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + final l$user = json['user']; + return Mutation$AddSshKey$addSshKey( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + user: l$user == null + ? null + : Fragment$userFields.fromJson((l$user as Map)), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + final Fragment$userFields? user; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + final l$user = user; + _resultData['user'] = l$user?.toJson(); + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + final l$user = user; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + l$user, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$AddSshKey$addSshKey) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + final l$user = user; + final lOther$user = other.user; + if (l$user != lOther$user) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$AddSshKey$addSshKey + on Mutation$AddSshKey$addSshKey { + CopyWith$Mutation$AddSshKey$addSshKey + get copyWith => CopyWith$Mutation$AddSshKey$addSshKey( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$AddSshKey$addSshKey { + factory CopyWith$Mutation$AddSshKey$addSshKey( + Mutation$AddSshKey$addSshKey instance, + TRes Function(Mutation$AddSshKey$addSshKey) then, + ) = _CopyWithImpl$Mutation$AddSshKey$addSshKey; + + factory CopyWith$Mutation$AddSshKey$addSshKey.stub(TRes res) = + _CopyWithStubImpl$Mutation$AddSshKey$addSshKey; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + Fragment$userFields? user, + }); + CopyWith$Fragment$userFields get user; +} + +class _CopyWithImpl$Mutation$AddSshKey$addSshKey + implements CopyWith$Mutation$AddSshKey$addSshKey { + _CopyWithImpl$Mutation$AddSshKey$addSshKey( + this._instance, + this._then, + ); + + final Mutation$AddSshKey$addSshKey _instance; + + final TRes Function(Mutation$AddSshKey$addSshKey) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + Object? user = _undefined, + }) => + _then(Mutation$AddSshKey$addSshKey( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + user: user == _undefined + ? _instance.user + : (user as Fragment$userFields?), + )); + CopyWith$Fragment$userFields get user { + final local$user = _instance.user; + return local$user == null + ? CopyWith$Fragment$userFields.stub(_then(_instance)) + : CopyWith$Fragment$userFields(local$user, (e) => call(user: e)); + } +} + +class _CopyWithStubImpl$Mutation$AddSshKey$addSshKey + implements CopyWith$Mutation$AddSshKey$addSshKey { + _CopyWithStubImpl$Mutation$AddSshKey$addSshKey(this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + Fragment$userFields? user, + }) => + _res; + CopyWith$Fragment$userFields get user => + CopyWith$Fragment$userFields.stub(_res); +} + +class Variables$Mutation$RemoveSshKey { + factory Variables$Mutation$RemoveSshKey( + {required Input$SshMutationInput sshInput}) => + Variables$Mutation$RemoveSshKey._({ + r'sshInput': sshInput, + }); + + Variables$Mutation$RemoveSshKey._(this._$data); + + factory Variables$Mutation$RemoveSshKey.fromJson(Map data) { + final result$data = {}; + final l$sshInput = data['sshInput']; + result$data['sshInput'] = + Input$SshMutationInput.fromJson((l$sshInput as Map)); + return Variables$Mutation$RemoveSshKey._(result$data); + } + + Map _$data; + + Input$SshMutationInput get sshInput => + (_$data['sshInput'] as Input$SshMutationInput); + Map toJson() { + final result$data = {}; + final l$sshInput = sshInput; + result$data['sshInput'] = l$sshInput.toJson(); + return result$data; + } + + CopyWith$Variables$Mutation$RemoveSshKey + get copyWith => CopyWith$Variables$Mutation$RemoveSshKey( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Variables$Mutation$RemoveSshKey) || + runtimeType != other.runtimeType) { + return false; + } + final l$sshInput = sshInput; + final lOther$sshInput = other.sshInput; + if (l$sshInput != lOther$sshInput) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$sshInput = sshInput; + return Object.hashAll([l$sshInput]); + } +} + +abstract class CopyWith$Variables$Mutation$RemoveSshKey { + factory CopyWith$Variables$Mutation$RemoveSshKey( + Variables$Mutation$RemoveSshKey instance, + TRes Function(Variables$Mutation$RemoveSshKey) then, + ) = _CopyWithImpl$Variables$Mutation$RemoveSshKey; + + factory CopyWith$Variables$Mutation$RemoveSshKey.stub(TRes res) = + _CopyWithStubImpl$Variables$Mutation$RemoveSshKey; + + TRes call({Input$SshMutationInput? sshInput}); +} + +class _CopyWithImpl$Variables$Mutation$RemoveSshKey + implements CopyWith$Variables$Mutation$RemoveSshKey { + _CopyWithImpl$Variables$Mutation$RemoveSshKey( + this._instance, + this._then, + ); + + final Variables$Mutation$RemoveSshKey _instance; + + final TRes Function(Variables$Mutation$RemoveSshKey) _then; + + static const _undefined = {}; + + TRes call({Object? sshInput = _undefined}) => + _then(Variables$Mutation$RemoveSshKey._({ + ..._instance._$data, + if (sshInput != _undefined && sshInput != null) + 'sshInput': (sshInput as Input$SshMutationInput), + })); +} + +class _CopyWithStubImpl$Variables$Mutation$RemoveSshKey + implements CopyWith$Variables$Mutation$RemoveSshKey { + _CopyWithStubImpl$Variables$Mutation$RemoveSshKey(this._res); + + TRes _res; + + call({Input$SshMutationInput? sshInput}) => _res; +} + +class Mutation$RemoveSshKey { + Mutation$RemoveSshKey({ + required this.removeSshKey, + this.$__typename = 'Mutation', + }); + + factory Mutation$RemoveSshKey.fromJson(Map json) { + final l$removeSshKey = json['removeSshKey']; + final l$$__typename = json['__typename']; + return Mutation$RemoveSshKey( + removeSshKey: Mutation$RemoveSshKey$removeSshKey.fromJson( + (l$removeSshKey as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Mutation$RemoveSshKey$removeSshKey removeSshKey; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$removeSshKey = removeSshKey; + _resultData['removeSshKey'] = l$removeSshKey.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$removeSshKey = removeSshKey; + final l$$__typename = $__typename; + return Object.hashAll([ + l$removeSshKey, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$RemoveSshKey) || runtimeType != other.runtimeType) { + return false; + } + final l$removeSshKey = removeSshKey; + final lOther$removeSshKey = other.removeSshKey; + if (l$removeSshKey != lOther$removeSshKey) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$RemoveSshKey on Mutation$RemoveSshKey { + CopyWith$Mutation$RemoveSshKey get copyWith => + CopyWith$Mutation$RemoveSshKey( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$RemoveSshKey { + factory CopyWith$Mutation$RemoveSshKey( + Mutation$RemoveSshKey instance, + TRes Function(Mutation$RemoveSshKey) then, + ) = _CopyWithImpl$Mutation$RemoveSshKey; + + factory CopyWith$Mutation$RemoveSshKey.stub(TRes res) = + _CopyWithStubImpl$Mutation$RemoveSshKey; + + TRes call({ + Mutation$RemoveSshKey$removeSshKey? removeSshKey, + String? $__typename, + }); + CopyWith$Mutation$RemoveSshKey$removeSshKey get removeSshKey; +} + +class _CopyWithImpl$Mutation$RemoveSshKey + implements CopyWith$Mutation$RemoveSshKey { + _CopyWithImpl$Mutation$RemoveSshKey( + this._instance, + this._then, + ); + + final Mutation$RemoveSshKey _instance; + + final TRes Function(Mutation$RemoveSshKey) _then; + + static const _undefined = {}; + + TRes call({ + Object? removeSshKey = _undefined, + Object? $__typename = _undefined, + }) => + _then(Mutation$RemoveSshKey( + removeSshKey: removeSshKey == _undefined || removeSshKey == null + ? _instance.removeSshKey + : (removeSshKey as Mutation$RemoveSshKey$removeSshKey), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Mutation$RemoveSshKey$removeSshKey get removeSshKey { + final local$removeSshKey = _instance.removeSshKey; + return CopyWith$Mutation$RemoveSshKey$removeSshKey( + local$removeSshKey, (e) => call(removeSshKey: e)); + } +} + +class _CopyWithStubImpl$Mutation$RemoveSshKey + implements CopyWith$Mutation$RemoveSshKey { + _CopyWithStubImpl$Mutation$RemoveSshKey(this._res); + + TRes _res; + + call({ + Mutation$RemoveSshKey$removeSshKey? removeSshKey, + String? $__typename, + }) => + _res; + CopyWith$Mutation$RemoveSshKey$removeSshKey get removeSshKey => + CopyWith$Mutation$RemoveSshKey$removeSshKey.stub(_res); +} + +const documentNodeMutationRemoveSshKey = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.mutation, + name: NameNode(value: 'RemoveSshKey'), + variableDefinitions: [ + VariableDefinitionNode( + variable: VariableNode(name: NameNode(value: 'sshInput')), + type: NamedTypeNode( + name: NameNode(value: 'SshMutationInput'), + isNonNull: true, + ), + defaultValue: DefaultValueNode(value: null), + directives: [], + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'removeSshKey'), + alias: null, + arguments: [ + ArgumentNode( + name: NameNode(value: 'sshInput'), + value: VariableNode(name: NameNode(value: 'sshInput')), + ) + ], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'basicMutationReturnFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: 'user'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FragmentSpreadNode( + name: NameNode(value: 'userFields'), + directives: [], + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + fragmentDefinitionbasicMutationReturnFields, + fragmentDefinitionuserFields, +]); +Mutation$RemoveSshKey _parserFn$Mutation$RemoveSshKey( + Map data) => + Mutation$RemoveSshKey.fromJson(data); +typedef OnMutationCompleted$Mutation$RemoveSshKey = FutureOr Function( + Map?, + Mutation$RemoveSshKey?, +); + +class Options$Mutation$RemoveSshKey + extends graphql.MutationOptions { + Options$Mutation$RemoveSshKey({ + String? operationName, + required Variables$Mutation$RemoveSshKey variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RemoveSshKey? typedOptimisticResult, + graphql.Context? context, + OnMutationCompleted$Mutation$RemoveSshKey? onCompleted, + graphql.OnMutationUpdate? update, + graphql.OnError? onError, + }) : onCompletedWithParsed = onCompleted, + super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + onCompleted: onCompleted == null + ? null + : (data) => onCompleted( + data, + data == null ? null : _parserFn$Mutation$RemoveSshKey(data), + ), + update: update, + onError: onError, + document: documentNodeMutationRemoveSshKey, + parserFn: _parserFn$Mutation$RemoveSshKey, + ); + + final OnMutationCompleted$Mutation$RemoveSshKey? onCompletedWithParsed; + + @override + List get properties => [ + ...super.onCompleted == null + ? super.properties + : super.properties.where((property) => property != onCompleted), + onCompletedWithParsed, + ]; +} + +class WatchOptions$Mutation$RemoveSshKey + extends graphql.WatchQueryOptions { + WatchOptions$Mutation$RemoveSshKey({ + String? operationName, + required Variables$Mutation$RemoveSshKey variables, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Mutation$RemoveSshKey? typedOptimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + variables: variables.toJson(), + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult ?? typedOptimisticResult?.toJson(), + context: context, + document: documentNodeMutationRemoveSshKey, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Mutation$RemoveSshKey, + ); +} + +extension ClientExtension$Mutation$RemoveSshKey on graphql.GraphQLClient { + Future> mutate$RemoveSshKey( + Options$Mutation$RemoveSshKey options) async => + await this.mutate(options); + graphql.ObservableQuery watchMutation$RemoveSshKey( + WatchOptions$Mutation$RemoveSshKey options) => + this.watchMutation(options); +} + +class Mutation$RemoveSshKey$removeSshKey + implements Fragment$basicMutationReturnFields$$UserMutationReturn { + Mutation$RemoveSshKey$removeSshKey({ + required this.code, + required this.message, + required this.success, + this.$__typename = 'UserMutationReturn', + this.user, + }); + + factory Mutation$RemoveSshKey$removeSshKey.fromJson( + Map json) { + final l$code = json['code']; + final l$message = json['message']; + final l$success = json['success']; + final l$$__typename = json['__typename']; + final l$user = json['user']; + return Mutation$RemoveSshKey$removeSshKey( + code: (l$code as int), + message: (l$message as String), + success: (l$success as bool), + $__typename: (l$$__typename as String), + user: l$user == null + ? null + : Fragment$userFields.fromJson((l$user as Map)), + ); + } + + final int code; + + final String message; + + final bool success; + + final String $__typename; + + final Fragment$userFields? user; + + Map toJson() { + final _resultData = {}; + final l$code = code; + _resultData['code'] = l$code; + final l$message = message; + _resultData['message'] = l$message; + final l$success = success; + _resultData['success'] = l$success; + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + final l$user = user; + _resultData['user'] = l$user?.toJson(); + return _resultData; + } + + @override + int get hashCode { + final l$code = code; + final l$message = message; + final l$success = success; + final l$$__typename = $__typename; + final l$user = user; + return Object.hashAll([ + l$code, + l$message, + l$success, + l$$__typename, + l$user, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Mutation$RemoveSshKey$removeSshKey) || + runtimeType != other.runtimeType) { + return false; + } + final l$code = code; + final lOther$code = other.code; + if (l$code != lOther$code) { + return false; + } + final l$message = message; + final lOther$message = other.message; + if (l$message != lOther$message) { + return false; + } + final l$success = success; + final lOther$success = other.success; + if (l$success != lOther$success) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + final l$user = user; + final lOther$user = other.user; + if (l$user != lOther$user) { + return false; + } + return true; + } +} + +extension UtilityExtension$Mutation$RemoveSshKey$removeSshKey + on Mutation$RemoveSshKey$removeSshKey { + CopyWith$Mutation$RemoveSshKey$removeSshKey< + Mutation$RemoveSshKey$removeSshKey> + get copyWith => CopyWith$Mutation$RemoveSshKey$removeSshKey( + this, + (i) => i, + ); +} + +abstract class CopyWith$Mutation$RemoveSshKey$removeSshKey { + factory CopyWith$Mutation$RemoveSshKey$removeSshKey( + Mutation$RemoveSshKey$removeSshKey instance, + TRes Function(Mutation$RemoveSshKey$removeSshKey) then, + ) = _CopyWithImpl$Mutation$RemoveSshKey$removeSshKey; + + factory CopyWith$Mutation$RemoveSshKey$removeSshKey.stub(TRes res) = + _CopyWithStubImpl$Mutation$RemoveSshKey$removeSshKey; + + TRes call({ + int? code, + String? message, + bool? success, + String? $__typename, + Fragment$userFields? user, + }); + CopyWith$Fragment$userFields get user; +} + +class _CopyWithImpl$Mutation$RemoveSshKey$removeSshKey + implements CopyWith$Mutation$RemoveSshKey$removeSshKey { + _CopyWithImpl$Mutation$RemoveSshKey$removeSshKey( + this._instance, + this._then, + ); + + final Mutation$RemoveSshKey$removeSshKey _instance; + + final TRes Function(Mutation$RemoveSshKey$removeSshKey) _then; + + static const _undefined = {}; + + TRes call({ + Object? code = _undefined, + Object? message = _undefined, + Object? success = _undefined, + Object? $__typename = _undefined, + Object? user = _undefined, + }) => + _then(Mutation$RemoveSshKey$removeSshKey( + code: + code == _undefined || code == null ? _instance.code : (code as int), + message: message == _undefined || message == null + ? _instance.message + : (message as String), + success: success == _undefined || success == null + ? _instance.success + : (success as bool), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + user: user == _undefined + ? _instance.user + : (user as Fragment$userFields?), + )); + CopyWith$Fragment$userFields get user { + final local$user = _instance.user; + return local$user == null + ? CopyWith$Fragment$userFields.stub(_then(_instance)) + : CopyWith$Fragment$userFields(local$user, (e) => call(user: e)); + } +} + +class _CopyWithStubImpl$Mutation$RemoveSshKey$removeSshKey + implements CopyWith$Mutation$RemoveSshKey$removeSshKey { + _CopyWithStubImpl$Mutation$RemoveSshKey$removeSshKey(this._res); + + TRes _res; + + call({ + int? code, + String? message, + bool? success, + String? $__typename, + Fragment$userFields? user, + }) => + _res; + CopyWith$Fragment$userFields get user => + CopyWith$Fragment$userFields.stub(_res); +} diff --git a/lib/logic/api_maps/graphql_maps/server_api/backups_api.dart b/lib/logic/api_maps/graphql_maps/server_api/backups_api.dart new file mode 100644 index 00000000..8e77d452 --- /dev/null +++ b/lib/logic/api_maps/graphql_maps/server_api/backups_api.dart @@ -0,0 +1,334 @@ +part of 'server_api.dart'; + +mixin BackupsApi on GraphQLApiMap { + Future> getBackups() async { + List backups; + QueryResult response; + + try { + final GraphQLClient client = await getClient(); + response = await client.query$AllBackupSnapshots(); + if (response.hasException) { + final message = response.exception.toString(); + print(message); + backups = []; + } + final List parsed = response.parsedData!.backup.allSnapshots + .map( + ( + final Query$AllBackupSnapshots$backup$allSnapshots snapshot, + ) => + Backup.fromGraphQL(snapshot), + ) + .toList(); + backups = parsed; + } catch (e) { + print(e); + backups = []; + } + + return backups; + } + + Future getBackupsConfiguration() async { + BackupConfiguration? backupConfiguration; + QueryResult response; + try { + final GraphQLClient client = await getClient(); + response = await client.query$BackupConfiguration(); + if (response.hasException) { + final message = response.exception.toString(); + print(message); + backupConfiguration = null; + } + final BackupConfiguration parsed = BackupConfiguration.fromGraphQL( + response.parsedData!.backup.configuration, + ); + backupConfiguration = parsed; + } catch (e) { + print(e); + backupConfiguration = null; + } + + return backupConfiguration; + } + + Future forceBackupListReload() async { + try { + final GraphQLClient client = await getClient(); + await client.mutate$ForceSnapshotsReload(); + } catch (e) { + print(e); + return GenericResult( + success: false, + data: null, + message: e.toString(), + ); + } + + return GenericResult( + success: true, + data: null, + ); + } + + Future> startBackup(final String serviceId) async { + QueryResult response; + GenericResult? result; + + try { + final GraphQLClient client = await getClient(); + final variables = Variables$Mutation$StartBackup(serviceId: serviceId); + final options = Options$Mutation$StartBackup(variables: variables); + response = await client.mutate$StartBackup(options); + if (response.hasException) { + final message = response.exception.toString(); + print(message); + result = GenericResult( + success: false, + data: null, + message: message, + ); + } + result = GenericResult( + success: true, + data: ServerJob.fromGraphQL( + response.parsedData!.backup.startBackup.job!, + ), + ); + } catch (e) { + print(e); + result = GenericResult( + success: false, + data: null, + message: e.toString(), + ); + } + + return result; + } + + Future setAutobackupPeriod({final int? period}) async { + QueryResult response; + GenericResult? result; + + try { + final GraphQLClient client = await getClient(); + final variables = Variables$Mutation$SetAutobackupPeriod(period: period); + final options = + Options$Mutation$SetAutobackupPeriod(variables: variables); + response = await client.mutate$SetAutobackupPeriod(options); + if (response.hasException) { + final message = response.exception.toString(); + print(message); + result = GenericResult( + success: false, + data: null, + message: message, + ); + } + result = GenericResult( + success: true, + data: null, + ); + } catch (e) { + print(e); + result = GenericResult( + success: false, + data: null, + message: e.toString(), + ); + } + + return result; + } + + Future setAutobackupQuotas( + final AutobackupQuotas quotas, + ) async { + QueryResult response; + GenericResult? result; + + try { + final GraphQLClient client = await getClient(); + final variables = Variables$Mutation$setAutobackupQuotas( + quotas: Input$AutobackupQuotasInput( + last: quotas.last, + daily: quotas.daily, + weekly: quotas.weekly, + monthly: quotas.monthly, + yearly: quotas.yearly, + ), + ); + final options = + Options$Mutation$setAutobackupQuotas(variables: variables); + response = await client.mutate$setAutobackupQuotas(options); + if (response.hasException) { + final message = response.exception.toString(); + print(message); + result = GenericResult( + success: false, + data: null, + message: message, + ); + } + result = GenericResult( + success: true, + data: null, + ); + } catch (e) { + print(e); + result = GenericResult( + success: false, + data: null, + message: e.toString(), + ); + } + + return result; + } + + Future removeRepository() async { + try { + final GraphQLClient client = await getClient(); + await client.mutate$RemoveRepository(); + } catch (e) { + print(e); + return GenericResult( + success: false, + data: null, + message: e.toString(), + ); + } + + return GenericResult( + success: true, + data: null, + ); + } + + Future initializeRepository( + final InitializeRepositoryInput input, + ) async { + QueryResult response; + GenericResult? result; + + try { + final GraphQLClient client = await getClient(); + final variables = Variables$Mutation$InitializeRepository( + repository: Input$InitializeRepositoryInput( + locationId: input.locationId, + locationName: input.locationName, + login: input.login, + password: input.password, + provider: input.provider.toGraphQL(), + ), + ); + final options = + Options$Mutation$InitializeRepository(variables: variables); + response = await client.mutate$InitializeRepository(options); + if (response.hasException) { + final message = response.exception.toString(); + print(message); + result = GenericResult( + success: false, + data: null, + message: message, + ); + } + result = GenericResult( + success: true, + data: null, + ); + } catch (e) { + print(e); + result = GenericResult( + success: false, + data: null, + message: e.toString(), + ); + } + + return result; + } + + Future> restoreBackup( + final String snapshotId, + final BackupRestoreStrategy strategy, + ) async { + QueryResult response; + GenericResult? result; + + try { + final GraphQLClient client = await getClient(); + final variables = Variables$Mutation$RestoreBackup( + snapshotId: snapshotId, + strategy: strategy.toGraphQL, + ); + final options = Options$Mutation$RestoreBackup(variables: variables); + response = await client.mutate$RestoreBackup(options); + if (response.hasException) { + final message = response.exception.toString(); + print(message); + result = GenericResult( + success: false, + data: null, + message: message, + ); + } + result = GenericResult( + success: true, + data: ServerJob.fromGraphQL( + response.parsedData!.backup.restoreBackup.job!, + ), + ); + } catch (e) { + print(e); + result = GenericResult( + success: false, + data: null, + message: e.toString(), + ); + } + + return result; + } + + Future> forgetSnapshot( + final String snapshotId, + ) async { + QueryResult response; + GenericResult? result; + + try { + final GraphQLClient client = await getClient(); + final variables = Variables$Mutation$ForgetSnapshot( + snapshotId: snapshotId, + ); + final options = Options$Mutation$ForgetSnapshot(variables: variables); + response = await client.mutate$ForgetSnapshot(options); + if (response.hasException) { + final message = response.exception.toString(); + print(message); + result = GenericResult( + success: false, + data: null, + message: message, + ); + } + result = GenericResult( + success: true, + data: response.parsedData!.backup.forgetSnapshot.success, + ); + } catch (e) { + print(e); + result = GenericResult( + success: false, + data: null, + message: e.toString(), + ); + } + + return result; + } +} diff --git a/lib/logic/api_maps/graphql_maps/server_api/jobs_api.dart b/lib/logic/api_maps/graphql_maps/server_api/jobs_api.dart new file mode 100644 index 00000000..8ed73a5d --- /dev/null +++ b/lib/logic/api_maps/graphql_maps/server_api/jobs_api.dart @@ -0,0 +1,47 @@ +part of 'server_api.dart'; + +mixin JobsApi on GraphQLApiMap { + Future> getServerJobs() async { + QueryResult response; + List jobsList = []; + + try { + final GraphQLClient client = await getClient(); + response = await client.query$GetApiJobs(); + if (response.hasException) { + print(response.exception.toString()); + } + jobsList = jobsList = response.parsedData?.jobs.getJobs + .map((final job) => ServerJob.fromGraphQL(job)) + .toList() ?? + []; + } catch (e) { + print(e); + } + + return jobsList; + } + + Future> removeApiJob(final String uid) async { + try { + final GraphQLClient client = await getClient(); + final variables = Variables$Mutation$RemoveJob(jobId: uid); + final mutation = Options$Mutation$RemoveJob(variables: variables); + final response = await client.mutate$RemoveJob(mutation); + return GenericResult( + data: response.parsedData?.removeJob.success ?? false, + success: true, + code: response.parsedData?.removeJob.code ?? 0, + message: response.parsedData?.removeJob.message, + ); + } catch (e) { + print(e); + return GenericResult( + data: false, + success: false, + code: 0, + message: e.toString(), + ); + } + } +} diff --git a/lib/logic/api_maps/graphql_maps/server_api/server_actions_api.dart b/lib/logic/api_maps/graphql_maps/server_api/server_actions_api.dart new file mode 100644 index 00000000..05269d33 --- /dev/null +++ b/lib/logic/api_maps/graphql_maps/server_api/server_actions_api.dart @@ -0,0 +1,72 @@ +part of 'server_api.dart'; + +mixin ServerActionsApi on GraphQLApiMap { + Future _commonBoolRequest(final Function graphQLMethod) async { + QueryResult response; + bool result = false; + + try { + response = await graphQLMethod(); + if (response.hasException) { + print(response.exception.toString()); + result = false; + } else { + result = true; + } + } catch (e) { + print(e); + } + + return result; + } + + Future> reboot() async { + DateTime? time; + try { + final GraphQLClient client = await getClient(); + final response = await client.mutate$RebootSystem(); + if (response.hasException) { + print(response.exception.toString()); + } + if (response.parsedData!.rebootSystem.success) { + time = DateTime.now(); + } + } catch (e) { + print(e); + return GenericResult(data: time, success: false); + } + + return GenericResult(data: time, success: true); + } + + Future pullConfigurationUpdate() async { + try { + final GraphQLClient client = await getClient(); + return await _commonBoolRequest( + () async => client.mutate$PullRepositoryChanges(), + ); + } catch (e) { + return false; + } + } + + Future upgrade() async { + try { + final GraphQLClient client = await getClient(); + return _commonBoolRequest( + () async => client.mutate$RunSystemUpgrade(), + ); + } catch (e) { + return false; + } + } + + Future apply() async { + try { + final GraphQLClient client = await getClient(); + await client.mutate$RunSystemRebuild(); + } catch (e) { + print(e); + } + } +} diff --git a/lib/logic/api_maps/graphql_maps/server_api/server_api.dart b/lib/logic/api_maps/graphql_maps/server_api/server_api.dart new file mode 100644 index 00000000..bc28a681 --- /dev/null +++ b/lib/logic/api_maps/graphql_maps/server_api/server_api.dart @@ -0,0 +1,522 @@ +import 'package:graphql/client.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/api_maps/generic_result.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/graphql_api_map.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/backups.graphql.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/disk_volumes.graphql.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/schema.graphql.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server_api.graphql.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server_settings.graphql.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/services.graphql.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/users.graphql.dart'; +import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart'; +import 'package:selfprivacy/logic/models/hive/server_details.dart'; +import 'package:selfprivacy/logic/models/hive/server_domain.dart'; +import 'package:selfprivacy/logic/models/hive/user.dart'; +import 'package:selfprivacy/logic/models/initialize_repository_input.dart'; +import 'package:selfprivacy/logic/models/json/api_token.dart'; +import 'package:selfprivacy/logic/models/backup.dart'; +import 'package:selfprivacy/logic/models/json/device_token.dart'; +import 'package:selfprivacy/logic/models/json/dns_records.dart'; +import 'package:selfprivacy/logic/models/json/recovery_token_status.dart'; +import 'package:selfprivacy/logic/models/json/server_disk_volume.dart'; +import 'package:selfprivacy/logic/models/json/server_job.dart'; +import 'package:selfprivacy/logic/models/service.dart'; +import 'package:selfprivacy/logic/models/ssh_settings.dart'; +import 'package:selfprivacy/logic/models/system_settings.dart'; + +export 'package:selfprivacy/logic/api_maps/generic_result.dart'; + +part 'jobs_api.dart'; +part 'server_actions_api.dart'; +part 'services_api.dart'; +part 'users_api.dart'; +part 'volume_api.dart'; +part 'backups_api.dart'; + +class ServerApi extends GraphQLApiMap + with + VolumeApi, + JobsApi, + ServerActionsApi, + ServicesApi, + UsersApi, + BackupsApi { + ServerApi({ + this.hasLogger = false, + this.isWithToken = true, + this.customToken = '', + this.overrideDomain, + }); + + @override + bool hasLogger; + @override + bool isWithToken; + @override + String customToken; + @override + String? get rootAddress => + overrideDomain ?? getIt().serverDomain?.domainName; + String? overrideDomain; + + Future getApiVersion() async { + QueryResult response; + String? apiVersion; + + try { + final GraphQLClient client = await getClient(); + response = await client.query$GetApiVersion(); + if (response.hasException) { + print(response.exception.toString()); + } + apiVersion = response.data!['api']['version']; + } catch (e) { + print(e); + } + return apiVersion; + } + + Future getServerProviderType() async { + QueryResult response; + ServerProviderType providerType = ServerProviderType.unknown; + + try { + final GraphQLClient client = await getClient(); + response = await client.query$SystemServerProvider(); + if (response.hasException) { + print(response.exception.toString()); + } + providerType = ServerProviderType.fromGraphQL( + response.parsedData!.system.provider.provider, + ); + } catch (e) { + print(e); + } + return providerType; + } + + Future getDnsProviderType() async { + QueryResult response; + DnsProviderType providerType = DnsProviderType.unknown; + + try { + final GraphQLClient client = await getClient(); + response = await client.query$SystemDnsProvider(); + if (response.hasException) { + print(response.exception.toString()); + } + providerType = DnsProviderType.fromGraphQL( + response.parsedData!.system.domainInfo.provider, + ); + } catch (e) { + print(e); + } + return providerType; + } + + Future isUsingBinds() async { + QueryResult response; + bool usesBinds = false; + + try { + final GraphQLClient client = await getClient(); + response = await client.query$SystemIsUsingBinds(); + if (response.hasException) { + print(response.exception.toString()); + } + usesBinds = response.data!['system']['info']['usingBinds']; + } catch (e) { + print(e); + } + return usesBinds; + } + + Future switchService(final String uid, final bool needTurnOn) async { + try { + final GraphQLClient client = await getClient(); + if (needTurnOn) { + final variables = Variables$Mutation$EnableService(serviceId: uid); + final mutation = Options$Mutation$EnableService(variables: variables); + await client.mutate$EnableService(mutation); + } else { + final variables = Variables$Mutation$DisableService(serviceId: uid); + final mutation = Options$Mutation$DisableService(variables: variables); + await client.mutate$DisableService(mutation); + } + } catch (e) { + print(e); + } + } + + Future setAutoUpgradeSettings( + final AutoUpgradeSettings settings, + ) async { + try { + final GraphQLClient client = await getClient(); + final input = Input$AutoUpgradeSettingsInput( + allowReboot: settings.allowReboot, + enableAutoUpgrade: settings.enable, + ); + final variables = Variables$Mutation$ChangeAutoUpgradeSettings( + settings: input, + ); + final mutation = Options$Mutation$ChangeAutoUpgradeSettings( + variables: variables, + ); + await client.mutate$ChangeAutoUpgradeSettings(mutation); + } catch (e) { + print(e); + } + } + + Future setTimezone(final String timezone) async { + try { + final GraphQLClient client = await getClient(); + final variables = Variables$Mutation$ChangeTimezone( + timezone: timezone, + ); + final mutation = Options$Mutation$ChangeTimezone( + variables: variables, + ); + await client.mutate$ChangeTimezone(mutation); + } catch (e) { + print(e); + } + } + + Future getSystemSettings() async { + QueryResult response; + SystemSettings settings = SystemSettings( + autoUpgradeSettings: AutoUpgradeSettings( + allowReboot: false, + enable: false, + ), + sshSettings: SshSettings( + enable: false, + passwordAuthentication: false, + ), + timezone: 'Unknown', + ); + + try { + final GraphQLClient client = await getClient(); + response = await client.query$SystemSettings(); + if (response.hasException) { + print(response.exception.toString()); + } + settings = SystemSettings.fromGraphQL(response.parsedData!.system); + } catch (e) { + print(e); + } + + return settings; + } + + Future> getRecoveryTokenStatus() async { + RecoveryKeyStatus? key; + QueryResult response; + String? error; + + try { + final GraphQLClient client = await getClient(); + response = await client.query$RecoveryKey(); + if (response.hasException) { + print(response.exception.toString()); + error = response.exception.toString(); + } + key = RecoveryKeyStatus.fromGraphQL(response.parsedData!.api.recoveryKey); + } catch (e) { + print(e); + } + + return GenericResult( + success: error == null, + data: key, + message: error, + ); + } + + Future> generateRecoveryToken( + final DateTime? expirationDate, + final int? numberOfUses, + ) async { + GenericResult key; + QueryResult response; + + try { + final GraphQLClient client = await getClient(); + + final input = Input$RecoveryKeyLimitsInput( + expirationDate: expirationDate, + uses: numberOfUses, + ); + final variables = Variables$Mutation$GetNewRecoveryApiKey( + limits: input, + ); + final mutation = Options$Mutation$GetNewRecoveryApiKey( + variables: variables, + ); + response = await client.mutate$GetNewRecoveryApiKey( + mutation, + ); + if (response.hasException) { + print(response.exception.toString()); + key = GenericResult( + success: false, + data: '', + message: response.exception.toString(), + ); + } + key = GenericResult( + success: true, + data: response.parsedData!.getNewRecoveryApiKey.key!, + ); + } catch (e) { + print(e); + key = GenericResult( + success: false, + data: '', + message: e.toString(), + ); + } + + return key; + } + + Future> getDnsRecords() async { + List records = []; + QueryResult response; + + try { + final GraphQLClient client = await getClient(); + response = await client.query$DomainInfo(); + if (response.hasException) { + print(response.exception.toString()); + } + records = response.parsedData!.system.domainInfo.requiredDnsRecords + .map( + ( + final Fragment$fragmentDnsRecords record, + ) => + DnsRecord.fromGraphQL(record), + ) + .toList(); + } catch (e) { + print(e); + } + + return records; + } + + Future>> getApiTokens() async { + GenericResult> tokens; + QueryResult response; + + try { + final GraphQLClient client = await getClient(); + response = await client.query$GetApiTokens(); + if (response.hasException) { + final message = response.exception.toString(); + print(message); + tokens = GenericResult>( + success: false, + data: [], + message: message, + ); + } + final List parsed = response.parsedData!.api.devices + .map( + ( + final Query$GetApiTokens$api$devices device, + ) => + ApiToken.fromGraphQL(device), + ) + .toList(); + tokens = GenericResult>( + success: true, + data: parsed, + ); + } catch (e) { + print(e); + tokens = GenericResult>( + success: false, + data: [], + message: e.toString(), + ); + } + + return tokens; + } + + Future> deleteApiToken(final String name) async { + GenericResult returnable; + QueryResult response; + + try { + final GraphQLClient client = await getClient(); + + final variables = Variables$Mutation$DeleteDeviceApiToken( + device: name, + ); + final mutation = Options$Mutation$DeleteDeviceApiToken( + variables: variables, + ); + response = await client.mutate$DeleteDeviceApiToken( + mutation, + ); + if (response.hasException) { + print(response.exception.toString()); + returnable = GenericResult( + success: false, + data: null, + message: response.exception.toString(), + ); + } + returnable = GenericResult( + success: true, + data: null, + ); + } catch (e) { + print(e); + returnable = GenericResult( + success: false, + data: null, + message: e.toString(), + ); + } + + return returnable; + } + + Future> createDeviceToken() async { + GenericResult token; + QueryResult response; + + try { + final GraphQLClient client = await getClient(); + + final mutation = Options$Mutation$GetNewDeviceApiKey(); + response = await client.mutate$GetNewDeviceApiKey( + mutation, + ); + if (response.hasException) { + print(response.exception.toString()); + token = GenericResult( + success: false, + data: '', + message: response.exception.toString(), + ); + } + token = GenericResult( + success: true, + data: response.parsedData!.getNewDeviceApiKey.key!, + ); + } catch (e) { + print(e); + token = GenericResult( + success: false, + data: '', + message: e.toString(), + ); + } + + return token; + } + + Future isHttpServerWorking() async => (await getApiVersion()) != null; + + Future> authorizeDevice( + final DeviceToken deviceToken, + ) async { + GenericResult token; + QueryResult response; + + try { + final GraphQLClient client = await getClient(); + + final input = Input$UseNewDeviceKeyInput( + deviceName: deviceToken.device, + key: deviceToken.token, + ); + + final variables = Variables$Mutation$AuthorizeWithNewDeviceApiKey( + input: input, + ); + final mutation = Options$Mutation$AuthorizeWithNewDeviceApiKey( + variables: variables, + ); + response = await client.mutate$AuthorizeWithNewDeviceApiKey( + mutation, + ); + if (response.hasException) { + print(response.exception.toString()); + token = GenericResult( + success: false, + data: '', + message: response.exception.toString(), + ); + } + token = GenericResult( + success: true, + data: response.parsedData!.authorizeWithNewDeviceApiKey.token!, + ); + } catch (e) { + print(e); + token = GenericResult( + success: false, + data: '', + message: e.toString(), + ); + } + + return token; + } + + Future> useRecoveryToken( + final DeviceToken deviceToken, + ) async { + GenericResult token; + QueryResult response; + + try { + final GraphQLClient client = await getClient(); + + final input = Input$UseRecoveryKeyInput( + deviceName: deviceToken.device, + key: deviceToken.token, + ); + + final variables = Variables$Mutation$UseRecoveryApiKey( + input: input, + ); + final mutation = Options$Mutation$UseRecoveryApiKey( + variables: variables, + ); + response = await client.mutate$UseRecoveryApiKey( + mutation, + ); + if (response.hasException) { + print(response.exception.toString()); + token = GenericResult( + success: false, + data: '', + message: response.exception.toString(), + ); + } + token = GenericResult( + success: true, + data: response.parsedData!.useRecoveryApiKey.token!, + ); + } catch (e) { + print(e); + token = GenericResult( + success: false, + data: '', + message: e.toString(), + ); + } + + return token; + } +} diff --git a/lib/logic/api_maps/graphql_maps/server_api/services_api.dart b/lib/logic/api_maps/graphql_maps/server_api/services_api.dart new file mode 100644 index 00000000..1632533b --- /dev/null +++ b/lib/logic/api_maps/graphql_maps/server_api/services_api.dart @@ -0,0 +1,177 @@ +part of 'server_api.dart'; + +mixin ServicesApi on GraphQLApiMap { + Future> getAllServices() async { + QueryResult response; + List services = []; + try { + final GraphQLClient client = await getClient(); + response = await client.query$AllServices(); + if (response.hasException) { + print(response.exception.toString()); + } + services = response.parsedData?.services.allServices + .map((final service) => Service.fromGraphQL(service)) + .toList() ?? + []; + } catch (e) { + print(e); + } + return services; + } + + Future> enableService( + final String serviceId, + ) async { + try { + final GraphQLClient client = await getClient(); + final variables = Variables$Mutation$EnableService(serviceId: serviceId); + final mutation = Options$Mutation$EnableService(variables: variables); + final response = await client.mutate$EnableService(mutation); + return GenericResult( + data: response.parsedData?.enableService.success ?? false, + success: true, + code: response.parsedData?.enableService.code ?? 0, + message: response.parsedData?.enableService.message, + ); + } catch (e) { + print(e); + return GenericResult( + data: false, + success: false, + code: 0, + message: e.toString(), + ); + } + } + + Future> disableService( + final String serviceId, + ) async { + try { + final GraphQLClient client = await getClient(); + final variables = Variables$Mutation$DisableService(serviceId: serviceId); + final mutation = Options$Mutation$DisableService(variables: variables); + final response = await client.mutate$DisableService(mutation); + return GenericResult( + data: null, + success: response.parsedData?.disableService.success ?? false, + code: response.parsedData?.disableService.code ?? 0, + message: response.parsedData?.disableService.message, + ); + } catch (e) { + print(e); + return GenericResult( + data: null, + success: false, + code: 0, + message: e.toString(), + ); + } + } + + Future> stopService( + final String serviceId, + ) async { + try { + final GraphQLClient client = await getClient(); + final variables = Variables$Mutation$StopService(serviceId: serviceId); + final mutation = Options$Mutation$StopService(variables: variables); + final response = await client.mutate$StopService(mutation); + return GenericResult( + data: response.parsedData?.stopService.success ?? false, + success: true, + code: response.parsedData?.stopService.code ?? 0, + message: response.parsedData?.stopService.message, + ); + } catch (e) { + print(e); + return GenericResult( + data: false, + success: false, + code: 0, + message: e.toString(), + ); + } + } + + Future startService(final String serviceId) async { + try { + final GraphQLClient client = await getClient(); + final variables = Variables$Mutation$StartService(serviceId: serviceId); + final mutation = Options$Mutation$StartService(variables: variables); + final response = await client.mutate$StartService(mutation); + return GenericResult( + data: null, + success: response.parsedData?.startService.success ?? false, + code: response.parsedData?.startService.code ?? 0, + message: response.parsedData?.startService.message, + ); + } catch (e) { + print(e); + return GenericResult( + data: null, + success: false, + code: 0, + message: e.toString(), + ); + } + } + + Future> restartService( + final String serviceId, + ) async { + try { + final GraphQLClient client = await getClient(); + final variables = Variables$Mutation$RestartService(serviceId: serviceId); + final mutation = Options$Mutation$RestartService(variables: variables); + final response = await client.mutate$RestartService(mutation); + return GenericResult( + data: response.parsedData?.restartService.success ?? false, + success: true, + code: response.parsedData?.restartService.code ?? 0, + message: response.parsedData?.restartService.message, + ); + } catch (e) { + print(e); + return GenericResult( + data: false, + success: false, + code: 0, + message: e.toString(), + ); + } + } + + Future> moveService( + final String serviceId, + final String destination, + ) async { + try { + final GraphQLClient client = await getClient(); + final variables = Variables$Mutation$MoveService( + input: Input$MoveServiceInput( + serviceId: serviceId, + location: destination, + ), + ); + final mutation = Options$Mutation$MoveService(variables: variables); + final response = await client.mutate$MoveService(mutation); + final jobJson = response.parsedData?.moveService.job?.toJson(); + return GenericResult( + success: true, + code: response.parsedData?.moveService.code ?? 0, + message: response.parsedData?.moveService.message, + data: jobJson != null ? ServerJob.fromJson(jobJson) : null, + ); + } catch (e) { + print(e); + return GenericResult( + success: false, + code: 0, + message: e.toString(), + data: null, + ); + } + } +} diff --git a/lib/logic/api_maps/graphql_maps/server_api/users_api.dart b/lib/logic/api_maps/graphql_maps/server_api/users_api.dart new file mode 100644 index 00000000..11327290 --- /dev/null +++ b/lib/logic/api_maps/graphql_maps/server_api/users_api.dart @@ -0,0 +1,198 @@ +part of 'server_api.dart'; + +mixin UsersApi on GraphQLApiMap { + Future> getAllUsers() async { + QueryResult response; + List users = []; + try { + final GraphQLClient client = await getClient(); + response = await client.query$AllUsers(); + if (response.hasException) { + print(response.exception.toString()); + } + users = response.parsedData?.users.allUsers + .map((final user) => User.fromGraphQL(user)) + .toList() ?? + []; + final rootUser = response.parsedData?.users.rootUser; + if (rootUser != null) { + users.add(User.fromGraphQL(rootUser)); + } + } catch (e) { + print(e); + } + return users; + } + + Future getUser(final String login) async { + QueryResult response; + User? user; + try { + final GraphQLClient client = await getClient(); + final variables = Variables$Query$GetUser(username: login); + response = await client + .query$GetUser(Options$Query$GetUser(variables: variables)); + if (response.hasException) { + print(response.exception.toString()); + } + final responseUser = response.parsedData?.users.getUser; + if (responseUser != null) { + user = User.fromGraphQL(responseUser); + } + } catch (e) { + print(e); + } + return user; + } + + Future> createUser( + final String username, + final String password, + ) async { + try { + final GraphQLClient client = await getClient(); + final variables = Variables$Mutation$CreateUser( + user: Input$UserMutationInput(username: username, password: password), + ); + final mutation = Options$Mutation$CreateUser(variables: variables); + final response = await client.mutate$CreateUser(mutation); + return GenericResult( + success: true, + code: response.parsedData?.createUser.code ?? 500, + message: response.parsedData?.createUser.message, + data: response.parsedData?.createUser.user != null + ? User.fromGraphQL(response.parsedData!.createUser.user!) + : null, + ); + } catch (e) { + print(e); + return GenericResult( + success: false, + code: 0, + message: e.toString(), + data: null, + ); + } + } + + Future> deleteUser( + final String username, + ) async { + try { + final GraphQLClient client = await getClient(); + final variables = Variables$Mutation$DeleteUser(username: username); + final mutation = Options$Mutation$DeleteUser(variables: variables); + final response = await client.mutate$DeleteUser(mutation); + return GenericResult( + data: response.parsedData?.deleteUser.success ?? false, + success: true, + code: response.parsedData?.deleteUser.code ?? 500, + message: response.parsedData?.deleteUser.message, + ); + } catch (e) { + print(e); + return GenericResult( + data: false, + success: false, + code: 500, + message: e.toString(), + ); + } + } + + Future> updateUser( + final String username, + final String password, + ) async { + try { + final GraphQLClient client = await getClient(); + final variables = Variables$Mutation$UpdateUser( + user: Input$UserMutationInput(username: username, password: password), + ); + final mutation = Options$Mutation$UpdateUser(variables: variables); + final response = await client.mutate$UpdateUser(mutation); + return GenericResult( + success: true, + code: response.parsedData?.updateUser.code ?? 500, + message: response.parsedData?.updateUser.message, + data: response.parsedData?.updateUser.user != null + ? User.fromGraphQL(response.parsedData!.updateUser.user!) + : null, + ); + } catch (e) { + print(e); + return GenericResult( + data: null, + success: false, + code: 0, + message: e.toString(), + ); + } + } + + Future> addSshKey( + final String username, + final String sshKey, + ) async { + try { + final GraphQLClient client = await getClient(); + final variables = Variables$Mutation$AddSshKey( + sshInput: Input$SshMutationInput( + username: username, + sshKey: sshKey, + ), + ); + final mutation = Options$Mutation$AddSshKey(variables: variables); + final response = await client.mutate$AddSshKey(mutation); + return GenericResult( + success: true, + code: response.parsedData?.addSshKey.code ?? 500, + message: response.parsedData?.addSshKey.message, + data: response.parsedData?.addSshKey.user != null + ? User.fromGraphQL(response.parsedData!.addSshKey.user!) + : null, + ); + } catch (e) { + print(e); + return GenericResult( + data: null, + success: false, + code: 0, + message: e.toString(), + ); + } + } + + Future> removeSshKey( + final String username, + final String sshKey, + ) async { + try { + final GraphQLClient client = await getClient(); + final variables = Variables$Mutation$RemoveSshKey( + sshInput: Input$SshMutationInput( + username: username, + sshKey: sshKey, + ), + ); + final mutation = Options$Mutation$RemoveSshKey(variables: variables); + final response = await client.mutate$RemoveSshKey(mutation); + return GenericResult( + success: response.parsedData?.removeSshKey.success ?? false, + code: response.parsedData?.removeSshKey.code ?? 500, + message: response.parsedData?.removeSshKey.message, + data: response.parsedData?.removeSshKey.user != null + ? User.fromGraphQL(response.parsedData!.removeSshKey.user!) + : null, + ); + } catch (e) { + print(e); + return GenericResult( + data: null, + success: false, + code: 0, + message: e.toString(), + ); + } + } +} diff --git a/lib/logic/api_maps/graphql_maps/server_api/volume_api.dart b/lib/logic/api_maps/graphql_maps/server_api/volume_api.dart new file mode 100644 index 00000000..a7d23ba8 --- /dev/null +++ b/lib/logic/api_maps/graphql_maps/server_api/volume_api.dart @@ -0,0 +1,99 @@ +part of 'server_api.dart'; + +mixin VolumeApi on GraphQLApiMap { + Future> getServerDiskVolumes() async { + QueryResult response; + List volumes = []; + + try { + final GraphQLClient client = await getClient(); + response = await client.query$GetServerDiskVolumes(); + if (response.hasException) { + print(response.exception.toString()); + } + volumes = response.data!['storage']['volumes'] + .map((final e) => ServerDiskVolume.fromJson(e)) + .toList(); + } catch (e) { + print(e); + } + + return volumes; + } + + Future mountVolume(final String volumeName) async { + try { + final GraphQLClient client = await getClient(); + final variables = Variables$Mutation$MountVolume(name: volumeName); + final mountVolumeMutation = + Options$Mutation$MountVolume(variables: variables); + await client.mutate$MountVolume(mountVolumeMutation); + } catch (e) { + print(e); + } + } + + Future unmountVolume(final String volumeName) async { + try { + final GraphQLClient client = await getClient(); + final variables = Variables$Mutation$UnmountVolume(name: volumeName); + final unmountVolumeMutation = + Options$Mutation$UnmountVolume(variables: variables); + await client.mutate$UnmountVolume(unmountVolumeMutation); + } catch (e) { + print(e); + } + } + + Future resizeVolume(final String volumeName) async { + try { + final GraphQLClient client = await getClient(); + final variables = Variables$Mutation$ResizeVolume(name: volumeName); + final resizeVolumeMutation = + Options$Mutation$ResizeVolume(variables: variables); + await client.mutate$ResizeVolume(resizeVolumeMutation); + } catch (e) { + print(e); + } + } + + Future> migrateToBinds( + final Map serviceToDisk, + ) async { + GenericResult? mutation; + + try { + final GraphQLClient client = await getClient(); + final input = Input$MigrateToBindsInput( + bitwardenBlockDevice: serviceToDisk['bitwarden']!, + emailBlockDevice: serviceToDisk['mailserver']!, + giteaBlockDevice: serviceToDisk['gitea']!, + nextcloudBlockDevice: serviceToDisk['nextcloud']!, + pleromaBlockDevice: serviceToDisk['pleroma']!, + ); + final variables = Variables$Mutation$MigrateToBinds(input: input); + final migrateMutation = + Options$Mutation$MigrateToBinds(variables: variables); + final QueryResult result = + await client.mutate$MigrateToBinds( + migrateMutation, + ); + mutation = mutation = GenericResult( + success: true, + code: result.parsedData!.migrateToBinds.code, + message: result.parsedData!.migrateToBinds.message, + data: result.parsedData!.migrateToBinds.job?.uid, + ); + } catch (e) { + print(e); + mutation = GenericResult( + success: false, + code: 0, + message: e.toString(), + data: null, + ); + } + + return mutation; + } +} diff --git a/lib/logic/api_maps/hetzner.dart b/lib/logic/api_maps/hetzner.dart deleted file mode 100644 index 4de4f36f..00000000 --- a/lib/logic/api_maps/hetzner.dart +++ /dev/null @@ -1,277 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:dio/dio.dart'; -import 'package:selfprivacy/config/get_it_config.dart'; -import 'package:selfprivacy/logic/api_maps/api_map.dart'; -import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart'; -import 'package:selfprivacy/logic/models/hive/server_details.dart'; -import 'package:selfprivacy/logic/models/hive/user.dart'; -import 'package:selfprivacy/utils/password_generator.dart'; - -class HetznerApi extends ApiMap { - HetznerApi({this.hasLogger = false, this.isWithToken = true}); - @override - bool hasLogger; - @override - bool isWithToken; - - @override - BaseOptions get options { - final BaseOptions options = BaseOptions(baseUrl: rootAddress); - if (isWithToken) { - final String? token = getIt().hetznerKey; - assert(token != null); - options.headers = {'Authorization': 'Bearer $token'}; - } - - if (validateStatus != null) { - options.validateStatus = validateStatus!; - } - - return options; - } - - @override - String rootAddress = 'https://api.hetzner.cloud/v1'; - - Future isValid(final String token) async { - validateStatus = (final int? status) => - status == HttpStatus.ok || status == HttpStatus.unauthorized; - final Dio client = await getClient(); - final Response response = await client.get( - '/servers', - options: Options( - headers: {'Authorization': 'Bearer $token'}, - ), - ); - close(client); - - if (response.statusCode == HttpStatus.ok) { - return true; - } else if (response.statusCode == HttpStatus.unauthorized) { - return false; - } else { - throw Exception('code: ${response.statusCode}'); - } - } - - Future createVolume() async { - final Dio client = await getClient(); - final Response dbCreateResponse = await client.post( - '/volumes', - data: { - 'size': 10, - 'name': StringGenerators.dbStorageName(), - 'labels': {'labelkey': 'value'}, - 'location': 'fsn1', - 'automount': false, - 'format': 'ext4' - }, - ); - final dbId = dbCreateResponse.data['volume']['id']; - return ServerVolume( - id: dbId, - name: dbCreateResponse.data['volume']['name'], - ); - } - - Future createServer({ - required final String cloudFlareKey, - required final User rootUser, - required final String domainName, - required final ServerVolume dataBase, - }) async { - final Dio client = await getClient(); - - final String dbPassword = StringGenerators.dbPassword(); - final int dbId = dataBase.id; - - final String apiToken = StringGenerators.apiToken(); - - final String hostname = getHostnameFromDomain(domainName); - - final String base64Password = - base64.encode(utf8.encode(rootUser.password ?? 'PASS')); - - print('hostname: $hostname'); - - /// add ssh key when you need it: e.g. "ssh_keys":["kherel"] - /// check the branch name, it could be "development" or "master". - /// - final String userdataString = - "#cloud-config\nruncmd:\n- curl https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-21.05 DOMAIN='$domainName' LUSER='${rootUser.login}' ENCODED_PASSWORD='$base64Password' CF_TOKEN=$cloudFlareKey DB_PASSWORD=$dbPassword API_TOKEN=$apiToken HOSTNAME=$hostname bash 2>&1 | tee /tmp/infect.log"; - print(userdataString); - - final Map data = { - 'name': hostname, - 'server_type': 'cx11', - 'start_after_create': false, - 'image': 'ubuntu-20.04', - 'volumes': [dbId], - 'networks': [], - 'user_data': userdataString, - 'labels': {}, - 'automount': true, - 'location': 'fsn1' - }; - print('Decoded data: $data'); - - ServerHostingDetails? serverDetails; - - try { - final Response serverCreateResponse = await client.post( - '/servers', - data: data, - ); - print(serverCreateResponse.data); - serverDetails = ServerHostingDetails( - id: serverCreateResponse.data['server']['id'], - ip4: serverCreateResponse.data['server']['public_net']['ipv4']['ip'], - createTime: DateTime.now(), - volume: dataBase, - apiToken: apiToken, - provider: ServerProvider.hetzner, - ); - } on DioError catch (e) { - print(e); - rethrow; - } catch (e) { - print(e); - } finally { - client.close(); - } - - return serverDetails; - } - - static String getHostnameFromDomain(final String domain) { - // Replace all non-alphanumeric characters with an underscore - String hostname = - domain.split('.')[0].replaceAll(RegExp(r'[^a-zA-Z0-9]'), '-'); - if (hostname.endsWith('-')) { - hostname = hostname.substring(0, hostname.length - 1); - } - if (hostname.startsWith('-')) { - hostname = hostname.substring(1); - } - if (hostname.isEmpty) { - hostname = 'selfprivacy-server'; - } - - return hostname; - } - - Future deleteSelfprivacyServerAndAllVolumes({ - required final String domainName, - }) async { - final Dio client = await getClient(); - - final String hostname = getHostnameFromDomain(domainName); - - final Response serversReponse = await client.get('/servers'); - final List servers = serversReponse.data['servers']; - final Map server = servers.firstWhere((final el) => el['name'] == hostname); - final List volumes = server['volumes']; - final List laterFutures = []; - - for (final volumeId in volumes) { - await client.post('/volumes/$volumeId/actions/detach'); - } - await Future.delayed(const Duration(seconds: 10)); - - for (final volumeId in volumes) { - laterFutures.add(client.delete('/volumes/$volumeId')); - } - laterFutures.add(client.delete('/servers/${server['id']}')); - - await Future.wait(laterFutures); - close(client); - } - - Future reset() async { - final ServerHostingDetails server = getIt().serverDetails!; - - final Dio client = await getClient(); - await client.post('/servers/${server.id}/actions/reset'); - close(client); - - return server.copyWith(startTime: DateTime.now()); - } - - Future powerOn() async { - final ServerHostingDetails server = getIt().serverDetails!; - - final Dio client = await getClient(); - await client.post('/servers/${server.id}/actions/poweron'); - close(client); - - return server.copyWith(startTime: DateTime.now()); - } - - Future> getMetrics( - final DateTime start, - final DateTime end, - final String type, - ) async { - final ServerHostingDetails? hetznerServer = - getIt().serverDetails; - final Dio client = await getClient(); - - final Map queryParameters = { - 'start': start.toUtc().toIso8601String(), - 'end': end.toUtc().toIso8601String(), - 'type': type - }; - final Response res = await client.get( - '/servers/${hetznerServer!.id}/metrics', - queryParameters: queryParameters, - ); - close(client); - return res.data; - } - - Future getInfo() async { - final ServerHostingDetails? hetznerServer = - getIt().serverDetails; - final Dio client = await getClient(); - final Response response = await client.get('/servers/${hetznerServer!.id}'); - close(client); - - return HetznerServerInfo.fromJson(response.data!['server']); - } - - Future> getServers() async { - final Dio client = await getClient(); - final Response response = await client.get('/servers'); - close(client); - - return (response.data!['servers'] as List) - // ignore: unnecessary_lambdas - .map((final e) => HetznerServerInfo.fromJson(e)) - .toList(); - } - - Future createReverseDns({ - required final String ip4, - required final String domainName, - }) async { - final ServerHostingDetails? hetznerServer = - getIt().serverDetails; - - final Dio client = await getClient(); - try { - await client.post( - '/servers/${hetznerServer!.id}/actions/change_dns_ptr', - data: { - 'ip': ip4, - 'dns_ptr': domainName, - }, - ); - } catch (e) { - print(e); - } finally { - close(client); - } - } -} diff --git a/lib/logic/api_maps/backblaze.dart b/lib/logic/api_maps/rest_maps/backblaze.dart similarity index 61% rename from lib/logic/api_maps/backblaze.dart rename to lib/logic/api_maps/rest_maps/backblaze.dart index 8d827e78..d908cd26 100644 --- a/lib/logic/api_maps/backblaze.dart +++ b/lib/logic/api_maps/rest_maps/backblaze.dart @@ -2,8 +2,13 @@ import 'dart:io'; import 'package:dio/dio.dart'; import 'package:selfprivacy/config/get_it_config.dart'; -import 'package:selfprivacy/logic/api_maps/api_map.dart'; -import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart'; +import 'package:selfprivacy/logic/models/backup.dart'; +import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart'; +import 'package:selfprivacy/logic/models/hive/backups_credential.dart'; +import 'package:selfprivacy/logic/api_maps/generic_result.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/rest_api_map.dart'; + +export 'package:selfprivacy/logic/api_maps/generic_result.dart'; class BackblazeApiAuth { BackblazeApiAuth({required this.authorizationToken, required this.apiUrl}); @@ -22,14 +27,18 @@ class BackblazeApplicationKey { final String applicationKey; } -class BackblazeApi extends ApiMap { +class BackblazeApi extends RestApiMap { BackblazeApi({this.hasLogger = false, this.isWithToken = true}); @override BaseOptions get options { - final BaseOptions options = BaseOptions(baseUrl: rootAddress); + final BaseOptions options = BaseOptions( + baseUrl: rootAddress, + contentType: Headers.jsonContentType, + responseType: ResponseType.json, + ); if (isWithToken) { - final BackblazeCredential? backblazeCredential = + final BackupsCredential? backblazeCredential = getIt().backblazeCredential; final String token = backblazeCredential!.applicationKey; options.headers = {'Authorization': 'Basic $token'}; @@ -49,7 +58,7 @@ class BackblazeApi extends ApiMap { Future getAuthorizationToken() async { final Dio client = await getClient(); - final BackblazeCredential? backblazeCredential = + final BackupsCredential? backblazeCredential = getIt().backblazeCredential; if (backblazeCredential == null) { throw Exception('Backblaze credential is null'); @@ -71,34 +80,50 @@ class BackblazeApi extends ApiMap { ); } - Future isValid(final String encodedApiKey) async { + Future> isApiTokenValid( + final String encodedApiKey, + ) async { final Dio client = await getClient(); + bool isTokenValid = false; try { final Response response = await client.get( 'b2_authorize_account', - options: Options(headers: {'Authorization': 'Basic $encodedApiKey'}), + options: Options( + followRedirects: false, + validateStatus: (final status) => + status != null && (status >= 200 || status == 401), + headers: {'Authorization': 'Basic $encodedApiKey'}, + ), ); if (response.statusCode == HttpStatus.ok) { - if (response.data['allowed']['capabilities'].contains('listBuckets')) { - return true; - } - return false; + isTokenValid = + response.data['allowed']['capabilities'].contains('listBuckets'); } else if (response.statusCode == HttpStatus.unauthorized) { - return false; + isTokenValid = false; } else { throw Exception('code: ${response.statusCode}'); } - } on DioError { - return false; + } on DioError catch (e) { + print(e); + return GenericResult( + data: false, + success: false, + message: e.toString(), + ); } finally { close(client); } + + return GenericResult( + data: isTokenValid, + success: true, + ); } // Create bucket Future createBucket(final String bucketName) async { final BackblazeApiAuth auth = await getAuthorizationToken(); - final BackblazeCredential? backblazeCredential = + final BackupsCredential? backblazeCredential = getIt().backblazeCredential; final Dio client = await getClient(); client.options.baseUrl = auth.apiUrl; @@ -156,6 +181,42 @@ class BackblazeApi extends ApiMap { } } + Future fetchBucket( + final BackupsCredential credentials, + final BackupConfiguration configuration, + ) async { + BackblazeBucket? bucket; + final BackblazeApiAuth auth = await getAuthorizationToken(); + final Dio client = await getClient(); + client.options.baseUrl = auth.apiUrl; + final Response response = await client.get( + '$apiPrefix/b2_list_buckets', + queryParameters: { + 'accountId': getIt().backblazeCredential!.keyId, + }, + options: Options( + headers: {'Authorization': auth.authorizationToken}, + ), + ); + close(client); + if (response.statusCode == HttpStatus.ok) { + for (final rawBucket in response.data['buckets']) { + if (rawBucket['bucketId'] == configuration.locationId) { + bucket = BackblazeBucket( + bucketId: rawBucket['bucketId'], + bucketName: rawBucket['bucketName'], + encryptionKey: configuration.encryptionKey, + applicationKeyId: '', + applicationKey: '', + ); + } + } + return bucket; + } else { + throw Exception('code: ${response.statusCode}'); + } + } + @override bool hasLogger; diff --git a/lib/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare_api.dart b/lib/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare_api.dart new file mode 100644 index 00000000..0ba88ee3 --- /dev/null +++ b/lib/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare_api.dart @@ -0,0 +1,222 @@ +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/api_maps/generic_result.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/rest_api_map.dart'; +import 'package:selfprivacy/logic/models/json/cloudflare_dns_info.dart'; + +class CloudflareApi extends RestApiMap { + CloudflareApi({ + this.hasLogger = false, + this.isWithToken = true, + this.customToken, + }); + @override + final bool hasLogger; + @override + final bool isWithToken; + + final String? customToken; + + @override + BaseOptions get options { + final BaseOptions options = BaseOptions( + baseUrl: rootAddress, + contentType: Headers.jsonContentType, + responseType: ResponseType.json, + ); + if (isWithToken) { + final String? token = getIt().dnsProviderKey; + assert(token != null); + options.headers = {'Authorization': 'Bearer $token'}; + } + + if (customToken != null) { + options.headers = {'Authorization': 'Bearer $customToken'}; + } + + if (validateStatus != null) { + options.validateStatus = validateStatus!; + } + return options; + } + + @override + String rootAddress = 'https://api.cloudflare.com/client/v4'; + + Future> isApiTokenValid(final String token) async { + bool isValid = false; + Response? response; + String message = ''; + final Dio client = await getClient(); + try { + response = await client.get( + '/user/tokens/verify', + options: Options( + followRedirects: false, + validateStatus: (final status) => + status != null && (status >= 200 || status == 401), + headers: {'Authorization': 'Bearer $token'}, + ), + ); + } catch (e) { + print(e); + isValid = false; + message = e.toString(); + } finally { + close(client); + } + + if (response == null) { + return GenericResult( + data: isValid, + success: false, + message: message, + ); + } + + if (response.statusCode == HttpStatus.ok) { + isValid = true; + } else if (response.statusCode == HttpStatus.unauthorized) { + isValid = false; + } else { + throw Exception('code: ${response.statusCode}'); + } + + return GenericResult( + data: isValid, + success: true, + message: response.statusMessage, + ); + } + + Future>> getZones() async { + final String url = '$rootAddress/zones'; + List domains = []; + + late final Response? response; + final Dio client = await getClient(); + try { + response = await client.get( + url, + queryParameters: {'per_page': 50}, + ); + domains = response.data['result']! + .map( + (final json) => CloudflareZone.fromJson(json), + ) + .toList(); + } catch (e) { + print(e); + return GenericResult( + success: false, + data: domains, + code: response?.statusCode, + message: response?.statusMessage, + ); + } finally { + close(client); + } + + return GenericResult( + success: true, + data: domains, + code: response.statusCode, + message: response.statusMessage, + ); + } + + Future> createMultipleDnsRecords({ + required final String zoneId, + required final List records, + }) async { + final List allCreateFutures = []; + + final Dio client = await getClient(); + try { + for (final CloudflareDnsRecord record in records) { + allCreateFutures.add( + client.post( + '/zones/$zoneId/dns_records', + data: record.toJson(), + ), + ); + } + await Future.wait(allCreateFutures); + } on DioError catch (e) { + print(e.message); + rethrow; + } catch (e) { + print(e); + return GenericResult( + success: false, + data: null, + message: e.toString(), + ); + } finally { + close(client); + } + + return GenericResult(success: true, data: null); + } + + Future> removeSimilarRecords({ + required final String zoneId, + required final List records, + }) async { + final String url = '/zones/$zoneId/dns_records'; + + final Dio client = await getClient(); + try { + final List allDeleteFutures = []; + + for (final record in records) { + allDeleteFutures.add( + client.delete('$url/${record.id}'), + ); + } + await Future.wait(allDeleteFutures); + } catch (e) { + print(e); + return GenericResult( + success: false, + data: null, + message: e.toString(), + ); + } finally { + close(client); + } + + return GenericResult(success: true, data: null); + } + + Future>> getDnsRecords({ + required final String zoneId, + }) async { + Response response; + List allRecords = []; + + final String url = '/zones/$zoneId/dns_records'; + final Dio client = await getClient(); + try { + response = await client.get(url); + allRecords = response.data['result']! + .map( + (final json) => CloudflareDnsRecord.fromJson(json), + ) + .toList(); + } catch (e) { + print(e); + return GenericResult( + data: [], + success: false, + message: e.toString(), + ); + } finally { + close(client); + } + + return GenericResult(data: allRecords, success: true); + } +} diff --git a/lib/logic/api_maps/rest_maps/dns_providers/desec/desec_api.dart b/lib/logic/api_maps/rest_maps/dns_providers/desec/desec_api.dart new file mode 100644 index 00000000..9856242d --- /dev/null +++ b/lib/logic/api_maps/rest_maps/dns_providers/desec/desec_api.dart @@ -0,0 +1,215 @@ +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/api_maps/generic_result.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/rest_api_map.dart'; +import 'package:selfprivacy/logic/models/json/desec_dns_info.dart'; + +class DesecApi extends RestApiMap { + DesecApi({ + this.hasLogger = false, + this.isWithToken = true, + this.customToken, + }); + @override + final bool hasLogger; + @override + final bool isWithToken; + + final String? customToken; + + @override + BaseOptions get options { + final BaseOptions options = BaseOptions( + baseUrl: rootAddress, + contentType: Headers.jsonContentType, + responseType: ResponseType.json, + ); + if (isWithToken) { + final String? token = getIt().dnsProviderKey; + assert(token != null); + options.headers = {'Authorization': 'Token $token'}; + } + + if (customToken != null) { + options.headers = {'Authorization': 'Token $customToken'}; + } + + if (validateStatus != null) { + options.validateStatus = validateStatus!; + } + return options; + } + + @override + String rootAddress = 'https://desec.io/api/v1/domains/'; + + Future> isApiTokenValid(final String token) async { + bool isValid = false; + Response? response; + String message = ''; + final Dio client = await getClient(); + try { + response = await client.get( + '', + options: Options( + followRedirects: false, + validateStatus: (final status) => + status != null && (status >= 200 || status == 401), + headers: {'Authorization': 'Token $token'}, + ), + ); + await Future.delayed(const Duration(seconds: 1)); + } catch (e) { + print(e); + isValid = false; + message = e.toString(); + } finally { + close(client); + } + + if (response == null) { + return GenericResult( + data: isValid, + success: false, + message: message, + ); + } + + if (response.statusCode == HttpStatus.ok) { + isValid = true; + } else if (response.statusCode == HttpStatus.unauthorized) { + isValid = false; + } else { + throw Exception('code: ${response.statusCode}'); + } + + return GenericResult( + data: isValid, + success: true, + message: response.statusMessage, + ); + } + + Future>> getDomains() async { + List domains = []; + + late final Response? response; + final Dio client = await getClient(); + try { + response = await client.get( + '', + ); + await Future.delayed(const Duration(seconds: 1)); + domains = response.data! + .map( + (final e) => DesecDomain.fromJson(e), + ) + .toList(); + } catch (e) { + print(e); + return GenericResult( + success: false, + data: domains, + code: response?.statusCode, + message: response?.statusMessage, + ); + } finally { + close(client); + } + + return GenericResult( + success: true, + data: domains, + code: response.statusCode, + message: response.statusMessage, + ); + } + + Future> createMultipleDnsRecords({ + required final String domainName, + required final List records, + }) async { + final String url = '/$domainName/rrsets/'; + + final Dio client = await getClient(); + try { + await client.post( + url, + data: records.map((final rec) => rec.toJson()).toList(), + ); + await Future.delayed(const Duration(seconds: 1)); + } catch (e) { + print(e); + return GenericResult( + success: false, + data: null, + message: e.toString(), + ); + } finally { + close(client); + } + + return GenericResult(success: true, data: null); + } + + Future> removeSimilarRecords({ + required final String domainName, + required final List records, + }) async { + final String url = '/$domainName/rrsets/'; + + final Dio client = await getClient(); + try { + await client.put( + url, + data: records.map((final rec) => rec.toJson()).toList(), + ); + await Future.delayed(const Duration(seconds: 1)); + } catch (e) { + print(e); + return GenericResult( + success: false, + data: null, + message: e.toString(), + ); + } finally { + close(client); + } + + return GenericResult(success: true, data: null); + } + + Future>> getDnsRecords( + final String domainName, + ) async { + Response? response; + List allRecords = []; + + final String url = '/$domainName/rrsets/'; + + final Dio client = await getClient(); + try { + response = await client.get(url); + await Future.delayed(const Duration(seconds: 1)); + allRecords = response.data! + .map( + (final e) => DesecDnsRecord.fromJson(e), + ) + .toList(); + } catch (e) { + print(e); + return GenericResult( + data: allRecords, + success: false, + message: e.toString(), + code: response?.statusCode, + ); + } finally { + close(client); + } + + return GenericResult(data: allRecords, success: true); + } +} diff --git a/lib/logic/api_maps/rest_maps/dns_providers/desired_dns_record.dart b/lib/logic/api_maps/rest_maps/dns_providers/desired_dns_record.dart new file mode 100644 index 00000000..4a64d49e --- /dev/null +++ b/lib/logic/api_maps/rest_maps/dns_providers/desired_dns_record.dart @@ -0,0 +1,44 @@ +enum DnsRecordsCategory { + services, + email, + other, +} + +class DesiredDnsRecord { + const DesiredDnsRecord({ + required this.name, + required this.content, + this.type = 'A', + this.description = '', + this.category = DnsRecordsCategory.services, + this.isSatisfied = false, + this.displayName, + }); + + final String name; + final String type; + final String content; + final String description; + final String? displayName; + final DnsRecordsCategory category; + final bool isSatisfied; + + DesiredDnsRecord copyWith({ + final String? name, + final String? type, + final String? content, + final String? description, + final String? displayName, + final DnsRecordsCategory? category, + final bool? isSatisfied, + }) => + DesiredDnsRecord( + name: name ?? this.name, + type: type ?? this.type, + content: content ?? this.content, + description: description ?? this.description, + category: category ?? this.category, + isSatisfied: isSatisfied ?? this.isSatisfied, + displayName: displayName ?? this.displayName, + ); +} diff --git a/lib/logic/api_maps/rest_maps/dns_providers/digital_ocean_dns/digital_ocean_dns_api.dart b/lib/logic/api_maps/rest_maps/dns_providers/digital_ocean_dns/digital_ocean_dns_api.dart new file mode 100644 index 00000000..e1b5c601 --- /dev/null +++ b/lib/logic/api_maps/rest_maps/dns_providers/digital_ocean_dns/digital_ocean_dns_api.dart @@ -0,0 +1,215 @@ +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/api_maps/generic_result.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/rest_api_map.dart'; +import 'package:selfprivacy/logic/models/json/digital_ocean_dns_info.dart'; + +class DigitalOceanDnsApi extends RestApiMap { + DigitalOceanDnsApi({ + this.hasLogger = false, + this.isWithToken = true, + this.customToken, + }); + @override + final bool hasLogger; + @override + final bool isWithToken; + + final String? customToken; + + @override + BaseOptions get options { + final BaseOptions options = BaseOptions( + baseUrl: rootAddress, + contentType: Headers.jsonContentType, + responseType: ResponseType.json, + ); + if (isWithToken) { + final String? token = getIt().dnsProviderKey; + assert(token != null); + options.headers = {'Authorization': 'Bearer $token'}; + } + + if (customToken != null) { + options.headers = {'Authorization': 'Bearer $customToken'}; + } + + if (validateStatus != null) { + options.validateStatus = validateStatus!; + } + return options; + } + + @override + String rootAddress = 'https://api.digitalocean.com/v2'; + + Future> isApiTokenValid(final String token) async { + bool isValid = false; + Response? response; + String message = ''; + final Dio client = await getClient(); + try { + response = await client.get( + '/account', + options: Options( + followRedirects: false, + validateStatus: (final status) => + status != null && (status >= 200 || status == 401), + headers: {'Authorization': 'Bearer $token'}, + ), + ); + } catch (e) { + print(e); + isValid = false; + message = e.toString(); + } finally { + close(client); + } + + if (response == null) { + return GenericResult( + data: isValid, + success: false, + message: message, + ); + } + + if (response.statusCode == HttpStatus.ok) { + isValid = true; + } else if (response.statusCode == HttpStatus.unauthorized) { + isValid = false; + } else { + throw Exception('code: ${response.statusCode}'); + } + + return GenericResult( + data: isValid, + success: true, + message: response.statusMessage, + ); + } + + Future>> getDomains() async { + List domains = []; + + final Dio client = await getClient(); + try { + final Response response = await client.get('/domains'); + domains = response.data['domains']! + .map( + (final e) => DigitalOceanDomain.fromJson(e), + ) + .toList(); + } catch (e) { + print(e); + return GenericResult( + data: domains, + success: false, + message: e.toString(), + ); + } finally { + close(client); + } + + return GenericResult(data: domains, success: true); + } + + Future> createMultipleDnsRecords({ + required final String domainName, + required final List records, + }) async { + final List allCreateFutures = []; + + final Dio client = await getClient(); + try { + for (final DigitalOceanDnsRecord record in records) { + allCreateFutures.add( + client.post( + '/domains/$domainName/records', + data: record.toJson(), + ), + ); + } + await Future.wait(allCreateFutures); + } on DioError catch (e) { + print(e.message); + rethrow; + } catch (e) { + print(e); + return GenericResult( + success: false, + data: null, + message: e.toString(), + ); + } finally { + close(client); + } + + return GenericResult(success: true, data: null); + } + + Future> removeSimilarRecords({ + required final String domainName, + required final List records, + }) async { + final Dio client = await getClient(); + try { + final List allDeleteFutures = []; + for (final record in records) { + allDeleteFutures.add( + client.delete('/domains/$domainName/records/${record.id}'), + ); + } + await Future.wait(allDeleteFutures); + } catch (e) { + print(e); + return GenericResult( + success: false, + data: null, + message: e.toString(), + ); + } finally { + close(client); + } + + return GenericResult(success: true, data: null); + } + + Future>> getDnsRecords( + final String domainName, + ) async { + Response response; + List allRecords = []; + + /// Default amount is 20, but we will eventually overflow it, + /// so I hardcode it to the maximum available amount in advance just in case + /// + /// https://docs.digitalocean.com/reference/api/api-reference/#operation/domains_list_records + const int amountPerPage = 200; + final String url = '/domains/$domainName/records?per_page=$amountPerPage'; + + final Dio client = await getClient(); + try { + response = await client.get(url); + allRecords = response.data['domain_records'] + .map( + (final e) => DigitalOceanDnsRecord.fromJson(e), + ) + .toList() ?? + []; + } catch (e) { + print(e); + GenericResult( + data: allRecords, + success: false, + message: e.toString(), + ); + } finally { + close(client); + } + + return GenericResult(data: allRecords, success: true); + } +} diff --git a/lib/logic/api_maps/api_map.dart b/lib/logic/api_maps/rest_maps/rest_api_map.dart similarity index 77% rename from lib/logic/api_maps/api_map.dart rename to lib/logic/api_maps/rest_maps/rest_api_map.dart index 007bfd98..547ce4aa 100644 --- a/lib/logic/api_maps/api_map.dart +++ b/lib/logic/api_maps/rest_maps/rest_api_map.dart @@ -2,20 +2,20 @@ import 'dart:async'; import 'dart:developer'; import 'dart:io'; -import 'package:dio/adapter.dart'; import 'package:dio/dio.dart'; +import 'package:dio/io.dart'; import 'package:pretty_dio_logger/pretty_dio_logger.dart'; import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/models/message.dart'; -abstract class ApiMap { - Future getClient() async { - final Dio dio = Dio(await options); +abstract class RestApiMap { + Future getClient({final BaseOptions? customOptions}) async { + final Dio dio = Dio(customOptions ?? (await options)); if (hasLogger) { dio.interceptors.add(PrettyDioLogger()); } dio.interceptors.add(ConsoleInterceptor()); - (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = + (dio.httpClientAdapter as IOHttpClientAdapter).onHttpClientCreate = (final HttpClient client) { client.badCertificateCallback = (final X509Certificate cert, final String host, final int port) => @@ -41,7 +41,8 @@ abstract class ApiMap { FutureOr get options; - abstract final String rootAddress; + String get rootAddress; + abstract final bool hasLogger; abstract final bool isWithToken; @@ -64,9 +65,11 @@ class ConsoleInterceptor extends InterceptorsWrapper { final RequestInterceptorHandler handler, ) async { addMessage( - Message( - text: - 'request-uri: ${options.uri}\nheaders: ${options.headers}\ndata: ${options.data}', + RestApiRequestMessage( + method: options.method, + data: options.data.toString(), + headers: options.headers, + uri: options.uri, ), ); return super.onRequest(options, handler); @@ -78,9 +81,11 @@ class ConsoleInterceptor extends InterceptorsWrapper { final ResponseInterceptorHandler handler, ) async { addMessage( - Message( - text: - 'response-uri: ${response.realUri}\ncode: ${response.statusCode}\ndata: ${response.toString()}\n', + RestApiResponseMessage( + method: response.requestOptions.method, + statusCode: response.statusCode, + data: response.data.toString(), + uri: response.realUri, ), ); return super.onResponse( diff --git a/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean_api.dart b/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean_api.dart new file mode 100644 index 00000000..9ffdb666 --- /dev/null +++ b/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean_api.dart @@ -0,0 +1,559 @@ +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/api_maps/generic_result.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/rest_api_map.dart'; +import 'package:selfprivacy/logic/api_maps/tls_options.dart'; +import 'package:selfprivacy/logic/models/disk_size.dart'; +import 'package:selfprivacy/logic/models/hive/user.dart'; +import 'package:selfprivacy/logic/models/json/digital_ocean_server_info.dart'; +import 'package:selfprivacy/utils/password_generator.dart'; + +class DigitalOceanApi extends RestApiMap { + DigitalOceanApi({ + required this.region, + this.hasLogger = true, + this.isWithToken = true, + }); + @override + bool hasLogger; + @override + bool isWithToken; + + final String? region; + + @override + BaseOptions get options { + final BaseOptions options = BaseOptions( + baseUrl: rootAddress, + contentType: Headers.jsonContentType, + responseType: ResponseType.json, + ); + if (isWithToken) { + final String? token = getIt().serverProviderKey; + assert(token != null); + options.headers = {'Authorization': 'Bearer $token'}; + } + + if (validateStatus != null) { + options.validateStatus = validateStatus!; + } + + return options; + } + + @override + String get rootAddress => 'https://api.digitalocean.com/v2'; + String get infectProviderName => 'digitalocean'; + + Future> getServers() async { + List servers = []; + + final Dio client = await getClient(); + try { + final Response response = await client.get('/droplets'); + servers = response.data['droplets']; + } catch (e) { + print(e); + return GenericResult( + success: false, + data: servers, + message: e.toString(), + ); + } finally { + close(client); + } + + return GenericResult(success: true, data: servers); + } + + Future> createServer({ + required final String dnsApiToken, + required final String dnsProviderType, + required final String serverApiToken, + required final User rootUser, + required final String base64Password, + required final String databasePassword, + required final String domainName, + required final String hostName, + required final String serverType, + }) async { + final String stagingAcme = TlsOptions.stagingAcme ? 'true' : 'false'; + + int? dropletId; + Response? serverCreateResponse; + final Dio client = await getClient(); + try { + final Map data = { + 'name': hostName, + 'size': serverType, + 'image': 'ubuntu-20-04-x64', + 'user_data': '#cloud-config\n' + 'runcmd:\n' + '- curl https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-infect/raw/branch/providers/digital-ocean/nixos-infect | ' + "PROVIDER=$infectProviderName DNS_PROVIDER_TYPE=$dnsProviderType STAGING_ACME='$stagingAcme' DOMAIN='$domainName' " + "LUSER='${rootUser.login}' ENCODED_PASSWORD='$base64Password' CF_TOKEN=$dnsApiToken DB_PASSWORD=$databasePassword " + 'API_TOKEN=$serverApiToken HOSTNAME=$hostName bash 2>&1 | tee /tmp/infect.log', + 'region': region!, + }; + print('Decoded data: $data'); + + serverCreateResponse = await client.post( + '/droplets', + data: data, + ); + dropletId = serverCreateResponse.data['droplet']['id']; + } catch (e) { + print(e); + return GenericResult( + success: false, + data: null, + message: e.toString(), + ); + } finally { + close(client); + } + + return GenericResult( + data: dropletId, + success: true, + code: serverCreateResponse.statusCode, + message: serverCreateResponse.statusMessage, + ); + } + + Future> deleteServer(final int serverId) async { + final Dio client = await getClient(); + try { + await client.delete('/droplets/$serverId'); + } catch (e) { + print(e); + return GenericResult( + success: false, + data: null, + message: e.toString(), + ); + } finally { + close(client); + } + + return GenericResult(success: true, data: null); + } + + Future> isApiTokenValid(final String token) async { + bool isValid = false; + Response? response; + String message = ''; + final Dio client = await getClient(); + try { + response = await client.get( + '/account', + options: Options( + followRedirects: false, + validateStatus: (final status) => + status != null && (status >= 200 || status == 401), + headers: {'Authorization': 'Bearer $token'}, + ), + ); + } catch (e) { + print(e); + isValid = false; + message = e.toString(); + } finally { + close(client); + } + + if (response == null) { + return GenericResult( + data: isValid, + success: false, + message: message, + ); + } + + if (response.statusCode == HttpStatus.ok) { + isValid = true; + } else if (response.statusCode == HttpStatus.unauthorized) { + isValid = false; + } else { + throw Exception('code: ${response.statusCode}'); + } + + return GenericResult( + data: isValid, + success: true, + message: response.statusMessage, + ); + } + + Future>> + getAvailableLocations() async { + final List locations = []; + + final Dio client = await getClient(); + try { + final Response response = await client.get( + '/regions', + ); + + for (final region in response.data!['regions']) { + locations.add(DigitalOceanLocation.fromJson(region)); + } + } catch (e) { + print(e); + return GenericResult( + data: [], + success: false, + message: e.toString(), + ); + } finally { + close(client); + } + + return GenericResult(data: locations, success: true); + } + + Future>> + getAvailableServerTypes() async { + final List types = []; + + final Dio client = await getClient(); + try { + final Response response = await client.get( + '/sizes', + ); + for (final size in response.data!['sizes']) { + types.add(DigitalOceanServerType.fromJson(size)); + } + } catch (e) { + print(e); + return GenericResult( + data: [], + success: false, + message: e.toString(), + ); + } finally { + close(client); + } + + return GenericResult(data: types, success: true); + } + + Future> powerOn(final int serverId) async { + final Dio client = await getClient(); + try { + await client.post( + '/droplets/$serverId/actions', + data: { + 'type': 'power_on', + }, + ); + } catch (e) { + print(e); + return GenericResult( + success: false, + data: null, + message: e.toString(), + ); + } finally { + close(client); + } + + return GenericResult(success: true, data: null); + } + + Future> restart(final int serverId) async { + final Dio client = await getClient(); + try { + await client.post( + '/droplets/$serverId/actions', + data: { + 'type': 'reboot', + }, + ); + } catch (e) { + print(e); + return GenericResult( + success: false, + data: null, + message: e.toString(), + ); + } finally { + close(client); + } + + return GenericResult(success: true, data: null); + } + + Future>> getVolumes({ + final String? status, + }) async { + final List volumes = []; + + Response? getVolumesResponse; + final Dio client = await getClient(); + try { + getVolumesResponse = await client.get( + '/volumes', + queryParameters: { + 'status': status, + }, + ); + for (final volume in getVolumesResponse.data['volumes']) { + volumes.add(DigitalOceanVolume.fromJson(volume)); + } + } catch (e) { + print(e); + return GenericResult( + data: [], + success: false, + message: e.toString(), + ); + } finally { + client.close(); + } + + return GenericResult( + data: volumes, + success: true, + ); + } + + Future> createVolume(final int gb) async { + DigitalOceanVolume? volume; + Response? createVolumeResponse; + final Dio client = await getClient(); + try { + await Future.delayed(const Duration(seconds: 6)); + + createVolumeResponse = await client.post( + '/volumes', + data: { + 'size_gigabytes': gb, + 'name': 'volume${StringGenerators.storageName()}', + 'labels': {'labelkey': 'value'}, + 'region': region, + 'filesystem_type': 'ext4', + }, + ); + volume = DigitalOceanVolume.fromJson(createVolumeResponse.data['volume']); + } catch (e) { + print(e); + return GenericResult( + data: null, + success: false, + message: e.toString(), + ); + } finally { + client.close(); + } + + return GenericResult( + data: volume, + success: true, + code: createVolumeResponse.statusCode, + message: createVolumeResponse.statusMessage, + ); + } + + Future> attachVolume( + final String name, + final int serverId, + ) async { + bool success = false; + + Response? attachVolumeResponse; + final Dio client = await getClient(); + try { + attachVolumeResponse = await client.post( + '/volumes/actions', + data: { + 'type': 'attach', + 'volume_name': name, + 'region': region, + 'droplet_id': serverId, + }, + ); + success = + attachVolumeResponse.data['action']['status'].toString() != 'error'; + } catch (e) { + print(e); + return GenericResult( + data: false, + success: false, + message: e.toString(), + ); + } finally { + close(client); + } + + return GenericResult( + data: success, + success: true, + code: attachVolumeResponse.statusCode, + message: attachVolumeResponse.statusMessage, + ); + } + + Future> detachVolume( + final String name, + final int serverId, + ) async { + bool success = false; + + final Response detachVolumeResponse; + final Dio client = await getClient(); + try { + detachVolumeResponse = await client.post( + '/volumes/actions', + data: { + 'type': 'detach', + 'volume_name': name, + 'droplet_id': serverId, + 'region': region, + }, + ); + success = + detachVolumeResponse.data['action']['status'].toString() != 'error'; + } catch (e) { + print(e); + return GenericResult( + data: false, + success: false, + message: e.toString(), + ); + } finally { + client.close(); + } + + return GenericResult( + data: success, + success: true, + ); + } + + Future> deleteVolume(final String uuid) async { + final Dio client = await getClient(); + try { + await client.delete('/volumes/$uuid'); + } catch (e) { + print(e); + return GenericResult( + data: null, + success: false, + message: e.toString(), + ); + } finally { + client.close(); + } + + return GenericResult( + data: null, + success: true, + ); + } + + Future> resizeVolume( + final String uuid, + final int gb, + ) async { + bool success = false; + + final Response resizeVolumeResponse; + final Dio client = await getClient(); + try { + resizeVolumeResponse = await client.post( + '/volumes/$uuid/actions', + data: { + 'type': 'resize', + 'size_gigabytes': gb, + 'region': region, + }, + ); + success = + resizeVolumeResponse.data['action']['status'].toString() != 'error'; + } catch (e) { + print(e); + return GenericResult( + data: false, + success: false, + message: e.toString(), + ); + } finally { + client.close(); + } + + return GenericResult( + data: success, + success: true, + ); + } + + Future> getMetricsCpu( + final int serverId, + final DateTime start, + final DateTime end, + ) async { + List metrics = []; + + final Dio client = await getClient(); + try { + final Response response = await client.get( + '/monitoring/metrics/droplet/cpu', + queryParameters: { + 'start': '${(start.microsecondsSinceEpoch / 1000000).round()}', + 'end': '${(end.microsecondsSinceEpoch / 1000000).round()}', + 'host_id': '$serverId', + }, + ); + metrics = response.data['data']['result']; + } catch (e) { + print(e); + return GenericResult( + success: false, + data: [], + message: e.toString(), + ); + } finally { + close(client); + } + + return GenericResult(success: true, data: metrics); + } + + Future> getMetricsBandwidth( + final int serverId, + final DateTime start, + final DateTime end, + final bool isInbound, + ) async { + List metrics = []; + + final Dio client = await getClient(); + try { + final Response response = await client.get( + '/monitoring/metrics/droplet/bandwidth', + queryParameters: { + 'start': '${(start.microsecondsSinceEpoch / 1000000).round()}', + 'end': '${(end.microsecondsSinceEpoch / 1000000).round()}', + 'host_id': '$serverId', + 'interface': 'public', + 'direction': isInbound ? 'inbound' : 'outbound', + }, + ); + metrics = response.data['data']['result'][0]['values']; + } catch (e) { + print(e); + return GenericResult( + success: false, + data: [], + message: e.toString(), + ); + } finally { + close(client); + } + + return GenericResult(success: true, data: metrics); + } +} diff --git a/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner_api.dart b/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner_api.dart new file mode 100644 index 00000000..5029561d --- /dev/null +++ b/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner_api.dart @@ -0,0 +1,609 @@ +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/api_maps/generic_result.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/rest_api_map.dart'; +import 'package:selfprivacy/logic/api_maps/tls_options.dart'; +import 'package:selfprivacy/logic/models/disk_size.dart'; +import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart'; +import 'package:selfprivacy/logic/models/hive/user.dart'; +import 'package:selfprivacy/utils/password_generator.dart'; + +class HetznerApi extends RestApiMap { + HetznerApi({ + this.region, + this.hasLogger = true, + this.isWithToken = true, + }); + @override + bool hasLogger; + @override + bool isWithToken; + + final String? region; + + @override + BaseOptions get options { + final BaseOptions options = BaseOptions( + baseUrl: rootAddress, + contentType: Headers.jsonContentType, + responseType: ResponseType.json, + ); + if (isWithToken) { + final String? token = getIt().serverProviderKey; + assert(token != null); + options.headers = {'Authorization': 'Bearer $token'}; + } + + if (validateStatus != null) { + options.validateStatus = validateStatus!; + } + + return options; + } + + @override + String get rootAddress => 'https://api.hetzner.cloud/v1'; + String get infectProviderName => 'hetzner'; + + Future>> getServers() async { + List servers = []; + + final Dio client = await getClient(); + try { + final Response response = await client.get('/servers'); + servers = response.data!['servers'] + .map( + (final e) => HetznerServerInfo.fromJson(e), + ) + .toList(); + } catch (e) { + print(e); + return GenericResult( + success: false, + data: [], + message: e.toString(), + ); + } finally { + close(client); + } + + return GenericResult(data: servers, success: true); + } + + Future> createServer({ + required final String dnsApiToken, + required final String dnsProviderType, + required final String serverApiToken, + required final User rootUser, + required final String base64Password, + required final String databasePassword, + required final String domainName, + required final String hostName, + required final int volumeId, + required final String serverType, + }) async { + final String stagingAcme = TlsOptions.stagingAcme ? 'true' : 'false'; + Response? serverCreateResponse; + HetznerServerInfo? serverInfo; + DioError? hetznerError; + bool success = false; + + final Dio client = await getClient(); + try { + final Map data = { + 'name': hostName, + 'server_type': serverType, + 'start_after_create': false, + 'image': 'ubuntu-20.04', + 'volumes': [volumeId], + 'networks': [], + 'user_data': '#cloud-config\n' + 'runcmd:\n' + '- curl https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-infect/raw/branch/providers/hetzner/nixos-infect | ' + "STAGING_ACME='$stagingAcme' PROVIDER=$infectProviderName DNS_PROVIDER_TYPE=$dnsProviderType " + "NIX_CHANNEL=nixos-21.05 DOMAIN='$domainName' LUSER='${rootUser.login}' ENCODED_PASSWORD='$base64Password' " + 'CF_TOKEN=$dnsApiToken DB_PASSWORD=$databasePassword API_TOKEN=$serverApiToken HOSTNAME=$hostName bash 2>&1 | ' + 'tee /tmp/infect.log', + 'labels': {}, + 'automount': true, + 'location': region!, + }; + print('Decoded data: $data'); + + serverCreateResponse = await client.post('/servers', data: data); + serverInfo = HetznerServerInfo.fromJson( + serverCreateResponse.data['server'], + ); + success = true; + } on DioError catch (e) { + print(e); + hetznerError = e; + } catch (e) { + print(e); + } finally { + close(client); + } + + String? apiResultMessage = serverCreateResponse?.statusMessage; + if (hetznerError != null && + hetznerError.response!.data['error']['code'] == 'uniqueness_error') { + apiResultMessage = 'uniqueness_error'; + } + + return GenericResult( + data: serverInfo, + success: success && hetznerError == null, + code: serverCreateResponse?.statusCode ?? + hetznerError?.response?.statusCode, + message: apiResultMessage, + ); + } + + Future> createReverseDns({ + required final int serverId, + required final String ip4, + required final String dnsPtr, + }) async { + final Dio client = await getClient(); + try { + await client.post( + '/servers/$serverId/actions/change_dns_ptr', + data: { + 'ip': ip4, + 'dns_ptr': dnsPtr, + }, + ); + } catch (e) { + print(e); + return GenericResult( + success: false, + data: null, + message: e.toString(), + ); + } finally { + close(client); + } + + return GenericResult(success: true, data: null); + } + + Future> deleteServer({ + required final int serverId, + }) async { + final Dio client = await getClient(); + try { + await client.delete('/servers/$serverId'); + } catch (e) { + print(e); + return GenericResult( + success: false, + data: null, + message: e.toString(), + ); + } finally { + close(client); + } + + return GenericResult(success: true, data: null); + } + + Future> isApiTokenValid(final String token) async { + bool isValid = false; + Response? response; + String message = ''; + final Dio client = await getClient(); + try { + response = await client.get( + '/servers', + options: Options( + followRedirects: false, + validateStatus: (final status) => + status != null && (status >= 200 || status == 401), + headers: {'Authorization': 'Bearer $token'}, + ), + ); + } catch (e) { + print(e); + isValid = false; + message = e.toString(); + } finally { + close(client); + } + + if (response == null) { + return GenericResult( + data: isValid, + success: false, + message: message, + ); + } + + if (response.statusCode == HttpStatus.ok) { + isValid = true; + } else if (response.statusCode == HttpStatus.unauthorized) { + isValid = false; + } else { + throw Exception('code: ${response.statusCode}'); + } + + return GenericResult( + data: isValid, + success: true, + message: response.statusMessage, + ); + } + + Future>> getAvailableLocations() async { + final List locations = []; + + final Dio client = await getClient(); + try { + final Response response = await client.get('/locations'); + for (final location in response.data!['locations']) { + locations.add(HetznerLocation.fromJson(location)); + } + } catch (e) { + print(e); + return GenericResult( + success: false, + data: [], + message: e.toString(), + ); + } finally { + close(client); + } + + return GenericResult(success: true, data: locations); + } + + Future>> + getAvailableServerTypes() async { + final List types = []; + + final Dio client = await getClient(); + try { + final Response response = await client.get( + '/server_types', + ); + for (final type in response.data!['server_types']) { + types.add(HetznerServerTypeInfo.fromJson(type)); + } + } catch (e) { + print(e); + return GenericResult( + data: [], + success: false, + message: e.toString(), + ); + } finally { + close(client); + } + + return GenericResult(data: types, success: true); + } + + Future> powerOn(final int serverId) async { + final Dio client = await getClient(); + try { + await client.post('/servers/$serverId/actions/poweron'); + } catch (e) { + print(e); + return GenericResult( + success: false, + data: null, + message: e.toString(), + ); + } finally { + close(client); + } + + return GenericResult(success: true, data: null); + } + + Future> restart(final int serverId) async { + final Dio client = await getClient(); + try { + await client.post('/servers/$serverId/actions/reset'); + } catch (e) { + print(e); + return GenericResult( + success: false, + data: null, + message: e.toString(), + ); + } finally { + close(client); + } + + return GenericResult(success: true, data: null); + } + + Future> getPricing() async { + HetznerPricing? pricing; + + final Response pricingResponse; + final Dio client = await getClient(); + try { + pricingResponse = await client.get('/pricing'); + + final volume = pricingResponse.data['pricing']['volume']; + final volumePrice = volume['price_per_gb_month']['gross']; + final primaryIps = pricingResponse.data['pricing']['primary_ips']; + String? ipPrice; + for (final primaryIp in primaryIps) { + if (primaryIp['type'] == 'ipv4') { + for (final primaryIpPrice in primaryIp['prices']) { + if (primaryIpPrice['location'] == region!) { + ipPrice = primaryIpPrice['price_monthly']['gross']; + } + } + } + } + pricing = HetznerPricing( + region!, + double.parse(volumePrice), + double.parse(ipPrice!), + ); + } catch (e) { + print(e); + return GenericResult( + success: false, + data: pricing, + message: e.toString(), + ); + } finally { + client.close(); + } + + return GenericResult(success: true, data: pricing); + } + + Future>> getVolumes({ + final String? status, + }) async { + final List volumes = []; + + Response? getVolumesResonse; + final Dio client = await getClient(); + try { + getVolumesResonse = await client.get( + '/volumes', + queryParameters: { + 'status': status, + }, + ); + for (final volume in getVolumesResonse.data['volumes']) { + volumes.add(HetznerVolume.fromJson(volume)); + } + } catch (e) { + print(e); + return GenericResult( + data: [], + success: false, + message: e.toString(), + ); + } finally { + client.close(); + } + + return GenericResult( + data: volumes, + success: true, + code: getVolumesResonse.statusCode, + message: getVolumesResonse.statusMessage, + ); + } + + Future> createVolume(final int gb) async { + Response? createVolumeResponse; + HetznerVolume? volume; + final Dio client = await getClient(); + try { + createVolumeResponse = await client.post( + '/volumes', + data: { + 'size': gb, + 'name': StringGenerators.storageName(), + 'labels': {'labelkey': 'value'}, + 'location': region, + 'automount': false, + 'format': 'ext4' + }, + ); + volume = HetznerVolume.fromJson(createVolumeResponse.data['volume']); + } catch (e) { + print(e); + return GenericResult( + data: null, + success: false, + message: e.toString(), + ); + } finally { + client.close(); + } + + return GenericResult( + data: volume, + success: true, + code: createVolumeResponse.statusCode, + message: createVolumeResponse.statusMessage, + ); + } + + Future> deleteVolume(final int volumeId) async { + final Dio client = await getClient(); + try { + await client.delete('/volumes/$volumeId'); + } catch (e) { + print(e); + return GenericResult( + success: false, + data: false, + message: e.toString(), + ); + } finally { + client.close(); + } + + return GenericResult( + success: true, + data: true, + ); + } + + Future> getVolume( + final String volumeId, + ) async { + HetznerVolume? volume; + + final Response getVolumeResponse; + final Dio client = await getClient(); + try { + getVolumeResponse = await client.get('/volumes/$volumeId'); + volume = HetznerVolume.fromJson(getVolumeResponse.data['volume']); + } catch (e) { + print(e); + return GenericResult( + data: null, + success: false, + message: e.toString(), + ); + } finally { + client.close(); + } + + return GenericResult( + data: volume, + success: true, + ); + } + + Future> detachVolume(final int volumeId) async { + bool success = false; + + final Response detachVolumeResponse; + final Dio client = await getClient(); + try { + detachVolumeResponse = await client.post( + '/volumes/$volumeId/actions/detach', + ); + success = + detachVolumeResponse.data['action']['status'].toString() != 'error'; + } catch (e) { + print(e); + return GenericResult( + success: false, + data: false, + message: e.toString(), + ); + } finally { + client.close(); + } + + return GenericResult( + success: false, + data: success, + ); + } + + Future> attachVolume( + final HetznerVolume volume, + final int serverId, + ) async { + bool success = false; + + Response? attachVolumeResponse; + final Dio client = await getClient(); + try { + attachVolumeResponse = await client.post( + '/volumes/${volume.id}/actions/attach', + data: { + 'automount': true, + 'server': serverId, + }, + ); + success = + attachVolumeResponse.data['action']['status'].toString() != 'error'; + } catch (e) { + print(e); + } finally { + client.close(); + } + + return GenericResult( + data: success, + success: true, + code: attachVolumeResponse?.statusCode, + message: attachVolumeResponse?.statusMessage, + ); + } + + Future> resizeVolume( + final HetznerVolume volume, + final DiskSize size, + ) async { + bool success = false; + + final Response resizeVolumeResponse; + final Dio client = await getClient(); + try { + resizeVolumeResponse = await client.post( + '/volumes/${volume.id}/actions/resize', + data: { + 'size': size.gibibyte, + }, + ); + success = + resizeVolumeResponse.data['action']['status'].toString() != 'error'; + } catch (e) { + print(e); + return GenericResult( + data: false, + success: false, + message: e.toString(), + ); + } finally { + client.close(); + } + + return GenericResult( + data: success, + success: true, + ); + } + + Future>> getMetrics( + final int serverId, + final DateTime start, + final DateTime end, + final String type, + ) async { + Map metrics = {}; + final Dio client = await getClient(); + try { + final Map queryParameters = { + 'start': start.toUtc().toIso8601String(), + 'end': end.toUtc().toIso8601String(), + 'type': type + }; + final Response res = await client.get( + '/servers/$serverId/metrics', + queryParameters: queryParameters, + ); + metrics = res.data['metrics']; + } catch (e) { + print(e); + return GenericResult( + success: false, + data: {}, + message: e.toString(), + ); + } finally { + close(client); + } + + return GenericResult(data: metrics, success: true); + } +} diff --git a/lib/logic/api_maps/server.dart b/lib/logic/api_maps/server.dart deleted file mode 100644 index 67a0739c..00000000 --- a/lib/logic/api_maps/server.dart +++ /dev/null @@ -1,931 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; - -import 'package:dio/dio.dart'; -import 'package:selfprivacy/config/get_it_config.dart'; -import 'package:selfprivacy/logic/common_enum/common_enum.dart'; -import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart'; -import 'package:selfprivacy/logic/models/hive/server_domain.dart'; -import 'package:selfprivacy/logic/models/hive/user.dart'; -import 'package:selfprivacy/logic/models/json/api_token.dart'; -import 'package:selfprivacy/logic/models/json/auto_upgrade_settings.dart'; -import 'package:selfprivacy/logic/models/json/backup.dart'; -import 'package:selfprivacy/logic/models/json/device_token.dart'; -import 'package:selfprivacy/logic/models/json/recovery_token_status.dart'; -import 'package:selfprivacy/logic/models/timezone_settings.dart'; - -import 'package:selfprivacy/logic/api_maps/api_map.dart'; - -class ApiResponse { - ApiResponse({ - required this.statusCode, - required this.data, - this.errorMessage, - }); - final int statusCode; - final String? errorMessage; - final D data; - - bool get isSuccess => statusCode >= 200 && statusCode < 300; -} - -class ServerApi extends ApiMap { - ServerApi({ - this.hasLogger = false, - this.isWithToken = true, - this.overrideDomain, - this.customToken, - }); - @override - bool hasLogger; - @override - bool isWithToken; - String? overrideDomain; - String? customToken; - - @override - BaseOptions get options { - BaseOptions options = BaseOptions(); - - if (isWithToken) { - final ServerDomain? cloudFlareDomain = - getIt().serverDomain; - final String domainName = cloudFlareDomain!.domainName; - final String? apiToken = getIt().serverDetails?.apiToken; - - options = BaseOptions( - baseUrl: 'https://api.$domainName', - headers: { - 'Authorization': 'Bearer $apiToken', - }, - ); - } - - if (overrideDomain != null) { - options = BaseOptions( - baseUrl: 'https://api.$overrideDomain', - headers: customToken != null - ? {'Authorization': 'Bearer $customToken'} - : null, - ); - } - - return options; - } - - Future getApiVersion() async { - Response response; - - final Dio client = await getClient(); - String? apiVersion; - - try { - response = await client.get('/api/version'); - apiVersion = response.data['version']; - } on DioError catch (e) { - print(e.message); - } finally { - close(client); - } - return apiVersion; - } - - Future isHttpServerWorking() async { - bool res = false; - Response response; - - final Dio client = await getClient(); - try { - response = await client.get('/services/status'); - res = response.statusCode == HttpStatus.ok; - } on DioError catch (e) { - print(e.message); - } finally { - close(client); - } - return res; - } - - Future> createUser(final User user) async { - Response response; - - final Dio client = await getClient(); - try { - response = await client.post( - '/users', - data: { - 'username': user.login, - 'password': user.password, - }, - ); - } on DioError catch (e) { - print(e.message); - return ApiResponse( - errorMessage: e.error.toString(), - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: User( - login: user.login, - password: user.password, - isFoundOnServer: false, - ), - ); - } finally { - close(client); - } - - bool isFoundOnServer = false; - int code = 0; - - final bool isUserCreated = (response.statusCode != null) && - (response.statusCode == HttpStatus.created); - - if (isUserCreated) { - isFoundOnServer = true; - code = response.statusCode!; - } else { - isFoundOnServer = false; - code = HttpStatus.notAcceptable; - } - - return ApiResponse( - statusCode: code, - data: User( - login: user.login, - password: user.password, - isFoundOnServer: isFoundOnServer, - ), - ); - } - - Future>> getUsersList({ - final withMainUser = false, - }) async { - final List res = []; - Response response; - - final Dio client = await getClient(); - try { - response = await client.get( - '/users', - queryParameters: withMainUser ? {'withMainUser': 'true'} : null, - ); - for (final user in response.data) { - res.add(user.toString()); - } - } on DioError catch (e) { - print(e.message); - return ApiResponse( - errorMessage: e.message, - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: [], - ); - } catch (e) { - print(e); - return ApiResponse( - errorMessage: e.toString(), - statusCode: HttpStatus.internalServerError, - data: [], - ); - } finally { - close(client); - } - - final int code = response.statusCode ?? HttpStatus.internalServerError; - - return ApiResponse( - statusCode: code, - data: res, - ); - } - - Future> addUserSshKey( - final User user, - final String sshKey, - ) async { - late Response response; - - final Dio client = await getClient(); - try { - response = await client.post( - '/services/ssh/keys/${user.login}', - data: { - 'public_key': sshKey, - }, - ); - } on DioError catch (e) { - print(e.message); - return ApiResponse( - errorMessage: e.message, - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: null, - ); - } finally { - close(client); - } - - final int code = response.statusCode ?? HttpStatus.internalServerError; - - return ApiResponse( - statusCode: code, - data: null, - ); - } - - Future> addRootSshKey(final String ssh) async { - late Response response; - - final Dio client = await getClient(); - try { - response = await client.put( - '/services/ssh/key/send', - data: {'public_key': ssh}, - ); - } on DioError catch (e) { - print(e.message); - return ApiResponse( - errorMessage: e.message, - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: null, - ); - } finally { - close(client); - } - - final int code = response.statusCode ?? HttpStatus.internalServerError; - - return ApiResponse( - statusCode: code, - data: null, - ); - } - - Future>> getUserSshKeys(final User user) async { - List res; - Response response; - - final Dio client = await getClient(); - try { - response = await client.get('/services/ssh/keys/${user.login}'); - res = (response.data as List) - .map((final e) => e as String) - .toList(); - } on DioError catch (e) { - print(e.message); - return ApiResponse>( - errorMessage: e.message, - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: [], - ); - } catch (e) { - return ApiResponse>( - errorMessage: e.toString(), - statusCode: HttpStatus.internalServerError, - data: [], - ); - } finally { - close(client); - } - - final int code = response.statusCode ?? HttpStatus.internalServerError; - - return ApiResponse>( - statusCode: code, - data: res, - errorMessage: response.data is List - ? null - : response.data?.containsKey('error') ?? false - ? response.data['error'] - : null, - ); - } - - Future> deleteUserSshKey( - final User user, - final String sshKey, - ) async { - Response response; - - final Dio client = await getClient(); - try { - response = await client.delete( - '/services/ssh/keys/${user.login}', - data: {'public_key': sshKey}, - ); - } on DioError catch (e) { - print(e.message); - return ApiResponse( - errorMessage: e.message, - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: null, - ); - } finally { - close(client); - } - - final int code = response.statusCode ?? HttpStatus.internalServerError; - - return ApiResponse( - statusCode: code, - data: null, - errorMessage: response.data?.containsKey('error') ?? false - ? response.data['error'] - : null, - ); - } - - Future deleteUser(final User user) async { - bool res = false; - Response response; - - final Dio client = await getClient(); - try { - response = await client.delete('/users/${user.login}'); - res = response.statusCode == HttpStatus.ok || - response.statusCode == HttpStatus.notFound; - } on DioError catch (e) { - print(e.message); - res = false; - } finally { - close(client); - } - return res; - } - - @override - String get rootAddress => - throw UnimplementedError('not used in with implementation'); - - Future apply() async { - bool res = false; - Response response; - - final Dio client = await getClient(); - try { - response = await client.get('/system/configuration/apply'); - res = response.statusCode == HttpStatus.ok; - } on DioError catch (e) { - print(e.message); - res = false; - } finally { - close(client); - } - return res; - } - - Future switchService( - final ServiceTypes type, - final bool needToTurnOn, - ) async { - final Dio client = await getClient(); - try { - client.post( - '/services/${type.url}/${needToTurnOn ? 'enable' : 'disable'}', - ); - } on DioError catch (e) { - print(e.message); - } finally { - close(client); - } - } - - Future> servicesPowerCheck() async { - Response response; - - final Dio client = await getClient(); - try { - response = await client.get('/services/status'); - } on DioError catch (e) { - print(e.message); - return {}; - } finally { - close(client); - } - - return { - ServiceTypes.passwordManager: response.data['bitwarden'] == 0, - ServiceTypes.git: response.data['gitea'] == 0, - ServiceTypes.cloud: response.data['nextcloud'] == 0, - ServiceTypes.vpn: response.data['ocserv'] == 0, - ServiceTypes.socialNetwork: response.data['pleroma'] == 0, - }; - } - - Future uploadBackblazeConfig(final BackblazeBucket bucket) async { - final Dio client = await getClient(); - try { - client.put( - '/services/restic/backblaze/config', - data: { - 'accountId': bucket.applicationKeyId, - 'accountKey': bucket.applicationKey, - 'bucket': bucket.bucketName, - }, - ); - } on DioError catch (e) { - print(e.message); - } finally { - close(client); - } - } - - Future startBackup() async { - final Dio client = await getClient(); - try { - client.put('/services/restic/backup/create'); - } on DioError catch (e) { - print(e.message); - } finally { - close(client); - } - } - - Future> getBackups() async { - Response response; - List backups = []; - - final Dio client = await getClient(); - try { - response = await client.get('/services/restic/backup/list'); - backups = - response.data.map((final e) => Backup.fromJson(e)).toList(); - } on DioError catch (e) { - print(e.message); - } catch (e) { - print(e); - } finally { - close(client); - } - return backups; - } - - Future getBackupStatus() async { - Response response; - BackupStatus status = BackupStatus( - status: BackupStatusEnum.error, - errorMessage: 'Network error', - progress: 0, - ); - - final Dio client = await getClient(); - try { - response = await client.get('/services/restic/backup/status'); - status = BackupStatus.fromJson(response.data); - } on DioError catch (e) { - print(e.message); - } finally { - close(client); - } - return status; - } - - Future forceBackupListReload() async { - final Dio client = await getClient(); - try { - client.get('/services/restic/backup/reload'); - } on DioError catch (e) { - print(e.message); - } finally { - close(client); - } - } - - Future restoreBackup(final String backupId) async { - final Dio client = await getClient(); - try { - client.put( - '/services/restic/backup/restore', - data: {'backupId': backupId}, - ); - } on DioError catch (e) { - print(e.message); - } finally { - close(client); - } - } - - Future pullConfigurationUpdate() async { - Response response; - bool result = false; - - final Dio client = await getClient(); - try { - response = await client.get('/system/configuration/pull'); - result = (response.statusCode != null) - ? (response.statusCode == HttpStatus.ok) - : false; - } on DioError catch (e) { - print(e.message); - } finally { - close(client); - } - return result; - } - - Future reboot() async { - Response response; - bool result = false; - - final Dio client = await getClient(); - try { - response = await client.get('/system/reboot'); - result = (response.statusCode != null) - ? (response.statusCode == HttpStatus.ok) - : false; - } on DioError catch (e) { - print(e.message); - } finally { - close(client); - } - return result; - } - - Future upgrade() async { - Response response; - bool result = false; - - final Dio client = await getClient(); - try { - response = await client.get('/system/configuration/upgrade'); - result = (response.statusCode != null) - ? (response.statusCode == HttpStatus.ok) - : false; - } on DioError catch (e) { - print(e.message); - } finally { - close(client); - } - return result; - } - - Future getAutoUpgradeSettings() async { - Response response; - AutoUpgradeSettings settings = const AutoUpgradeSettings( - enable: false, - allowReboot: false, - ); - - final Dio client = await getClient(); - try { - response = await client.get('/system/configuration/autoUpgrade'); - if (response.data != null) { - settings = AutoUpgradeSettings.fromJson(response.data); - } - } on DioError catch (e) { - print(e.message); - } finally { - close(client); - } - return settings; - } - - Future updateAutoUpgradeSettings( - final AutoUpgradeSettings settings, - ) async { - final Dio client = await getClient(); - try { - await client.put( - '/system/configuration/autoUpgrade', - data: settings.toJson(), - ); - } on DioError catch (e) { - print(e.message); - } finally { - close(client); - } - } - - Future getServerTimezone() async { - // I am not sure how to initialize TimeZoneSettings with default value... - final Dio client = await getClient(); - final Response response = - await client.get('/system/configuration/timezone'); - close(client); - - return TimeZoneSettings.fromString(response.data); - } - - Future updateServerTimezone(final TimeZoneSettings settings) async { - final Dio client = await getClient(); - try { - await client.put( - '/system/configuration/timezone', - data: settings.toString(), - ); - } on DioError catch (e) { - print(e.message); - } finally { - close(client); - } - } - - Future getDkim() async { - Response response; - - final Dio client = await getClient(); - try { - response = await client.get('/services/mailserver/dkim'); - } on DioError catch (e) { - print(e.message); - return null; - } finally { - close(client); - } - - if (response.statusCode == null) { - return null; - } - - if (response.statusCode == HttpStatus.notFound || response.data == null) { - throw Exception('No DKIM key found'); - } - - if (response.statusCode != HttpStatus.ok) { - return ''; - } - - final Codec base64toString = utf8.fuse(base64); - - return base64toString - .decode(response.data) - .split('(')[1] - .split(')')[0] - .replaceAll('"', ''); - } - - Future> getRecoveryTokenStatus() async { - Response response; - - final Dio client = await getClient(); - try { - response = await client.get('/auth/recovery_token'); - } on DioError catch (e) { - print(e.message); - return ApiResponse( - errorMessage: e.message, - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: const RecoveryKeyStatus(exists: false, valid: false), - ); - } finally { - close(client); - } - - final int code = response.statusCode ?? HttpStatus.internalServerError; - - return ApiResponse( - statusCode: code, - data: response.data != null - ? RecoveryKeyStatus.fromJson(response.data) - : null, - ); - } - - Future> generateRecoveryToken( - final DateTime? expiration, - final int? uses, - ) async { - Response response; - - final Dio client = await getClient(); - final Map data = {}; - if (expiration != null) { - data['expiration'] = '${expiration.toIso8601String()}Z'; - print(data['expiration']); - } - if (uses != null) { - data['uses'] = uses; - } - try { - response = await client.post( - '/auth/recovery_token', - data: data, - ); - } on DioError catch (e) { - print(e.message); - return ApiResponse( - errorMessage: e.message, - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: '', - ); - } finally { - close(client); - } - - final int code = response.statusCode ?? HttpStatus.internalServerError; - - return ApiResponse( - statusCode: code, - data: response.data != null ? response.data['token'] : '', - ); - } - - Future> useRecoveryToken(final DeviceToken token) async { - Response response; - - final Dio client = await getClient(); - try { - response = await client.post( - '/auth/recovery_token/use', - data: { - 'token': token.token, - 'device': token.device, - }, - ); - } on DioError catch (e) { - print(e.message); - return ApiResponse( - errorMessage: e.message, - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: '', - ); - } finally { - client.close(); - } - - final int code = response.statusCode ?? HttpStatus.internalServerError; - - return ApiResponse( - statusCode: code, - data: response.data != null ? response.data['token'] : '', - ); - } - - Future> authorizeDevice(final DeviceToken token) async { - Response response; - - final Dio client = await getClient(); - try { - response = await client.post( - '/auth/new_device/authorize', - data: { - 'token': token.token, - 'device': token.device, - }, - ); - } on DioError catch (e) { - print(e.message); - return ApiResponse( - errorMessage: e.message, - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: '', - ); - } finally { - client.close(); - } - - final int code = response.statusCode ?? HttpStatus.internalServerError; - - return ApiResponse(statusCode: code, data: response.data['token'] ?? ''); - } - - Future> createDeviceToken() async { - Response response; - - final Dio client = await getClient(); - try { - response = await client.post('/auth/new_device'); - } on DioError catch (e) { - print(e.message); - return ApiResponse( - errorMessage: e.message, - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: '', - ); - } finally { - client.close(); - } - - final int code = response.statusCode ?? HttpStatus.internalServerError; - - return ApiResponse( - statusCode: code, - data: response.data != null ? response.data['token'] : '', - ); - } - - Future> deleteDeviceToken() async { - Response response; - - final Dio client = await getClient(); - try { - response = await client.delete('/auth/new_device'); - } on DioError catch (e) { - print(e.message); - return ApiResponse( - errorMessage: e.message, - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: '', - ); - } finally { - client.close(); - } - - final int code = response.statusCode ?? HttpStatus.internalServerError; - - return ApiResponse(statusCode: code, data: response.data ?? ''); - } - - Future>> getApiTokens() async { - Response response; - - final Dio client = await getClient(); - try { - response = await client.get('/auth/tokens'); - } on DioError catch (e) { - print(e.message); - return ApiResponse( - errorMessage: e.message, - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: [], - ); - } finally { - client.close(); - } - - final int code = response.statusCode ?? HttpStatus.internalServerError; - - return ApiResponse( - statusCode: code, - data: (response.data != null) - ? response.data - .map((final e) => ApiToken.fromJson(e)) - .toList() - : [], - ); - } - - Future> refreshCurrentApiToken() async { - Response response; - - final Dio client = await getClient(); - try { - response = await client.post('/auth/tokens'); - } on DioError catch (e) { - print(e.message); - return ApiResponse( - errorMessage: e.message, - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: '', - ); - } finally { - client.close(); - } - - final int code = response.statusCode ?? HttpStatus.internalServerError; - - return ApiResponse( - statusCode: code, - data: response.data != null ? response.data['token'] : '', - ); - } - - Future> deleteApiToken(final String device) async { - Response response; - final Dio client = await getClient(); - try { - response = await client.delete( - '/auth/tokens', - data: { - 'token_name': device, - }, - ); - } on DioError catch (e) { - print(e.message); - return ApiResponse( - errorMessage: e.message, - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: null, - ); - } finally { - client.close(); - } - - final int code = response.statusCode ?? HttpStatus.internalServerError; - return ApiResponse(statusCode: code, data: null); - } -} - -extension UrlServerExt on ServiceTypes { - String get url { - switch (this) { - // case ServiceTypes.mail: - // return ''; // cannot be switch off - // case ServiceTypes.messenger: - // return ''; // external service - // case ServiceTypes.video: - // return ''; // jitsi meet not working - case ServiceTypes.passwordManager: - return 'bitwarden'; - case ServiceTypes.cloud: - return 'nextcloud'; - case ServiceTypes.socialNetwork: - return 'pleroma'; - case ServiceTypes.git: - return 'gitea'; - case ServiceTypes.vpn: - return 'ocserv'; - default: - throw Exception('wrong state'); - } - } -} diff --git a/lib/logic/api_maps/tls_options.dart b/lib/logic/api_maps/tls_options.dart new file mode 100644 index 00000000..b216841c --- /dev/null +++ b/lib/logic/api_maps/tls_options.dart @@ -0,0 +1,16 @@ +/// Controls staging environment for network +class TlsOptions { + /// Whether we request for staging temprorary certificates. + /// Hardcode to 'true' in the middle of testing to not + /// get your domain banned by constant certificate renewal + /// + /// If set to 'true', the 'verifyCertificate' becomes useless + static bool stagingAcme = false; + + /// Should we consider CERTIFICATE_VERIFY_FAILED code an error + /// For now it's just a global variable and DNS API + /// classes can change it at will + /// + /// Doesn't matter if 'statingAcme' is set to 'true' + static bool verifyCertificate = false; +} diff --git a/lib/logic/common_enum/common_enum.dart b/lib/logic/common_enum/common_enum.dart index 3a1a4d80..557448b1 100644 --- a/lib/logic/common_enum/common_enum.dart +++ b/lib/logic/common_enum/common_enum.dart @@ -1,7 +1,3 @@ -import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/material.dart'; -import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; - enum LoadingStatus { uninitialized, refreshing, @@ -9,133 +5,19 @@ enum LoadingStatus { error, } -enum InitializingSteps { - setHetznerKey, - setCloudFlareKey, - setDomainName, - setRootUser, - createServer, - checkCloudFlareDns, - startServer, - checkSystemDnsAndDkimSet, -} - -enum Period { hour, day, month } - -enum ServiceTypes { - mail, - messenger, - passwordManager, - video, - cloud, - socialNetwork, - git, - vpn, -} - -extension ServiceTypesExt on ServiceTypes { - String get title { - switch (this) { - case ServiceTypes.mail: - return 'services.mail.title'.tr(); - case ServiceTypes.messenger: - return 'services.messenger.title'.tr(); - case ServiceTypes.passwordManager: - return 'services.password_manager.title'.tr(); - case ServiceTypes.video: - return 'services.video.title'.tr(); - case ServiceTypes.cloud: - return 'services.cloud.title'.tr(); - case ServiceTypes.socialNetwork: - return 'services.social_network.title'.tr(); - case ServiceTypes.git: - return 'services.git.title'.tr(); - case ServiceTypes.vpn: - return 'services.vpn.title'.tr(); - } - } - - String get subtitle { - switch (this) { - case ServiceTypes.mail: - return 'services.mail.subtitle'.tr(); - case ServiceTypes.messenger: - return 'services.messenger.subtitle'.tr(); - case ServiceTypes.passwordManager: - return 'services.password_manager.subtitle'.tr(); - case ServiceTypes.video: - return 'services.video.subtitle'.tr(); - case ServiceTypes.cloud: - return 'services.cloud.subtitle'.tr(); - case ServiceTypes.socialNetwork: - return 'services.social_network.subtitle'.tr(); - case ServiceTypes.git: - return 'services.git.subtitle'.tr(); - case ServiceTypes.vpn: - return 'services.vpn.subtitle'.tr(); - } - } - - String get loginInfo { - switch (this) { - case ServiceTypes.mail: - return 'services.mail.login_info'.tr(); - case ServiceTypes.messenger: - return 'services.messenger.login_info'.tr(); - case ServiceTypes.passwordManager: - return 'services.password_manager.login_info'.tr(); - case ServiceTypes.video: - return 'services.video.login_info'.tr(); - case ServiceTypes.cloud: - return 'services.cloud.login_info'.tr(); - case ServiceTypes.socialNetwork: - return 'services.social_network.login_info'.tr(); - case ServiceTypes.git: - return 'services.git.login_info'.tr(); - case ServiceTypes.vpn: - return ''; - } - } - - String get subdomain { - switch (this) { - case ServiceTypes.passwordManager: - return 'password'; - case ServiceTypes.video: - return 'meet'; - case ServiceTypes.cloud: - return 'cloud'; - case ServiceTypes.socialNetwork: - return 'social'; - case ServiceTypes.git: - return 'git'; - case ServiceTypes.vpn: - case ServiceTypes.messenger: - default: - return ''; - } - } - - IconData get icon { - switch (this) { - case ServiceTypes.mail: - return BrandIcons.envelope; - case ServiceTypes.messenger: - return BrandIcons.messanger; - case ServiceTypes.passwordManager: - return BrandIcons.key; - case ServiceTypes.video: - return BrandIcons.webcam; - case ServiceTypes.cloud: - return BrandIcons.upload; - case ServiceTypes.socialNetwork: - return BrandIcons.social; - case ServiceTypes.git: - return BrandIcons.git; - case ServiceTypes.vpn: - return Icons.vpn_lock_outlined; - } - } - - String get txt => toString().split('.')[1]; +enum Period { + hour, + day, + month; + + int get stepPeriodInSeconds { + switch (this) { + case Period.hour: + return 18; + case Period.day: + return 432; + case Period.month: + return 6480; + } + } } diff --git a/lib/logic/cubit/app_settings/app_settings_cubit.dart b/lib/logic/cubit/app_settings/app_settings_cubit.dart index 06b46730..af1ab3e0 100644 --- a/lib/logic/cubit/app_settings/app_settings_cubit.dart +++ b/lib/logic/cubit/app_settings/app_settings_cubit.dart @@ -1,7 +1,12 @@ +import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:hive/hive.dart'; +import 'package:material_color_utilities/material_color_utilities.dart' + as color_utils; +import 'package:selfprivacy/config/brand_colors.dart'; import 'package:selfprivacy/config/hive_config.dart'; +import 'package:selfprivacy/theming/factory/app_theme_factory.dart'; export 'package:provider/provider.dart'; @@ -10,25 +15,37 @@ part 'app_settings_state.dart'; class AppSettingsCubit extends Cubit { AppSettingsCubit({ required final bool isDarkModeOn, + required final bool isAutoDarkModeOn, required final bool isOnboardingShowing, }) : super( AppSettingsState( isDarkModeOn: isDarkModeOn, + isAutoDarkModeOn: isAutoDarkModeOn, isOnboardingShowing: isOnboardingShowing, ), ); Box box = Hive.box(BNames.appSettingsBox); - void load() { + void load() async { final bool? isDarkModeOn = box.get(BNames.isDarkModeOn); + final bool? isAutoDarkModeOn = box.get(BNames.isAutoDarkModeOn); final bool? isOnboardingShowing = box.get(BNames.isOnboardingShowing); emit( state.copyWith( isDarkModeOn: isDarkModeOn, + isAutoDarkModeOn: isAutoDarkModeOn, isOnboardingShowing: isOnboardingShowing, ), ); + WidgetsFlutterBinding.ensureInitialized(); + final color_utils.CorePalette? colorPalette = + await AppThemeFactory.getCorePalette(); + emit( + state.copyWith( + corePalette: colorPalette, + ), + ); } void updateDarkMode({required final bool isDarkModeOn}) { @@ -36,9 +53,14 @@ class AppSettingsCubit extends Cubit { emit(state.copyWith(isDarkModeOn: isDarkModeOn)); } - void turnOffOnboarding() { - box.put(BNames.isOnboardingShowing, false); + void updateAutoDarkMode({required final bool isAutoDarkModeOn}) { + box.put(BNames.isAutoDarkModeOn, isAutoDarkModeOn); + emit(state.copyWith(isAutoDarkModeOn: isAutoDarkModeOn)); + } - emit(state.copyWith(isOnboardingShowing: false)); + void turnOffOnboarding({final bool isOnboardingShowing = false}) { + box.put(BNames.isOnboardingShowing, isOnboardingShowing); + + emit(state.copyWith(isOnboardingShowing: isOnboardingShowing)); } } diff --git a/lib/logic/cubit/app_settings/app_settings_state.dart b/lib/logic/cubit/app_settings/app_settings_state.dart index 92da9667..ad364d66 100644 --- a/lib/logic/cubit/app_settings/app_settings_state.dart +++ b/lib/logic/cubit/app_settings/app_settings_state.dart @@ -3,21 +3,33 @@ part of 'app_settings_cubit.dart'; class AppSettingsState extends Equatable { const AppSettingsState({ required this.isDarkModeOn, + required this.isAutoDarkModeOn, required this.isOnboardingShowing, + this.corePalette, }); final bool isDarkModeOn; + final bool isAutoDarkModeOn; final bool isOnboardingShowing; + final color_utils.CorePalette? corePalette; AppSettingsState copyWith({ final bool? isDarkModeOn, + final bool? isAutoDarkModeOn, final bool? isOnboardingShowing, + final color_utils.CorePalette? corePalette, }) => AppSettingsState( isDarkModeOn: isDarkModeOn ?? this.isDarkModeOn, + isAutoDarkModeOn: isAutoDarkModeOn ?? this.isAutoDarkModeOn, isOnboardingShowing: isOnboardingShowing ?? this.isOnboardingShowing, + corePalette: corePalette ?? this.corePalette, ); + color_utils.CorePalette get corePaletteOrDefault => + corePalette ?? color_utils.CorePalette.of(BrandColors.primary.value); + @override - List get props => [isDarkModeOn, isOnboardingShowing]; + List get props => + [isDarkModeOn, isAutoDarkModeOn, isOnboardingShowing, corePalette]; } diff --git a/lib/logic/cubit/backups/backups_cubit.dart b/lib/logic/cubit/backups/backups_cubit.dart index 63cdfb3e..ca7e0a50 100644 --- a/lib/logic/cubit/backups/backups_cubit.dart +++ b/lib/logic/cubit/backups/backups_cubit.dart @@ -2,11 +2,14 @@ import 'dart:async'; import 'package:easy_localization/easy_localization.dart'; import 'package:selfprivacy/config/get_it_config.dart'; -import 'package:selfprivacy/logic/api_maps/backblaze.dart'; -import 'package:selfprivacy/logic/api_maps/server.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/backblaze.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart'; import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart'; -import 'package:selfprivacy/logic/models/json/backup.dart'; +import 'package:selfprivacy/logic/models/backup.dart'; +import 'package:selfprivacy/logic/models/hive/backups_credential.dart'; +import 'package:selfprivacy/logic/models/initialize_repository_input.dart'; +import 'package:selfprivacy/logic/models/service.dart'; part 'backups_state.dart'; @@ -24,145 +27,149 @@ class BackupsCubit extends ServerInstallationDependendCubit { Future load() async { if (serverInstallationCubit.state is ServerInstallationFinished) { final BackblazeBucket? bucket = getIt().backblazeBucket; - if (bucket == null) { - emit( - const BackupsState( - isInitialized: false, - preventActions: false, - refreshing: false, - ), - ); - } else { - final BackupStatus status = await api.getBackupStatus(); - switch (status.status) { - case BackupStatusEnum.noKey: - case BackupStatusEnum.notInitialized: - emit( - BackupsState( - backups: const [], - isInitialized: true, - preventActions: false, - progress: 0, - status: status.status, - refreshing: false, - ), - ); - break; - case BackupStatusEnum.initializing: - emit( - BackupsState( - backups: const [], - isInitialized: true, - preventActions: false, - progress: 0, - status: status.status, - refreshTimer: const Duration(seconds: 10), - refreshing: false, - ), - ); - break; - case BackupStatusEnum.initialized: - case BackupStatusEnum.error: - final List backups = await api.getBackups(); - emit( - BackupsState( - backups: backups, - isInitialized: true, - preventActions: false, - progress: status.progress, - status: status.status, - error: status.errorMessage ?? '', - refreshing: false, - ), - ); - break; - case BackupStatusEnum.backingUp: - case BackupStatusEnum.restoring: - final List backups = await api.getBackups(); - emit( - BackupsState( - backups: backups, - isInitialized: true, - preventActions: true, - progress: status.progress, - status: status.status, - error: status.errorMessage ?? '', - refreshTimer: const Duration(seconds: 5), - refreshing: false, - ), - ); - break; - default: - emit(const BackupsState()); - } - Timer(state.refreshTimer, () => updateBackups(useTimer: true)); - } + final BackupConfiguration? backupConfig = + await api.getBackupsConfiguration(); + final List backups = await api.getBackups(); + backups.sort((final a, final b) => b.time.compareTo(a.time)); + emit( + state.copyWith( + backblazeBucket: bucket, + isInitialized: backupConfig?.isInitialized, + autobackupPeriod: backupConfig?.autobackupPeriod ?? Duration.zero, + autobackupQuotas: backupConfig?.autobackupQuotas, + backups: backups, + preventActions: false, + refreshing: false, + ), + ); } } - Future createBucket() async { + Future initializeBackups() async { emit(state.copyWith(preventActions: true)); - final String domain = serverInstallationCubit.state.serverDomain!.domainName - .replaceAll(RegExp(r'[^a-zA-Z0-9]'), '-'); - final int serverId = serverInstallationCubit.state.serverDetails!.id; - String bucketName = 'selfprivacy-$domain-$serverId'; - // If bucket name is too long, shorten it - if (bucketName.length > 49) { - bucketName = bucketName.substring(0, 49); + final String? encryptionKey = + (await api.getBackupsConfiguration())?.encryptionKey; + if (encryptionKey == null) { + getIt() + .showSnackBar("Couldn't get encryption key from your server."); + emit(state.copyWith(preventActions: false)); + return; } - final String bucketId = await backblaze.createBucket(bucketName); - final BackblazeApplicationKey key = await backblaze.createKey(bucketId); - final BackblazeBucket bucket = BackblazeBucket( - bucketId: bucketId, - bucketName: bucketName, - applicationKey: key.applicationKey, - applicationKeyId: key.applicationKeyId, + final BackblazeBucket bucket; + + if (state.backblazeBucket == null) { + final String domain = serverInstallationCubit + .state.serverDomain!.domainName + .replaceAll(RegExp(r'[^a-zA-Z0-9]'), '-'); + final int serverId = serverInstallationCubit.state.serverDetails!.id; + String bucketName = 'selfprivacy-$domain-$serverId'; + // If bucket name is too long, shorten it + if (bucketName.length > 49) { + bucketName = bucketName.substring(0, 49); + } + final String bucketId = await backblaze.createBucket(bucketName); + + final BackblazeApplicationKey key = await backblaze.createKey(bucketId); + bucket = BackblazeBucket( + bucketId: bucketId, + bucketName: bucketName, + applicationKey: key.applicationKey, + applicationKeyId: key.applicationKeyId, + encryptionKey: encryptionKey, + ); + + await getIt().storeBackblazeBucket(bucket); + emit(state.copyWith(backblazeBucket: bucket)); + } else { + bucket = state.backblazeBucket!; + } + + final GenericResult result = await api.initializeRepository( + InitializeRepositoryInput( + provider: BackupsProviderType.backblaze, + locationId: bucket.bucketId, + locationName: bucket.bucketName, + login: bucket.applicationKeyId, + password: bucket.applicationKey, + ), + ); + if (result.success == false) { + getIt() + .showSnackBar(result.message ?? 'Unknown error'); + emit(state.copyWith(preventActions: false)); + return; + } + await updateBackups(); + getIt().showSnackBar( + 'Backups repository is now initializing. It may take a while.', ); - await getIt().storeBackblazeBucket(bucket); - await api.uploadBackblazeConfig(bucket); - await updateBackups(); - - emit(state.copyWith(isInitialized: true, preventActions: false)); + emit(state.copyWith(preventActions: false)); } Future reuploadKey() async { emit(state.copyWith(preventActions: true)); - final BackblazeBucket? bucket = getIt().backblazeBucket; + BackblazeBucket? bucket = getIt().backblazeBucket; if (bucket == null) { emit(state.copyWith(isInitialized: false)); } else { - await api.uploadBackblazeConfig(bucket); - emit(state.copyWith(isInitialized: true, preventActions: false)); - getIt().showSnackBar('providers.backup.reuploadedKey'); + String login = bucket.applicationKeyId; + String password = bucket.applicationKey; + if (login.isEmpty || password.isEmpty) { + final BackblazeApplicationKey key = + await backblaze.createKey(bucket.bucketId); + login = key.applicationKeyId; + password = key.applicationKey; + bucket = BackblazeBucket( + bucketId: bucket.bucketId, + bucketName: bucket.bucketName, + encryptionKey: bucket.encryptionKey, + applicationKey: password, + applicationKeyId: login, + ); + await getIt().storeBackblazeBucket(bucket); + emit(state.copyWith(backblazeBucket: bucket)); + } + final GenericResult result = await api.initializeRepository( + InitializeRepositoryInput( + provider: BackupsProviderType.backblaze, + locationId: bucket.bucketId, + locationName: bucket.bucketName, + login: login, + password: password, + ), + ); + if (result.success == false) { + getIt() + .showSnackBar(result.message ?? 'Unknown error'); + emit(state.copyWith(preventActions: false)); + return; + } else { + emit(state.copyWith(preventActions: false)); + getIt().showSnackBar('backup.reuploaded_key'.tr()); + await updateBackups(); + } } } - Duration refreshTimeFromState(final BackupStatusEnum status) { - switch (status) { - case BackupStatusEnum.backingUp: - case BackupStatusEnum.restoring: - return const Duration(seconds: 5); - case BackupStatusEnum.initializing: - return const Duration(seconds: 10); - default: - return const Duration(seconds: 60); - } - } + @Deprecated("we don't have states") + Duration refreshTimeFromState() => const Duration(seconds: 60); Future updateBackups({final bool useTimer = false}) async { emit(state.copyWith(refreshing: true)); - final List backups = await api.getBackups(); - final BackupStatus status = await api.getBackupStatus(); + final backups = await api.getBackups(); + backups.sort((final a, final b) => b.time.compareTo(a.time)); + final backupConfig = await api.getBackupsConfiguration(); + emit( state.copyWith( backups: backups, - progress: status.progress, - status: status.status, - error: status.errorMessage, - refreshTimer: refreshTimeFromState(status.status), + refreshTimer: refreshTimeFromState(), refreshing: false, + isInitialized: backupConfig?.isInitialized ?? false, + autobackupPeriod: backupConfig?.autobackupPeriod, + autobackupQuotas: backupConfig?.autobackupQuotas, ), ); if (useTimer) { @@ -172,25 +179,99 @@ class BackupsCubit extends ServerInstallationDependendCubit { Future forceUpdateBackups() async { emit(state.copyWith(preventActions: true)); + getIt().showSnackBar('backup.refetching_list'.tr()); await api.forceBackupListReload(); - getIt() - .showSnackBar('providers.backup.refetchingList'.tr()); emit(state.copyWith(preventActions: false)); } - Future createBackup() async { + Future createMultipleBackups(final List services) async { emit(state.copyWith(preventActions: true)); - await api.startBackup(); + for (final service in services) { + await api.startBackup(service.id); + } await updateBackups(); emit(state.copyWith(preventActions: false)); } - Future restoreBackup(final String backupId) async { + Future createBackup(final String serviceId) async { emit(state.copyWith(preventActions: true)); - await api.restoreBackup(backupId); + await api.startBackup(serviceId); + await updateBackups(); emit(state.copyWith(preventActions: false)); } + Future restoreBackup( + final String backupId, + final BackupRestoreStrategy strategy, + ) async { + emit(state.copyWith(preventActions: true)); + await api.restoreBackup(backupId, strategy); + emit(state.copyWith(preventActions: false)); + } + + Future setAutobackupPeriod(final Duration? period) async { + emit(state.copyWith(preventActions: true)); + final result = await api.setAutobackupPeriod(period: period?.inMinutes); + if (result.success == false) { + getIt() + .showSnackBar(result.message ?? 'Unknown error'); + emit(state.copyWith(preventActions: false)); + } else { + getIt() + .showSnackBar('backup.autobackup_period_set'.tr()); + emit( + state.copyWith( + preventActions: false, + autobackupPeriod: period ?? Duration.zero, + ), + ); + } + await updateBackups(); + } + + Future setAutobackupQuotas(final AutobackupQuotas quotas) async { + emit(state.copyWith(preventActions: true)); + final result = await api.setAutobackupQuotas(quotas); + if (result.success == false) { + getIt() + .showSnackBar(result.message ?? 'Unknown error'); + emit(state.copyWith(preventActions: false)); + } else { + getIt().showSnackBar('backup.quotas_set'.tr()); + emit( + state.copyWith( + preventActions: false, + autobackupQuotas: quotas, + ), + ); + } + await updateBackups(); + } + + Future forgetSnapshot(final String snapshotId) async { + final result = await api.forgetSnapshot(snapshotId); + if (!result.success) { + getIt().showSnackBar('jobs.generic_error'.tr()); + return; + } + + if (result.data == false) { + getIt() + .showSnackBar('backup.forget_snapshot_error'.tr()); + } + + // Optimistic update + final backups = state.backups; + final index = + backups.indexWhere((final snapshot) => snapshot.id == snapshotId); + if (index != -1) { + backups.removeAt(index); + emit(state.copyWith(backups: backups)); + } + + await updateBackups(); + } + @override void clear() async { emit(const BackupsState()); diff --git a/lib/logic/cubit/backups/backups_state.dart b/lib/logic/cubit/backups/backups_state.dart index 33ec52c8..887396d7 100644 --- a/lib/logic/cubit/backups/backups_state.dart +++ b/lib/logic/cubit/backups/backups_state.dart @@ -4,53 +4,58 @@ class BackupsState extends ServerInstallationDependendState { const BackupsState({ this.isInitialized = false, this.backups = const [], - this.progress = 0.0, - this.status = BackupStatusEnum.noKey, this.preventActions = true, - this.error = '', this.refreshTimer = const Duration(seconds: 60), this.refreshing = true, + this.autobackupPeriod, + this.backblazeBucket, + this.autobackupQuotas, }); final bool isInitialized; final List backups; - final double progress; - final BackupStatusEnum status; final bool preventActions; - final String error; final Duration refreshTimer; final bool refreshing; + final Duration? autobackupPeriod; + final BackblazeBucket? backblazeBucket; + final AutobackupQuotas? autobackupQuotas; + + List serviceBackups(final String serviceId) => backups + .where((final backup) => backup.serviceId == serviceId) + .toList(growable: false); @override List get props => [ isInitialized, backups, - progress, preventActions, - status, - error, refreshTimer, - refreshing + refreshing, ]; BackupsState copyWith({ final bool? isInitialized, final List? backups, - final double? progress, - final BackupStatusEnum? status, final bool? preventActions, - final String? error, final Duration? refreshTimer, final bool? refreshing, + final Duration? autobackupPeriod, + final BackblazeBucket? backblazeBucket, + final AutobackupQuotas? autobackupQuotas, }) => BackupsState( isInitialized: isInitialized ?? this.isInitialized, backups: backups ?? this.backups, - progress: progress ?? this.progress, - status: status ?? this.status, preventActions: preventActions ?? this.preventActions, - error: error ?? this.error, refreshTimer: refreshTimer ?? this.refreshTimer, refreshing: refreshing ?? this.refreshing, + // The autobackupPeriod might be null, so if the duration is set to 0, we + // set it to null. + autobackupPeriod: autobackupPeriod?.inSeconds == 0 + ? null + : autobackupPeriod ?? this.autobackupPeriod, + backblazeBucket: backblazeBucket ?? this.backblazeBucket, + autobackupQuotas: autobackupQuotas ?? this.autobackupQuotas, ); } diff --git a/lib/logic/cubit/client_jobs/client_jobs_cubit.dart b/lib/logic/cubit/client_jobs/client_jobs_cubit.dart new file mode 100644 index 00000000..59a33673 --- /dev/null +++ b/lib/logic/cubit/client_jobs/client_jobs_cubit.dart @@ -0,0 +1,98 @@ +import 'dart:async'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart'; +import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; +import 'package:selfprivacy/logic/cubit/users/users_cubit.dart'; +import 'package:selfprivacy/logic/models/job.dart'; + +export 'package:provider/provider.dart'; + +part 'client_jobs_state.dart'; + +class JobsCubit extends Cubit { + JobsCubit({ + required this.usersCubit, + required this.servicesCubit, + }) : super(JobsStateEmpty()); + + final ServerApi api = ServerApi(); + final UsersCubit usersCubit; + final ServicesCubit servicesCubit; + + void addJob(final ClientJob job) { + final jobs = currentJobList; + if (job.canAddTo(jobs)) { + _updateJobsState([ + ...jobs, + ...[job], + ]); + } + } + + void removeJob(final String id) { + final JobsState newState = (state as JobsStateWithJobs).removeById(id); + emit(newState); + } + + List get currentJobList { + final List jobs = []; + if (state is JobsStateWithJobs) { + jobs.addAll((state as JobsStateWithJobs).clientJobList); + } + + return jobs; + } + + void _updateJobsState(final List newJobs) { + getIt().showSnackBar('jobs.job_added'.tr()); + emit(JobsStateWithJobs(newJobs)); + } + + Future rebootServer() async { + emit(JobsStateLoading()); + final rebootResult = await api.reboot(); + if (rebootResult.success && rebootResult.data != null) { + getIt().showSnackBar('jobs.reboot_success'.tr()); + } else { + getIt().showSnackBar('jobs.reboot_failed'.tr()); + } + emit(JobsStateEmpty()); + } + + Future upgradeServer() async { + emit(JobsStateLoading()); + final bool isPullSuccessful = await api.pullConfigurationUpdate(); + final bool isSuccessful = await api.upgrade(); + if (isSuccessful) { + if (!isPullSuccessful) { + getIt().showSnackBar('jobs.config_pull_failed'.tr()); + } else { + getIt().showSnackBar('jobs.upgrade_success'.tr()); + } + } else { + getIt().showSnackBar('jobs.upgrade_failed'.tr()); + } + emit(JobsStateEmpty()); + } + + Future applyAll() async { + if (state is JobsStateWithJobs) { + final List jobs = (state as JobsStateWithJobs).clientJobList; + emit(JobsStateLoading()); + + for (final ClientJob job in jobs) { + job.execute(this); + } + + await api.pullConfigurationUpdate(); + await api.apply(); + await servicesCubit.load(); + + emit(JobsStateEmpty()); + } + } +} diff --git a/lib/logic/cubit/jobs/jobs_state.dart b/lib/logic/cubit/client_jobs/client_jobs_state.dart similarity index 59% rename from lib/logic/cubit/jobs/jobs_state.dart rename to lib/logic/cubit/client_jobs/client_jobs_state.dart index dbcf968e..2bb31856 100644 --- a/lib/logic/cubit/jobs/jobs_state.dart +++ b/lib/logic/cubit/client_jobs/client_jobs_state.dart @@ -1,4 +1,4 @@ -part of 'jobs_cubit.dart'; +part of 'client_jobs_cubit.dart'; abstract class JobsState extends Equatable { @override @@ -10,13 +10,11 @@ class JobsStateLoading extends JobsState {} class JobsStateEmpty extends JobsState {} class JobsStateWithJobs extends JobsState { - JobsStateWithJobs(this.jobList); - final List jobList; - + JobsStateWithJobs(this.clientJobList); + final List clientJobList; JobsState removeById(final String id) { - final List newJobsList = - jobList.where((final element) => element.id != id).toList(); - + final List newJobsList = + clientJobList.where((final element) => element.id != id).toList(); if (newJobsList.isEmpty) { return JobsStateEmpty(); } @@ -24,5 +22,5 @@ class JobsStateWithJobs extends JobsState { } @override - List get props => jobList; + List get props => clientJobList; } diff --git a/lib/logic/cubit/devices/devices_cubit.dart b/lib/logic/cubit/devices/devices_cubit.dart index f0380635..5debf20e 100644 --- a/lib/logic/cubit/devices/devices_cubit.dart +++ b/lib/logic/cubit/devices/devices_cubit.dart @@ -1,5 +1,5 @@ import 'package:selfprivacy/config/get_it_config.dart'; -import 'package:selfprivacy/logic/api_maps/server.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart'; import 'package:selfprivacy/logic/common_enum/common_enum.dart'; import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; import 'package:selfprivacy/logic/models/json/api_token.dart'; @@ -15,18 +15,17 @@ class ApiDevicesCubit @override void load() async { - if (serverInstallationCubit.state is ServerInstallationFinished) { - final List? devices = await _getApiTokens(); - if (devices != null) { - emit(ApiDevicesState(devices, LoadingStatus.success)); - } else { - emit(const ApiDevicesState([], LoadingStatus.error)); - } - } + // if (serverInstallationCubit.state is ServerInstallationFinished) { + _refetch(); + // } } Future refresh() async { emit(const ApiDevicesState([], LoadingStatus.refreshing)); + _refetch(); + } + + void _refetch() async { final List? devices = await _getApiTokens(); if (devices != null) { emit(ApiDevicesState(devices, LoadingStatus.success)); @@ -36,8 +35,8 @@ class ApiDevicesCubit } Future?> _getApiTokens() async { - final ApiResponse> response = await api.getApiTokens(); - if (response.isSuccess) { + final GenericResult> response = await api.getApiTokens(); + if (response.success) { return response.data; } else { return null; @@ -45,8 +44,8 @@ class ApiDevicesCubit } Future deleteDevice(final ApiToken device) async { - final ApiResponse response = await api.deleteApiToken(device.name); - if (response.isSuccess) { + final GenericResult response = await api.deleteApiToken(device.name); + if (response.success) { emit( ApiDevicesState( state.devices.where((final d) => d.name != device.name).toList(), @@ -55,17 +54,17 @@ class ApiDevicesCubit ); } else { getIt() - .showSnackBar(response.errorMessage ?? 'Error deleting device'); + .showSnackBar(response.message ?? 'Error deleting device'); } } Future getNewDeviceKey() async { - final ApiResponse response = await api.createDeviceToken(); - if (response.isSuccess) { + final GenericResult response = await api.createDeviceToken(); + if (response.success) { return response.data; } else { getIt().showSnackBar( - response.errorMessage ?? 'Error getting new device key', + response.message ?? 'Error getting new device key', ); return null; } diff --git a/lib/logic/cubit/dns_records/dns_records_cubit.dart b/lib/logic/cubit/dns_records/dns_records_cubit.dart index 0590b065..6f295274 100644 --- a/lib/logic/cubit/dns_records/dns_records_cubit.dart +++ b/lib/logic/cubit/dns_records/dns_records_cubit.dart @@ -1,10 +1,12 @@ import 'package:cubit_form/cubit_form.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/desired_dns_record.dart'; import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/logic/models/json/dns_records.dart'; -import 'package:selfprivacy/logic/api_maps/cloudflare.dart'; -import 'package:selfprivacy/logic/api_maps/server.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart'; +import 'package:selfprivacy/logic/providers/providers_controller.dart'; +import 'package:selfprivacy/utils/network_utils.dart'; part 'dns_records_state.dart'; @@ -17,81 +19,51 @@ class DnsRecordsCubit ); final ServerApi api = ServerApi(); - final CloudflareApi cloudflare = CloudflareApi(); @override Future load() async { emit( DnsRecordsState( dnsState: DnsRecordsStatus.refreshing, - dnsRecords: _getDesiredDnsRecords( - serverInstallationCubit.state.serverDomain?.domainName, - '', - '', - ), + dnsRecords: + ProvidersController.currentDnsProvider?.getDesiredDnsRecords( + serverInstallationCubit.state.serverDomain?.domainName, + '', + '', + ) ?? + [], ), ); - print('Loading DNS status'); + if (serverInstallationCubit.state is ServerInstallationFinished) { final ServerDomain? domain = serverInstallationCubit.state.serverDomain; final String? ipAddress = serverInstallationCubit.state.serverDetails?.ip4; - if (domain != null && ipAddress != null) { - final List records = - await cloudflare.getDnsRecords(cloudFlareDomain: domain); - final String? dkimPublicKey = await api.getDkim(); - final List desiredRecords = - _getDesiredDnsRecords(domain.domainName, ipAddress, dkimPublicKey); - final List foundRecords = []; - for (final DesiredDnsRecord record in desiredRecords) { - if (record.description == - 'providers.domain.record_description.dkim') { - final DnsRecord foundRecord = records.firstWhere( - (final r) => r.name == record.name && r.type == record.type, - orElse: () => DnsRecord( - name: record.name, - type: record.type, - content: '', - ttl: 800, - proxied: false, - ), - ); - // remove all spaces and tabulators from - // the foundRecord.content and the record.content - // to compare them - final String? foundContent = - foundRecord.content?.replaceAll(RegExp(r'\s+'), ''); - final String content = - record.content.replaceAll(RegExp(r'\s+'), ''); - if (foundContent == content) { - foundRecords.add(record.copyWith(isSatisfied: true)); - } else { - foundRecords.add(record.copyWith(isSatisfied: false)); - } - } else { - if (records.any( - (final r) => - r.name == record.name && - r.type == record.type && - r.content == record.content, - )) { - foundRecords.add(record.copyWith(isSatisfied: true)); - } else { - foundRecords.add(record.copyWith(isSatisfied: false)); - } - } - } - emit( - DnsRecordsState( - dnsRecords: foundRecords, - dnsState: foundRecords.any((final r) => r.isSatisfied == false) - ? DnsRecordsStatus.error - : DnsRecordsStatus.good, - ), - ); - } else { + if (domain == null && ipAddress == null) { emit(const DnsRecordsState()); + return; } + + final foundRecords = + await ProvidersController.currentDnsProvider!.validateDnsRecords( + domain!, + ipAddress!, + extractDkimRecord(await api.getDnsRecords())?.content ?? '', + ); + + if (!foundRecords.success || foundRecords.data.isEmpty) { + emit(const DnsRecordsState()); + return; + } + + emit( + DnsRecordsState( + dnsRecords: foundRecords.data, + dnsState: foundRecords.data.any((final r) => r.isSatisfied == false) + ? DnsRecordsStatus.error + : DnsRecordsStatus.good, + ), + ); } } @@ -103,7 +75,7 @@ class DnsRecordsCubit @override Future clear() async { - emit(const DnsRecordsState(dnsState: DnsRecordsStatus.error)); + emit(const DnsRecordsState(dnsState: DnsRecordsStatus.uninitialized)); } Future refresh() async { @@ -115,93 +87,23 @@ class DnsRecordsCubit emit(state.copyWith(dnsState: DnsRecordsStatus.refreshing)); final ServerDomain? domain = serverInstallationCubit.state.serverDomain; final String? ipAddress = serverInstallationCubit.state.serverDetails?.ip4; - final String? dkimPublicKey = await api.getDkim(); - await cloudflare.removeSimilarRecords(cloudFlareDomain: domain!); - await cloudflare.createMultipleDnsRecords( - cloudFlareDomain: domain, + await ProvidersController.currentDnsProvider!.removeDomainRecords( + domain: domain!, + ); + await ProvidersController.currentDnsProvider!.createDomainRecords( + domain: domain, ip4: ipAddress, ); - await cloudflare.setDkim(dkimPublicKey ?? '', domain); + + final List records = await api.getDnsRecords(); + final DnsRecord? dkimRecord = extractDkimRecord(records); + if (dkimRecord != null) { + await ProvidersController.currentDnsProvider!.setDnsRecord( + dkimRecord, + domain, + ); + } + await load(); } - - List _getDesiredDnsRecords( - final String? domainName, - final String? ipAddress, - final String? dkimPublicKey, - ) { - if (domainName == null || ipAddress == null || dkimPublicKey == null) { - return []; - } - return [ - DesiredDnsRecord( - name: domainName, - content: ipAddress, - description: 'providers.domain.record_description.root', - ), - DesiredDnsRecord( - name: 'api.$domainName', - content: ipAddress, - description: 'providers.domain.record_description.api', - ), - DesiredDnsRecord( - name: 'cloud.$domainName', - content: ipAddress, - description: 'providers.domain.record_description.cloud', - ), - DesiredDnsRecord( - name: 'git.$domainName', - content: ipAddress, - description: 'providers.domain.record_description.git', - ), - DesiredDnsRecord( - name: 'meet.$domainName', - content: ipAddress, - description: 'providers.domain.record_description.meet', - ), - DesiredDnsRecord( - name: 'social.$domainName', - content: ipAddress, - description: 'providers.domain.record_description.social', - ), - DesiredDnsRecord( - name: 'password.$domainName', - content: ipAddress, - description: 'providers.domain.record_description.password', - ), - DesiredDnsRecord( - name: 'vpn.$domainName', - content: ipAddress, - description: 'providers.domain.record_description.vpn', - ), - DesiredDnsRecord( - name: domainName, - content: domainName, - description: 'providers.domain.record_description.mx', - type: 'MX', - category: DnsRecordsCategory.email, - ), - DesiredDnsRecord( - name: '_dmarc.$domainName', - content: 'v=DMARC1; p=none', - description: 'providers.domain.record_description.dmarc', - type: 'TXT', - category: DnsRecordsCategory.email, - ), - DesiredDnsRecord( - name: domainName, - content: 'v=spf1 a mx ip4:$ipAddress -all', - description: 'providers.domain.record_description.spf', - type: 'TXT', - category: DnsRecordsCategory.email, - ), - DesiredDnsRecord( - name: 'selector._domainkey.$domainName', - content: dkimPublicKey, - description: 'providers.domain.record_description.dkim', - type: 'TXT', - category: DnsRecordsCategory.email, - ), - ]; - } } diff --git a/lib/logic/cubit/dns_records/dns_records_state.dart b/lib/logic/cubit/dns_records/dns_records_state.dart index 4b39d014..222dda66 100644 --- a/lib/logic/cubit/dns_records/dns_records_state.dart +++ b/lib/logic/cubit/dns_records/dns_records_state.dart @@ -7,12 +7,6 @@ enum DnsRecordsStatus { error, } -enum DnsRecordsCategory { - services, - email, - other, -} - class DnsRecordsState extends ServerInstallationDependendState { const DnsRecordsState({ this.dnsState = DnsRecordsStatus.uninitialized, @@ -37,38 +31,3 @@ class DnsRecordsState extends ServerInstallationDependendState { dnsRecords: dnsRecords ?? this.dnsRecords, ); } - -class DesiredDnsRecord { - const DesiredDnsRecord({ - required this.name, - required this.content, - this.type = 'A', - this.description = '', - this.category = DnsRecordsCategory.services, - this.isSatisfied = false, - }); - - final String name; - final String type; - final String content; - final String description; - final DnsRecordsCategory category; - final bool isSatisfied; - - DesiredDnsRecord copyWith({ - final String? name, - final String? type, - final String? content, - final String? description, - final DnsRecordsCategory? category, - final bool? isSatisfied, - }) => - DesiredDnsRecord( - name: name ?? this.name, - type: type ?? this.type, - content: content ?? this.content, - description: description ?? this.description, - category: category ?? this.category, - isSatisfied: isSatisfied ?? this.isSatisfied, - ); -} diff --git a/lib/logic/cubit/forms/factories/field_cubit_factory.dart b/lib/logic/cubit/forms/factories/field_cubit_factory.dart index 62067cea..724dfb4c 100644 --- a/lib/logic/cubit/forms/factories/field_cubit_factory.dart +++ b/lib/logic/cubit/forms/factories/field_cubit_factory.dart @@ -28,7 +28,7 @@ class FieldCubitFactory { ValidationModel( (final String login) => context.read().state.isLoginRegistered(login), - 'validations.user_already_exist'.tr(), + 'validations.already_exist'.tr(), ), RequiredStringValidation('validations.required'.tr()), LengthStringLongerValidation(userMaxLength), @@ -52,7 +52,7 @@ class FieldCubitFactory { RequiredStringValidation('validations.required'.tr()), ValidationModel( passwordForbiddenRegExp.hasMatch, - 'validations.invalid_format'.tr(), + 'validations.invalid_format_password'.tr(), ), ], ); diff --git a/lib/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart b/lib/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart index 4769286d..3907b7e1 100644 --- a/lib/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart +++ b/lib/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart @@ -1,13 +1,13 @@ import 'dart:async'; import 'package:cubit_form/cubit_form.dart'; -import 'package:selfprivacy/logic/api_maps/backblaze.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/backblaze.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; -import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart'; +import 'package:selfprivacy/logic/models/hive/backups_credential.dart'; import 'package:easy_localization/easy_localization.dart'; class BackblazeFormCubit extends FormCubit { BackblazeFormCubit(this.serverInstallationCubit) { - //var regExp = RegExp(r"\s+|[-!$%^&*()@+|~=`{}\[\]:<>?,.\/]"); keyId = FieldCubit( initalValue: '', validations: [ @@ -40,7 +40,7 @@ class BackblazeFormCubit extends FormCubit { @override FutureOr asyncValidation() async { - late bool isKeyValid; + late GenericResult backblazeResponse; final BackblazeApi apiClient = BackblazeApi(isWithToken: false); try { @@ -48,17 +48,30 @@ class BackblazeFormCubit extends FormCubit { keyId.state.value, applicationKey.state.value, ); - isKeyValid = await apiClient.isValid(encodedApiKey); + backblazeResponse = await apiClient.isApiTokenValid(encodedApiKey); } catch (e) { addError(e); - isKeyValid = false; + backblazeResponse = GenericResult( + success: false, + data: false, + message: e.toString(), + ); } - if (!isKeyValid) { - keyId.setError('bad key'); - applicationKey.setError('bad key'); + if (!backblazeResponse.success) { + getIt().showSnackBar( + 'initializing.could_not_connect'.tr(), + ); + keyId.setError(''); + applicationKey.setError(''); return false; } - return true; + + if (!backblazeResponse.data) { + keyId.setError('initializing.backblaze_bad_key_error'.tr()); + applicationKey.setError('initializing.backblaze_bad_key_error'.tr()); + } + + return backblazeResponse.data; } } diff --git a/lib/logic/cubit/forms/setup/initializing/cloudflare_form_cubit.dart b/lib/logic/cubit/forms/setup/initializing/cloudflare_form_cubit.dart deleted file mode 100644 index 01d26835..00000000 --- a/lib/logic/cubit/forms/setup/initializing/cloudflare_form_cubit.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'dart:async'; - -import 'package:cubit_form/cubit_form.dart'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:selfprivacy/logic/api_maps/cloudflare.dart'; -import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; -import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart'; - -class CloudFlareFormCubit extends FormCubit { - CloudFlareFormCubit(this.initializingCubit) { - final RegExp regExp = RegExp(r'\s+|[!$%^&*()@+|~=`{}\[\]:<>?,.\/]'); - apiKey = FieldCubit( - initalValue: '', - validations: [ - RequiredStringValidation('validations.required'.tr()), - ValidationModel( - regExp.hasMatch, - 'validations.key_format'.tr(), - ), - LengthStringNotEqualValidation(40) - ], - ); - - super.addFields([apiKey]); - } - - @override - FutureOr onSubmit() async { - initializingCubit.setCloudflareKey(apiKey.state.value); - } - - final ServerInstallationCubit initializingCubit; - - late final FieldCubit apiKey; - - @override - FutureOr asyncValidation() async { - late bool isKeyValid; - final CloudflareApi apiClient = CloudflareApi(isWithToken: false); - - try { - isKeyValid = await apiClient.isValid(apiKey.state.value); - } catch (e) { - addError(e); - } - - if (!isKeyValid) { - apiKey.setError('bad key'); - return false; - } - return true; - } -} diff --git a/lib/logic/cubit/forms/setup/initializing/dns_provider_form_cubit.dart b/lib/logic/cubit/forms/setup/initializing/dns_provider_form_cubit.dart new file mode 100644 index 00000000..084adb83 --- /dev/null +++ b/lib/logic/cubit/forms/setup/initializing/dns_provider_form_cubit.dart @@ -0,0 +1,49 @@ +import 'dart:async'; + +import 'package:cubit_form/cubit_form.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; + +class DnsProviderFormCubit extends FormCubit { + DnsProviderFormCubit(this.initializingCubit) { + apiKey = FieldCubit( + initalValue: '', + validations: [ + RequiredStringValidation('validations.required'.tr()), + ], + ); + + super.addFields([apiKey]); + } + + @override + FutureOr onSubmit() async { + initializingCubit.setDnsApiToken(apiKey.state.value); + } + + final ServerInstallationCubit initializingCubit; + late final FieldCubit apiKey; + + @override + FutureOr asyncValidation() async { + bool? isKeyValid; + + try { + isKeyValid = await initializingCubit + .isDnsProviderApiTokenValid(apiKey.state.value); + } catch (e) { + addError(e); + } + + if (isKeyValid == null) { + apiKey.setError(''); + return false; + } + + if (!isKeyValid) { + apiKey.setError('initializing.dns_provider_bad_key_error'.tr()); + } + + return isKeyValid; + } +} diff --git a/lib/logic/cubit/forms/setup/initializing/domain_cloudflare.dart b/lib/logic/cubit/forms/setup/initializing/domain_setup_cubit.dart similarity index 61% rename from lib/logic/cubit/forms/setup/initializing/domain_cloudflare.dart rename to lib/logic/cubit/forms/setup/initializing/domain_setup_cubit.dart index 89b50a62..bccbc551 100644 --- a/lib/logic/cubit/forms/setup/initializing/domain_cloudflare.dart +++ b/lib/logic/cubit/forms/setup/initializing/domain_setup_cubit.dart @@ -1,7 +1,8 @@ import 'package:cubit_form/cubit_form.dart'; -import 'package:selfprivacy/logic/api_maps/cloudflare.dart'; +import 'package:selfprivacy/logic/api_maps/generic_result.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart'; +import 'package:selfprivacy/logic/providers/providers_controller.dart'; class DomainSetupCubit extends Cubit { DomainSetupCubit(this.serverInstallationCubit) : super(Initial()); @@ -10,34 +11,25 @@ class DomainSetupCubit extends Cubit { Future load() async { emit(Loading(LoadingTypes.loadingDomain)); - final CloudflareApi api = CloudflareApi(); - - final List list = await api.domainList(); - if (list.isEmpty) { + final GenericResult> result = + await ProvidersController.currentDnsProvider!.domainList(); + if (!result.success || result.data.isEmpty) { emit(Empty()); - } else if (list.length == 1) { - emit(Loaded(list.first)); + } else if (result.data.length == 1) { + emit(Loaded(result.data.first)); } else { - emit(MoreThenOne()); + emit(MoreThenOne(result.data)); } } - @override - Future close() => super.close(); - - Future saveDomain() async { - assert(state is Loaded, 'wrong state'); - final String domainName = (state as Loaded).domain; - final CloudflareApi api = CloudflareApi(); - + Future saveDomain(final String domainName) async { emit(Loading(LoadingTypes.saving)); - final String zoneId = await api.getZoneId(domainName); + final dnsProvider = ProvidersController.currentDnsProvider!; final ServerDomain domain = ServerDomain( domainName: domainName, - zoneId: zoneId, - provider: DnsProvider.cloudflare, + provider: dnsProvider.type, ); serverInstallationCubit.setDomain(domain); @@ -51,7 +43,10 @@ class Initial extends DomainSetupState {} class Empty extends DomainSetupState {} -class MoreThenOne extends DomainSetupState {} +class MoreThenOne extends DomainSetupState { + MoreThenOne(this.domains); + final List domains; +} class Loading extends DomainSetupState { Loading(this.type); diff --git a/lib/logic/cubit/forms/setup/initializing/hetzner_form_cubit.dart b/lib/logic/cubit/forms/setup/initializing/hetzner_form_cubit.dart deleted file mode 100644 index b8f47e10..00000000 --- a/lib/logic/cubit/forms/setup/initializing/hetzner_form_cubit.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'dart:async'; - -import 'package:cubit_form/cubit_form.dart'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:selfprivacy/logic/api_maps/hetzner.dart'; -import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; -import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart'; - -class HetznerFormCubit extends FormCubit { - HetznerFormCubit(this.serverInstallationCubit) { - final RegExp regExp = RegExp(r'\s+|[-!$%^&*()@+|~=`{}\[\]:<>?,.\/]'); - apiKey = FieldCubit( - initalValue: '', - validations: [ - RequiredStringValidation('validations.required'.tr()), - ValidationModel( - regExp.hasMatch, - 'validations.key_format'.tr(), - ), - LengthStringNotEqualValidation(64) - ], - ); - - super.addFields([apiKey]); - } - - @override - FutureOr onSubmit() async { - serverInstallationCubit.setHetznerKey(apiKey.state.value); - } - - final ServerInstallationCubit serverInstallationCubit; - - late final FieldCubit apiKey; - - @override - FutureOr asyncValidation() async { - late bool isKeyValid; - final HetznerApi apiClient = HetznerApi(isWithToken: false); - - try { - isKeyValid = await apiClient.isValid(apiKey.state.value); - } catch (e) { - addError(e); - } - - if (!isKeyValid) { - apiKey.setError('bad key'); - return false; - } - return true; - } -} diff --git a/lib/logic/cubit/forms/setup/initializing/root_user_form_cubit.dart b/lib/logic/cubit/forms/setup/initializing/root_user_form_cubit.dart index b3cf606f..cc4ef6cf 100644 --- a/lib/logic/cubit/forms/setup/initializing/root_user_form_cubit.dart +++ b/lib/logic/cubit/forms/setup/initializing/root_user_form_cubit.dart @@ -1,8 +1,8 @@ import 'dart:async'; import 'package:cubit_form/cubit_form.dart'; -import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart'; +import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/models/hive/user.dart'; class RootUserFormCubit extends FormCubit { @@ -22,6 +22,7 @@ class RootUserFormCubit extends FormCubit { FutureOr onSubmit() async { final User user = User( login: userName.state.value, + type: UserType.primary, password: password.state.value, ); serverInstallationCubit.setRootUser(user); diff --git a/lib/logic/cubit/forms/setup/initializing/server_provider_form_cubit.dart b/lib/logic/cubit/forms/setup/initializing/server_provider_form_cubit.dart new file mode 100644 index 00000000..5df3e31a --- /dev/null +++ b/lib/logic/cubit/forms/setup/initializing/server_provider_form_cubit.dart @@ -0,0 +1,49 @@ +import 'dart:async'; + +import 'package:cubit_form/cubit_form.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; + +class ServerProviderFormCubit extends FormCubit { + ServerProviderFormCubit(this.serverInstallationCubit) { + apiKey = FieldCubit( + initalValue: '', + validations: [ + RequiredStringValidation('validations.required'.tr()), + ], + ); + + super.addFields([apiKey]); + } + + @override + FutureOr onSubmit() async { + serverInstallationCubit.setServerProviderKey(apiKey.state.value); + } + + final ServerInstallationCubit serverInstallationCubit; + late final FieldCubit apiKey; + + @override + FutureOr asyncValidation() async { + bool? isKeyValid; + + try { + isKeyValid = await serverInstallationCubit + .isServerProviderApiTokenValid(apiKey.state.value); + } catch (e) { + addError(e); + } + + if (isKeyValid == null) { + apiKey.setError(''); + return false; + } + + if (!isKeyValid) { + apiKey.setError('initializing.provider_bad_key_error'.tr()); + } + + return isKeyValid; + } +} diff --git a/lib/logic/cubit/forms/setup/recovering/recovery_domain_form_cubit.dart b/lib/logic/cubit/forms/setup/recovering/recovery_domain_form_cubit.dart index 664b87b8..7f840d0a 100644 --- a/lib/logic/cubit/forms/setup/recovering/recovery_domain_form_cubit.dart +++ b/lib/logic/cubit/forms/setup/recovering/recovery_domain_form_cubit.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:cubit_form/cubit_form.dart'; import 'package:easy_localization/easy_localization.dart'; -import 'package:selfprivacy/logic/api_maps/server.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart'; @@ -18,8 +18,9 @@ class RecoveryDomainFormCubit extends FormCubit { @override FutureOr onSubmit() async { - initializingCubit - .submitDomainForAccessRecovery(serverDomainField.state.value); + initializingCubit.submitDomainForAccessRecovery( + serverDomainField.state.value.toLowerCase(), + ); } @override diff --git a/lib/logic/cubit/forms/user/ssh_form_cubit.dart b/lib/logic/cubit/forms/user/ssh_form_cubit.dart index ba38a642..3305d647 100644 --- a/lib/logic/cubit/forms/user/ssh_form_cubit.dart +++ b/lib/logic/cubit/forms/user/ssh_form_cubit.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:cubit_form/cubit_form.dart'; import 'package:easy_localization/easy_localization.dart'; -import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart'; +import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart'; import 'package:selfprivacy/logic/models/job.dart'; import 'package:selfprivacy/logic/models/hive/user.dart'; @@ -12,7 +12,7 @@ class SshFormCubit extends FormCubit { required this.user, }) { final RegExp keyRegExp = RegExp( - r'^(ssh-rsa AAAAB3NzaC1yc2|ssh-ed25519 AAAAC3NzaC1lZDI1NTE5)[0-9A-Za-z+/]+[=]{0,3}( .*)?$', + r'^(ecdsa-sha2-nistp256 AAAAE2VjZH|ssh-rsa AAAAB3NzaC1yc2|ssh-ed25519 AAAAC3NzaC1lZDI1NTE5)[0-9A-Za-z+/]+[=]{0,3}( .*)?$', ); key = FieldCubit( @@ -21,7 +21,7 @@ class SshFormCubit extends FormCubit { ValidationModel( (final String newKey) => user.sshKeys.any((final String key) => key == newKey), - 'validations.key_already_exists'.tr(), + 'validations.already_exist'.tr(), ), RequiredStringValidation('validations.required'.tr()), ValidationModel( @@ -30,7 +30,7 @@ class SshFormCubit extends FormCubit { print(keyRegExp.hasMatch(s)); return !keyRegExp.hasMatch(s); }, - 'validations.invalid_format'.tr(), + 'validations.invalid_format_ssh'.tr(), ), ], ); @@ -41,7 +41,8 @@ class SshFormCubit extends FormCubit { @override FutureOr onSubmit() { print(key.state.isValid); - jobsCubit.addJob(CreateSSHKeyJob(user: user, publicKey: key.state.value)); + jobsCubit + .addJob(CreateSSHKeyJob(user: user, publicKey: key.state.value.trim())); } late FieldCubit key; diff --git a/lib/logic/cubit/forms/user/user_form_cubit.dart b/lib/logic/cubit/forms/user/user_form_cubit.dart index a385befb..5524417d 100644 --- a/lib/logic/cubit/forms/user/user_form_cubit.dart +++ b/lib/logic/cubit/forms/user/user_form_cubit.dart @@ -1,37 +1,56 @@ import 'dart:async'; import 'package:cubit_form/cubit_form.dart'; +import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart'; -import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart'; -import 'package:selfprivacy/logic/models/job.dart'; import 'package:selfprivacy/logic/models/hive/user.dart'; +import 'package:selfprivacy/logic/models/job.dart'; import 'package:selfprivacy/utils/password_generator.dart'; class UserFormCubit extends FormCubit { UserFormCubit({ required this.jobsCubit, required final FieldCubitFactory fieldFactory, - final User? user, + this.initialUser, }) { - final bool isEdit = user != null; + if (initialUser == null) { + login = fieldFactory.createUserLoginField(); + login.setValue(''); + password = fieldFactory.createUserPasswordField(); + password.setValue( + StringGenerators.userPassword(), + ); - login = fieldFactory.createUserLoginField(); - login.setValue(isEdit ? user.login : ''); - password = fieldFactory.createUserPasswordField(); - password.setValue( - isEdit ? (user.password ?? '') : StringGenerators.userPassword(), - ); + super.addFields([login, password]); + } else { + login = fieldFactory.createRequiredStringField(); + login.setValue(initialUser!.login); + password = fieldFactory.createUserPasswordField(); + password.setValue( + initialUser?.password ?? '', + ); - super.addFields([login, password]); + super.addFields([login, password]); + } } @override FutureOr onSubmit() { - final User user = User( - login: login.state.value, - password: password.state.value, - ); - jobsCubit.addJob(CreateUserJob(user: user)); + if (initialUser == null) { + final User user = User( + login: login.state.value, + type: UserType.normal, + password: password.state.value, + ); + jobsCubit.addJob(CreateUserJob(user: user)); + } else { + final User user = User( + login: initialUser?.login ?? login.state.value, + type: initialUser?.type ?? UserType.normal, + password: password.state.value, + ); + jobsCubit.addJob(ResetUserPasswordJob(user: user)); + } } late FieldCubit login; @@ -42,4 +61,5 @@ class UserFormCubit extends FormCubit { } final JobsCubit jobsCubit; + final User? initialUser; } diff --git a/lib/logic/cubit/hetzner_metrics/hetzner_metrics_cubit.dart b/lib/logic/cubit/hetzner_metrics/hetzner_metrics_cubit.dart deleted file mode 100644 index aaae36c5..00000000 --- a/lib/logic/cubit/hetzner_metrics/hetzner_metrics_cubit.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'dart:async'; - -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:equatable/equatable.dart'; -import 'package:selfprivacy/logic/common_enum/common_enum.dart'; -import 'package:selfprivacy/logic/models/hetzner_metrics.dart'; - -import 'package:selfprivacy/logic/cubit/hetzner_metrics/hetzner_metrics_repository.dart'; - -part 'hetzner_metrics_state.dart'; - -class HetznerMetricsCubit extends Cubit { - HetznerMetricsCubit() : super(const HetznerMetricsLoading(Period.day)); - - final HetznerMetricsRepository repository = HetznerMetricsRepository(); - - Timer? timer; - - @override - Future close() { - closeTimer(); - return super.close(); - } - - void closeTimer() { - if (timer != null && timer!.isActive) { - timer!.cancel(); - } - } - - void changePeriod(final Period period) async { - closeTimer(); - emit(HetznerMetricsLoading(period)); - load(period); - } - - void restart() async { - load(state.period); - } - - void load(final Period period) async { - final HetznerMetricsLoaded newState = await repository.getMetrics(period); - timer = Timer( - Duration(seconds: newState.stepInSeconds.toInt()), - () => load(newState.period), - ); - - emit(newState); - } -} diff --git a/lib/logic/cubit/hetzner_metrics/hetzner_metrics_repository.dart b/lib/logic/cubit/hetzner_metrics/hetzner_metrics_repository.dart deleted file mode 100644 index de7f3d43..00000000 --- a/lib/logic/cubit/hetzner_metrics/hetzner_metrics_repository.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'package:selfprivacy/logic/api_maps/hetzner.dart'; -import 'package:selfprivacy/logic/common_enum/common_enum.dart'; -import 'package:selfprivacy/logic/models/hetzner_metrics.dart'; - -import 'package:selfprivacy/logic/cubit/hetzner_metrics/hetzner_metrics_cubit.dart'; - -class HetznerMetricsRepository { - Future getMetrics(final Period period) async { - final DateTime end = DateTime.now(); - DateTime start; - - switch (period) { - case Period.hour: - start = end.subtract(const Duration(hours: 1)); - break; - case Period.day: - start = end.subtract(const Duration(days: 1)); - break; - case Period.month: - start = end.subtract(const Duration(days: 15)); - break; - } - - final HetznerApi api = HetznerApi(hasLogger: true); - - final List> results = await Future.wait([ - api.getMetrics(start, end, 'cpu'), - api.getMetrics(start, end, 'network'), - ]); - - final cpuMetricsData = results[0]['metrics']; - final networkMetricsData = results[1]['metrics']; - - return HetznerMetricsLoaded( - period: period, - start: start, - end: end, - stepInSeconds: cpuMetricsData['step'], - cpu: timeSeriesSerializer(cpuMetricsData, 'cpu'), - ppsIn: timeSeriesSerializer(networkMetricsData, 'network.0.pps.in'), - ppsOut: timeSeriesSerializer(networkMetricsData, 'network.0.pps.out'), - bandwidthIn: - timeSeriesSerializer(networkMetricsData, 'network.0.bandwidth.in'), - bandwidthOut: timeSeriesSerializer( - networkMetricsData, - 'network.0.bandwidth.out', - ), - ); - } -} - -List timeSeriesSerializer( - final Map json, - final String type, -) { - final List list = json['time_series'][type]['values']; - return list - .map((final el) => TimeSeriesData(el[0], double.parse(el[1]))) - .toList(); -} diff --git a/lib/logic/cubit/hetzner_metrics/hetzner_metrics_state.dart b/lib/logic/cubit/hetzner_metrics/hetzner_metrics_state.dart deleted file mode 100644 index b6204db9..00000000 --- a/lib/logic/cubit/hetzner_metrics/hetzner_metrics_state.dart +++ /dev/null @@ -1,45 +0,0 @@ -part of 'hetzner_metrics_cubit.dart'; - -abstract class HetznerMetricsState extends Equatable { - const HetznerMetricsState(); - - abstract final Period period; -} - -class HetznerMetricsLoading extends HetznerMetricsState { - const HetznerMetricsLoading(this.period); - @override - final Period period; - - @override - List get props => [period]; -} - -class HetznerMetricsLoaded extends HetznerMetricsState { - const HetznerMetricsLoaded({ - required this.period, - required this.start, - required this.end, - required this.stepInSeconds, - required this.cpu, - required this.ppsIn, - required this.ppsOut, - required this.bandwidthIn, - required this.bandwidthOut, - }); - - @override - final Period period; - final DateTime start; - final DateTime end; - final num stepInSeconds; - - final List cpu; - final List ppsIn; - final List ppsOut; - final List bandwidthIn; - final List bandwidthOut; - - @override - List get props => [period, start, end]; -} diff --git a/lib/logic/cubit/jobs/jobs_cubit.dart b/lib/logic/cubit/jobs/jobs_cubit.dart deleted file mode 100644 index 6de64677..00000000 --- a/lib/logic/cubit/jobs/jobs_cubit.dart +++ /dev/null @@ -1,132 +0,0 @@ -import 'package:easy_localization/easy_localization.dart'; -import 'package:equatable/equatable.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:selfprivacy/config/get_it_config.dart'; -import 'package:selfprivacy/logic/api_maps/server.dart'; -import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; -import 'package:selfprivacy/logic/cubit/users/users_cubit.dart'; -import 'package:selfprivacy/logic/models/job.dart'; - -export 'package:provider/provider.dart'; - -part 'jobs_state.dart'; - -class JobsCubit extends Cubit { - JobsCubit({ - required this.usersCubit, - required this.servicesCubit, - }) : super(JobsStateEmpty()); - - final ServerApi api = ServerApi(); - final UsersCubit usersCubit; - final ServicesCubit servicesCubit; - - void addJob(final Job job) { - final List newJobsList = []; - if (state is JobsStateWithJobs) { - newJobsList.addAll((state as JobsStateWithJobs).jobList); - } - newJobsList.add(job); - getIt().showSnackBar('jobs.jobAdded'.tr()); - emit(JobsStateWithJobs(newJobsList)); - } - - void removeJob(final String id) { - final JobsState newState = (state as JobsStateWithJobs).removeById(id); - emit(newState); - } - - void createOrRemoveServiceToggleJob(final ToggleJob job) { - final List newJobsList = []; - if (state is JobsStateWithJobs) { - newJobsList.addAll((state as JobsStateWithJobs).jobList); - } - final bool needToRemoveJob = newJobsList - .any((final el) => el is ServiceToggleJob && el.type == job.type); - if (needToRemoveJob) { - final Job removingJob = newJobsList.firstWhere( - (final el) => el is ServiceToggleJob && el.type == job.type, - ); - removeJob(removingJob.id); - } else { - newJobsList.add(job); - getIt().showSnackBar('jobs.jobAdded'.tr()); - emit(JobsStateWithJobs(newJobsList)); - } - } - - void createShhJobIfNotExist(final CreateSSHKeyJob job) { - final List newJobsList = []; - if (state is JobsStateWithJobs) { - newJobsList.addAll((state as JobsStateWithJobs).jobList); - } - final bool isExistInJobList = - newJobsList.any((final el) => el is CreateSSHKeyJob); - if (!isExistInJobList) { - newJobsList.add(job); - getIt().showSnackBar('jobs.jobAdded'.tr()); - emit(JobsStateWithJobs(newJobsList)); - } - } - - Future rebootServer() async { - emit(JobsStateLoading()); - final bool isSuccessful = await api.reboot(); - if (isSuccessful) { - getIt().showSnackBar('jobs.rebootSuccess'.tr()); - } else { - getIt().showSnackBar('jobs.rebootFailed'.tr()); - } - emit(JobsStateEmpty()); - } - - Future upgradeServer() async { - emit(JobsStateLoading()); - final bool isPullSuccessful = await api.pullConfigurationUpdate(); - final bool isSuccessful = await api.upgrade(); - if (isSuccessful) { - if (!isPullSuccessful) { - getIt().showSnackBar('jobs.configPullFailed'.tr()); - } else { - getIt().showSnackBar('jobs.upgradeSuccess'.tr()); - } - } else { - getIt().showSnackBar('jobs.upgradeFailed'.tr()); - } - emit(JobsStateEmpty()); - } - - Future applyAll() async { - if (state is JobsStateWithJobs) { - final List jobs = (state as JobsStateWithJobs).jobList; - emit(JobsStateLoading()); - bool hasServiceJobs = false; - for (final Job job in jobs) { - if (job is CreateUserJob) { - await usersCubit.createUser(job.user); - } - if (job is DeleteUserJob) { - await usersCubit.deleteUser(job.user); - } - if (job is ServiceToggleJob) { - hasServiceJobs = true; - await api.switchService(job.type, job.needToTurnOn); - } - if (job is CreateSSHKeyJob) { - await usersCubit.addSshKey(job.user, job.publicKey); - } - if (job is DeleteSSHKeyJob) { - await usersCubit.deleteSshKey(job.user, job.publicKey); - } - } - - await api.pullConfigurationUpdate(); - await api.apply(); - if (hasServiceJobs) { - await servicesCubit.load(); - } - - emit(JobsStateEmpty()); - } - } -} diff --git a/lib/logic/cubit/metrics/metrics_cubit.dart b/lib/logic/cubit/metrics/metrics_cubit.dart new file mode 100644 index 00000000..2a2dec28 --- /dev/null +++ b/lib/logic/cubit/metrics/metrics_cubit.dart @@ -0,0 +1,58 @@ +import 'dart:async'; + +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:selfprivacy/logic/common_enum/common_enum.dart'; +import 'package:selfprivacy/logic/models/metrics.dart'; + +import 'package:selfprivacy/logic/cubit/metrics/metrics_repository.dart'; + +part 'metrics_state.dart'; + +class MetricsCubit extends Cubit { + MetricsCubit() : super(const MetricsLoading(Period.day)); + + final MetricsRepository repository = MetricsRepository(); + + Timer? timer; + + @override + Future close() { + closeTimer(); + return super.close(); + } + + void closeTimer() { + if (timer != null && timer!.isActive) { + timer!.cancel(); + } + } + + void changePeriod(final Period period) async { + closeTimer(); + emit(MetricsLoading(period)); + load(period); + } + + void restart() async { + load(state.period); + } + + void load(final Period period) async { + try { + final MetricsLoaded newState = await repository.getMetrics(period); + timer = Timer( + Duration(seconds: newState.metrics.stepsInSecond.toInt()), + () => load(newState.period), + ); + emit(newState); + } on StateError { + print('Tried to emit metrics when cubit is closed'); + } on MetricsLoadException { + timer = Timer( + Duration(seconds: state.period.stepPeriodInSeconds), + () => load(state.period), + ); + } + } +} diff --git a/lib/logic/cubit/metrics/metrics_repository.dart b/lib/logic/cubit/metrics/metrics_repository.dart new file mode 100644 index 00000000..0c6a82ef --- /dev/null +++ b/lib/logic/cubit/metrics/metrics_repository.dart @@ -0,0 +1,49 @@ +import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/common_enum/common_enum.dart'; + +import 'package:selfprivacy/logic/cubit/metrics/metrics_cubit.dart'; +import 'package:selfprivacy/logic/providers/providers_controller.dart'; + +class MetricsLoadException implements Exception { + MetricsLoadException(this.message); + final String message; +} + +class MetricsRepository { + Future getMetrics(final Period period) async { + if (ProvidersController.currentServerProvider == null) { + throw MetricsLoadException('Server Provider data is null'); + } + + final DateTime end = DateTime.now(); + DateTime start; + + switch (period) { + case Period.hour: + start = end.subtract(const Duration(hours: 1)); + break; + case Period.day: + start = end.subtract(const Duration(days: 1)); + break; + case Period.month: + start = end.subtract(const Duration(days: 15)); + break; + } + + final serverId = getIt().serverDetails!.id; + final result = await ProvidersController.currentServerProvider!.getMetrics( + serverId, + start, + end, + ); + + if (result.data == null || !result.success) { + throw MetricsLoadException('Metrics data is null'); + } + + return MetricsLoaded( + period: period, + metrics: result.data!, + ); + } +} diff --git a/lib/logic/cubit/metrics/metrics_state.dart b/lib/logic/cubit/metrics/metrics_state.dart new file mode 100644 index 00000000..b27546ce --- /dev/null +++ b/lib/logic/cubit/metrics/metrics_state.dart @@ -0,0 +1,31 @@ +part of 'metrics_cubit.dart'; + +abstract class MetricsState extends Equatable { + const MetricsState(); + + abstract final Period period; +} + +class MetricsLoading extends MetricsState { + const MetricsLoading(this.period); + @override + final Period period; + + @override + List get props => [period]; +} + +class MetricsLoaded extends MetricsState { + const MetricsLoaded({ + required this.period, + required this.metrics, + }); + + @override + final Period period; + + final ServerMetrics metrics; + + @override + List get props => [period, metrics]; +} diff --git a/lib/logic/cubit/provider_volumes/provider_volume_cubit.dart b/lib/logic/cubit/provider_volumes/provider_volume_cubit.dart new file mode 100644 index 00000000..fed7b083 --- /dev/null +++ b/lib/logic/cubit/provider_volumes/provider_volume_cubit.dart @@ -0,0 +1,150 @@ +import 'dart:async'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart'; +import 'package:selfprivacy/logic/common_enum/common_enum.dart'; +import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; +import 'package:selfprivacy/logic/models/disk_size.dart'; +import 'package:selfprivacy/logic/models/hive/server_details.dart'; +import 'package:selfprivacy/logic/models/disk_status.dart'; +import 'package:selfprivacy/logic/models/price.dart'; +import 'package:selfprivacy/logic/providers/providers_controller.dart'; + +part 'provider_volume_state.dart'; + +class ApiProviderVolumeCubit + extends ServerInstallationDependendCubit { + ApiProviderVolumeCubit(final ServerInstallationCubit serverInstallationCubit) + : super(serverInstallationCubit, const ApiProviderVolumeState.initial()); + final ServerApi serverApi = ServerApi(); + + @override + Future load() async { + if (serverInstallationCubit.state is ServerInstallationFinished) { + unawaited(_refetch()); + } + } + + Future getPricePerGb() async { + Price? price; + final pricingResult = + await ProvidersController.currentServerProvider!.getAdditionalPricing(); + if (pricingResult.data == null || !pricingResult.success) { + getIt().showSnackBar('server.pricing_error'.tr()); + return price; + } + price = pricingResult.data!.perVolumeGb; + return price; + } + + Future refresh() async { + emit(const ApiProviderVolumeState([], LoadingStatus.refreshing, false)); + unawaited(_refetch()); + } + + Future _refetch() async { + if (ProvidersController.currentServerProvider == null) { + return emit(const ApiProviderVolumeState([], LoadingStatus.error, false)); + } + + final volumesResult = + await ProvidersController.currentServerProvider!.getVolumes(); + + if (!volumesResult.success || volumesResult.data.isEmpty) { + return emit(const ApiProviderVolumeState([], LoadingStatus.error, false)); + } + + emit( + ApiProviderVolumeState( + volumesResult.data, + LoadingStatus.success, + false, + ), + ); + } + + Future attachVolume(final DiskVolume volume) async { + final ServerHostingDetails server = getIt().serverDetails!; + await ProvidersController.currentServerProvider! + .attachVolume(volume.providerVolume!, server.id); + unawaited(refresh()); + } + + Future detachVolume(final DiskVolume volume) async { + await ProvidersController.currentServerProvider! + .detachVolume(volume.providerVolume!); + unawaited(refresh()); + } + + Future resizeVolume( + final DiskVolume volume, + final DiskSize newSize, + final Function() callback, + ) async { + getIt().showSnackBar( + 'Starting resize', + ); + emit(state.copyWith(isResizing: true)); + final resizedResult = + await ProvidersController.currentServerProvider!.resizeVolume( + volume.providerVolume!, + newSize, + ); + + if (!resizedResult.success || !resizedResult.data) { + getIt().showSnackBar( + 'storage.extending_volume_error'.tr(), + ); + emit(state.copyWith(isResizing: false)); + return false; + } + + getIt().showSnackBar( + 'Provider volume resized, waiting 10 seconds', + ); + await Future.delayed(const Duration(seconds: 10)); + + await ServerApi().resizeVolume(volume.name); + getIt().showSnackBar( + 'Server volume resized, waiting 20 seconds', + ); + + await Future.delayed(const Duration(seconds: 20)); + getIt().showSnackBar( + 'Restarting server', + ); + + await refresh(); + emit(state.copyWith(isResizing: false)); + await callback(); + await serverApi.reboot(); + return true; + } + + Future createVolume(final DiskSize size) async { + final ServerVolume? volume = (await ProvidersController + .currentServerProvider! + .createVolume(size.gibibyte.toInt())) + .data; + + final diskVolume = DiskVolume(providerVolume: volume); + await attachVolume(diskVolume); + + await Future.delayed(const Duration(seconds: 10)); + + await ServerApi().mountVolume(volume!.name); + unawaited(refresh()); + } + + Future deleteVolume(final DiskVolume volume) async { + await ProvidersController.currentServerProvider! + .deleteVolume(volume.providerVolume!); + unawaited(refresh()); + } + + @override + void clear() { + emit(const ApiProviderVolumeState.initial()); + } +} diff --git a/lib/logic/cubit/provider_volumes/provider_volume_state.dart b/lib/logic/cubit/provider_volumes/provider_volume_state.dart new file mode 100644 index 00000000..3858ef14 --- /dev/null +++ b/lib/logic/cubit/provider_volumes/provider_volume_state.dart @@ -0,0 +1,27 @@ +part of 'provider_volume_cubit.dart'; + +class ApiProviderVolumeState extends ServerInstallationDependendState { + const ApiProviderVolumeState(this._volumes, this.status, this.isResizing); + + const ApiProviderVolumeState.initial() + : this(const [], LoadingStatus.uninitialized, false); + final List _volumes; + final LoadingStatus status; + final bool isResizing; + + List get volumes => _volumes; + + ApiProviderVolumeState copyWith({ + final List? volumes, + final LoadingStatus? status, + final bool? isResizing, + }) => + ApiProviderVolumeState( + volumes ?? _volumes, + status ?? this.status, + isResizing ?? this.isResizing, + ); + + @override + List get props => [_volumes, status, isResizing]; +} diff --git a/lib/logic/cubit/recovery_key/recovery_key_cubit.dart b/lib/logic/cubit/recovery_key/recovery_key_cubit.dart index abd7b2fa..c5f68d57 100644 --- a/lib/logic/cubit/recovery_key/recovery_key_cubit.dart +++ b/lib/logic/cubit/recovery_key/recovery_key_cubit.dart @@ -1,4 +1,6 @@ -import 'package:selfprivacy/logic/api_maps/server.dart'; +import 'dart:async'; + +import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart'; import 'package:selfprivacy/logic/common_enum/common_enum.dart'; import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; import 'package:selfprivacy/logic/models/json/recovery_token_status.dart'; @@ -14,27 +16,27 @@ class RecoveryKeyCubit @override void load() async { - if (serverInstallationCubit.state is ServerInstallationFinished) { - final RecoveryKeyStatus? status = await _getRecoveryKeyStatus(); - if (status == null) { - emit(state.copyWith(loadingStatus: LoadingStatus.error)); - } else { - emit( - state.copyWith( - status: status, - loadingStatus: LoadingStatus.success, - ), - ); - } + // if (serverInstallationCubit.state is ServerInstallationFinished) { + final RecoveryKeyStatus? status = await _getRecoveryKeyStatus(); + if (status == null) { + emit(state.copyWith(loadingStatus: LoadingStatus.error)); } else { - emit(state.copyWith(loadingStatus: LoadingStatus.uninitialized)); + emit( + state.copyWith( + status: status, + loadingStatus: LoadingStatus.success, + ), + ); } + // } else { + // emit(state.copyWith(loadingStatus: LoadingStatus.uninitialized)); + // } } Future _getRecoveryKeyStatus() async { - final ApiResponse response = + final GenericResult response = await api.getRecoveryTokenStatus(); - if (response.isSuccess) { + if (response.success) { return response.data; } else { return null; @@ -57,13 +59,13 @@ class RecoveryKeyCubit final DateTime? expirationDate, final int? numberOfUses, }) async { - final ApiResponse response = + final GenericResult response = await api.generateRecoveryToken(expirationDate, numberOfUses); - if (response.isSuccess) { - refresh(); + if (response.success) { + unawaited(refresh()); return response.data; } else { - throw GenerationError(response.errorMessage ?? 'Unknown error'); + throw GenerationError(response.message ?? 'Unknown error'); } } diff --git a/lib/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart b/lib/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart index 613069b0..b6a39733 100644 --- a/lib/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart +++ b/lib/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart @@ -1,33 +1,48 @@ -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:equatable/equatable.dart'; import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_detailed_info/server_detailed_info_repository.dart'; -import 'package:selfprivacy/logic/models/json/auto_upgrade_settings.dart'; -import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart'; +import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart'; +import 'package:selfprivacy/logic/models/server_metadata.dart'; import 'package:selfprivacy/logic/models/timezone_settings.dart'; part 'server_detailed_info_state.dart'; -class ServerDetailsCubit extends Cubit { - ServerDetailsCubit() : super(ServerDetailsInitial()); +class ServerDetailsCubit + extends ServerInstallationDependendCubit { + ServerDetailsCubit(final ServerInstallationCubit serverInstallationCubit) + : super(serverInstallationCubit, ServerDetailsInitial()); ServerDetailsRepository repository = ServerDetailsRepository(); void check() async { final bool isReadyToCheck = getIt().serverDetails != null; - if (isReadyToCheck) { - emit(ServerDetailsLoading()); - final ServerDetailsRepositoryDto data = await repository.load(); - emit( - Loaded( - serverInfo: data.hetznerServerInfo, - autoUpgradeSettings: data.autoUpgradeSettings, - serverTimezone: data.serverTimezone, - checkTime: DateTime.now(), - ), - ); - } else { - emit(ServerDetailsNotReady()); + try { + if (isReadyToCheck) { + emit(ServerDetailsLoading()); + final ServerDetailsRepositoryDto data = await repository.load(); + emit( + Loaded( + metadata: data.metadata, + autoUpgradeSettings: data.autoUpgradeSettings, + serverTimezone: data.serverTimezone, + checkTime: DateTime.now(), + ), + ); + } else { + emit(ServerDetailsNotReady()); + } + } on StateError { + print('Tried to emit server info state when cubit is closed'); } } + + @override + void clear() { + emit(ServerDetailsNotReady()); + } + + @override + void load() async { + check(); + } } diff --git a/lib/logic/cubit/server_detailed_info/server_detailed_info_repository.dart b/lib/logic/cubit/server_detailed_info/server_detailed_info_repository.dart index 97dc6292..0d2d80e3 100644 --- a/lib/logic/cubit/server_detailed_info/server_detailed_info_repository.dart +++ b/lib/logic/cubit/server_detailed_info/server_detailed_info_repository.dart @@ -1,32 +1,68 @@ -import 'package:selfprivacy/logic/api_maps/hetzner.dart'; -import 'package:selfprivacy/logic/api_maps/server.dart'; -import 'package:selfprivacy/logic/models/json/auto_upgrade_settings.dart'; -import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart'; +import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart'; +import 'package:selfprivacy/logic/models/server_metadata.dart'; import 'package:selfprivacy/logic/models/timezone_settings.dart'; +import 'package:selfprivacy/logic/providers/providers_controller.dart'; class ServerDetailsRepository { - HetznerApi hetznerAPi = HetznerApi(); - ServerApi selfprivacyServer = ServerApi(); + ServerApi server = ServerApi(); Future load() async { - print('load'); + final settings = await server.getSystemSettings(); return ServerDetailsRepositoryDto( - autoUpgradeSettings: await selfprivacyServer.getAutoUpgradeSettings(), - hetznerServerInfo: await hetznerAPi.getInfo(), - serverTimezone: await selfprivacyServer.getServerTimezone(), + autoUpgradeSettings: settings.autoUpgradeSettings, + metadata: await metadata, + serverTimezone: TimeZoneSettings.fromString( + settings.timezone, + ), ); } + + Future> get metadata async { + List data = []; + + final serverProviderApi = ProvidersController.currentServerProvider; + final dnsProviderApi = ProvidersController.currentDnsProvider; + if (serverProviderApi != null && dnsProviderApi != null) { + final serverId = getIt().serverDetails?.id ?? 0; + final metadataResult = await serverProviderApi.getMetadata(serverId); + metadataResult.data.add( + ServerMetadataEntity( + trId: 'server.dns_provider', + value: dnsProviderApi.type.displayName, + type: MetadataType.other, + ), + ); + + data = metadataResult.data; + } + + return data; + } + + Future setAutoUpgradeSettings( + final AutoUpgradeSettings settings, + ) async { + await server.setAutoUpgradeSettings(settings); + } + + Future setTimezone( + final String timezone, + ) async { + if (timezone.isNotEmpty) { + await server.setTimezone(timezone); + } + } } class ServerDetailsRepositoryDto { ServerDetailsRepositoryDto({ - required this.hetznerServerInfo, + required this.metadata, required this.serverTimezone, required this.autoUpgradeSettings, }); - final HetznerServerInfo hetznerServerInfo; - + final List metadata; final TimeZoneSettings serverTimezone; - final AutoUpgradeSettings autoUpgradeSettings; } diff --git a/lib/logic/cubit/server_detailed_info/server_detailed_info_state.dart b/lib/logic/cubit/server_detailed_info/server_detailed_info_state.dart index ef226c1e..64f4d91d 100644 --- a/lib/logic/cubit/server_detailed_info/server_detailed_info_state.dart +++ b/lib/logic/cubit/server_detailed_info/server_detailed_info_state.dart @@ -1,6 +1,6 @@ part of 'server_detailed_info_cubit.dart'; -abstract class ServerDetailsState extends Equatable { +abstract class ServerDetailsState extends ServerInstallationDependendState { const ServerDetailsState(); @override @@ -17,21 +17,19 @@ class Loading extends ServerDetailsState {} class Loaded extends ServerDetailsState { const Loaded({ - required this.serverInfo, + required this.metadata, required this.serverTimezone, required this.autoUpgradeSettings, required this.checkTime, }); - final HetznerServerInfo serverInfo; - + final List metadata; final TimeZoneSettings serverTimezone; - final AutoUpgradeSettings autoUpgradeSettings; final DateTime checkTime; @override List get props => [ - serverInfo, + metadata, serverTimezone, autoUpgradeSettings, checkTime, diff --git a/lib/logic/cubit/server_installation/server_installation_cubit.dart b/lib/logic/cubit/server_installation/server_installation_cubit.dart index ef83104d..8884b472 100644 --- a/lib/logic/cubit/server_installation/server_installation_cubit.dart +++ b/lib/logic/cubit/server_installation/server_installation_cubit.dart @@ -4,13 +4,25 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:equatable/equatable.dart'; import 'package:selfprivacy/config/get_it_config.dart'; -import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/backblaze.dart'; +import 'package:selfprivacy/logic/api_maps/tls_options.dart'; +import 'package:selfprivacy/logic/models/disk_size.dart'; +import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart'; +import 'package:selfprivacy/logic/models/hive/backups_credential.dart'; +import 'package:selfprivacy/logic/models/callback_dialogue_branching.dart'; import 'package:selfprivacy/logic/models/hive/server_details.dart'; -import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/logic/models/hive/user.dart'; -import 'package:selfprivacy/logic/models/server_basic_info.dart'; - +import 'package:selfprivacy/logic/models/launch_installation_data.dart'; +import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_repository.dart'; +import 'package:selfprivacy/logic/models/price.dart'; +import 'package:selfprivacy/logic/models/server_basic_info.dart'; +import 'package:selfprivacy/logic/models/server_provider_location.dart'; +import 'package:selfprivacy/logic/models/server_type.dart'; +import 'package:selfprivacy/logic/providers/provider_settings.dart'; +import 'package:selfprivacy/logic/providers/providers_controller.dart'; +import 'package:selfprivacy/ui/helpers/modals.dart'; export 'package:provider/provider.dart'; @@ -24,6 +36,8 @@ class ServerInstallationCubit extends Cubit { Timer? timer; + final DiskSize initialStorage = DiskSize.fromGibibyte(10); + Future load() async { final ServerInstallationState state = await repository.load(); @@ -49,13 +63,116 @@ class ServerInstallationCubit extends Cubit { } } - void setHetznerKey(final String hetznerKey) async { - await repository.saveHetznerKey(hetznerKey); + void setServerProviderType(final ServerProviderType providerType) async { + await repository.saveServerProviderType(providerType); + ProvidersController.initServerProvider( + ServerProviderSettings(provider: providerType), + ); + } + + void setDnsProviderType(final DnsProviderType providerType) async { + await repository.saveDnsProviderType(providerType); + ProvidersController.initDnsProvider( + DnsProviderSettings( + provider: providerType, + ), + ); + } + + Future isServerProviderApiTokenValid( + final String providerToken, + ) async { + final GenericResult apiResponse = + await ProvidersController.currentServerProvider!.tryInitApiByToken( + providerToken, + ); + + if (!apiResponse.success) { + getIt().showSnackBar( + 'initializing.could_not_connect'.tr(), + ); + return null; + } + + return apiResponse.data; + } + + Future isDnsProviderApiTokenValid( + final String providerToken, + ) async { + final GenericResult apiResponse = + await ProvidersController.currentDnsProvider!.tryInitApiByToken( + providerToken, + ); + + if (!apiResponse.success) { + getIt().showSnackBar( + 'initializing.could_not_connect'.tr(), + ); + return null; + } + + return apiResponse.data; + } + + Future> fetchAvailableLocations() async { + if (ProvidersController.currentServerProvider == null) { + return []; + } + + final GenericResult apiResponse = await ProvidersController + .currentServerProvider! + .getAvailableLocations(); + + if (!apiResponse.success) { + getIt().showSnackBar( + 'initializing.could_not_connect'.tr(), + ); + } + + return apiResponse.data; + } + + Future> fetchAvailableTypesByLocation( + final ServerProviderLocation location, + ) async { + if (ProvidersController.currentServerProvider == null) { + return []; + } + + final GenericResult apiResult = await ProvidersController + .currentServerProvider! + .getServerTypes(location: location); + + if (!apiResult.success) { + getIt().showSnackBar( + 'initializing.could_not_connect'.tr(), + ); + } + + return apiResult.data; + } + + Future fetchAvailableAdditionalPricing() async { + AdditionalPricing? prices; + final pricingResult = + await ProvidersController.currentServerProvider!.getAdditionalPricing(); + if (pricingResult.data == null || !pricingResult.success) { + getIt().showSnackBar('server.pricing_error'.tr()); + return prices; + } + + prices = pricingResult.data; + return prices; + } + + void setServerProviderKey(final String serverProviderKey) async { + await repository.saveServerProviderKey(serverProviderKey); if (state is ServerInstallationRecovery) { emit( (state as ServerInstallationRecovery).copyWith( - hetznerKey: hetznerKey, + providerApiToken: serverProviderKey, currentStep: RecoveryStep.serverSelection, ), ); @@ -63,29 +180,63 @@ class ServerInstallationCubit extends Cubit { } emit( - (state as ServerInstallationNotFinished).copyWith(hetznerKey: hetznerKey), + (state as ServerInstallationNotFinished).copyWith( + providerApiToken: serverProviderKey, + ), ); } - void setCloudflareKey(final String cloudFlareKey) async { + Future setLocationIdentifier(final String locationId) async { + await ProvidersController.currentServerProvider! + .trySetServerLocation(locationId); + } + + void setServerType(final ServerType serverType) async { + await repository.saveServerType(serverType); + + emit( + (state as ServerInstallationNotFinished).copyWith( + serverTypeIdentificator: serverType.identifier, + ), + ); + } + + void setDnsApiToken(final String dnsApiToken) async { if (state is ServerInstallationRecovery) { - setAndValidateCloudflareToken(cloudFlareKey); + await setAndValidateDnsApiToken(dnsApiToken); return; } - await repository.saveCloudFlareKey(cloudFlareKey); + await repository.setDnsApiToken(dnsApiToken); + emit( (state as ServerInstallationNotFinished) - .copyWith(cloudFlareKey: cloudFlareKey), + .copyWith(dnsApiToken: dnsApiToken), ); } void setBackblazeKey(final String keyId, final String applicationKey) async { - final BackblazeCredential backblazeCredential = BackblazeCredential( + final BackupsCredential backblazeCredential = BackupsCredential( keyId: keyId, applicationKey: applicationKey, + provider: BackupsProviderType.backblaze, ); + final BackblazeBucket? bucket; await repository.saveBackblazeKey(backblazeCredential); if (state is ServerInstallationRecovery) { + final configuration = await ServerApi( + customToken: + (state as ServerInstallationRecovery).serverDetails!.apiToken, + isWithToken: true, + ).getBackupsConfiguration(); + if (configuration != null) { + try { + bucket = await BackblazeApi() + .fetchBucket(backblazeCredential, configuration); + await getIt().storeBackblazeBucket(bucket!); + } catch (e) { + print(e); + } + } finishRecoveryProcess(backblazeCredential); return; } @@ -108,41 +259,54 @@ class ServerInstallationCubit extends Cubit { emit((state as ServerInstallationNotFinished).copyWith(rootUser: rootUser)); } + Future onCreateServerSuccess( + final ServerHostingDetails serverDetails, + ) async { + await repository.saveServerDetails(serverDetails); + await ProvidersController.currentDnsProvider!.removeDomainRecords( + ip4: serverDetails.ip4, + domain: state.serverDomain!, + ); + await ProvidersController.currentDnsProvider!.createDomainRecords( + ip4: serverDetails.ip4, + domain: state.serverDomain!, + ); + + emit( + (state as ServerInstallationNotFinished).copyWith( + isLoading: false, + serverDetails: serverDetails, + installationDialoguePopUp: null, + ), + ); + runDelayed(startServerIfDnsIsOkay, const Duration(seconds: 30), null); + } + void createServerAndSetDnsRecords() async { - final ServerInstallationNotFinished stateCopy = - state as ServerInstallationNotFinished; - void onCancel() => emit( - (state as ServerInstallationNotFinished).copyWith(isLoading: false), - ); + emit((state as ServerInstallationNotFinished).copyWith(isLoading: true)); - Future onSuccess(final ServerHostingDetails serverDetails) async { - await repository.createDnsRecords( - serverDetails.ip4, - state.serverDomain!, - onCancel: onCancel, - ); + final installationData = LaunchInstallationData( + rootUser: state.rootUser!, + dnsApiToken: state.dnsApiToken!, + dnsProviderType: state.serverDomain!.provider, + serverDomain: state.serverDomain!, + serverTypeId: state.serverTypeIdentificator!, + errorCallback: clearAppConfig, + successCallback: onCreateServerSuccess, + storageSize: initialStorage, + ); + final result = + await ProvidersController.currentServerProvider!.launchInstallation( + installationData, + ); + + if (!result.success && result.data != null) { emit( (state as ServerInstallationNotFinished).copyWith( - isLoading: false, - serverDetails: serverDetails, + installationDialoguePopUp: result.data, ), ); - runDelayed(startServerIfDnsIsOkay, const Duration(seconds: 30), null); - } - - try { - emit((state as ServerInstallationNotFinished).copyWith(isLoading: true)); - await repository.createServer( - state.rootUser!, - state.serverDomain!.domainName, - state.cloudFlareKey!, - state.backblazeCredential!, - onCancel: onCancel, - onSuccess: onSuccess, - ); - } catch (e) { - emit(stateCopy); } } @@ -167,6 +331,7 @@ class ServerInstallationCubit extends Cubit { final ServerHostingDetails server = await repository.startServer( dataState.serverDetails!, ); + await repository.saveServerDetails(server); await repository.saveIsServerStarted(true); @@ -216,14 +381,13 @@ class ServerInstallationCubit extends Cubit { ), ); timer = Timer(pauseDuration, () async { - final ServerHostingDetails hetznerServerDetails = - await repository.restart(); + final ServerHostingDetails serverDetails = await repository.restart(); await repository.saveIsServerResetedFirstTime(true); - await repository.saveServerDetails(hetznerServerDetails); + await repository.saveServerDetails(serverDetails); final ServerInstallationNotFinished newState = dataState.copyWith( isServerResetedFirstTime: true, - serverDetails: hetznerServerDetails, + serverDetails: serverDetails, isLoading: false, ); @@ -258,14 +422,13 @@ class ServerInstallationCubit extends Cubit { ), ); timer = Timer(pauseDuration, () async { - final ServerHostingDetails hetznerServerDetails = - await repository.restart(); + final ServerHostingDetails serverDetails = await repository.restart(); await repository.saveIsServerResetedSecondTime(true); - await repository.saveServerDetails(hetznerServerDetails); + await repository.saveServerDetails(serverDetails); final ServerInstallationNotFinished newState = dataState.copyWith( isServerResetedSecondTime: true, - serverDetails: hetznerServerDetails, + serverDetails: serverDetails, isLoading: false, ); @@ -290,12 +453,25 @@ class ServerInstallationCubit extends Cubit { emit(TimerState(dataState: dataState, isLoading: true)); final bool isServerWorking = await repository.isHttpServerWorking(); + TlsOptions.verifyCertificate = true; if (isServerWorking) { - await repository.createDkimRecord(dataState.serverDomain!); - await repository.saveHasFinalChecked(true); - - emit(dataState.finish()); + bool dkimCreated = true; + try { + await repository.createDkimRecord(dataState.serverDomain!); + } catch (e) { + dkimCreated = false; + } + if (dkimCreated) { + await repository.saveHasFinalChecked(true); + emit(dataState.finish()); + } else { + runDelayed( + finishCheckIfServerIsOkay, + const Duration(seconds: 60), + dataState, + ); + } } else { runDelayed( finishCheckIfServerIsOkay, @@ -327,8 +503,7 @@ class ServerInstallationCubit extends Cubit { void submitDomainForAccessRecovery(final String domain) async { final ServerDomain serverDomain = ServerDomain( domainName: domain, - provider: DnsProvider.unknown, - zoneId: '', + provider: DnsProviderType.unknown, ); final ServerRecoveryCapabilities recoveryCapabilities = await repository.getRecoveryCapabilities(serverDomain); @@ -379,11 +554,43 @@ class ServerInstallationCubit extends Cubit { token, dataState.recoveryCapabilities, ); - await repository.saveServerDetails(serverDetails); + final ServerProviderType serverProvider = await ServerApi( + customToken: serverDetails.apiToken, + isWithToken: true, + ).getServerProviderType(); + final dnsProvider = await ServerApi( + customToken: serverDetails.apiToken, + isWithToken: true, + ).getDnsProviderType(); + if (serverProvider == ServerProviderType.unknown || + dnsProvider == DnsProviderType.unknown) { + getIt() + .showSnackBar('recovering.generic_error'.tr()); + return; + } + final newServerDetails = ServerHostingDetails( + provider: serverProvider, + apiToken: serverDetails.apiToken, + createTime: serverDetails.createTime, + id: serverDetails.id, + ip4: serverDetails.ip4, + volume: serverDetails.volume, + startTime: serverDetails.startTime, + ); + final newServerDomain = ServerDomain( + domainName: serverDomain.domainName, + provider: dnsProvider, + ); + await repository.saveServerDetails(newServerDetails); + await repository.saveDnsProviderType(dnsProvider); + await repository.saveDomain(newServerDomain); + setServerProviderType(serverProvider); + setDnsProviderType(dnsProvider); emit( dataState.copyWith( - serverDetails: serverDetails, - currentStep: RecoveryStep.hetznerToken, + serverDetails: newServerDetails, + serverDomain: newServerDomain, + currentStep: RecoveryStep.serverProviderToken, ), ); } on ServerAuthorizationException { @@ -417,11 +624,11 @@ class ServerInstallationCubit extends Cubit { ), ); break; - case RecoveryStep.serverSelection: - repository.deleteHetznerKey(); + case RecoveryStep.dnsProviderToken: + repository.deleteServerDetails(); emit( dataState.copyWith( - currentStep: RecoveryStep.hetznerToken, + currentStep: RecoveryStep.serverSelection, ), ); break; @@ -459,22 +666,32 @@ class ServerInstallationCubit extends Cubit { } } - Future> - getServersOnHetznerAccount() async { + Future> getAvailableServers() async { final ServerInstallationRecovery dataState = state as ServerInstallationRecovery; final List servers = - await repository.getServersOnHetznerAccount(); - final Iterable validated = servers.map( - (final ServerBasicInfo server) => - ServerBasicInfoWithValidators.fromServerBasicInfo( - serverBasicInfo: server, - isIpValid: server.ip == dataState.serverDetails?.ip4, - isReverseDnsValid: - server.reverseDns == dataState.serverDomain?.domainName, - ), - ); - return validated.toList(); + await repository.getServersOnProviderAccount(); + List validatedList = []; + try { + final Iterable validated = servers.map( + (final ServerBasicInfo server) => + ServerBasicInfoWithValidators.fromServerBasicInfo( + serverBasicInfo: server, + isIpValid: server.ip == dataState.serverDetails?.ip4, + isReverseDnsValid: + server.reverseDns == dataState.serverDomain?.domainName || + server.reverseDns == + dataState.serverDomain?.domainName.split('.')[0], + ), + ); + validatedList = validated.toList(); + } catch (e) { + print(e); + getIt() + .showSnackBar('modals.server_validators_error'.tr()); + } + + return validatedList; } Future setServerId(final ServerBasicInfo server) async { @@ -489,101 +706,125 @@ class ServerInstallationCubit extends Cubit { id: server.id, createTime: server.created, volume: ServerVolume( - id: server.volumeId, + id: 0, name: 'recovered_volume', + sizeByte: 0, + serverId: server.id, + linuxDevice: '', ), apiToken: dataState.serverDetails!.apiToken, - provider: ServerProvider.hetzner, + provider: ServerProviderType.hetzner, ); await repository.saveDomain(serverDomain); await repository.saveServerDetails(serverDetails); emit( dataState.copyWith( serverDetails: serverDetails, - currentStep: RecoveryStep.cloudflareToken, + currentStep: RecoveryStep.dnsProviderToken, ), ); } - Future setAndValidateCloudflareToken(final String token) async { + Future setAndValidateDnsApiToken(final String token) async { final ServerInstallationRecovery dataState = state as ServerInstallationRecovery; final ServerDomain? serverDomain = dataState.serverDomain; if (serverDomain == null) { return; } - final String? zoneId = - await repository.getDomainId(token, serverDomain.domainName); - if (zoneId == null) { + final isTokenValid = + await repository.validateDnsToken(token, serverDomain.domainName); + if (!isTokenValid) { getIt() .showSnackBar('recovering.domain_not_available_on_token'.tr()); return; } + final dnsProviderType = await ServerApi( + customToken: dataState.serverDetails!.apiToken, + isWithToken: true, + ).getDnsProviderType(); await repository.saveDomain( ServerDomain( domainName: serverDomain.domainName, - zoneId: zoneId, - provider: DnsProvider.cloudflare, + provider: dnsProviderType, ), ); - await repository.saveCloudFlareKey(token); emit( dataState.copyWith( serverDomain: ServerDomain( domainName: serverDomain.domainName, - zoneId: zoneId, - provider: DnsProvider.cloudflare, + provider: dnsProviderType, ), - cloudFlareKey: token, + dnsApiToken: token, currentStep: RecoveryStep.backblazeToken, ), ); } void finishRecoveryProcess( - final BackblazeCredential backblazeCredential, + final BackupsCredential backblazeCredential, ) async { await repository.saveIsServerStarted(true); await repository.saveIsServerResetedFirstTime(true); await repository.saveIsServerResetedSecondTime(true); await repository.saveHasFinalChecked(true); await repository.saveIsRecoveringServer(false); + final serverType = await ProvidersController.currentServerProvider! + .getServerType(state.serverDetails!.id); + await repository.saveServerType(serverType.data!); + await ProvidersController.currentServerProvider! + .trySetServerLocation(serverType.data!.location.identifier); final User mainUser = await repository.getMainUser(); await repository.saveRootUser(mainUser); final ServerInstallationRecovery updatedState = (state as ServerInstallationRecovery).copyWith( backblazeCredential: backblazeCredential, rootUser: mainUser, + serverTypeIdentificator: serverType.data!.identifier, ); emit(updatedState.finish()); } @override void onChange(final Change change) { + if (change.nextState.installationDialoguePopUp != null && + change.currentState.installationDialoguePopUp != + change.nextState.installationDialoguePopUp) { + final branching = change.nextState.installationDialoguePopUp; + showPopUpAlert( + alertTitle: branching!.title, + description: branching.description, + actionButtonTitle: branching.choices[1].title, + actionButtonOnPressed: () async { + final branchingResult = await branching.choices[1].callback!(); + if (!branchingResult.success) { + emit( + (state as ServerInstallationNotFinished).copyWith( + installationDialoguePopUp: branchingResult.data, + ), + ); + } + }, + cancelButtonTitle: branching.choices[0].title, + cancelButtonOnPressed: () async { + final branchingResult = await branching.choices[0].callback!(); + if (!branchingResult.success) { + emit( + (state as ServerInstallationNotFinished).copyWith( + installationDialoguePopUp: branchingResult.data, + ), + ); + } + }, + ); + } super.onChange(change); - print('================================'); - print('ServerInstallationState changed!'); - print('Current type: ${change.nextState.runtimeType}'); - print('Hetzner key: ${change.nextState.hetznerKey}'); - print('Cloudflare key: ${change.nextState.cloudFlareKey}'); - print('Domain: ${change.nextState.serverDomain}'); - print('BackblazeCredential: ${change.nextState.backblazeCredential}'); - if (change.nextState is ServerInstallationRecovery) { - print( - 'Recovery Step: ${(change.nextState as ServerInstallationRecovery).currentStep}', - ); - print( - 'Recovery Capabilities: ${(change.nextState as ServerInstallationRecovery).recoveryCapabilities}', - ); - } - if (change.nextState is TimerState) { - print('Timer: ${(change.nextState as TimerState).duration}'); - } } void clearAppConfig() { closeTimer(); - + ProvidersController.clearProviders(); + TlsOptions.verifyCertificate = false; repository.clearAppConfig(); emit(const ServerInstallationEmpty()); } @@ -592,14 +833,18 @@ class ServerInstallationCubit extends Cubit { closeTimer(); if (state.serverDetails != null) { - await repository.deleteServer(state.serverDomain!); + final bool deletionResult = + await repository.deleteServer(state.serverDomain!); + if (!deletionResult) { + return; + } } await repository.deleteServerRelatedRecords(); emit( ServerInstallationNotFinished( - hetznerKey: state.hetznerKey, + providerApiToken: state.providerApiToken, serverDomain: state.serverDomain, - cloudFlareKey: state.cloudFlareKey, + dnsApiToken: state.dnsApiToken, backblazeCredential: state.backblazeCredential, rootUser: state.rootUser, serverDetails: null, diff --git a/lib/logic/cubit/server_installation/server_installation_repository.dart b/lib/logic/cubit/server_installation/server_installation_repository.dart index 5d4db8fe..30bccfd5 100644 --- a/lib/logic/cubit/server_installation/server_installation_repository.dart +++ b/lib/logic/cubit/server_installation/server_installation_repository.dart @@ -1,30 +1,26 @@ +import 'dart:async'; import 'dart:io'; -import 'package:basic_utils/basic_utils.dart'; -import 'package:device_info_plus/device_info_plus.dart'; -import 'package:dio/dio.dart'; import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; import 'package:pub_semver/pub_semver.dart'; import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/config/hive_config.dart'; -import 'package:selfprivacy/logic/api_maps/cloudflare.dart'; -import 'package:selfprivacy/logic/api_maps/hetzner.dart'; -import 'package:selfprivacy/logic/api_maps/server.dart'; -import 'package:selfprivacy/logic/common_enum/common_enum.dart'; -import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart'; +import 'package:selfprivacy/logic/models/json/dns_records.dart'; +import 'package:selfprivacy/logic/providers/provider_settings.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart'; +import 'package:selfprivacy/logic/api_maps/tls_options.dart'; +import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; +import 'package:selfprivacy/logic/models/hive/backups_credential.dart'; import 'package:selfprivacy/logic/models/hive/server_details.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/logic/models/hive/user.dart'; import 'package:selfprivacy/logic/models/json/device_token.dart'; -import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart'; -import 'package:selfprivacy/logic/models/message.dart'; import 'package:selfprivacy/logic/models/server_basic_info.dart'; -import 'package:selfprivacy/ui/components/action_button/action_button.dart'; -import 'package:selfprivacy/ui/components/brand_alert/brand_alert.dart'; - -import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; +import 'package:selfprivacy/logic/models/server_type.dart'; +import 'package:selfprivacy/logic/providers/providers_controller.dart'; +import 'package:selfprivacy/utils/network_utils.dart'; +import 'package:selfprivacy/utils/platform_adapter.dart'; class IpNotFoundException implements Exception { IpNotFoundException(this.message); @@ -41,42 +37,97 @@ class ServerInstallationRepository { Box usersBox = Hive.box(BNames.usersBox); Future load() async { - final String? hetznerToken = getIt().hetznerKey; - final String? cloudflareToken = getIt().cloudFlareKey; + final String? providerApiToken = getIt().serverProviderKey; + final String? location = getIt().serverLocation; + final String? dnsApiToken = getIt().dnsProviderKey; + final String? serverTypeIdentificator = getIt().serverType; final ServerDomain? serverDomain = getIt().serverDomain; - final BackblazeCredential? backblazeCredential = + final DnsProviderType? dnsProvider = getIt().dnsProvider; + final ServerProviderType? serverProvider = + getIt().serverProvider; + final BackupsCredential? backblazeCredential = getIt().backblazeCredential; final ServerHostingDetails? serverDetails = getIt().serverDetails; - if (box.get(BNames.hasFinalChecked, defaultValue: false)) { - return ServerInstallationFinished( - hetznerKey: hetznerToken!, - cloudFlareKey: cloudflareToken!, - serverDomain: serverDomain!, - backblazeCredential: backblazeCredential!, - serverDetails: serverDetails!, - rootUser: box.get(BNames.rootUser), - isServerStarted: box.get(BNames.isServerStarted, defaultValue: false), - isServerResetedFirstTime: - box.get(BNames.isServerResetedFirstTime, defaultValue: false), - isServerResetedSecondTime: - box.get(BNames.isServerResetedSecondTime, defaultValue: false), + if (serverProvider != null || + (serverDetails != null && + serverDetails.provider != ServerProviderType.unknown)) { + ProvidersController.initServerProvider( + ServerProviderSettings( + provider: serverProvider ?? serverDetails!.provider, + isAuthorized: providerApiToken != null, + location: location, + ), ); } + if (dnsProvider != null || + (serverDomain != null && + serverDomain.provider != DnsProviderType.unknown)) { + ProvidersController.initDnsProvider( + DnsProviderSettings( + isAuthorized: dnsApiToken != null, + provider: dnsProvider ?? serverDomain!.provider, + ), + ); + } + + if (box.get(BNames.hasFinalChecked, defaultValue: false)) { + TlsOptions.verifyCertificate = true; + if (serverTypeIdentificator == null && serverDetails != null) { + final finalServerType = await ProvidersController.currentServerProvider! + .getServerType(serverDetails.id); + await saveServerType(finalServerType.data!); + await ProvidersController.currentServerProvider! + .trySetServerLocation(finalServerType.data!.location.identifier); + return ServerInstallationFinished( + installationDialoguePopUp: null, + providerApiToken: providerApiToken!, + serverTypeIdentificator: finalServerType.data!.identifier, + dnsApiToken: dnsApiToken!, + serverDomain: serverDomain!, + backblazeCredential: backblazeCredential!, + serverDetails: serverDetails, + rootUser: box.get(BNames.rootUser), + isServerStarted: box.get(BNames.isServerStarted, defaultValue: false), + isServerResetedFirstTime: + box.get(BNames.isServerResetedFirstTime, defaultValue: false), + isServerResetedSecondTime: + box.get(BNames.isServerResetedSecondTime, defaultValue: false), + ); + } else { + return ServerInstallationFinished( + installationDialoguePopUp: null, + providerApiToken: providerApiToken!, + serverTypeIdentificator: serverTypeIdentificator!, + dnsApiToken: dnsApiToken!, + serverDomain: serverDomain!, + backblazeCredential: backblazeCredential!, + serverDetails: serverDetails!, + rootUser: box.get(BNames.rootUser), + isServerStarted: box.get(BNames.isServerStarted, defaultValue: false), + isServerResetedFirstTime: + box.get(BNames.isServerResetedFirstTime, defaultValue: false), + isServerResetedSecondTime: + box.get(BNames.isServerResetedSecondTime, defaultValue: false), + ); + } + } + if (box.get(BNames.isRecoveringServer, defaultValue: false) && serverDomain != null) { return ServerInstallationRecovery( - hetznerKey: hetznerToken, - cloudFlareKey: cloudflareToken, + providerApiToken: providerApiToken, + dnsApiToken: dnsApiToken, serverDomain: serverDomain, + serverTypeIdentificator: serverTypeIdentificator, backblazeCredential: backblazeCredential, serverDetails: serverDetails, rootUser: box.get(BNames.rootUser), currentStep: _getCurrentRecoveryStep( - hetznerToken, - cloudflareToken, + providerApiToken, + dnsApiToken, serverDomain, serverDetails, ), @@ -85,9 +136,10 @@ class ServerInstallationRepository { } return ServerInstallationNotFinished( - hetznerKey: hetznerToken, - cloudFlareKey: cloudflareToken, + providerApiToken: providerApiToken, + dnsApiToken: dnsApiToken, serverDomain: serverDomain, + serverTypeIdentificator: serverTypeIdentificator, backblazeCredential: backblazeCredential, serverDetails: serverDetails, rootUser: box.get(BNames.rootUser), @@ -102,22 +154,22 @@ class ServerInstallationRepository { } RecoveryStep _getCurrentRecoveryStep( - final String? hetznerToken, - final String? cloudflareToken, + final String? serverProviderToken, + final String? dnsProviderToken, final ServerDomain serverDomain, final ServerHostingDetails? serverDetails, ) { if (serverDetails != null) { - if (hetznerToken != null) { - if (serverDetails.provider != ServerProvider.unknown) { - if (serverDomain.provider != DnsProvider.unknown) { + if (serverProviderToken != null) { + if (serverDetails.provider != ServerProviderType.unknown) { + if (serverDomain.provider != DnsProviderType.unknown) { return RecoveryStep.backblazeToken; } - return RecoveryStep.cloudflareToken; + return RecoveryStep.dnsProviderToken; } return RecoveryStep.serverSelection; } - return RecoveryStep.hetznerToken; + return RecoveryStep.serverProviderToken; } return RecoveryStep.selecting; } @@ -128,26 +180,36 @@ class ServerInstallationRepository { } Future startServer( - final ServerHostingDetails hetznerServer, + final ServerHostingDetails server, ) async { - final HetznerApi hetznerApi = HetznerApi(); - final ServerHostingDetails serverDetails = await hetznerApi.powerOn(); - - return serverDetails; - } - - Future getDomainId(final String token, final String domain) async { - final CloudflareApi cloudflareApi = CloudflareApi( - isWithToken: false, - customToken: token, + final result = await ProvidersController.currentServerProvider!.powerOn( + server.id, ); - try { - final String domainId = await cloudflareApi.getZoneId(domain); - return domainId; - } on DomainNotFoundException { - return null; + if (result.success && result.data != null) { + server.copyWith(startTime: result.data); } + + return server; + } + + Future validateDnsToken( + final String token, + final String domain, + ) async { + final result = + await ProvidersController.currentDnsProvider!.tryInitApiByToken(token); + if (!result.success) { + return false; + } + await setDnsApiToken(token); + final domainResult = + await ProvidersController.currentDnsProvider!.domainList(); + if (!domainResult.success || domainResult.data.isEmpty) { + return false; + } + + return domainResult.data.contains(domain); } Future> isDnsAddressesMatch( @@ -155,200 +217,67 @@ class ServerInstallationRepository { final String? ip4, final Map skippedMatches, ) async { - final List addresses = [ - '$domainName', - 'api.$domainName', - 'cloud.$domainName', - 'meet.$domainName', - 'password.$domainName' - ]; - final Map matches = {}; - - for (final String address in addresses) { - if (skippedMatches[address] ?? false) { - matches[address] = true; - continue; - } - final List? lookupRecordRes = await DnsUtils.lookupRecord( - address, - RRecordType.A, - provider: DnsApiProvider.CLOUDFLARE, + try { + await InternetAddress.lookup(domainName!).then( + (final records) { + for (final record in records) { + if (skippedMatches[record.host] ?? false) { + matches[record.host] = true; + continue; + } + if (record.address == ip4!) { + matches[record.host] = true; + } + } + }, ); - getIt.get().addMessage( - Message( - text: - 'DnsLookup: address: $address, $RRecordType, provider: CLOUDFLARE, ip4: $ip4', - ), - ); - getIt.get().addMessage( - Message( - text: - 'DnsLookup: ${lookupRecordRes == null ? 'empty' : (lookupRecordRes[0].data != ip4 ? 'wrong ip4' : 'right ip4')}', - ), - ); - if (lookupRecordRes == null || - lookupRecordRes.isEmpty || - lookupRecordRes[0].data != ip4) { - matches[address] = false; - } else { - matches[address] = true; - } + } catch (e) { + print(e); } return matches; } - Future createServer( - final User rootUser, - final String domainName, - final String cloudFlareKey, - final BackblazeCredential backblazeCredential, { - required final void Function() onCancel, - required final Future Function(ServerHostingDetails serverDetails) - onSuccess, - }) async { - final HetznerApi hetznerApi = HetznerApi(); - late ServerVolume dataBase; - - try { - dataBase = await hetznerApi.createVolume(); - - final ServerHostingDetails? serverDetails = await hetznerApi.createServer( - cloudFlareKey: cloudFlareKey, - rootUser: rootUser, - domainName: domainName, - dataBase: dataBase, - ); - if (serverDetails == null) { - print('Server is not initialized!'); - return; - } - saveServerDetails(serverDetails); - onSuccess(serverDetails); - } on DioError catch (e) { - if (e.response!.data['error']['code'] == 'uniqueness_error') { - final NavigationService nav = getIt.get(); - nav.showPopUpDialog( - BrandAlert( - title: 'modals.1'.tr(), - contentText: 'modals.2'.tr(), - actions: [ - ActionButton( - text: 'basis.delete'.tr(), - isRed: true, - onPressed: () async { - await hetznerApi.deleteSelfprivacyServerAndAllVolumes( - domainName: domainName, - ); - - final ServerHostingDetails? serverDetails = - await hetznerApi.createServer( - cloudFlareKey: cloudFlareKey, - rootUser: rootUser, - domainName: domainName, - dataBase: dataBase, - ); - if (serverDetails == null) { - print('Server is not initialized!'); - return; - } - await saveServerDetails(serverDetails); - onSuccess(serverDetails); - }, - ), - ActionButton( - text: 'basis.cancel'.tr(), - onPressed: onCancel, - ), - ], - ), - ); - } - } - } - - Future createDnsRecords( - final String ip4, - final ServerDomain cloudFlareDomain, { - required final void Function() onCancel, - }) async { - final CloudflareApi cloudflareApi = CloudflareApi(); - - await cloudflareApi.removeSimilarRecords( - ip4: ip4, - cloudFlareDomain: cloudFlareDomain, - ); - - try { - await cloudflareApi.createMultipleDnsRecords( - ip4: ip4, - cloudFlareDomain: cloudFlareDomain, - ); - } on DioError catch (e) { - final HetznerApi hetznerApi = HetznerApi(); - final NavigationService nav = getIt.get(); - nav.showPopUpDialog( - BrandAlert( - title: e.response!.data['errors'][0]['code'] == 1038 - ? 'modals.10'.tr() - : 'providers.domain.states.error'.tr(), - contentText: 'modals.6'.tr(), - actions: [ - ActionButton( - text: 'basis.delete'.tr(), - isRed: true, - onPressed: () async { - await hetznerApi.deleteSelfprivacyServerAndAllVolumes( - domainName: cloudFlareDomain.domainName, - ); - - onCancel(); - }, - ), - ActionButton( - text: 'basis.cancel'.tr(), - onPressed: onCancel, - ), - ], - ), - ); - } - - await HetznerApi().createReverseDns( - ip4: ip4, - domainName: cloudFlareDomain.domainName, - ); - } - - Future createDkimRecord(final ServerDomain cloudFlareDomain) async { - final CloudflareApi cloudflareApi = CloudflareApi(); + Future createDkimRecord(final ServerDomain domain) async { final ServerApi api = ServerApi(); - final String? dkimRecordString = await api.getDkim(); + late DnsRecord record; + try { + record = extractDkimRecord(await api.getDnsRecords())!; + } catch (e) { + print(e); + rethrow; + } - await cloudflareApi.setDkim(dkimRecordString ?? '', cloudFlareDomain); + await ProvidersController.currentDnsProvider!.setDnsRecord( + record, + domain, + ); } Future isHttpServerWorking() async { final ServerApi api = ServerApi(); - final bool isHttpServerWorking = await api.isHttpServerWorking(); - try { - await api.getDkim(); - } catch (e) { - return false; - } - return isHttpServerWorking; + return api.isHttpServerWorking(); } Future restart() async { - final HetznerApi hetznerApi = HetznerApi(); - return hetznerApi.reset(); + final server = getIt().serverDetails!; + + final result = await ServerApi().reboot(); + + if (result.success && result.data != null) { + server.copyWith(startTime: result.data); + } else { + getIt().showSnackBar('jobs.reboot_failed'.tr()); + } + + return server; } Future powerOn() async { - final HetznerApi hetznerApi = HetznerApi(); - return hetznerApi.powerOn(); + final server = getIt().serverDetails!; + return startServer(server); } Future getRecoveryCapabilities( @@ -374,49 +303,18 @@ class ServerInstallationRepository { } Future getServerIpFromDomain(final ServerDomain serverDomain) async { - final List? lookup = await DnsUtils.lookupRecord( - serverDomain.domainName, - RRecordType.A, - provider: DnsApiProvider.CLOUDFLARE, + String? domain; + await InternetAddress.lookup(serverDomain.domainName).then( + (final records) { + for (final record in records) { + domain = record.address; + } + }, ); - if (lookup == null || lookup.isEmpty) { + if (domain == null || domain!.isEmpty) { throw IpNotFoundException('No IP found for domain $serverDomain'); } - return lookup[0].data; - } - - Future getDeviceName() async { - final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); - if (kIsWeb) { - return deviceInfo.webBrowserInfo.then( - (final WebBrowserInfo value) => - '${value.browserName} ${value.platform}', - ); - } else { - if (Platform.isAndroid) { - return deviceInfo.androidInfo.then( - (final AndroidDeviceInfo value) => - '${value.model} ${value.version.release}', - ); - } else if (Platform.isIOS) { - return deviceInfo.iosInfo.then( - (final IosDeviceInfo value) => - '${value.utsname.machine} ${value.systemName} ${value.systemVersion}', - ); - } else if (Platform.isLinux) { - return deviceInfo.linuxInfo - .then((final LinuxDeviceInfo value) => value.prettyName); - } else if (Platform.isMacOS) { - return deviceInfo.macOsInfo.then( - (final MacOsDeviceInfo value) => - '${value.hostName} ${value.computerName}', - ); - } else if (Platform.isWindows) { - return deviceInfo.windowsInfo - .then((final WindowsDeviceInfo value) => value.computerName); - } - } - return 'Unidentified'; + return domain!; } Future authorizeByNewDeviceKey( @@ -429,18 +327,24 @@ class ServerInstallationRepository { overrideDomain: serverDomain.domainName, ); final String serverIp = await getServerIpFromDomain(serverDomain); - final ApiResponse apiResponse = await serverApi.authorizeDevice( - DeviceToken(device: await getDeviceName(), token: newDeviceKey), + final GenericResult result = await serverApi.authorizeDevice( + DeviceToken( + device: await PlatformAdapter.deviceName, + token: newDeviceKey, + ), ); - if (apiResponse.isSuccess) { + if (result.success) { return ServerHostingDetails( - apiToken: apiResponse.data, + apiToken: result.data, volume: ServerVolume( id: 0, name: '', + sizeByte: 0, + serverId: 0, + linuxDevice: '', ), - provider: ServerProvider.unknown, + provider: ServerProviderType.unknown, id: 0, ip4: serverIp, startTime: null, @@ -449,7 +353,7 @@ class ServerInstallationRepository { } throw ServerAuthorizationException( - apiResponse.errorMessage ?? apiResponse.data, + result.message ?? result.data, ); } @@ -463,18 +367,21 @@ class ServerInstallationRepository { overrideDomain: serverDomain.domainName, ); final String serverIp = await getServerIpFromDomain(serverDomain); - final ApiResponse apiResponse = await serverApi.useRecoveryToken( - DeviceToken(device: await getDeviceName(), token: recoveryKey), + final GenericResult result = await serverApi.useRecoveryToken( + DeviceToken(device: await PlatformAdapter.deviceName, token: recoveryKey), ); - if (apiResponse.isSuccess) { + if (result.success) { return ServerHostingDetails( - apiToken: apiResponse.data, + apiToken: result.data, volume: ServerVolume( id: 0, name: '', + sizeByte: 0, + serverId: 0, + linuxDevice: '', ), - provider: ServerProvider.unknown, + provider: ServerProviderType.unknown, id: 0, ip4: serverIp, startTime: null, @@ -483,7 +390,7 @@ class ServerInstallationRepository { } throw ServerAuthorizationException( - apiResponse.errorMessage ?? apiResponse.data, + result.message ?? result.data, ); } @@ -493,22 +400,23 @@ class ServerInstallationRepository { final ServerRecoveryCapabilities recoveryCapabilities, ) async { final ServerApi serverApi = ServerApi( - isWithToken: false, + isWithToken: true, overrideDomain: serverDomain.domainName, customToken: apiToken, ); final String serverIp = await getServerIpFromDomain(serverDomain); if (recoveryCapabilities == ServerRecoveryCapabilities.legacy) { - final Map apiResponse = - await serverApi.servicesPowerCheck(); - if (apiResponse.isNotEmpty) { + if (await serverApi.isHttpServerWorking()) { return ServerHostingDetails( apiToken: apiToken, volume: ServerVolume( id: 0, name: '', + serverId: 0, + sizeByte: 0, + linuxDevice: '', ), - provider: ServerProvider.unknown, + provider: ServerProviderType.unknown, id: 0, ip4: serverIp, startTime: null, @@ -520,20 +428,26 @@ class ServerInstallationRepository { ); } } - final ApiResponse deviceAuthKey = + final GenericResult deviceAuthKey = await serverApi.createDeviceToken(); - final ApiResponse apiResponse = await serverApi.authorizeDevice( - DeviceToken(device: await getDeviceName(), token: deviceAuthKey.data), + final GenericResult result = await serverApi.authorizeDevice( + DeviceToken( + device: await PlatformAdapter.deviceName, + token: deviceAuthKey.data, + ), ); - if (apiResponse.isSuccess) { + if (result.success) { return ServerHostingDetails( - apiToken: apiResponse.data, + apiToken: result.data, volume: ServerVolume( id: 0, name: '', + sizeByte: 0, + serverId: 0, + linuxDevice: '', ), - provider: ServerProvider.unknown, + provider: ServerProviderType.unknown, id: 0, ip4: serverIp, startTime: null, @@ -542,7 +456,7 @@ class ServerInstallationRepository { } throw ServerAuthorizationException( - apiResponse.errorMessage ?? apiResponse.data, + result.message ?? result.data, ); } @@ -550,15 +464,15 @@ class ServerInstallationRepository { final ServerApi serverApi = ServerApi(); const User fallbackUser = User( isFoundOnServer: false, + type: UserType.primary, note: "Couldn't find main user on server, API is outdated", login: 'UNKNOWN', sshKeys: [], ); final String? serverApiVersion = await serverApi.getApiVersion(); - final ApiResponse> users = - await serverApi.getUsersList(withMainUser: true); - if (serverApiVersion == null || !users.isSuccess) { + final users = await serverApi.getAllUsers(); + if (serverApiVersion == null || users.isEmpty) { return fallbackUser; } try { @@ -566,31 +480,16 @@ class ServerInstallationRepository { if (!VersionConstraint.parse('>=1.2.5').allows(parsedVersion)) { return fallbackUser; } - return User( - isFoundOnServer: true, - login: users.data[0], + return users.firstWhere( + (final User user) => user.type == UserType.primary, ); } on FormatException { return fallbackUser; } } - Future> getServersOnHetznerAccount() async { - final HetznerApi hetznerApi = HetznerApi(); - final List servers = await hetznerApi.getServers(); - return servers - .map( - (final HetznerServerInfo server) => ServerBasicInfo( - id: server.id, - name: server.name, - ip: server.publicNet.ipv4.ip, - reverseDns: server.publicNet.ipv4.reverseDns, - created: server.created, - volumeId: server.volumes.isNotEmpty ? server.volumes[0] : 0, - ), - ) - .toList(); - } + Future> getServersOnProviderAccount() async => + (await ProvidersController.currentServerProvider!.getServers()).data; Future saveServerDetails( final ServerHostingDetails serverDetails, @@ -598,24 +497,55 @@ class ServerInstallationRepository { await getIt().storeServerDetails(serverDetails); } - Future saveHetznerKey(final String key) async { - print('saved'); - await getIt().storeHetznerKey(key); + Future deleteServerDetails() async { + await box.delete(BNames.serverDetails); + getIt().init(); } - Future deleteHetznerKey() async { + Future saveServerProviderType(final ServerProviderType type) async { + await getIt().storeServerProviderType(type); + } + + Future saveDnsProviderType(final DnsProviderType type) async { + await getIt().storeDnsProviderType(type); + } + + Future saveServerProviderKey(final String key) async { + await getIt().storeServerProviderKey(key); + } + + Future saveServerType(final ServerType serverType) async { + await getIt().storeServerTypeIdentifier( + serverType.identifier, + ); + await getIt().storeServerLocation( + serverType.location.identifier, + ); + } + + Future deleteServerProviderKey() async { await box.delete(BNames.hetznerKey); getIt().init(); } Future saveBackblazeKey( - final BackblazeCredential backblazeCredential, + final BackupsCredential backblazeCredential, ) async { await getIt().storeBackblazeCredential(backblazeCredential); } - Future saveCloudFlareKey(final String key) async { - await getIt().storeCloudFlareKey(key); + Future deleteBackblazeKey() async { + await box.delete(BNames.backblazeCredential); + getIt().init(); + } + + Future setDnsApiToken(final String key) async { + await getIt().storeDnsProviderKey(key); + } + + Future deleteDnsProviderKey() async { + await box.delete(BNames.cloudFlareKey); + getIt().init(); } Future saveDomain(final ServerDomain serverDomain) async { @@ -651,14 +581,18 @@ class ServerInstallationRepository { await box.put(BNames.hasFinalChecked, value); } - Future deleteServer(final ServerDomain serverDomain) async { - final HetznerApi hetznerApi = HetznerApi(); - final CloudflareApi cloudFlare = CloudflareApi(); - - await hetznerApi.deleteSelfprivacyServerAndAllVolumes( - domainName: serverDomain.domainName, + Future deleteServer(final ServerDomain serverDomain) async { + final deletionResult = + await ProvidersController.currentServerProvider!.deleteServer( + serverDomain.domainName, ); + if (!deletionResult.success) { + getIt() + .showSnackBar('modals.server_validators_error'.tr()); + return false; + } + await box.put(BNames.hasFinalChecked, false); await box.put(BNames.isServerStarted, false); await box.put(BNames.isServerResetedFirstTime, false); @@ -666,7 +600,14 @@ class ServerInstallationRepository { await box.put(BNames.isLoading, false); await box.put(BNames.serverDetails, null); - await cloudFlare.removeSimilarRecords(cloudFlareDomain: serverDomain); + final GenericResult removalResult = await ProvidersController + .currentDnsProvider! + .removeDomainRecords(domain: serverDomain); + + if (!removalResult.success) { + getIt().showSnackBar('modals.dns_removal_error'.tr()); + } + return true; } Future deleteServerRelatedRecords() async { diff --git a/lib/logic/cubit/server_installation/server_installation_state.dart b/lib/logic/cubit/server_installation/server_installation_state.dart index b3128e71..9f701a0a 100644 --- a/lib/logic/cubit/server_installation/server_installation_state.dart +++ b/lib/logic/cubit/server_installation/server_installation_state.dart @@ -2,8 +2,9 @@ part of '../server_installation/server_installation_cubit.dart'; abstract class ServerInstallationState extends Equatable { const ServerInstallationState({ - required this.hetznerKey, - required this.cloudFlareKey, + required this.providerApiToken, + required this.serverTypeIdentificator, + required this.dnsApiToken, required this.backblazeCredential, required this.serverDomain, required this.rootUser, @@ -11,35 +12,41 @@ abstract class ServerInstallationState extends Equatable { required this.isServerStarted, required this.isServerResetedFirstTime, required this.isServerResetedSecondTime, + required this.installationDialoguePopUp, }); @override List get props => [ - hetznerKey, - cloudFlareKey, + providerApiToken, + serverTypeIdentificator, + dnsApiToken, backblazeCredential, serverDomain, rootUser, serverDetails, isServerStarted, isServerResetedFirstTime, + installationDialoguePopUp ]; - final String? hetznerKey; - final String? cloudFlareKey; - final BackblazeCredential? backblazeCredential; + final String? providerApiToken; + final String? dnsApiToken; + final String? serverTypeIdentificator; + final BackupsCredential? backblazeCredential; final ServerDomain? serverDomain; final User? rootUser; final ServerHostingDetails? serverDetails; final bool isServerStarted; final bool isServerResetedFirstTime; final bool isServerResetedSecondTime; + final CallbackDialogueBranching? installationDialoguePopUp; - bool get isHetznerFilled => hetznerKey != null; - bool get isCloudFlareFilled => cloudFlareKey != null; - bool get isBackblazeFilled => backblazeCredential != null; - bool get isDomainFilled => serverDomain != null; - bool get isUserFilled => rootUser != null; + bool get isServerProviderApiKeyFilled => providerApiToken != null; + bool get isServerTypeFilled => serverTypeIdentificator != null; + bool get isDnsProviderFilled => dnsApiToken != null; + bool get isBackupsProviderFilled => backblazeCredential != null; + bool get isDomainSelected => serverDomain != null; + bool get isPrimaryUserFilled => rootUser != null; bool get isServerCreated => serverDetails != null; bool get isFullyInitilized => _fulfilementList.every((final el) => el!); @@ -58,11 +65,12 @@ abstract class ServerInstallationState extends Equatable { List get _fulfilementList { final List res = [ - isHetznerFilled, - isCloudFlareFilled, - isBackblazeFilled, - isDomainFilled, - isUserFilled, + isServerProviderApiKeyFilled, + isServerTypeFilled, + isDnsProviderFilled, + isBackupsProviderFilled, + isDomainSelected, + isPrimaryUserFilled, isServerCreated, isServerStarted, isServerResetedFirstTime, @@ -76,12 +84,13 @@ abstract class ServerInstallationState extends Equatable { class TimerState extends ServerInstallationNotFinished { TimerState({ required this.dataState, - required final super.isLoading, + required super.isLoading, this.timerStart, this.duration, }) : super( - hetznerKey: dataState.hetznerKey, - cloudFlareKey: dataState.cloudFlareKey, + providerApiToken: dataState.providerApiToken, + serverTypeIdentificator: dataState.serverTypeIdentificator, + dnsApiToken: dataState.dnsApiToken, backblazeCredential: dataState.backblazeCredential, serverDomain: dataState.serverDomain, rootUser: dataState.rootUser, @@ -90,6 +99,7 @@ class TimerState extends ServerInstallationNotFinished { isServerResetedFirstTime: dataState.isServerResetedFirstTime, isServerResetedSecondTime: dataState.isServerResetedSecondTime, dnsMatches: dataState.dnsMatches, + installationDialoguePopUp: dataState.installationDialoguePopUp, ); final ServerInstallationNotFinished dataState; @@ -106,8 +116,9 @@ class TimerState extends ServerInstallationNotFinished { enum ServerSetupProgress { nothingYet, - hetznerFilled, - cloudFlareFilled, + serverProviderFilled, + servertTypeFilled, + dnsProviderFilled, backblazeFilled, domainFilled, userFilled, @@ -119,25 +130,28 @@ enum ServerSetupProgress { class ServerInstallationNotFinished extends ServerInstallationState { const ServerInstallationNotFinished({ - required final super.isServerStarted, - required final super.isServerResetedFirstTime, - required final super.isServerResetedSecondTime, - required final this.isLoading, + required super.isServerStarted, + required super.isServerResetedFirstTime, + required super.isServerResetedSecondTime, + required this.isLoading, required this.dnsMatches, - final super.hetznerKey, - final super.cloudFlareKey, - final super.backblazeCredential, - final super.serverDomain, - final super.rootUser, - final super.serverDetails, + super.providerApiToken, + super.serverTypeIdentificator, + super.dnsApiToken, + super.backblazeCredential, + super.serverDomain, + super.rootUser, + super.serverDetails, + super.installationDialoguePopUp, }); final bool isLoading; final Map? dnsMatches; @override List get props => [ - hetznerKey, - cloudFlareKey, + providerApiToken, + serverTypeIdentificator, + dnsApiToken, backblazeCredential, serverDomain, rootUser, @@ -146,12 +160,14 @@ class ServerInstallationNotFinished extends ServerInstallationState { isServerResetedFirstTime, isLoading, dnsMatches, + installationDialoguePopUp, ]; ServerInstallationNotFinished copyWith({ - final String? hetznerKey, - final String? cloudFlareKey, - final BackblazeCredential? backblazeCredential, + final String? providerApiToken, + final String? serverTypeIdentificator, + final String? dnsApiToken, + final BackupsCredential? backblazeCredential, final ServerDomain? serverDomain, final User? rootUser, final ServerHostingDetails? serverDetails, @@ -160,10 +176,13 @@ class ServerInstallationNotFinished extends ServerInstallationState { final bool? isServerResetedSecondTime, final bool? isLoading, final Map? dnsMatches, + final CallbackDialogueBranching? installationDialoguePopUp, }) => ServerInstallationNotFinished( - hetznerKey: hetznerKey ?? this.hetznerKey, - cloudFlareKey: cloudFlareKey ?? this.cloudFlareKey, + providerApiToken: providerApiToken ?? this.providerApiToken, + serverTypeIdentificator: + serverTypeIdentificator ?? this.serverTypeIdentificator, + dnsApiToken: dnsApiToken ?? this.dnsApiToken, backblazeCredential: backblazeCredential ?? this.backblazeCredential, serverDomain: serverDomain ?? this.serverDomain, rootUser: rootUser ?? this.rootUser, @@ -175,11 +194,14 @@ class ServerInstallationNotFinished extends ServerInstallationState { isServerResetedSecondTime ?? this.isServerResetedSecondTime, isLoading: isLoading ?? this.isLoading, dnsMatches: dnsMatches ?? this.dnsMatches, + installationDialoguePopUp: + installationDialoguePopUp ?? this.installationDialoguePopUp, ); ServerInstallationFinished finish() => ServerInstallationFinished( - hetznerKey: hetznerKey!, - cloudFlareKey: cloudFlareKey!, + providerApiToken: providerApiToken!, + serverTypeIdentificator: serverTypeIdentificator!, + dnsApiToken: dnsApiToken!, backblazeCredential: backblazeCredential!, serverDomain: serverDomain!, rootUser: rootUser!, @@ -187,14 +209,16 @@ class ServerInstallationNotFinished extends ServerInstallationState { isServerStarted: isServerStarted, isServerResetedFirstTime: isServerResetedFirstTime, isServerResetedSecondTime: isServerResetedSecondTime, + installationDialoguePopUp: installationDialoguePopUp, ); } class ServerInstallationEmpty extends ServerInstallationNotFinished { const ServerInstallationEmpty() : super( - hetznerKey: null, - cloudFlareKey: null, + providerApiToken: null, + serverTypeIdentificator: null, + dnsApiToken: null, backblazeCredential: null, serverDomain: null, rootUser: null, @@ -204,32 +228,37 @@ class ServerInstallationEmpty extends ServerInstallationNotFinished { isServerResetedSecondTime: false, isLoading: false, dnsMatches: null, + installationDialoguePopUp: null, ); } class ServerInstallationFinished extends ServerInstallationState { const ServerInstallationFinished({ - required final String super.hetznerKey, - required final String super.cloudFlareKey, - required final BackblazeCredential super.backblazeCredential, - required final ServerDomain super.serverDomain, - required final User super.rootUser, - required final ServerHostingDetails super.serverDetails, - required final super.isServerStarted, - required final super.isServerResetedFirstTime, - required final super.isServerResetedSecondTime, + required String super.providerApiToken, + required String super.serverTypeIdentificator, + required String super.dnsApiToken, + required BackupsCredential super.backblazeCredential, + required ServerDomain super.serverDomain, + required User super.rootUser, + required ServerHostingDetails super.serverDetails, + required super.isServerStarted, + required super.isServerResetedFirstTime, + required super.isServerResetedSecondTime, + required super.installationDialoguePopUp, }); @override List get props => [ - hetznerKey, - cloudFlareKey, + providerApiToken, + serverTypeIdentificator, + dnsApiToken, backblazeCredential, serverDomain, rootUser, serverDetails, isServerStarted, isServerResetedFirstTime, + installationDialoguePopUp, ]; } @@ -238,9 +267,9 @@ enum RecoveryStep { recoveryKey, newDeviceKey, oldToken, - hetznerToken, + serverProviderToken, serverSelection, - cloudflareToken, + dnsProviderToken, backblazeToken, } @@ -260,37 +289,42 @@ class ServerInstallationRecovery extends ServerInstallationState { const ServerInstallationRecovery({ required this.currentStep, required this.recoveryCapabilities, - final super.hetznerKey, - final super.cloudFlareKey, - final super.backblazeCredential, - final super.serverDomain, - final super.rootUser, - final super.serverDetails, + super.providerApiToken, + super.serverTypeIdentificator, + super.dnsApiToken, + super.backblazeCredential, + super.serverDomain, + super.rootUser, + super.serverDetails, }) : super( isServerStarted: true, isServerResetedFirstTime: true, isServerResetedSecondTime: true, + installationDialoguePopUp: null, ); final RecoveryStep currentStep; final ServerRecoveryCapabilities recoveryCapabilities; @override List get props => [ - hetznerKey, - cloudFlareKey, + providerApiToken, + serverTypeIdentificator, + dnsApiToken, backblazeCredential, serverDomain, rootUser, serverDetails, isServerStarted, isServerResetedFirstTime, - currentStep + currentStep, + installationDialoguePopUp ]; ServerInstallationRecovery copyWith({ - final String? hetznerKey, - final String? cloudFlareKey, - final BackblazeCredential? backblazeCredential, + final String? providerApiToken, + final String? serverTypeIdentificator, + final String? dnsApiToken, + final BackupsCredential? backblazeCredential, final ServerDomain? serverDomain, final User? rootUser, final ServerHostingDetails? serverDetails, @@ -298,8 +332,10 @@ class ServerInstallationRecovery extends ServerInstallationState { final ServerRecoveryCapabilities? recoveryCapabilities, }) => ServerInstallationRecovery( - hetznerKey: hetznerKey ?? this.hetznerKey, - cloudFlareKey: cloudFlareKey ?? this.cloudFlareKey, + providerApiToken: providerApiToken ?? this.providerApiToken, + serverTypeIdentificator: + serverTypeIdentificator ?? this.serverTypeIdentificator, + dnsApiToken: dnsApiToken ?? this.dnsApiToken, backblazeCredential: backblazeCredential ?? this.backblazeCredential, serverDomain: serverDomain ?? this.serverDomain, rootUser: rootUser ?? this.rootUser, @@ -309,8 +345,9 @@ class ServerInstallationRecovery extends ServerInstallationState { ); ServerInstallationFinished finish() => ServerInstallationFinished( - hetznerKey: hetznerKey!, - cloudFlareKey: cloudFlareKey!, + providerApiToken: providerApiToken!, + serverTypeIdentificator: serverTypeIdentificator!, + dnsApiToken: dnsApiToken!, backblazeCredential: backblazeCredential!, serverDomain: serverDomain!, rootUser: rootUser!, @@ -318,5 +355,6 @@ class ServerInstallationRecovery extends ServerInstallationState { isServerStarted: true, isServerResetedFirstTime: true, isServerResetedSecondTime: true, + installationDialoguePopUp: null, ); } diff --git a/lib/logic/cubit/server_jobs/server_jobs_cubit.dart b/lib/logic/cubit/server_jobs/server_jobs_cubit.dart new file mode 100644 index 00000000..4cc0cf97 --- /dev/null +++ b/lib/logic/cubit/server_jobs/server_jobs_cubit.dart @@ -0,0 +1,123 @@ +import 'dart:async'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart'; +import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; +import 'package:selfprivacy/logic/models/json/server_job.dart'; + +export 'package:provider/provider.dart'; + +part 'server_jobs_state.dart'; + +class ServerJobsCubit + extends ServerInstallationDependendCubit { + ServerJobsCubit(final ServerInstallationCubit serverInstallationCubit) + : super( + serverInstallationCubit, + ServerJobsState(), + ); + + Timer? timer; + final ServerApi api = ServerApi(); + + @override + void clear() async { + emit( + ServerJobsState(), + ); + if (timer != null && timer!.isActive) { + timer!.cancel(); + timer = null; + } + } + + @override + void load() async { + if (serverInstallationCubit.state is ServerInstallationFinished) { + final List jobs = await api.getServerJobs(); + emit( + ServerJobsState( + serverJobList: jobs, + ), + ); + timer = Timer(const Duration(seconds: 5), () => reload(useTimer: true)); + } + } + + Future migrateToBinds(final Map serviceToDisk) async { + final result = await api.migrateToBinds(serviceToDisk); + if (result.data == null) { + getIt().showSnackBar(result.message!); + return; + } + + emit( + ServerJobsState( + migrationJobUid: result.data, + ), + ); + } + + ServerJob? getServerJobByUid(final String uid) { + ServerJob? job; + + try { + job = state.serverJobList.firstWhere( + (final ServerJob job) => job.uid == uid, + ); + } catch (e) { + print(e); + } + + return job; + } + + Future removeServerJob(final String uid) async { + final result = await api.removeApiJob(uid); + if (!result.success) { + getIt().showSnackBar('jobs.generic_error'.tr()); + return; + } + + if (!result.data) { + getIt() + .showSnackBar(result.message ?? 'jobs.generic_error'.tr()); + return; + } + + emit( + ServerJobsState( + serverJobList: [ + for (final ServerJob job in state.serverJobList) + if (job.uid != uid) job + ], + ), + ); + } + + Future removeAllFinishedJobs() async { + final List finishedJobs = state.serverJobList + .where( + (final ServerJob job) => + job.status == JobStatusEnum.finished || + job.status == JobStatusEnum.error, + ) + .toList(); + for (final ServerJob job in finishedJobs) { + await removeServerJob(job.uid); + } + } + + Future reload({final bool useTimer = false}) async { + final List jobs = await api.getServerJobs(); + emit( + ServerJobsState( + serverJobList: jobs, + ), + ); + if (useTimer) { + timer = Timer(const Duration(seconds: 5), () => reload(useTimer: true)); + } + } +} diff --git a/lib/logic/cubit/server_jobs/server_jobs_state.dart b/lib/logic/cubit/server_jobs/server_jobs_state.dart new file mode 100644 index 00000000..9a18dd51 --- /dev/null +++ b/lib/logic/cubit/server_jobs/server_jobs_state.dart @@ -0,0 +1,49 @@ +part of 'server_jobs_cubit.dart'; + +class ServerJobsState extends ServerInstallationDependendState { + ServerJobsState({ + final serverJobList = const [], + this.migrationJobUid, + }) { + _serverJobList = serverJobList; + } + + late final List _serverJobList; + final String? migrationJobUid; + + List get serverJobList { + try { + final List list = _serverJobList; + list.sort((final a, final b) => b.createdAt.compareTo(a.createdAt)); + return list; + } on UnsupportedError { + return _serverJobList; + } + } + + List get backupJobList => serverJobList + .where( + // The backup jobs has the format of 'service..backup' + (final job) => + job.typeId.contains('backup') || job.typeId.contains('restore'), + ) + .toList(); + + bool get hasRemovableJobs => serverJobList.any( + (final job) => + job.status == JobStatusEnum.finished || + job.status == JobStatusEnum.error, + ); + + @override + List get props => [migrationJobUid, _serverJobList]; + + ServerJobsState copyWith({ + final List? serverJobList, + final String? migrationJobUid, + }) => + ServerJobsState( + serverJobList: serverJobList ?? _serverJobList, + migrationJobUid: migrationJobUid ?? this.migrationJobUid, + ); +} diff --git a/lib/logic/cubit/server_volumes/server_volume_cubit.dart b/lib/logic/cubit/server_volumes/server_volume_cubit.dart new file mode 100644 index 00000000..bf30c8c0 --- /dev/null +++ b/lib/logic/cubit/server_volumes/server_volume_cubit.dart @@ -0,0 +1,78 @@ +import 'dart:async'; + +import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart'; +import 'package:selfprivacy/logic/common_enum/common_enum.dart'; +import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; +import 'package:selfprivacy/logic/cubit/provider_volumes/provider_volume_cubit.dart'; +import 'package:selfprivacy/logic/models/disk_status.dart'; +import 'package:selfprivacy/logic/models/json/server_disk_volume.dart'; + +part 'server_volume_state.dart'; + +class ApiServerVolumeCubit + extends ServerInstallationDependendCubit { + ApiServerVolumeCubit( + final ServerInstallationCubit serverInstallationCubit, + this.providerVolumeCubit, + ) : super(serverInstallationCubit, ApiServerVolumeState.initial()) { + _providerVolumeSubscription = + providerVolumeCubit.stream.listen(checkProviderVolumes); + } + + final ServerApi serverApi = ServerApi(); + + @override + Future load() async { + if (serverInstallationCubit.state is ServerInstallationFinished) { + unawaited(reload()); + } + } + + late StreamSubscription _providerVolumeSubscription; + final ApiProviderVolumeCubit providerVolumeCubit; + + void checkProviderVolumes(final ApiProviderVolumeState state) { + emit( + ApiServerVolumeState( + this.state._volumes, + this.state.status, + this.state.usesBinds, + DiskStatus.fromVolumes(this.state._volumes, state.volumes), + ), + ); + return; + } + + Future reload() async { + final volumes = await serverApi.getServerDiskVolumes(); + final usesBinds = await serverApi.isUsingBinds(); + var status = LoadingStatus.error; + + if (volumes.isNotEmpty) { + status = LoadingStatus.success; + } + + emit( + ApiServerVolumeState( + volumes, + status, + usesBinds, + DiskStatus.fromVolumes( + volumes, + providerVolumeCubit.state.volumes, + ), + ), + ); + } + + @override + void clear() { + emit(ApiServerVolumeState.initial()); + } + + @override + Future close() { + _providerVolumeSubscription.cancel(); + return super.close(); + } +} diff --git a/lib/logic/cubit/server_volumes/server_volume_state.dart b/lib/logic/cubit/server_volumes/server_volume_state.dart new file mode 100644 index 00000000..d68daa58 --- /dev/null +++ b/lib/logic/cubit/server_volumes/server_volume_state.dart @@ -0,0 +1,42 @@ +part of 'server_volume_cubit.dart'; + +class ApiServerVolumeState extends ServerInstallationDependendState { + const ApiServerVolumeState( + this._volumes, + this.status, + this.usesBinds, + this._diskStatus, + ); + + ApiServerVolumeState.initial() + : this(const [], LoadingStatus.uninitialized, null, DiskStatus()); + + final List _volumes; + final DiskStatus _diskStatus; + final bool? usesBinds; + final LoadingStatus status; + + List get volumes => _diskStatus.diskVolumes; + DiskStatus get diskStatus => _diskStatus; + + DiskVolume getVolume(final String volumeName) => volumes.firstWhere( + (final volume) => volume.name == volumeName, + orElse: () => DiskVolume(), + ); + + ApiServerVolumeState copyWith({ + final List? volumes, + final LoadingStatus? status, + final bool? usesBinds, + final DiskStatus? diskStatus, + }) => + ApiServerVolumeState( + volumes ?? _volumes, + status ?? this.status, + usesBinds ?? this.usesBinds, + diskStatus ?? _diskStatus, + ); + + @override + List get props => [_volumes, status, usesBinds]; +} diff --git a/lib/logic/cubit/services/services_cubit.dart b/lib/logic/cubit/services/services_cubit.dart index f83a2a9a..60476c2d 100644 --- a/lib/logic/cubit/services/services_cubit.dart +++ b/lib/logic/cubit/services/services_cubit.dart @@ -1,31 +1,83 @@ -import 'package:selfprivacy/logic/api_maps/server.dart'; -import 'package:selfprivacy/logic/common_enum/common_enum.dart'; +import 'dart:async'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart'; import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; +import 'package:selfprivacy/logic/models/service.dart'; part 'services_state.dart'; class ServicesCubit extends ServerInstallationDependendCubit { ServicesCubit(final ServerInstallationCubit serverInstallationCubit) - : super(serverInstallationCubit, ServicesState.allOff()); + : super(serverInstallationCubit, const ServicesState.empty()); final ServerApi api = ServerApi(); + Timer? timer; @override Future load() async { if (serverInstallationCubit.state is ServerInstallationFinished) { - final Map statuses = await api.servicesPowerCheck(); + final List services = await api.getAllServices(); emit( ServicesState( - isPasswordManagerEnable: statuses[ServiceTypes.passwordManager]!, - isCloudEnable: statuses[ServiceTypes.cloud]!, - isGitEnable: statuses[ServiceTypes.git]!, - isSocialNetworkEnable: statuses[ServiceTypes.socialNetwork]!, - isVpnEnable: statuses[ServiceTypes.vpn]!, + services: services, + lockedServices: const [], ), ); + timer = Timer(const Duration(seconds: 10), () => reload(useTimer: true)); } } + Future reload({final bool useTimer = false}) async { + final List services = await api.getAllServices(); + emit( + state.copyWith( + services: services, + ), + ); + if (useTimer) { + timer = Timer(const Duration(seconds: 60), () => reload(useTimer: true)); + } + } + + Future restart(final String serviceId) async { + emit(state.copyWith(lockedServices: [...state.lockedServices, serviceId])); + final result = await api.restartService(serviceId); + if (!result.success) { + getIt().showSnackBar('jobs.generic_error'.tr()); + return; + } + if (!result.data) { + getIt() + .showSnackBar(result.message ?? 'jobs.generic_error'.tr()); + return; + } + + await Future.delayed(const Duration(seconds: 2)); + unawaited(reload()); + await Future.delayed(const Duration(seconds: 10)); + emit( + state.copyWith( + lockedServices: state.lockedServices + .where((final element) => element != serviceId) + .toList(), + ), + ); + unawaited(reload()); + } + + Future moveService( + final String serviceId, + final String destination, + ) async { + await api.moveService(serviceId, destination); + } + @override void clear() async { - emit(ServicesState.allOff()); + emit(const ServicesState.empty()); + if (timer != null && timer!.isActive) { + timer!.cancel(); + timer = null; + } } } diff --git a/lib/logic/cubit/services/services_state.dart b/lib/logic/cubit/services/services_state.dart index ffe90aee..7a136b54 100644 --- a/lib/logic/cubit/services/services_state.dart +++ b/lib/logic/cubit/services/services_state.dart @@ -1,93 +1,49 @@ part of 'services_cubit.dart'; class ServicesState extends ServerInstallationDependendState { - factory ServicesState.allOn() => const ServicesState( - isPasswordManagerEnable: true, - isCloudEnable: true, - isGitEnable: true, - isSocialNetworkEnable: true, - isVpnEnable: true, - ); - - factory ServicesState.allOff() => const ServicesState( - isPasswordManagerEnable: false, - isCloudEnable: false, - isGitEnable: false, - isSocialNetworkEnable: false, - isVpnEnable: false, - ); const ServicesState({ - required this.isPasswordManagerEnable, - required this.isCloudEnable, - required this.isGitEnable, - required this.isSocialNetworkEnable, - required this.isVpnEnable, + required this.services, + required this.lockedServices, }); - final bool isPasswordManagerEnable; - final bool isCloudEnable; - final bool isGitEnable; - final bool isSocialNetworkEnable; - final bool isVpnEnable; + const ServicesState.empty() + : this(services: const [], lockedServices: const []); - ServicesState enableList( - final List list, - ) => - ServicesState( - isPasswordManagerEnable: list.contains(ServiceTypes.passwordManager) - ? true - : isPasswordManagerEnable, - isCloudEnable: list.contains(ServiceTypes.cloud) ? true : isCloudEnable, - isGitEnable: - list.contains(ServiceTypes.git) ? true : isPasswordManagerEnable, - isSocialNetworkEnable: list.contains(ServiceTypes.socialNetwork) - ? true - : isPasswordManagerEnable, - isVpnEnable: - list.contains(ServiceTypes.vpn) ? true : isPasswordManagerEnable, - ); + final List services; + final List lockedServices; - ServicesState disableList( - final List list, - ) => - ServicesState( - isPasswordManagerEnable: list.contains(ServiceTypes.passwordManager) - ? false - : isPasswordManagerEnable, - isCloudEnable: - list.contains(ServiceTypes.cloud) ? false : isCloudEnable, - isGitEnable: - list.contains(ServiceTypes.git) ? false : isPasswordManagerEnable, - isSocialNetworkEnable: list.contains(ServiceTypes.socialNetwork) - ? false - : isPasswordManagerEnable, - isVpnEnable: - list.contains(ServiceTypes.vpn) ? false : isPasswordManagerEnable, - ); + List get servicesThatCanBeBackedUp => services + .where( + (final service) => service.canBeBackedUp, + ) + .toList(); + + bool isServiceLocked(final String serviceId) => + lockedServices.contains(serviceId); + + Service? getServiceById(final String id) { + final service = services.firstWhere( + (final service) => service.id == id, + orElse: () => Service.empty, + ); + if (service.id == 'empty') { + return null; + } + return service; + } @override List get props => [ - isPasswordManagerEnable, - isCloudEnable, - isGitEnable, - isSocialNetworkEnable, - isVpnEnable + services, + lockedServices, ]; - bool isEnableByType(final ServiceTypes type) { - switch (type) { - case ServiceTypes.passwordManager: - return isPasswordManagerEnable; - case ServiceTypes.cloud: - return isCloudEnable; - case ServiceTypes.socialNetwork: - return isSocialNetworkEnable; - case ServiceTypes.git: - return isGitEnable; - case ServiceTypes.vpn: - return isVpnEnable; - default: - throw Exception('wrong state'); - } - } + ServicesState copyWith({ + final List? services, + final List? lockedServices, + }) => + ServicesState( + services: services ?? this.services, + lockedServices: lockedServices ?? this.lockedServices, + ); } diff --git a/lib/logic/cubit/support_system/support_system_cubit.dart b/lib/logic/cubit/support_system/support_system_cubit.dart new file mode 100644 index 00000000..b6250740 --- /dev/null +++ b/lib/logic/cubit/support_system/support_system_cubit.dart @@ -0,0 +1,19 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; + +part 'support_system_state.dart'; + +class SupportSystemCubit extends Cubit { + SupportSystemCubit() : super(const SupportSystemState('about')); + + void showArticle({ + required final String article, + final BuildContext? context, + }) { + emit(SupportSystemState(article)); + if (context != null) { + Scaffold.of(context).openEndDrawer(); + } + } +} diff --git a/lib/logic/cubit/support_system/support_system_state.dart b/lib/logic/cubit/support_system/support_system_state.dart new file mode 100644 index 00000000..0c3c3087 --- /dev/null +++ b/lib/logic/cubit/support_system/support_system_state.dart @@ -0,0 +1,12 @@ +part of 'support_system_cubit.dart'; + +class SupportSystemState extends Equatable { + const SupportSystemState( + this.currentArticle, + ); + + final String currentArticle; + + @override + List get props => [currentArticle]; +} diff --git a/lib/logic/cubit/users/users_cubit.dart b/lib/logic/cubit/users/users_cubit.dart index 9b86c109..cb717441 100644 --- a/lib/logic/cubit/users/users_cubit.dart +++ b/lib/logic/cubit/users/users_cubit.dart @@ -1,10 +1,13 @@ +import 'dart:async'; + +import 'package:easy_localization/easy_localization.dart'; import 'package:hive/hive.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/config/hive_config.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart'; import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; import 'package:selfprivacy/logic/models/hive/user.dart'; -import 'package:selfprivacy/logic/api_maps/server.dart'; - export 'package:provider/provider.dart'; part 'users_state.dart'; @@ -15,8 +18,7 @@ class UsersCubit extends ServerInstallationDependendCubit { serverInstallationCubit, const UsersState( [], - User(login: 'root'), - User(login: 'loading...'), + false, ), ); Box box = Hive.box(BNames.usersBox); @@ -26,173 +28,43 @@ class UsersCubit extends ServerInstallationDependendCubit { @override Future load() async { - if (serverInstallationCubit.state is ServerInstallationFinished) { - final List loadedUsers = box.values.toList(); - final primaryUser = serverInstallationBox.get( - BNames.rootUser, - defaultValue: const User(login: 'loading...'), - ); - final List rootKeys = [ - ...serverInstallationBox.get(BNames.rootKeys, defaultValue: []) - ]; - if (loadedUsers.isNotEmpty) { - emit( - UsersState( - loadedUsers, - User(login: 'root', sshKeys: rootKeys), - primaryUser, - ), - ); - } - - final ApiResponse> usersFromServer = - await api.getUsersList(); - if (usersFromServer.isSuccess) { - final List updatedList = - mergeLocalAndServerUsers(loadedUsers, usersFromServer.data); - emit( - UsersState( - updatedList, - User(login: 'root', sshKeys: rootKeys), - primaryUser, - ), - ); - } - - final List usersWithSshKeys = await loadSshKeys(state.users); - // Update the users it the box - box.clear(); - box.addAll(usersWithSshKeys); - - final User rootUserWithSshKeys = - (await loadSshKeys([state.rootUser])).first; - serverInstallationBox.put(BNames.rootKeys, rootUserWithSshKeys.sshKeys); - final User primaryUserWithSshKeys = - (await loadSshKeys([state.primaryUser])).first; - serverInstallationBox.put(BNames.rootUser, primaryUserWithSshKeys); + if (serverInstallationCubit.state is! ServerInstallationFinished) { + return; + } + final List loadedUsers = box.values.toList(); + if (loadedUsers.isNotEmpty) { emit( UsersState( - usersWithSshKeys, - rootUserWithSshKeys, - primaryUserWithSshKeys, - ), - ); - } - } - - List mergeLocalAndServerUsers( - final List localUsers, - final List serverUsers, - ) { - // If local user not exists on server, add it with isFoundOnServer = false - // If server user not exists on local, add it - - final List mergedUsers = []; - final List serverUsersCopy = List.from(serverUsers); - - for (final User localUser in localUsers) { - if (serverUsersCopy.contains(localUser.login)) { - mergedUsers.add( - User( - login: localUser.login, - isFoundOnServer: true, - password: localUser.password, - sshKeys: localUser.sshKeys, - ), - ); - serverUsersCopy.remove(localUser.login); - } else { - mergedUsers.add( - User( - login: localUser.login, - isFoundOnServer: false, - password: localUser.password, - note: localUser.note, - ), - ); - } - } - - for (final String serverUser in serverUsersCopy) { - mergedUsers.add( - User( - login: serverUser, - isFoundOnServer: true, + loadedUsers, + false, ), ); } - return mergedUsers; - } - - Future> loadSshKeys(final List users) async { - final List updatedUsers = []; - - for (final User user in users) { - if (user.isFoundOnServer || - user.login == 'root' || - user.login == state.primaryUser.login) { - final ApiResponse> sshKeys = - await api.getUserSshKeys(user); - print('sshKeys for $user: ${sshKeys.data}'); - if (sshKeys.isSuccess) { - updatedUsers.add( - User( - login: user.login, - isFoundOnServer: true, - password: user.password, - sshKeys: sshKeys.data, - note: user.note, - ), - ); - } else { - updatedUsers.add( - User( - login: user.login, - isFoundOnServer: true, - password: user.password, - note: user.note, - ), - ); - } - } else { - updatedUsers.add( - User( - login: user.login, - isFoundOnServer: false, - password: user.password, - note: user.note, - ), - ); - } - } - return updatedUsers; + unawaited(refresh()); } Future refresh() async { - List updatedUsers = List.from(state.users); - final ApiResponse> usersFromServer = await api.getUsersList(); - if (usersFromServer.isSuccess) { - updatedUsers = - mergeLocalAndServerUsers(updatedUsers, usersFromServer.data); + if (serverInstallationCubit.state is! ServerInstallationFinished) { + return; + } + emit(state.copyWith(isLoading: true)); + final List usersFromServer = await api.getAllUsers(); + if (usersFromServer.isNotEmpty) { + emit( + UsersState( + usersFromServer, + false, + ), + ); + // Update the users it the box + await box.clear(); + await box.addAll(usersFromServer); + } else { + getIt() + .showSnackBar('users.could_not_fetch_users'.tr()); + emit(state.copyWith(isLoading: false)); } - final List usersWithSshKeys = await loadSshKeys(updatedUsers); - box.clear(); - box.addAll(usersWithSshKeys); - final User rootUserWithSshKeys = - (await loadSshKeys([state.rootUser])).first; - serverInstallationBox.put(BNames.rootKeys, rootUserWithSshKeys.sshKeys); - final User primaryUserWithSshKeys = - (await loadSshKeys([state.primaryUser])).first; - serverInstallationBox.put(BNames.rootUser, primaryUserWithSshKeys); - emit( - UsersState( - usersWithSshKeys, - rootUserWithSshKeys, - primaryUserWithSshKeys, - ), - ); - return; } Future createUser(final User user) async { @@ -201,18 +73,23 @@ class UsersCubit extends ServerInstallationDependendCubit { .any((final User u) => u.login == user.login && u.isFoundOnServer)) { return; } - // If user is root or primary user, do nothing - if (user.login == 'root' || user.login == state.primaryUser.login) { + final String? password = user.password; + if (password == null) { + getIt() + .showSnackBar('users.could_not_create_user'.tr()); return; } // If API returned error, do nothing - final ApiResponse result = await api.createUser(user); - if (!result.isSuccess) { + final GenericResult result = + await api.createUser(user.login, password); + if (result.data == null) { + getIt() + .showSnackBar(result.message ?? 'users.could_not_create_user'.tr()); return; } final List loadedUsers = List.from(state.users); - loadedUsers.add(result.data); + loadedUsers.add(result.data!); await box.clear(); await box.addAll(loadedUsers); emit(state.copyWith(users: loadedUsers)); @@ -220,142 +97,75 @@ class UsersCubit extends ServerInstallationDependendCubit { Future deleteUser(final User user) async { // If user is primary or root, don't delete - if (user.login == state.primaryUser.login || user.login == 'root') { + if (user.type != UserType.normal) { + getIt() + .showSnackBar('users.could_not_delete_user'.tr()); return; } final List loadedUsers = List.from(state.users); - final bool result = await api.deleteUser(user); - if (result) { + final GenericResult result = await api.deleteUser(user.login); + if (result.success && result.data) { loadedUsers.removeWhere((final User u) => u.login == user.login); await box.clear(); await box.addAll(loadedUsers); emit(state.copyWith(users: loadedUsers)); } + + if (!result.success) { + getIt().showSnackBar('jobs.generic_error'.tr()); + } + + if (!result.data) { + getIt() + .showSnackBar(result.message ?? 'jobs.generic_error'.tr()); + } + } + + Future changeUserPassword( + final User user, + final String newPassword, + ) async { + if (user.type == UserType.root) { + getIt() + .showSnackBar('users.could_not_change_password'.tr()); + return; + } + final GenericResult result = + await api.updateUser(user.login, newPassword); + if (result.data == null) { + getIt().showSnackBar( + result.message ?? 'users.could_not_change_password'.tr(), + ); + } } Future addSshKey(final User user, final String publicKey) async { - // If adding root key, use api.addRootSshKey - // Otherwise, use api.addUserSshKey - if (user.login == 'root') { - final ApiResponse result = await api.addRootSshKey(publicKey); - if (result.isSuccess) { - // Add ssh key to the array of root keys - final List rootKeys = serverInstallationBox - .get(BNames.rootKeys, defaultValue: []) as List; - rootKeys.add(publicKey); - serverInstallationBox.put(BNames.rootKeys, rootKeys); - emit( - state.copyWith( - rootUser: User( - login: state.rootUser.login, - isFoundOnServer: true, - password: state.rootUser.password, - sshKeys: rootKeys, - note: state.rootUser.note, - ), - ), - ); - } + final GenericResult result = + await api.addSshKey(user.login, publicKey); + if (result.data != null) { + final User updatedUser = result.data!; + final int index = + state.users.indexWhere((final User u) => u.login == user.login); + await box.putAt(index, updatedUser); + emit( + state.copyWith( + users: box.values.toList(), + ), + ); } else { - final ApiResponse result = await api.addUserSshKey(user, publicKey); - if (result.isSuccess) { - // If it is primary user, update primary user - if (user.login == state.primaryUser.login) { - final List primaryUserKeys = - List.from(state.primaryUser.sshKeys); - primaryUserKeys.add(publicKey); - final User updatedUser = User( - login: state.primaryUser.login, - isFoundOnServer: true, - password: state.primaryUser.password, - sshKeys: primaryUserKeys, - note: state.primaryUser.note, - ); - serverInstallationBox.put(BNames.rootUser, updatedUser); - emit( - state.copyWith( - primaryUser: updatedUser, - ), - ); - } else { - // If it is not primary user, update user - final List userKeys = List.from(user.sshKeys); - userKeys.add(publicKey); - final User updatedUser = User( - login: user.login, - isFoundOnServer: true, - password: user.password, - sshKeys: userKeys, - note: user.note, - ); - await box.putAt(box.values.toList().indexOf(user), updatedUser); - emit( - state.copyWith( - users: box.values.toList(), - ), - ); - } - } + getIt() + .showSnackBar(result.message ?? 'users.could_not_add_ssh_key'.tr()); } } Future deleteSshKey(final User user, final String publicKey) async { - // All keys are deleted via api.deleteUserSshKey - - final ApiResponse result = - await api.deleteUserSshKey(user, publicKey); - if (result.isSuccess) { - // If it is root user, delete key from root keys - // If it is primary user, update primary user - // If it is not primary user, update user - - if (user.login == 'root') { - final List rootKeys = serverInstallationBox - .get(BNames.rootKeys, defaultValue: []) as List; - rootKeys.remove(publicKey); - serverInstallationBox.put(BNames.rootKeys, rootKeys); - emit( - state.copyWith( - rootUser: User( - login: state.rootUser.login, - isFoundOnServer: true, - password: state.rootUser.password, - sshKeys: rootKeys, - note: state.rootUser.note, - ), - ), - ); - return; - } - if (user.login == state.primaryUser.login) { - final List primaryUserKeys = - List.from(state.primaryUser.sshKeys); - primaryUserKeys.remove(publicKey); - final User updatedUser = User( - login: state.primaryUser.login, - isFoundOnServer: true, - password: state.primaryUser.password, - sshKeys: primaryUserKeys, - note: state.primaryUser.note, - ); - serverInstallationBox.put(BNames.rootUser, updatedUser); - emit( - state.copyWith( - primaryUser: updatedUser, - ), - ); - return; - } - final List userKeys = List.from(user.sshKeys); - userKeys.remove(publicKey); - final User updatedUser = User( - login: user.login, - isFoundOnServer: true, - password: user.password, - sshKeys: userKeys, - note: user.note, - ); - await box.putAt(box.values.toList().indexOf(user), updatedUser); + final GenericResult result = + await api.removeSshKey(user.login, publicKey); + if (result.data != null) { + final User updatedUser = result.data!; + final int index = + state.users.indexWhere((final User u) => u.login == user.login); + await box.putAt(index, updatedUser); emit( state.copyWith( users: box.values.toList(), @@ -369,8 +179,7 @@ class UsersCubit extends ServerInstallationDependendCubit { emit( const UsersState( [], - User(login: 'root'), - User(login: 'loading...'), + false, ), ); } diff --git a/lib/logic/cubit/users/users_state.dart b/lib/logic/cubit/users/users_state.dart index fa4ed1cd..4e2ed42e 100644 --- a/lib/logic/cubit/users/users_state.dart +++ b/lib/logic/cubit/users/users_state.dart @@ -1,30 +1,60 @@ part of 'users_cubit.dart'; class UsersState extends ServerInstallationDependendState { - const UsersState(this.users, this.rootUser, this.primaryUser); + const UsersState(this.users, this.isLoading); final List users; - final User rootUser; - final User primaryUser; + final bool isLoading; + + User get rootUser => + users.firstWhere((final user) => user.type == UserType.root); + + User get primaryUser => + users.firstWhere((final user) => user.type == UserType.primary); + + List get normalUsers => + users.where((final user) => user.type == UserType.normal).toList(); @override - List get props => [users, rootUser, primaryUser]; + List get props => [users, isLoading]; + + /// Makes a copy of existing users list, but places 'primary' + /// to the beginning and sorts the rest alphabetically + /// + /// If found a 'root' user, it doesn't get copied into the result + List get orderedUsers { + User? primaryUser; + final List normalUsers = []; + for (final User user in users) { + if (user.type == UserType.primary) { + primaryUser = user; + continue; + } + if (user.type == UserType.root) { + continue; + } + normalUsers.add(user); + } + + normalUsers.sort( + (final User a, final User b) => + a.login.toLowerCase().compareTo(b.login.toLowerCase()), + ); + + return primaryUser == null ? normalUsers : [primaryUser] + normalUsers; + } UsersState copyWith({ final List? users, - final User? rootUser, - final User? primaryUser, + final bool? isLoading, }) => UsersState( users ?? this.users, - rootUser ?? this.rootUser, - primaryUser ?? this.primaryUser, + isLoading ?? this.isLoading, ); bool isLoginRegistered(final String login) => - users.any((final User user) => user.login == login) || - login == rootUser.login || - login == primaryUser.login; + users.any((final User user) => user.login == login); bool get isEmpty => users.isEmpty; } diff --git a/lib/logic/get_it/api_config.dart b/lib/logic/get_it/api_config.dart index 3f3e5ac0..ac889dcc 100644 --- a/lib/logic/get_it/api_config.dart +++ b/lib/logic/get_it/api_config.dart @@ -1,7 +1,7 @@ import 'package:hive/hive.dart'; import 'package:selfprivacy/config/hive_config.dart'; import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart'; -import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart'; +import 'package:selfprivacy/logic/models/hive/backups_credential.dart'; import 'package:selfprivacy/logic/models/hive/server_details.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart'; @@ -9,32 +9,66 @@ class ApiConfigModel { final Box _box = Hive.box(BNames.serverInstallationBox); ServerHostingDetails? get serverDetails => _serverDetails; - String? get hetznerKey => _hetznerKey; - String? get cloudFlareKey => _cloudFlareKey; - BackblazeCredential? get backblazeCredential => _backblazeCredential; + String? get localeCode => _localeCode; + String? get serverProviderKey => _serverProviderKey; + String? get serverLocation => _serverLocation; + String? get serverType => _serverType; + String? get dnsProviderKey => _dnsProviderKey; + ServerProviderType? get serverProvider => _serverProvider; + DnsProviderType? get dnsProvider => _dnsProvider; + + BackupsCredential? get backblazeCredential => _backblazeCredential; ServerDomain? get serverDomain => _serverDomain; BackblazeBucket? get backblazeBucket => _backblazeBucket; - String? _hetznerKey; - String? _cloudFlareKey; + String? _localeCode; + String? _serverProviderKey; + String? _serverLocation; + String? _dnsProviderKey; + String? _serverType; + ServerProviderType? _serverProvider; + DnsProviderType? _dnsProvider; ServerHostingDetails? _serverDetails; - BackblazeCredential? _backblazeCredential; + BackupsCredential? _backblazeCredential; ServerDomain? _serverDomain; BackblazeBucket? _backblazeBucket; - Future storeHetznerKey(final String value) async { + Future setLocaleCode(final String value) async { + _localeCode = value; + } + + Future storeServerProviderType(final ServerProviderType value) async { + await _box.put(BNames.serverProvider, value); + _serverProvider = value; + } + + Future storeDnsProviderType(final DnsProviderType value) async { + await _box.put(BNames.dnsProvider, value); + _dnsProvider = value; + } + + Future storeServerProviderKey(final String value) async { await _box.put(BNames.hetznerKey, value); - _hetznerKey = value; + _serverProviderKey = value; } - Future storeCloudFlareKey(final String value) async { + Future storeDnsProviderKey(final String value) async { await _box.put(BNames.cloudFlareKey, value); - _cloudFlareKey = value; + _dnsProviderKey = value; } - Future storeBackblazeCredential(final BackblazeCredential value) async { - await _box.put(BNames.backblazeCredential, value); + Future storeServerTypeIdentifier(final String typeIdentifier) async { + await _box.put(BNames.serverTypeIdentifier, typeIdentifier); + _serverType = typeIdentifier; + } + Future storeServerLocation(final String serverLocation) async { + await _box.put(BNames.serverLocation, serverLocation); + _serverLocation = serverLocation; + } + + Future storeBackblazeCredential(final BackupsCredential value) async { + await _box.put(BNames.backblazeCredential, value); _backblazeCredential = value; } @@ -54,21 +88,30 @@ class ApiConfigModel { } void clear() { - _hetznerKey = null; - _cloudFlareKey = null; + _localeCode = null; + _serverProviderKey = null; + _dnsProvider = null; + _serverLocation = null; + _dnsProviderKey = null; _backblazeCredential = null; _serverDomain = null; _serverDetails = null; _backblazeBucket = null; + _serverType = null; + _serverProvider = null; } void init() { - _hetznerKey = _box.get(BNames.hetznerKey); - - _cloudFlareKey = _box.get(BNames.cloudFlareKey); + _localeCode = 'en'; + _serverProviderKey = _box.get(BNames.hetznerKey); + _serverLocation = _box.get(BNames.serverLocation); + _dnsProviderKey = _box.get(BNames.cloudFlareKey); _backblazeCredential = _box.get(BNames.backblazeCredential); _serverDomain = _box.get(BNames.serverDomain); _serverDetails = _box.get(BNames.serverDetails); _backblazeBucket = _box.get(BNames.backblazeBucket); + _serverType = _box.get(BNames.serverTypeIdentifier); + _serverProvider = _box.get(BNames.serverProvider); + _dnsProvider = _box.get(BNames.dnsProvider); } } diff --git a/lib/logic/get_it/console.dart b/lib/logic/get_it/console.dart index 290f31ab..a523c5e8 100644 --- a/lib/logic/get_it/console.dart +++ b/lib/logic/get_it/console.dart @@ -9,5 +9,9 @@ class ConsoleModel extends ChangeNotifier { void addMessage(final Message message) { messages.add(message); notifyListeners(); + // Make sure we don't have too many messages + if (messages.length > 500) { + messages.removeAt(0); + } } } diff --git a/lib/logic/get_it/navigation.dart b/lib/logic/get_it/navigation.dart index 15adc982..dac1dc8d 100644 --- a/lib/logic/get_it/navigation.dart +++ b/lib/logic/get_it/navigation.dart @@ -1,16 +1,19 @@ import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/brand_colors.dart'; -import 'package:selfprivacy/config/text_themes.dart'; class NavigationService { final GlobalKey scaffoldMessengerKey = GlobalKey(); final GlobalKey navigatorKey = GlobalKey(); - NavigatorState? get navigator => navigatorKey.currentState; - void showPopUpDialog(final AlertDialog dialog) { - final BuildContext context = navigatorKey.currentState!.overlay!.context; + final BuildContext? context = navigatorKey.currentContext; + + if (context == null) { + showSnackBar( + 'Could not show dialog. This should not happen, please report this.', + ); + return; + } showDialog( context: context, @@ -18,12 +21,12 @@ class NavigationService { ); } - void showSnackBar(final String text) { + void showSnackBar(final String text, {final SnackBarBehavior? behavior}) { final ScaffoldMessengerState state = scaffoldMessengerKey.currentState!; final SnackBar snack = SnackBar( - backgroundColor: BrandColors.black.withOpacity(0.8), - content: Text(text, style: buttonTitleText), + content: Text(text), duration: const Duration(seconds: 2), + behavior: behavior, ); state.showSnackBar(snack); } diff --git a/lib/logic/models/auto_upgrade_settings.dart b/lib/logic/models/auto_upgrade_settings.dart new file mode 100644 index 00000000..19130422 --- /dev/null +++ b/lib/logic/models/auto_upgrade_settings.dart @@ -0,0 +1,18 @@ +import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server_settings.graphql.dart'; + +class AutoUpgradeSettings { + AutoUpgradeSettings({ + required this.enable, + required this.allowReboot, + }); + + AutoUpgradeSettings.fromGraphQL( + final Query$SystemSettings$system$settings$autoUpgrade autoUpgrade, + ) : this( + enable: autoUpgrade.enable, + allowReboot: autoUpgrade.allowReboot, + ); + + final bool enable; + final bool allowReboot; +} diff --git a/lib/logic/models/backup.dart b/lib/logic/models/backup.dart new file mode 100644 index 00000000..1dcf9129 --- /dev/null +++ b/lib/logic/models/backup.dart @@ -0,0 +1,143 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/backups.graphql.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/schema.graphql.dart'; +import 'package:selfprivacy/logic/models/hive/backups_credential.dart'; + +class Backup { + Backup.fromGraphQL( + final Query$AllBackupSnapshots$backup$allSnapshots snapshot, + ) : this( + id: snapshot.id, + time: snapshot.createdAt, + serviceId: snapshot.service.id, + fallbackServiceName: snapshot.service.displayName, + reason: snapshot.reason, + ); + + Backup({ + required this.time, + required this.id, + required this.serviceId, + required this.fallbackServiceName, + required this.reason, + }); + + // Time of the backup + final DateTime time; + @JsonKey(name: 'short_id') + final String id; + final String serviceId; + final String fallbackServiceName; + final Enum$BackupReason reason; +} + +extension BackupReasonExtension on Enum$BackupReason { + String get displayName => switch (this) { + Enum$BackupReason.AUTO => 'backup.snapshot_reasons.auto', + Enum$BackupReason.EXPLICIT => 'backup.snapshot_reasons.explicit', + Enum$BackupReason.PRE_RESTORE => 'backup.snapshot_reasons.pre_restore', + Enum$BackupReason.$unknown => 'backup.snapshot_reasons.unknown', + }; +} + +class BackupConfiguration { + BackupConfiguration.fromGraphQL( + final Query$BackupConfiguration$backup$configuration configuration, + ) : this( + // Provided by API as int of minutes + autobackupPeriod: configuration.autobackupPeriod != null + ? Duration(minutes: configuration.autobackupPeriod!) + : null, + encryptionKey: configuration.encryptionKey, + isInitialized: configuration.isInitialized, + locationId: configuration.locationId, + locationName: configuration.locationName, + provider: BackupsProviderType.fromGraphQL(configuration.provider), + autobackupQuotas: AutobackupQuotas.fromGraphQL( + configuration.autobackupQuotas, + ), + ); + + BackupConfiguration({ + required this.autobackupPeriod, + required this.encryptionKey, + required this.isInitialized, + required this.locationId, + required this.locationName, + required this.provider, + required this.autobackupQuotas, + }); + + final Duration? autobackupPeriod; + final String encryptionKey; + final bool isInitialized; + final String? locationId; + final String? locationName; + final BackupsProviderType provider; + final AutobackupQuotas autobackupQuotas; +} + +class AutobackupQuotas { + AutobackupQuotas.fromGraphQL( + final Query$BackupConfiguration$backup$configuration$autobackupQuotas + autobackupQuotas, + ) : this( + last: autobackupQuotas.last, + daily: autobackupQuotas.daily, + weekly: autobackupQuotas.weekly, + monthly: autobackupQuotas.monthly, + yearly: autobackupQuotas.yearly, + ); + + AutobackupQuotas({ + required this.last, + required this.daily, + required this.weekly, + required this.monthly, + required this.yearly, + }); + + final int last; + final int daily; + final int weekly; + final int monthly; + final int yearly; + + AutobackupQuotas copyWith({ + final int? last, + final int? daily, + final int? weekly, + final int? monthly, + final int? yearly, + }) => + AutobackupQuotas( + last: last ?? this.last, + daily: daily ?? this.daily, + weekly: weekly ?? this.weekly, + monthly: monthly ?? this.monthly, + yearly: yearly ?? this.yearly, + ); +} + +enum BackupRestoreStrategy { + inplace, + downloadVerifyOverwrite, + unknown; + + factory BackupRestoreStrategy.fromGraphQL( + final Enum$RestoreStrategy strategy, + ) => + switch (strategy) { + Enum$RestoreStrategy.INPLACE => inplace, + Enum$RestoreStrategy.DOWNLOAD_VERIFY_OVERWRITE => + downloadVerifyOverwrite, + Enum$RestoreStrategy.$unknown => unknown, + }; + + Enum$RestoreStrategy get toGraphQL => switch (this) { + inplace => Enum$RestoreStrategy.INPLACE, + downloadVerifyOverwrite => + Enum$RestoreStrategy.DOWNLOAD_VERIFY_OVERWRITE, + unknown => Enum$RestoreStrategy.$unknown, + }; +} diff --git a/lib/logic/models/callback_dialogue_branching.dart b/lib/logic/models/callback_dialogue_branching.dart new file mode 100644 index 00000000..614a7c22 --- /dev/null +++ b/lib/logic/models/callback_dialogue_branching.dart @@ -0,0 +1,21 @@ +import 'package:selfprivacy/logic/api_maps/generic_result.dart'; + +class CallbackDialogueBranching { + CallbackDialogueBranching({ + required this.title, + required this.description, + required this.choices, + }); + final String title; + final String description; + final List choices; +} + +class CallbackDialogueChoice { + CallbackDialogueChoice({ + required this.title, + required this.callback, + }); + final String title; + final Future> Function()? callback; +} diff --git a/lib/logic/models/disk_size.dart b/lib/logic/models/disk_size.dart new file mode 100644 index 00000000..6d335683 --- /dev/null +++ b/lib/logic/models/disk_size.dart @@ -0,0 +1,38 @@ +import 'package:easy_localization/easy_localization.dart'; + +class DiskSize { + const DiskSize({this.byte = 0}); + + DiskSize.fromKibibyte(final double kibibyte) + : this(byte: (kibibyte * 1024).round()); + DiskSize.fromMebibyte(final double mebibyte) + : this(byte: (mebibyte * 1024 * 1024).round()); + DiskSize.fromGibibyte(final double gibibyte) + : this(byte: (gibibyte * 1024 * 1024 * 1024).round()); + + final int byte; + + double get kibibyte => byte / 1024.0; + double get mebibyte => byte / 1024.0 / 1024.0; + double get gibibyte => byte / 1024.0 / 1024.0 / 1024.0; + + DiskSize operator +(final DiskSize other) => + DiskSize(byte: byte + other.byte); + DiskSize operator -(final DiskSize other) => + DiskSize(byte: byte - other.byte); + DiskSize operator *(final double other) => + DiskSize(byte: (byte * other).round()); + + @override + String toString() { + if (byte < 1024) { + return '${byte.toStringAsFixed(0)} ${tr('storage.bytes')}'; + } else if (byte < 1024 * 1024) { + return 'storage.kb'.tr(args: [kibibyte.toStringAsFixed(1)]); + } else if (byte < 1024 * 1024 * 1024) { + return 'storage.mb'.tr(args: [mebibyte.toStringAsFixed(1)]); + } else { + return 'storage.gb'.tr(args: [gibibyte.toStringAsFixed(1)]); + } + } +} diff --git a/lib/logic/models/disk_status.dart b/lib/logic/models/disk_status.dart new file mode 100644 index 00000000..2a37ad77 --- /dev/null +++ b/lib/logic/models/disk_status.dart @@ -0,0 +1,122 @@ +import 'package:selfprivacy/logic/models/disk_size.dart'; +import 'package:selfprivacy/logic/models/hive/server_details.dart'; +import 'package:selfprivacy/logic/models/json/server_disk_volume.dart'; + +class DiskVolume { + DiskVolume({ + this.name = '', + this.sizeTotal = const DiskSize(byte: 0), + this.sizeUsed = const DiskSize(byte: 0), + this.root = false, + this.isResizable = false, + this.serverDiskVolume, + this.providerVolume, + }); + + DiskVolume.fromServerDiscVolume( + final ServerDiskVolume volume, + final ServerVolume? providerVolume, + ) : this( + name: volume.name, + sizeTotal: DiskSize( + byte: + volume.totalSpace == 'None' ? 0 : int.parse(volume.totalSpace), + ), + sizeUsed: DiskSize( + byte: volume.usedSpace == 'None' ? 0 : int.parse(volume.usedSpace), + ), + root: volume.root, + isResizable: providerVolume != null, + serverDiskVolume: volume, + providerVolume: providerVolume, + ); + + /// Get the display name of the volume + /// + /// If it is sda1 and root the name is "System disk" + /// If there is a mapping to providerVolume, the name is "Expandable volume" + /// Otherwise the name is the name of the volume + String get displayName { + if (root) { + return 'System disk'; + } else if (providerVolume != null) { + return 'Expandable volume'; + } else { + return name; + } + } + + DiskSize sizeUsed; + DiskSize sizeTotal; + String name; + bool root; + bool isResizable; + ServerDiskVolume? serverDiskVolume; + ServerVolume? providerVolume; + + /// from 0.0 to 1.0 + double get percentage => + sizeTotal.byte == 0 ? 0 : sizeUsed.byte / sizeTotal.byte; + bool get isDiskOkay => + percentage < 0.8 && sizeTotal.gibibyte - sizeUsed.gibibyte > 2.0; + + DiskVolume copyWith({ + final DiskSize? sizeUsed, + final DiskSize? sizeTotal, + final String? name, + final bool? root, + final bool? isResizable, + final ServerDiskVolume? serverDiskVolume, + final ServerVolume? providerVolume, + }) => + DiskVolume( + sizeUsed: sizeUsed ?? this.sizeUsed, + sizeTotal: sizeTotal ?? this.sizeTotal, + name: name ?? this.name, + root: root ?? this.root, + isResizable: isResizable ?? this.isResizable, + serverDiskVolume: serverDiskVolume ?? this.serverDiskVolume, + providerVolume: providerVolume ?? this.providerVolume, + ); +} + +class DiskStatus { + DiskStatus.fromVolumes( + final List serverVolumes, + final List providerVolumes, + ) { + diskVolumes = serverVolumes.map(( + final ServerDiskVolume volume, + ) { + ServerVolume? providerVolume; + + for (final ServerVolume iterableProviderVolume in providerVolumes) { + if (iterableProviderVolume.linuxDevice == null || + volume.model == null || + volume.serial == null) { + continue; + } + + final String deviceId = + iterableProviderVolume.linuxDevice!.split('/').last; + if (deviceId.contains(volume.model!) && + deviceId.contains(volume.serial!)) { + providerVolume = iterableProviderVolume; + break; + } + } + + final DiskVolume diskVolume = + DiskVolume.fromServerDiscVolume(volume, providerVolume); + + return diskVolume; + }).toList(); + } + DiskStatus() { + diskVolumes = []; + } + + bool get isDiskOkay => diskVolumes.every((final volume) => volume.isDiskOkay); + + List diskVolumes = []; +} diff --git a/lib/logic/models/hetzner_metrics.dart b/lib/logic/models/hetzner_metrics.dart deleted file mode 100644 index 2f41a4b2..00000000 --- a/lib/logic/models/hetzner_metrics.dart +++ /dev/null @@ -1,11 +0,0 @@ -class TimeSeriesData { - TimeSeriesData( - this.secondsSinceEpoch, - this.value, - ); - - final int secondsSinceEpoch; - DateTime get time => - DateTime.fromMillisecondsSinceEpoch(secondsSinceEpoch * 1000); - final double value; -} diff --git a/lib/logic/models/hive/README.md b/lib/logic/models/hive/README.md index afdd6276..d50da34c 100644 --- a/lib/logic/models/hive/README.md +++ b/lib/logic/models/hive/README.md @@ -7,7 +7,9 @@ 5. ServerVolume 6. BackblazeBucket - ## Enums + 100. DnsProvider -101. ServerProvider \ No newline at end of file +101. ServerProvider +102. UserType +103. BackupsProvider diff --git a/lib/logic/models/hive/backblaze_bucket.dart b/lib/logic/models/hive/backblaze_bucket.dart index 39b98cf5..6c4bbeea 100644 --- a/lib/logic/models/hive/backblaze_bucket.dart +++ b/lib/logic/models/hive/backblaze_bucket.dart @@ -9,6 +9,7 @@ class BackblazeBucket { required this.bucketName, required this.applicationKeyId, required this.applicationKey, + required this.encryptionKey, }); @HiveField(0) @@ -23,6 +24,9 @@ class BackblazeBucket { @HiveField(3) final String bucketName; + @HiveField(4) + final String encryptionKey; + @override String toString() => bucketName; } diff --git a/lib/logic/models/hive/backblaze_bucket.g.dart b/lib/logic/models/hive/backblaze_bucket.g.dart index 18802bc0..129905a5 100644 --- a/lib/logic/models/hive/backblaze_bucket.g.dart +++ b/lib/logic/models/hive/backblaze_bucket.g.dart @@ -21,13 +21,14 @@ class BackblazeBucketAdapter extends TypeAdapter { bucketName: fields[3] as String, applicationKeyId: fields[1] as String, applicationKey: fields[2] as String, + encryptionKey: fields[4] as String, ); } @override void write(BinaryWriter writer, BackblazeBucket obj) { writer - ..writeByte(4) + ..writeByte(5) ..writeByte(0) ..write(obj.bucketId) ..writeByte(1) @@ -35,7 +36,9 @@ class BackblazeBucketAdapter extends TypeAdapter { ..writeByte(2) ..write(obj.applicationKey) ..writeByte(3) - ..write(obj.bucketName); + ..write(obj.bucketName) + ..writeByte(4) + ..write(obj.encryptionKey); } @override diff --git a/lib/logic/models/hive/backblaze_credential.dart b/lib/logic/models/hive/backblaze_credential.dart deleted file mode 100644 index d7bf2d06..00000000 --- a/lib/logic/models/hive/backblaze_credential.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'dart:convert'; - -import 'package:hive/hive.dart'; - -part 'backblaze_credential.g.dart'; - -@HiveType(typeId: 4) -class BackblazeCredential { - BackblazeCredential({required this.keyId, required this.applicationKey}); - - @HiveField(0) - final String keyId; - - @HiveField(1) - final String applicationKey; - - String get encodedApiKey => encodedBackblazeKey(keyId, applicationKey); - - @override - String toString() => '$keyId: $encodedApiKey'; -} - -String encodedBackblazeKey(final String? keyId, final String? applicationKey) { - final String apiKey = '$keyId:$applicationKey'; - final String encodedApiKey = base64.encode(utf8.encode(apiKey)); - return encodedApiKey; -} diff --git a/lib/logic/models/hive/backblaze_credential.g.dart b/lib/logic/models/hive/backblaze_credential.g.dart deleted file mode 100644 index c6ad373e..00000000 --- a/lib/logic/models/hive/backblaze_credential.g.dart +++ /dev/null @@ -1,44 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'backblaze_credential.dart'; - -// ************************************************************************** -// TypeAdapterGenerator -// ************************************************************************** - -class BackblazeCredentialAdapter extends TypeAdapter { - @override - final int typeId = 4; - - @override - BackblazeCredential read(BinaryReader reader) { - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), - }; - return BackblazeCredential( - keyId: fields[0] as String, - applicationKey: fields[1] as String, - ); - } - - @override - void write(BinaryWriter writer, BackblazeCredential obj) { - writer - ..writeByte(2) - ..writeByte(0) - ..write(obj.keyId) - ..writeByte(1) - ..write(obj.applicationKey); - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is BackblazeCredentialAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} diff --git a/lib/logic/models/hive/backups_credential.dart b/lib/logic/models/hive/backups_credential.dart new file mode 100644 index 00000000..0c0cf48d --- /dev/null +++ b/lib/logic/models/hive/backups_credential.dart @@ -0,0 +1,63 @@ +import 'dart:convert'; + +import 'package:hive/hive.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/schema.graphql.dart'; + +part 'backups_credential.g.dart'; + +@HiveType(typeId: 4) +class BackupsCredential { + BackupsCredential({ + required this.keyId, + required this.applicationKey, + required this.provider, + }); + + @HiveField(0) + final String keyId; + + @HiveField(1) + final String applicationKey; + + @HiveField(2, defaultValue: BackupsProviderType.backblaze) + final BackupsProviderType provider; + + String get encodedApiKey => encodedBackblazeKey(keyId, applicationKey); + + @override + String toString() => '$keyId: $encodedApiKey'; +} + +String encodedBackblazeKey(final String? keyId, final String? applicationKey) { + final String apiKey = '$keyId:$applicationKey'; + final String encodedApiKey = base64.encode(utf8.encode(apiKey)); + return encodedApiKey; +} + +@HiveType(typeId: 103) +enum BackupsProviderType { + @HiveField(0) + none, + @HiveField(1) + memory, + @HiveField(2) + file, + @HiveField(3) + backblaze; + + factory BackupsProviderType.fromGraphQL(final Enum$BackupProvider provider) => + switch (provider) { + Enum$BackupProvider.NONE => none, + Enum$BackupProvider.MEMORY => memory, + Enum$BackupProvider.FILE => file, + Enum$BackupProvider.BACKBLAZE => backblaze, + Enum$BackupProvider.$unknown => none + }; + + Enum$BackupProvider toGraphQL() => switch (this) { + none => Enum$BackupProvider.NONE, + memory => Enum$BackupProvider.MEMORY, + file => Enum$BackupProvider.FILE, + backblaze => Enum$BackupProvider.BACKBLAZE, + }; +} diff --git a/lib/logic/models/hive/backups_credential.g.dart b/lib/logic/models/hive/backups_credential.g.dart new file mode 100644 index 00000000..13166d31 --- /dev/null +++ b/lib/logic/models/hive/backups_credential.g.dart @@ -0,0 +1,98 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'backups_credential.dart'; + +// ************************************************************************** +// TypeAdapterGenerator +// ************************************************************************** + +class BackupsCredentialAdapter extends TypeAdapter { + @override + final int typeId = 4; + + @override + BackupsCredential read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = { + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), + }; + return BackupsCredential( + keyId: fields[0] as String, + applicationKey: fields[1] as String, + provider: fields[2] == null + ? BackupsProviderType.backblaze + : fields[2] as BackupsProviderType, + ); + } + + @override + void write(BinaryWriter writer, BackupsCredential obj) { + writer + ..writeByte(3) + ..writeByte(0) + ..write(obj.keyId) + ..writeByte(1) + ..write(obj.applicationKey) + ..writeByte(2) + ..write(obj.provider); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is BackupsCredentialAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} + +class BackupsProviderTypeAdapter extends TypeAdapter { + @override + final int typeId = 103; + + @override + BackupsProviderType read(BinaryReader reader) { + switch (reader.readByte()) { + case 0: + return BackupsProviderType.none; + case 1: + return BackupsProviderType.memory; + case 2: + return BackupsProviderType.file; + case 3: + return BackupsProviderType.backblaze; + default: + return BackupsProviderType.none; + } + } + + @override + void write(BinaryWriter writer, BackupsProviderType obj) { + switch (obj) { + case BackupsProviderType.none: + writer.writeByte(0); + break; + case BackupsProviderType.memory: + writer.writeByte(1); + break; + case BackupsProviderType.file: + writer.writeByte(2); + break; + case BackupsProviderType.backblaze: + writer.writeByte(3); + break; + } + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is BackupsProviderTypeAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} diff --git a/lib/logic/models/hive/server_details.dart b/lib/logic/models/hive/server_details.dart index 5188e62e..0e9d825a 100644 --- a/lib/logic/models/hive/server_details.dart +++ b/lib/logic/models/hive/server_details.dart @@ -1,4 +1,5 @@ import 'package:hive/hive.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/schema.graphql.dart'; part 'server_details.g.dart'; @@ -32,8 +33,8 @@ class ServerHostingDetails { @HiveField(5) final String apiToken; - @HiveField(6, defaultValue: ServerProvider.hetzner) - final ServerProvider provider; + @HiveField(6, defaultValue: ServerProviderType.hetzner) + final ServerProviderType provider; ServerHostingDetails copyWith({final DateTime? startTime}) => ServerHostingDetails( @@ -55,18 +56,49 @@ class ServerVolume { ServerVolume({ required this.id, required this.name, + required this.sizeByte, + required this.serverId, + required this.linuxDevice, + this.uuid, }); @HiveField(1) int id; @HiveField(2) String name; + @HiveField(3, defaultValue: 10737418240) // 10 Gb + int sizeByte; + @HiveField(4, defaultValue: null) + int? serverId; + @HiveField(5, defaultValue: null) + String? linuxDevice; + @HiveField(6, defaultValue: null) + String? uuid; } @HiveType(typeId: 101) -enum ServerProvider { +enum ServerProviderType { @HiveField(0) unknown, @HiveField(1) hetzner, + @HiveField(2) + digitalOcean; + + factory ServerProviderType.fromGraphQL(final Enum$ServerProvider provider) { + switch (provider) { + case Enum$ServerProvider.HETZNER: + return hetzner; + case Enum$ServerProvider.DIGITALOCEAN: + return digitalOcean; + default: + return unknown; + } + } + + String get displayName => switch (this) { + digitalOcean => 'Digital Ocean', + hetzner => 'Hetzner Cloud', + unknown => 'Unknown', + }; } diff --git a/lib/logic/models/hive/server_details.g.dart b/lib/logic/models/hive/server_details.g.dart index f10628e7..3bb443d7 100644 --- a/lib/logic/models/hive/server_details.g.dart +++ b/lib/logic/models/hive/server_details.g.dart @@ -23,8 +23,8 @@ class ServerHostingDetailsAdapter extends TypeAdapter { volume: fields[4] as ServerVolume, apiToken: fields[5] as String, provider: fields[6] == null - ? ServerProvider.hetzner - : fields[6] as ServerProvider, + ? ServerProviderType.hetzner + : fields[6] as ServerProviderType, startTime: fields[2] as DateTime?, ); } @@ -73,17 +73,29 @@ class ServerVolumeAdapter extends TypeAdapter { return ServerVolume( id: fields[1] as int, name: fields[2] as String, + sizeByte: fields[3] == null ? 10737418240 : fields[3] as int, + serverId: fields[4] as int?, + linuxDevice: fields[5] as String?, + uuid: fields[6] as String?, ); } @override void write(BinaryWriter writer, ServerVolume obj) { writer - ..writeByte(2) + ..writeByte(6) ..writeByte(1) ..write(obj.id) ..writeByte(2) - ..write(obj.name); + ..write(obj.name) + ..writeByte(3) + ..write(obj.sizeByte) + ..writeByte(4) + ..write(obj.serverId) + ..writeByte(5) + ..write(obj.linuxDevice) + ..writeByte(6) + ..write(obj.uuid); } @override @@ -97,31 +109,36 @@ class ServerVolumeAdapter extends TypeAdapter { typeId == other.typeId; } -class ServerProviderAdapter extends TypeAdapter { +class ServerProviderTypeAdapter extends TypeAdapter { @override final int typeId = 101; @override - ServerProvider read(BinaryReader reader) { + ServerProviderType read(BinaryReader reader) { switch (reader.readByte()) { case 0: - return ServerProvider.unknown; + return ServerProviderType.unknown; case 1: - return ServerProvider.hetzner; + return ServerProviderType.hetzner; + case 2: + return ServerProviderType.digitalOcean; default: - return ServerProvider.unknown; + return ServerProviderType.unknown; } } @override - void write(BinaryWriter writer, ServerProvider obj) { + void write(BinaryWriter writer, ServerProviderType obj) { switch (obj) { - case ServerProvider.unknown: + case ServerProviderType.unknown: writer.writeByte(0); break; - case ServerProvider.hetzner: + case ServerProviderType.hetzner: writer.writeByte(1); break; + case ServerProviderType.digitalOcean: + writer.writeByte(2); + break; } } @@ -131,7 +148,7 @@ class ServerProviderAdapter extends TypeAdapter { @override bool operator ==(Object other) => identical(this, other) || - other is ServerProviderAdapter && + other is ServerProviderTypeAdapter && runtimeType == other.runtimeType && typeId == other.typeId; } diff --git a/lib/logic/models/hive/server_domain.dart b/lib/logic/models/hive/server_domain.dart index 9b5d32c1..f86e228f 100644 --- a/lib/logic/models/hive/server_domain.dart +++ b/lib/logic/models/hive/server_domain.dart @@ -1,4 +1,5 @@ import 'package:hive/hive.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/schema.graphql.dart'; part 'server_domain.g.dart'; @@ -6,27 +7,53 @@ part 'server_domain.g.dart'; class ServerDomain { ServerDomain({ required this.domainName, - required this.zoneId, required this.provider, }); @HiveField(0) final String domainName; - @HiveField(1) - final String zoneId; + // @HiveField(1) - @HiveField(2, defaultValue: DnsProvider.cloudflare) - final DnsProvider provider; - - @override - String toString() => '$domainName: $zoneId'; + @HiveField(2, defaultValue: DnsProviderType.cloudflare) + final DnsProviderType provider; } @HiveType(typeId: 100) -enum DnsProvider { +enum DnsProviderType { @HiveField(0) unknown, @HiveField(1) cloudflare, + @HiveField(2) + desec, + @HiveField(3) + digitalOcean; + + factory DnsProviderType.fromGraphQL(final Enum$DnsProvider provider) { + switch (provider) { + case Enum$DnsProvider.CLOUDFLARE: + return cloudflare; + case Enum$DnsProvider.DESEC: + return desec; + case Enum$DnsProvider.DIGITALOCEAN: + return digitalOcean; + default: + return unknown; + } + } + + String toInfectName() => switch (this) { + digitalOcean => 'DIGITALOCEAN', + cloudflare => 'CLOUDFLARE', + desec => 'DESEC', + unknown => 'UNKNOWN', + }; + + String get displayName => switch (this) { + digitalOcean => 'Digital Ocean DNS', + cloudflare => 'Cloudflare', + desec => 'deSEC', + unknown => 'Unknown', + }; } diff --git a/lib/logic/models/hive/server_domain.g.dart b/lib/logic/models/hive/server_domain.g.dart index 3265db6b..2557ec43 100644 --- a/lib/logic/models/hive/server_domain.g.dart +++ b/lib/logic/models/hive/server_domain.g.dart @@ -18,20 +18,18 @@ class ServerDomainAdapter extends TypeAdapter { }; return ServerDomain( domainName: fields[0] as String, - zoneId: fields[1] as String, - provider: - fields[2] == null ? DnsProvider.cloudflare : fields[2] as DnsProvider, + provider: fields[2] == null + ? DnsProviderType.cloudflare + : fields[2] as DnsProviderType, ); } @override void write(BinaryWriter writer, ServerDomain obj) { writer - ..writeByte(3) + ..writeByte(2) ..writeByte(0) ..write(obj.domainName) - ..writeByte(1) - ..write(obj.zoneId) ..writeByte(2) ..write(obj.provider); } @@ -47,31 +45,41 @@ class ServerDomainAdapter extends TypeAdapter { typeId == other.typeId; } -class DnsProviderAdapter extends TypeAdapter { +class DnsProviderTypeAdapter extends TypeAdapter { @override final int typeId = 100; @override - DnsProvider read(BinaryReader reader) { + DnsProviderType read(BinaryReader reader) { switch (reader.readByte()) { case 0: - return DnsProvider.unknown; + return DnsProviderType.unknown; case 1: - return DnsProvider.cloudflare; + return DnsProviderType.cloudflare; + case 2: + return DnsProviderType.desec; + case 3: + return DnsProviderType.digitalOcean; default: - return DnsProvider.unknown; + return DnsProviderType.unknown; } } @override - void write(BinaryWriter writer, DnsProvider obj) { + void write(BinaryWriter writer, DnsProviderType obj) { switch (obj) { - case DnsProvider.unknown: + case DnsProviderType.unknown: writer.writeByte(0); break; - case DnsProvider.cloudflare: + case DnsProviderType.cloudflare: writer.writeByte(1); break; + case DnsProviderType.desec: + writer.writeByte(2); + break; + case DnsProviderType.digitalOcean: + writer.writeByte(3); + break; } } @@ -81,7 +89,7 @@ class DnsProviderAdapter extends TypeAdapter { @override bool operator ==(Object other) => identical(this, other) || - other is DnsProviderAdapter && + other is DnsProviderTypeAdapter && runtimeType == other.runtimeType && typeId == other.typeId; } diff --git a/lib/logic/models/hive/user.dart b/lib/logic/models/hive/user.dart index 942ce9fe..48c121b7 100644 --- a/lib/logic/models/hive/user.dart +++ b/lib/logic/models/hive/user.dart @@ -2,6 +2,8 @@ import 'dart:ui'; import 'package:equatable/equatable.dart'; import 'package:hive/hive.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/schema.graphql.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/users.graphql.dart'; import 'package:selfprivacy/utils/color_utils.dart'; part 'user.g.dart'; @@ -10,12 +12,21 @@ part 'user.g.dart'; class User extends Equatable { const User({ required this.login, + required this.type, this.password, this.sshKeys = const [], this.isFoundOnServer = true, this.note, }); + User.fromGraphQL(final Fragment$userFields user) + : this( + login: user.username, + type: UserType.fromGraphQL(user.userType), + sshKeys: user.sshKeys, + isFoundOnServer: true, + ); + @HiveField(0) final String login; @@ -31,6 +42,9 @@ class User extends Equatable { @HiveField(4) final String? note; + @HiveField(5, defaultValue: UserType.normal) + final UserType type; + @override List get props => [login, password, sshKeys, isFoundOnServer, note]; @@ -40,3 +54,26 @@ class User extends Equatable { String toString() => '$login, ${isFoundOnServer ? 'found' : 'not found'}, ${sshKeys.length} ssh keys, note: $note'; } + +@HiveType(typeId: 102) +enum UserType { + @HiveField(0) + root, + @HiveField(1) + primary, + @HiveField(2) + normal; + + factory UserType.fromGraphQL(final Enum$UserType type) { + switch (type) { + case Enum$UserType.ROOT: + return root; + case Enum$UserType.PRIMARY: + return primary; + case Enum$UserType.NORMAL: + return normal; + case Enum$UserType.$unknown: + return normal; + } + } +} diff --git a/lib/logic/models/hive/user.g.dart b/lib/logic/models/hive/user.g.dart index d9b28d65..8f5b7b20 100644 --- a/lib/logic/models/hive/user.g.dart +++ b/lib/logic/models/hive/user.g.dart @@ -11,13 +11,14 @@ class UserAdapter extends TypeAdapter { final int typeId = 1; @override - User read(final BinaryReader reader) { - final int numOfFields = reader.readByte(); - final Map fields = { + User read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = { for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), }; return User( login: fields[0] as String, + type: fields[5] == null ? UserType.normal : fields[5] as UserType, password: fields[1] as String?, sshKeys: fields[2] == null ? [] : (fields[2] as List).cast(), isFoundOnServer: fields[3] == null ? true : fields[3] as bool, @@ -26,9 +27,9 @@ class UserAdapter extends TypeAdapter { } @override - void write(final BinaryWriter writer, final User obj) { + void write(BinaryWriter writer, User obj) { writer - ..writeByte(5) + ..writeByte(6) ..writeByte(0) ..write(obj.login) ..writeByte(1) @@ -38,16 +39,62 @@ class UserAdapter extends TypeAdapter { ..writeByte(3) ..write(obj.isFoundOnServer) ..writeByte(4) - ..write(obj.note); + ..write(obj.note) + ..writeByte(5) + ..write(obj.type); } @override int get hashCode => typeId.hashCode; @override - bool operator ==(final Object other) => + bool operator ==(Object other) => identical(this, other) || other is UserAdapter && runtimeType == other.runtimeType && typeId == other.typeId; } + +class UserTypeAdapter extends TypeAdapter { + @override + final int typeId = 102; + + @override + UserType read(BinaryReader reader) { + switch (reader.readByte()) { + case 0: + return UserType.root; + case 1: + return UserType.primary; + case 2: + return UserType.normal; + default: + return UserType.root; + } + } + + @override + void write(BinaryWriter writer, UserType obj) { + switch (obj) { + case UserType.root: + writer.writeByte(0); + break; + case UserType.primary: + writer.writeByte(1); + break; + case UserType.normal: + writer.writeByte(2); + break; + } + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is UserTypeAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} diff --git a/lib/logic/models/initialize_repository_input.dart b/lib/logic/models/initialize_repository_input.dart new file mode 100644 index 00000000..7ccaa410 --- /dev/null +++ b/lib/logic/models/initialize_repository_input.dart @@ -0,0 +1,16 @@ +import 'package:selfprivacy/logic/models/hive/backups_credential.dart'; + +class InitializeRepositoryInput { + InitializeRepositoryInput({ + required this.provider, + required this.locationId, + required this.locationName, + required this.login, + required this.password, + }); + final BackupsProviderType provider; + final String locationId; + final String locationName; + final String login; + final String password; +} diff --git a/lib/logic/models/job.dart b/lib/logic/models/job.dart index b04d7d05..076e0d16 100644 --- a/lib/logic/models/job.dart +++ b/lib/logic/models/job.dart @@ -1,14 +1,15 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; -import 'package:selfprivacy/logic/common_enum/common_enum.dart'; +import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart'; +import 'package:selfprivacy/logic/models/service.dart'; import 'package:selfprivacy/utils/password_generator.dart'; import 'package:selfprivacy/logic/models/hive/user.dart'; @immutable -class Job extends Equatable { - Job({ +abstract class ClientJob extends Equatable { + ClientJob({ required this.title, final String? id, }) : id = id ?? StringGenerators.simpleId(); @@ -16,57 +17,109 @@ class Job extends Equatable { final String title; final String id; + bool canAddTo(final List jobs) => true; + void execute(final JobsCubit cubit); + @override List get props => [id, title]; } -class CreateUserJob extends Job { - CreateUserJob({ - required this.user, - }) : super(title: '${"jobs.createUser".tr()} ${user.login}'); - - final User user; - - @override - List get props => [id, title, user]; -} - -class DeleteUserJob extends Job { - DeleteUserJob({ - required this.user, - }) : super(title: '${"jobs.deleteUser".tr()} ${user.login}'); - - final User user; - - @override - List get props => [id, title, user]; -} - -class ToggleJob extends Job { - ToggleJob({ - required this.type, - required final super.title, +class RebuildServerJob extends ClientJob { + RebuildServerJob({ + required super.title, + super.id, }); - final dynamic type; + @override + bool canAddTo(final List jobs) => + !jobs.any((final job) => job is RebuildServerJob); @override - List get props => [...super.props, type]; + void execute(final JobsCubit cubit) async { + await cubit.upgradeServer(); + } } -class ServiceToggleJob extends ToggleJob { +class CreateUserJob extends ClientJob { + CreateUserJob({ + required this.user, + }) : super(title: '${"jobs.create_user".tr()} ${user.login}'); + + final User user; + + @override + void execute(final JobsCubit cubit) async { + await cubit.usersCubit.createUser(user); + } + + @override + List get props => [id, title, user]; +} + +class ResetUserPasswordJob extends ClientJob { + ResetUserPasswordJob({ + required this.user, + }) : super(title: '${"jobs.reset_user_password".tr()} ${user.login}'); + + final User user; + + @override + void execute(final JobsCubit cubit) async { + await cubit.usersCubit.changeUserPassword(user, user.password!); + } + + @override + List get props => [id, title, user]; +} + +class DeleteUserJob extends ClientJob { + DeleteUserJob({ + required this.user, + }) : super(title: '${"jobs.delete_user".tr()} ${user.login}'); + + final User user; + + @override + bool canAddTo(final List jobs) => !jobs.any( + (final job) => job is DeleteUserJob && job.user.login == user.login, + ); + + @override + void execute(final JobsCubit cubit) async { + await cubit.usersCubit.deleteUser(user); + } + + @override + List get props => [id, title, user]; +} + +class ServiceToggleJob extends ClientJob { ServiceToggleJob({ - required final ServiceTypes super.type, + required this.service, required this.needToTurnOn, }) : super( title: - '${needToTurnOn ? "jobs.serviceTurnOn".tr() : "jobs.serviceTurnOff".tr()} ${type.title}', + '${needToTurnOn ? "jobs.service_turn_on".tr() : "jobs.service_turn_off".tr()} ${service.displayName}', ); final bool needToTurnOn; + final Service service; + + @override + bool canAddTo(final List jobs) => !jobs.any( + (final job) => job is ServiceToggleJob && job.service.id == service.id, + ); + + @override + void execute(final JobsCubit cubit) async { + await cubit.api.switchService(service.id, needToTurnOn); + } + + @override + List get props => [...super.props, service]; } -class CreateSSHKeyJob extends Job { +class CreateSSHKeyJob extends ClientJob { CreateSSHKeyJob({ required this.user, required this.publicKey, @@ -75,11 +128,16 @@ class CreateSSHKeyJob extends Job { final User user; final String publicKey; + @override + void execute(final JobsCubit cubit) async { + await cubit.usersCubit.addSshKey(user, publicKey); + } + @override List get props => [id, title, user, publicKey]; } -class DeleteSSHKeyJob extends Job { +class DeleteSSHKeyJob extends ClientJob { DeleteSSHKeyJob({ required this.user, required this.publicKey, @@ -88,6 +146,19 @@ class DeleteSSHKeyJob extends Job { final User user; final String publicKey; + @override + bool canAddTo(final List jobs) => !jobs.any( + (final job) => + job is DeleteSSHKeyJob && + job.publicKey == publicKey && + job.user.login == user.login, + ); + + @override + void execute(final JobsCubit cubit) async { + await cubit.usersCubit.deleteSshKey(user, publicKey); + } + @override List get props => [id, title, user, publicKey]; } diff --git a/lib/logic/models/json/api_token.dart b/lib/logic/models/json/api_token.dart index 980d5132..f53f7f02 100644 --- a/lib/logic/models/json/api_token.dart +++ b/lib/logic/models/json/api_token.dart @@ -1,4 +1,5 @@ import 'package:json_annotation/json_annotation.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server_api.graphql.dart'; part 'api_token.g.dart'; @@ -12,6 +13,14 @@ class ApiToken { required this.isCaller, }); + ApiToken.fromGraphQL( + final Query$GetApiTokens$api$devices device, + ) : this( + name: device.name, + date: device.creationDate, + isCaller: device.isCaller, + ); + final String name; final DateTime date; @JsonKey(name: 'is_caller') diff --git a/lib/logic/models/json/api_token.g.dart b/lib/logic/models/json/api_token.g.dart index b6c8b8db..7f7af88c 100644 --- a/lib/logic/models/json/api_token.g.dart +++ b/lib/logic/models/json/api_token.g.dart @@ -11,3 +11,9 @@ ApiToken _$ApiTokenFromJson(Map json) => ApiToken( date: DateTime.parse(json['date'] as String), isCaller: json['is_caller'] as bool, ); + +Map _$ApiTokenToJson(ApiToken instance) => { + 'name': instance.name, + 'date': instance.date.toIso8601String(), + 'is_caller': instance.isCaller, + }; diff --git a/lib/logic/models/json/auto_upgrade_settings.dart b/lib/logic/models/json/auto_upgrade_settings.dart deleted file mode 100644 index 421f9b88..00000000 --- a/lib/logic/models/json/auto_upgrade_settings.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:json_annotation/json_annotation.dart'; - -part 'auto_upgrade_settings.g.dart'; - -@JsonSerializable(createToJson: true) -class AutoUpgradeSettings extends Equatable { - factory AutoUpgradeSettings.fromJson(final Map json) => - _$AutoUpgradeSettingsFromJson(json); - - const AutoUpgradeSettings({ - required this.enable, - required this.allowReboot, - }); - final bool enable; - final bool allowReboot; - - @override - List get props => [enable, allowReboot]; - - Map toJson() => _$AutoUpgradeSettingsToJson(this); -} diff --git a/lib/logic/models/json/auto_upgrade_settings.g.dart b/lib/logic/models/json/auto_upgrade_settings.g.dart deleted file mode 100644 index e6accc2f..00000000 --- a/lib/logic/models/json/auto_upgrade_settings.g.dart +++ /dev/null @@ -1,20 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'auto_upgrade_settings.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -AutoUpgradeSettings _$AutoUpgradeSettingsFromJson(Map json) => - AutoUpgradeSettings( - enable: json['enable'] as bool, - allowReboot: json['allowReboot'] as bool, - ); - -Map _$AutoUpgradeSettingsToJson( - AutoUpgradeSettings instance) => - { - 'enable': instance.enable, - 'allowReboot': instance.allowReboot, - }; diff --git a/lib/logic/models/json/backup.dart b/lib/logic/models/json/backup.dart deleted file mode 100644 index 2e1215db..00000000 --- a/lib/logic/models/json/backup.dart +++ /dev/null @@ -1,48 +0,0 @@ -import 'package:json_annotation/json_annotation.dart'; - -part 'backup.g.dart'; - -@JsonSerializable() -class Backup { - factory Backup.fromJson(final Map json) => - _$BackupFromJson(json); - Backup({required this.time, required this.id}); - - // Time of the backup - final DateTime time; - @JsonKey(name: 'short_id') - final String id; -} - -enum BackupStatusEnum { - @JsonValue('NO_KEY') - noKey, - @JsonValue('NOT_INITIALIZED') - notInitialized, - @JsonValue('INITIALIZED') - initialized, - @JsonValue('BACKING_UP') - backingUp, - @JsonValue('RESTORING') - restoring, - @JsonValue('ERROR') - error, - @JsonValue('INITIALIZING') - initializing, -} - -@JsonSerializable() -class BackupStatus { - factory BackupStatus.fromJson(final Map json) => - _$BackupStatusFromJson(json); - BackupStatus({ - required this.status, - required this.progress, - required this.errorMessage, - }); - - final BackupStatusEnum status; - final double progress; - @JsonKey(name: 'error_message') - final String? errorMessage; -} diff --git a/lib/logic/models/json/backup.g.dart b/lib/logic/models/json/backup.g.dart deleted file mode 100644 index c784abe1..00000000 --- a/lib/logic/models/json/backup.g.dart +++ /dev/null @@ -1,28 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'backup.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -Backup _$BackupFromJson(Map json) => Backup( - time: DateTime.parse(json['time'] as String), - id: json['short_id'] as String, - ); - -BackupStatus _$BackupStatusFromJson(Map json) => BackupStatus( - status: $enumDecode(_$BackupStatusEnumEnumMap, json['status']), - progress: (json['progress'] as num).toDouble(), - errorMessage: json['error_message'] as String?, - ); - -const _$BackupStatusEnumEnumMap = { - BackupStatusEnum.noKey: 'NO_KEY', - BackupStatusEnum.notInitialized: 'NOT_INITIALIZED', - BackupStatusEnum.initialized: 'INITIALIZED', - BackupStatusEnum.backingUp: 'BACKING_UP', - BackupStatusEnum.restoring: 'RESTORING', - BackupStatusEnum.error: 'ERROR', - BackupStatusEnum.initializing: 'INITIALIZING', -}; diff --git a/lib/logic/models/json/cloudflare_dns_info.dart b/lib/logic/models/json/cloudflare_dns_info.dart new file mode 100644 index 00000000..3cd11bcd --- /dev/null +++ b/lib/logic/models/json/cloudflare_dns_info.dart @@ -0,0 +1,86 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'cloudflare_dns_info.g.dart'; + +/// https://developers.cloudflare.com/api/operations/zones-get +@JsonSerializable() +class CloudflareZone { + CloudflareZone({ + required this.id, + required this.name, + }); + + /// Zone identifier + /// + /// `<= 32 characters` + /// + /// Example: 023e105f4ecef8ad9ca31a8372d0c353 + final String id; + + /// The domain name + /// + /// `<= 253 characters` + /// + /// Example: example.com + final String name; + + static CloudflareZone fromJson(final Map json) => + _$CloudflareZoneFromJson(json); +} + +/// https://developers.cloudflare.com/api/operations/dns-records-for-a-zone-list-dns-records +@JsonSerializable() +class CloudflareDnsRecord { + CloudflareDnsRecord({ + required this.type, + required this.name, + required this.content, + required this.zoneName, + this.ttl = 3600, + this.priority = 10, + this.id, + }); + + /// Record identifier + /// + /// `<= 32 characters` + /// Example: 023e105f4ecef8ad9ca31a8372d0c353 + final String? id; + + /// Record type. + /// + /// Example: A + final String type; + + /// DNS record name (or @ for the zone apex) in Punycode. + /// + /// `<= 255 characters` + /// + /// Example: example.com + final String? name; + + /// Valid DNS Record string content. + /// + /// Example: A valid IPv4 address "198.51.100.4" + final String? content; + + /// The domain of the record. + /// + /// Example: example.com + @JsonKey(name: 'zone_name') + final String zoneName; + + /// Time To Live (TTL) of the DNS record in seconds. Setting to 1 means 'automatic'. + /// + /// Value must be between 60 and 86400, with the minimum reduced to 30 for Enterprise zones. + final int ttl; + + /// Required for MX, SRV and URI records; unused by other record types. Records with lower priorities are preferred. + /// + /// `>= 0 <= 65535` + final int priority; + + static CloudflareDnsRecord fromJson(final Map json) => + _$CloudflareDnsRecordFromJson(json); + Map toJson() => _$CloudflareDnsRecordToJson(this); +} diff --git a/lib/logic/models/json/cloudflare_dns_info.g.dart b/lib/logic/models/json/cloudflare_dns_info.g.dart new file mode 100644 index 00000000..d02fc2d2 --- /dev/null +++ b/lib/logic/models/json/cloudflare_dns_info.g.dart @@ -0,0 +1,42 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'cloudflare_dns_info.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +CloudflareZone _$CloudflareZoneFromJson(Map json) => + CloudflareZone( + id: json['id'] as String, + name: json['name'] as String, + ); + +Map _$CloudflareZoneToJson(CloudflareZone instance) => + { + 'id': instance.id, + 'name': instance.name, + }; + +CloudflareDnsRecord _$CloudflareDnsRecordFromJson(Map json) => + CloudflareDnsRecord( + type: json['type'] as String, + name: json['name'] as String?, + content: json['content'] as String?, + zoneName: json['zone_name'] as String, + ttl: json['ttl'] as int? ?? 3600, + priority: json['priority'] as int? ?? 10, + id: json['id'] as String?, + ); + +Map _$CloudflareDnsRecordToJson( + CloudflareDnsRecord instance) => + { + 'id': instance.id, + 'type': instance.type, + 'name': instance.name, + 'content': instance.content, + 'zone_name': instance.zoneName, + 'ttl': instance.ttl, + 'priority': instance.priority, + }; diff --git a/lib/logic/models/json/desec_dns_info.dart b/lib/logic/models/json/desec_dns_info.dart new file mode 100644 index 00000000..e6023da7 --- /dev/null +++ b/lib/logic/models/json/desec_dns_info.dart @@ -0,0 +1,63 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'desec_dns_info.g.dart'; + +/// https://desec.readthedocs.io/en/latest/dns/domains.html#domain-management +@JsonSerializable() +class DesecDomain { + DesecDomain( + this.name, + this.minimumTtl, + ); + + /// Restrictions on what is a valid domain name apply on + /// a per-user basis. + /// + /// The maximum length is 191. + final String name; + + /// Smallest TTL that can be used in an RRset. + /// The value is set automatically by DESEC + @JsonKey(name: 'minimum_ttl') + final int minimumTtl; + + static DesecDomain fromJson(final Map json) => + _$DesecDomainFromJson(json); +} + +/// https://desec.readthedocs.io/en/latest/dns/rrsets.html#retrieving-and-creating-dns-records +@JsonSerializable() +class DesecDnsRecord { + DesecDnsRecord({ + required this.subname, + required this.type, + required this.ttl, + required this.records, + }); + + /// Subdomain string which, together with domain, defines the RRset name. + /// Typical examples are www or _443._tcp. + final String subname; + + /// RRset type (uppercase). A broad range of record types is supported, + /// with most DNSSEC-related types (and the SOA type) managed automagically + /// by the backend. + final String type; + + /// Time-to-live value, which dictates for how long resolvers may + /// cache this RRset, measured in seconds. + /// + /// The smallest acceptable value is given by the domain’s minimum TTL setting. + /// The maximum value is 86400 (one day). + final int ttl; + + /// Array of record content strings. + /// + /// The maximum number of array elements is 4091, + /// and the maximum length of the array is 64,000 (after JSON encoding). + final List records; + + static DesecDnsRecord fromJson(final Map json) => + _$DesecDnsRecordFromJson(json); + Map toJson() => _$DesecDnsRecordToJson(this); +} diff --git a/lib/logic/models/json/desec_dns_info.g.dart b/lib/logic/models/json/desec_dns_info.g.dart new file mode 100644 index 00000000..b82ed777 --- /dev/null +++ b/lib/logic/models/json/desec_dns_info.g.dart @@ -0,0 +1,35 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'desec_dns_info.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +DesecDomain _$DesecDomainFromJson(Map json) => DesecDomain( + json['name'] as String, + json['minimum_ttl'] as int, + ); + +Map _$DesecDomainToJson(DesecDomain instance) => + { + 'name': instance.name, + 'minimum_ttl': instance.minimumTtl, + }; + +DesecDnsRecord _$DesecDnsRecordFromJson(Map json) => + DesecDnsRecord( + subname: json['subname'] as String, + type: json['type'] as String, + ttl: json['ttl'] as int, + records: + (json['records'] as List).map((e) => e as String).toList(), + ); + +Map _$DesecDnsRecordToJson(DesecDnsRecord instance) => + { + 'subname': instance.subname, + 'type': instance.type, + 'ttl': instance.ttl, + 'records': instance.records, + }; diff --git a/lib/logic/models/json/device_token.g.dart b/lib/logic/models/json/device_token.g.dart index efe976c5..406530df 100644 --- a/lib/logic/models/json/device_token.g.dart +++ b/lib/logic/models/json/device_token.g.dart @@ -10,3 +10,9 @@ DeviceToken _$DeviceTokenFromJson(Map json) => DeviceToken( device: json['device'] as String, token: json['token'] as String, ); + +Map _$DeviceTokenToJson(DeviceToken instance) => + { + 'device': instance.device, + 'token': instance.token, + }; diff --git a/lib/logic/models/json/digital_ocean_dns_info.dart b/lib/logic/models/json/digital_ocean_dns_info.dart new file mode 100644 index 00000000..0881b214 --- /dev/null +++ b/lib/logic/models/json/digital_ocean_dns_info.dart @@ -0,0 +1,66 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'digital_ocean_dns_info.g.dart'; + +/// https://docs.digitalocean.com/reference/api/api-reference/#tag/Domains +@JsonSerializable() +class DigitalOceanDomain { + DigitalOceanDomain({ + required this.name, + this.ttl, + }); + + /// The name of the domain itself. + /// This should follow the standard domain format of domain.TLD. + /// + /// For instance, example.com is a valid domain name. + final String name; + + /// This value is the time to live for the records on this domain, in seconds. + /// + /// This defines the time frame that clients can cache queried information before a refresh should be requested. + final int? ttl; + + static DigitalOceanDomain fromJson(final Map json) => + _$DigitalOceanDomainFromJson(json); +} + +/// https://docs.digitalocean.com/reference/api/api-reference/#tag/Domain-Records +@JsonSerializable() +class DigitalOceanDnsRecord { + DigitalOceanDnsRecord({ + required this.id, + required this.name, + required this.type, + required this.ttl, + required this.data, + this.priority, + }); + + /// A unique identifier for each domain record. + final int? id; + + /// The host name, alias, or service being defined by the record. + final String name; + + /// The type of the DNS record. For example: A, CNAME, TXT, ... + final String type; + + /// This value is the time to live for the record, in seconds. + /// + /// This defines the time frame that clients can cache queried information before a refresh should be requested. + final int ttl; + + /// Variable data depending on record type. + /// + /// For example, the "data" value for an A record would be the IPv4 address to which the domain will be mapped. + /// For a CAA record, it would contain the domain name of the CA being granted permission to issue certificates. + final String data; + + /// The priority for SRV and MX records. + final int? priority; + + static DigitalOceanDnsRecord fromJson(final Map json) => + _$DigitalOceanDnsRecordFromJson(json); + Map toJson() => _$DigitalOceanDnsRecordToJson(this); +} diff --git a/lib/logic/models/json/digital_ocean_dns_info.g.dart b/lib/logic/models/json/digital_ocean_dns_info.g.dart new file mode 100644 index 00000000..d66c0352 --- /dev/null +++ b/lib/logic/models/json/digital_ocean_dns_info.g.dart @@ -0,0 +1,41 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'digital_ocean_dns_info.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +DigitalOceanDomain _$DigitalOceanDomainFromJson(Map json) => + DigitalOceanDomain( + name: json['name'] as String, + ttl: json['ttl'] as int?, + ); + +Map _$DigitalOceanDomainToJson(DigitalOceanDomain instance) => + { + 'name': instance.name, + 'ttl': instance.ttl, + }; + +DigitalOceanDnsRecord _$DigitalOceanDnsRecordFromJson( + Map json) => + DigitalOceanDnsRecord( + id: json['id'] as int?, + name: json['name'] as String, + type: json['type'] as String, + ttl: json['ttl'] as int, + data: json['data'] as String, + priority: json['priority'] as int?, + ); + +Map _$DigitalOceanDnsRecordToJson( + DigitalOceanDnsRecord instance) => + { + 'id': instance.id, + 'name': instance.name, + 'type': instance.type, + 'ttl': instance.ttl, + 'data': instance.data, + 'priority': instance.priority, + }; diff --git a/lib/logic/models/json/digital_ocean_server_info.dart b/lib/logic/models/json/digital_ocean_server_info.dart new file mode 100644 index 00000000..811536e4 --- /dev/null +++ b/lib/logic/models/json/digital_ocean_server_info.dart @@ -0,0 +1,102 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'digital_ocean_server_info.g.dart'; + +@JsonSerializable() +class DigitalOceanVolume { + DigitalOceanVolume( + this.id, + this.name, + this.sizeGigabytes, + this.dropletIds, + ); + + final String id; + final String name; + + @JsonKey(name: 'droplet_ids') + final List? dropletIds; + + @JsonKey(name: 'size_gigabytes') + final int sizeGigabytes; + + static DigitalOceanVolume fromJson(final Map json) => + _$DigitalOceanVolumeFromJson(json); +} + +@JsonSerializable() +class DigitalOceanLocation { + DigitalOceanLocation( + this.slug, + this.name, + ); + + final String slug; + final String name; + + static DigitalOceanLocation fromJson(final Map json) => + _$DigitalOceanLocationFromJson(json); + + String get flag { + String emoji = ''; + + switch (slug.substring(0, 3)) { + case 'fra': + emoji = '🇩🇪'; + break; + + case 'ams': + emoji = '🇳🇱'; + break; + + case 'sgp': + emoji = '🇸🇬'; + break; + + case 'lon': + emoji = '🇬🇧'; + break; + + case 'tor': + emoji = '🇨🇦'; + break; + + case 'blr': + emoji = '🇮🇳'; + break; + + case 'nyc': + case 'sfo': + emoji = '🇺🇸'; + break; + } + + return emoji; + } +} + +@JsonSerializable() +class DigitalOceanServerType { + DigitalOceanServerType( + this.regions, + this.memory, + this.description, + this.disk, + this.priceMonthly, + this.slug, + this.vcpus, + ); + + final List regions; + final double memory; + final String slug; + final String description; + final int vcpus; + final int disk; + + @JsonKey(name: 'price_monthly') + final double priceMonthly; + + static DigitalOceanServerType fromJson(final Map json) => + _$DigitalOceanServerTypeFromJson(json); +} diff --git a/lib/logic/models/json/digital_ocean_server_info.g.dart b/lib/logic/models/json/digital_ocean_server_info.g.dart new file mode 100644 index 00000000..9610dbce --- /dev/null +++ b/lib/logic/models/json/digital_ocean_server_info.g.dart @@ -0,0 +1,61 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'digital_ocean_server_info.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +DigitalOceanVolume _$DigitalOceanVolumeFromJson(Map json) => + DigitalOceanVolume( + json['id'] as String, + json['name'] as String, + json['size_gigabytes'] as int, + (json['droplet_ids'] as List?)?.map((e) => e as int).toList(), + ); + +Map _$DigitalOceanVolumeToJson(DigitalOceanVolume instance) => + { + 'id': instance.id, + 'name': instance.name, + 'droplet_ids': instance.dropletIds, + 'size_gigabytes': instance.sizeGigabytes, + }; + +DigitalOceanLocation _$DigitalOceanLocationFromJson( + Map json) => + DigitalOceanLocation( + json['slug'] as String, + json['name'] as String, + ); + +Map _$DigitalOceanLocationToJson( + DigitalOceanLocation instance) => + { + 'slug': instance.slug, + 'name': instance.name, + }; + +DigitalOceanServerType _$DigitalOceanServerTypeFromJson( + Map json) => + DigitalOceanServerType( + (json['regions'] as List).map((e) => e as String).toList(), + (json['memory'] as num).toDouble(), + json['description'] as String, + json['disk'] as int, + (json['price_monthly'] as num).toDouble(), + json['slug'] as String, + json['vcpus'] as int, + ); + +Map _$DigitalOceanServerTypeToJson( + DigitalOceanServerType instance) => + { + 'regions': instance.regions, + 'memory': instance.memory, + 'slug': instance.slug, + 'description': instance.description, + 'vcpus': instance.vcpus, + 'disk': instance.disk, + 'price_monthly': instance.priceMonthly, + }; diff --git a/lib/logic/models/json/dns_records.dart b/lib/logic/models/json/dns_records.dart index cd4867c3..8b0fdf23 100644 --- a/lib/logic/models/json/dns_records.dart +++ b/lib/logic/models/json/dns_records.dart @@ -1,4 +1,5 @@ import 'package:json_annotation/json_annotation.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server_settings.graphql.dart'; part 'dns_records.g.dart'; @@ -13,6 +14,16 @@ class DnsRecord { this.proxied = false, }); + DnsRecord.fromGraphQL( + final Fragment$fragmentDnsRecords record, + ) : this( + type: record.recordType, + name: record.name, + content: record.content, + ttl: record.ttl, + priority: record.priority ?? 10, + ); + final String type; final String? name; final String? content; diff --git a/lib/logic/models/json/hetzner_server_info.dart b/lib/logic/models/json/hetzner_server_info.dart index ccf036a1..6d51f2b8 100644 --- a/lib/logic/models/json/hetzner_server_info.dart +++ b/lib/logic/models/json/hetzner_server_info.dart @@ -39,7 +39,7 @@ class HetznerServerInfo { @JsonSerializable() class HetznerPublicNetInfo { HetznerPublicNetInfo(this.ipv4); - final HetznerIp4 ipv4; + final HetznerIp4? ipv4; static HetznerPublicNetInfo fromJson(final Map json) => _$HetznerPublicNetInfoFromJson(json); @@ -72,11 +72,21 @@ enum ServerStatus { @JsonSerializable() class HetznerServerTypeInfo { - HetznerServerTypeInfo(this.cores, this.memory, this.disk, this.prices); + HetznerServerTypeInfo( + this.cores, + this.memory, + this.disk, + this.prices, + this.name, + this.description, + ); final int cores; final num memory; final int disk; + final String name; + final String description; + final List prices; static HetznerServerTypeInfo fromJson(final Map json) => @@ -85,7 +95,11 @@ class HetznerServerTypeInfo { @JsonSerializable() class HetznerPriceInfo { - HetznerPriceInfo(this.hourly, this.monthly); + HetznerPriceInfo( + this.hourly, + this.monthly, + this.location, + ); @JsonKey(name: 'price_hourly', fromJson: HetznerPriceInfo.getPrice) final double hourly; @@ -93,6 +107,8 @@ class HetznerPriceInfo { @JsonKey(name: 'price_monthly', fromJson: HetznerPriceInfo.getPrice) final double monthly; + final String location; + static HetznerPriceInfo fromJson(final Map json) => _$HetznerPriceInfoFromJson(json); @@ -102,7 +118,14 @@ class HetznerPriceInfo { @JsonSerializable() class HetznerLocation { - HetznerLocation(this.country, this.city, this.description, this.zone); + HetznerLocation( + this.country, + this.city, + this.description, + this.zone, + this.name, + ); + final String name; final String country; final String city; final String description; @@ -112,4 +135,77 @@ class HetznerLocation { static HetznerLocation fromJson(final Map json) => _$HetznerLocationFromJson(json); + + String get flag { + String emoji = ''; + switch (country.substring(0, 2)) { + case 'DE': + emoji = '🇩🇪'; + break; + + case 'FI': + emoji = '🇫🇮'; + break; + + case 'US': + emoji = '🇺🇸'; + break; + } + return emoji; + } +} + +/// A Volume is a highly-available, scalable, and SSD-based block storage for Servers. +/// +/// Pricing for Volumes depends on the Volume size and Location, not the actual used storage. +/// +/// Please see Hetzner Docs for more details about Volumes. +/// https://docs.hetzner.cloud/#volumes +@JsonSerializable() +class HetznerVolume { + HetznerVolume( + this.id, + this.size, + this.serverId, + this.name, + this.linuxDevice, + ); + + /// ID of the Resource + final int id; + + /// Size in GB of the Volume + final int size; + + /// ID of the Server the Volume is attached to, null if it is not attached at all + final int? serverId; + + /// Name of the Resource. Is unique per Project. + final String name; + + /// Device path on the file system for the Volume + @JsonKey(name: 'linux_device') + final String? linuxDevice; + + static HetznerVolume fromJson(final Map json) => + _$HetznerVolumeFromJson(json); +} + +/// Prices for Hetzner resources in Euro (monthly). +/// https://docs.hetzner.cloud/#pricing +class HetznerPricing { + HetznerPricing( + this.region, + this.perVolumeGb, + this.perPublicIpv4, + ); + + /// Region name to which current price listing applies + final String region; + + /// The cost of Volume per GB/month + final double perVolumeGb; + + /// Costs of Primary IP type + final double perPublicIpv4; } diff --git a/lib/logic/models/json/hetzner_server_info.g.dart b/lib/logic/models/json/hetzner_server_info.g.dart index e8c21917..b73a0a9d 100644 --- a/lib/logic/models/json/hetzner_server_info.g.dart +++ b/lib/logic/models/json/hetzner_server_info.g.dart @@ -19,6 +19,18 @@ HetznerServerInfo _$HetznerServerInfoFromJson(Map json) => (json['volumes'] as List).map((e) => e as int).toList(), ); +Map _$HetznerServerInfoToJson(HetznerServerInfo instance) => + { + 'id': instance.id, + 'name': instance.name, + 'status': _$ServerStatusEnumMap[instance.status]!, + 'created': instance.created.toIso8601String(), + 'volumes': instance.volumes, + 'server_type': instance.serverType, + 'datacenter': instance.location, + 'public_net': instance.publicNet, + }; + const _$ServerStatusEnumMap = { ServerStatus.running: 'running', ServerStatus.initializing: 'initializing', @@ -34,9 +46,17 @@ const _$ServerStatusEnumMap = { HetznerPublicNetInfo _$HetznerPublicNetInfoFromJson( Map json) => HetznerPublicNetInfo( - HetznerIp4.fromJson(json['ipv4'] as Map), + json['ipv4'] == null + ? null + : HetznerIp4.fromJson(json['ipv4'] as Map), ); +Map _$HetznerPublicNetInfoToJson( + HetznerPublicNetInfo instance) => + { + 'ipv4': instance.ipv4, + }; + HetznerIp4 _$HetznerIp4FromJson(Map json) => HetznerIp4( json['id'] as int, json['ip'] as String, @@ -44,6 +64,14 @@ HetznerIp4 _$HetznerIp4FromJson(Map json) => HetznerIp4( json['dns_ptr'] as String, ); +Map _$HetznerIp4ToJson(HetznerIp4 instance) => + { + 'blocked': instance.blocked, + 'dns_ptr': instance.reverseDns, + 'id': instance.id, + 'ip': instance.ip, + }; + HetznerServerTypeInfo _$HetznerServerTypeInfoFromJson( Map json) => HetznerServerTypeInfo( @@ -53,18 +81,67 @@ HetznerServerTypeInfo _$HetznerServerTypeInfoFromJson( (json['prices'] as List) .map((e) => HetznerPriceInfo.fromJson(e as Map)) .toList(), + json['name'] as String, + json['description'] as String, ); +Map _$HetznerServerTypeInfoToJson( + HetznerServerTypeInfo instance) => + { + 'cores': instance.cores, + 'memory': instance.memory, + 'disk': instance.disk, + 'name': instance.name, + 'description': instance.description, + 'prices': instance.prices, + }; + HetznerPriceInfo _$HetznerPriceInfoFromJson(Map json) => HetznerPriceInfo( HetznerPriceInfo.getPrice(json['price_hourly'] as Map), HetznerPriceInfo.getPrice(json['price_monthly'] as Map), + json['location'] as String, ); +Map _$HetznerPriceInfoToJson(HetznerPriceInfo instance) => + { + 'price_hourly': instance.hourly, + 'price_monthly': instance.monthly, + 'location': instance.location, + }; + HetznerLocation _$HetznerLocationFromJson(Map json) => HetznerLocation( json['country'] as String, json['city'] as String, json['description'] as String, json['network_zone'] as String, + json['name'] as String, ); + +Map _$HetznerLocationToJson(HetznerLocation instance) => + { + 'name': instance.name, + 'country': instance.country, + 'city': instance.city, + 'description': instance.description, + 'network_zone': instance.zone, + }; + +HetznerVolume _$HetznerVolumeFromJson(Map json) => + HetznerVolume( + json['id'] as int, + json['size'] as int, + json['serverId'] as int?, + json['name'] as String, + json['linux_device'] as String?, + ); + +Map _$HetznerVolumeToJson(HetznerVolume instance) => + { + 'id': instance.id, + 'size': instance.size, + 'serverId': instance.serverId, + 'name': instance.name, + 'linux_device': instance.linuxDevice, + }; diff --git a/lib/logic/models/json/recovery_token_status.dart b/lib/logic/models/json/recovery_token_status.dart index 6e59b57d..b56ae4e6 100644 --- a/lib/logic/models/json/recovery_token_status.dart +++ b/lib/logic/models/json/recovery_token_status.dart @@ -1,5 +1,6 @@ import 'package:equatable/equatable.dart'; import 'package:json_annotation/json_annotation.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server_api.graphql.dart'; part 'recovery_token_status.g.dart'; @@ -15,6 +16,16 @@ class RecoveryKeyStatus extends Equatable { this.usesLeft, }); + RecoveryKeyStatus.fromGraphQL( + final Query$RecoveryKey$api$recoveryKey recoveryKey, + ) : this( + exists: recoveryKey.exists, + date: recoveryKey.creationDate, + expiration: recoveryKey.expirationDate, + usesLeft: recoveryKey.usesLeft, + valid: recoveryKey.valid, + ); + final bool exists; final DateTime? date; final DateTime? expiration; diff --git a/lib/logic/models/json/recovery_token_status.g.dart b/lib/logic/models/json/recovery_token_status.g.dart index cef9abbd..9fb6d6cd 100644 --- a/lib/logic/models/json/recovery_token_status.g.dart +++ b/lib/logic/models/json/recovery_token_status.g.dart @@ -17,3 +17,12 @@ RecoveryKeyStatus _$RecoveryKeyStatusFromJson(Map json) => : DateTime.parse(json['expiration'] as String), usesLeft: json['uses_left'] as int?, ); + +Map _$RecoveryKeyStatusToJson(RecoveryKeyStatus instance) => + { + 'exists': instance.exists, + 'date': instance.date?.toIso8601String(), + 'expiration': instance.expiration?.toIso8601String(), + 'uses_left': instance.usesLeft, + 'valid': instance.valid, + }; diff --git a/lib/logic/models/json/server_disk_volume.dart b/lib/logic/models/json/server_disk_volume.dart new file mode 100644 index 00000000..873b5d97 --- /dev/null +++ b/lib/logic/models/json/server_disk_volume.dart @@ -0,0 +1,28 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'server_disk_volume.g.dart'; + +@JsonSerializable() +class ServerDiskVolume { + factory ServerDiskVolume.fromJson(final Map json) => + _$ServerDiskVolumeFromJson(json); + ServerDiskVolume({ + required this.freeSpace, + required this.model, + required this.name, + required this.root, + required this.serial, + required this.totalSpace, + required this.type, + required this.usedSpace, + }); + + final String freeSpace; + final String? model; + final String name; + final bool root; + final String? serial; + final String totalSpace; + final String type; + final String usedSpace; +} diff --git a/lib/logic/models/json/server_disk_volume.g.dart b/lib/logic/models/json/server_disk_volume.g.dart new file mode 100644 index 00000000..81a9d86e --- /dev/null +++ b/lib/logic/models/json/server_disk_volume.g.dart @@ -0,0 +1,31 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'server_disk_volume.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +ServerDiskVolume _$ServerDiskVolumeFromJson(Map json) => + ServerDiskVolume( + freeSpace: json['freeSpace'] as String, + model: json['model'] as String?, + name: json['name'] as String, + root: json['root'] as bool, + serial: json['serial'] as String?, + totalSpace: json['totalSpace'] as String, + type: json['type'] as String, + usedSpace: json['usedSpace'] as String, + ); + +Map _$ServerDiskVolumeToJson(ServerDiskVolume instance) => + { + 'freeSpace': instance.freeSpace, + 'model': instance.model, + 'name': instance.name, + 'root': instance.root, + 'serial': instance.serial, + 'totalSpace': instance.totalSpace, + 'type': instance.type, + 'usedSpace': instance.usedSpace, + }; diff --git a/lib/logic/models/json/server_job.dart b/lib/logic/models/json/server_job.dart new file mode 100644 index 00000000..70d6d103 --- /dev/null +++ b/lib/logic/models/json/server_job.dart @@ -0,0 +1,79 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server_api.graphql.dart'; + +part 'server_job.g.dart'; + +@JsonSerializable() +class ServerJob { + factory ServerJob.fromJson(final Map json) => + _$ServerJobFromJson(json); + ServerJob({ + required this.name, + required this.description, + required this.status, + required this.uid, + required this.typeId, + required this.updatedAt, + required this.createdAt, + this.error, + this.progress, + this.result, + this.statusText, + this.finishedAt, + }); + + ServerJob.fromGraphQL(final Fragment$basicApiJobsFields serverJob) + : this( + createdAt: serverJob.createdAt, + description: serverJob.description, + error: serverJob.error, + finishedAt: serverJob.finishedAt, + name: serverJob.name, + progress: serverJob.progress, + result: serverJob.result, + status: JobStatusEnum.fromString(serverJob.status), + statusText: serverJob.statusText, + uid: serverJob.uid, + typeId: serverJob.typeId, + updatedAt: serverJob.updatedAt, + ); + final String name; + final String description; + final JobStatusEnum status; + final String uid; + final String typeId; + final DateTime updatedAt; + final DateTime createdAt; + + final String? error; + final int? progress; + final String? result; + final String? statusText; + final DateTime? finishedAt; +} + +enum JobStatusEnum { + @JsonValue('CREATED') + created, + @JsonValue('RUNNING') + running, + @JsonValue('FINISHED') + finished, + @JsonValue('ERROR') + error; + + factory JobStatusEnum.fromString(final String status) { + switch (status) { + case 'CREATED': + return created; + case 'RUNNING': + return running; + case 'FINISHED': + return finished; + case 'ERROR': + return error; + default: + throw Exception('Unknown status: $status'); + } + } +} diff --git a/lib/logic/models/json/server_job.g.dart b/lib/logic/models/json/server_job.g.dart new file mode 100644 index 00000000..712c086f --- /dev/null +++ b/lib/logic/models/json/server_job.g.dart @@ -0,0 +1,46 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'server_job.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +ServerJob _$ServerJobFromJson(Map json) => ServerJob( + name: json['name'] as String, + description: json['description'] as String, + status: $enumDecode(_$JobStatusEnumEnumMap, json['status']), + uid: json['uid'] as String, + typeId: json['typeId'] as String, + updatedAt: DateTime.parse(json['updatedAt'] as String), + createdAt: DateTime.parse(json['createdAt'] as String), + error: json['error'] as String?, + progress: json['progress'] as int?, + result: json['result'] as String?, + statusText: json['statusText'] as String?, + finishedAt: json['finishedAt'] == null + ? null + : DateTime.parse(json['finishedAt'] as String), + ); + +Map _$ServerJobToJson(ServerJob instance) => { + 'name': instance.name, + 'description': instance.description, + 'status': _$JobStatusEnumEnumMap[instance.status]!, + 'uid': instance.uid, + 'typeId': instance.typeId, + 'updatedAt': instance.updatedAt.toIso8601String(), + 'createdAt': instance.createdAt.toIso8601String(), + 'error': instance.error, + 'progress': instance.progress, + 'result': instance.result, + 'statusText': instance.statusText, + 'finishedAt': instance.finishedAt?.toIso8601String(), + }; + +const _$JobStatusEnumEnumMap = { + JobStatusEnum.created: 'CREATED', + JobStatusEnum.running: 'RUNNING', + JobStatusEnum.finished: 'FINISHED', + JobStatusEnum.error: 'ERROR', +}; diff --git a/lib/logic/models/launch_installation_data.dart b/lib/logic/models/launch_installation_data.dart new file mode 100644 index 00000000..00d89335 --- /dev/null +++ b/lib/logic/models/launch_installation_data.dart @@ -0,0 +1,26 @@ +import 'package:selfprivacy/logic/models/disk_size.dart'; +import 'package:selfprivacy/logic/models/hive/server_details.dart'; +import 'package:selfprivacy/logic/models/hive/server_domain.dart'; +import 'package:selfprivacy/logic/models/hive/user.dart'; + +class LaunchInstallationData { + LaunchInstallationData({ + required this.rootUser, + required this.dnsApiToken, + required this.dnsProviderType, + required this.serverDomain, + required this.serverTypeId, + required this.errorCallback, + required this.successCallback, + required this.storageSize, + }); + + final User rootUser; + final String dnsApiToken; + final ServerDomain serverDomain; + final DnsProviderType dnsProviderType; + final String serverTypeId; + final Function() errorCallback; + final Function(ServerHostingDetails details) successCallback; + final DiskSize storageSize; +} diff --git a/lib/logic/models/message.dart b/lib/logic/models/message.dart index 8bbc6dfd..aaaf0930 100644 --- a/lib/logic/models/message.dart +++ b/lib/logic/models/message.dart @@ -1,20 +1,74 @@ +import 'package:graphql/client.dart'; import 'package:intl/intl.dart'; final DateFormat formatter = DateFormat('hh:mm'); class Message { - Message({this.text, this.type = MessageType.normal}) : time = DateTime.now(); + Message({this.text, this.severity = MessageSeverity.normal}) + : time = DateTime.now(); Message.warn({this.text}) - : type = MessageType.warning, + : severity = MessageSeverity.warning, time = DateTime.now(); final String? text; final DateTime time; - final MessageType type; + final MessageSeverity severity; String get timeString => formatter.format(time); } -enum MessageType { +enum MessageSeverity { normal, warning, } + +class RestApiRequestMessage extends Message { + RestApiRequestMessage({ + this.method, + this.uri, + this.data, + this.headers, + }) : super(text: 'request-uri: $uri\nheaders: $headers\ndata: $data'); + + final String? method; + final Uri? uri; + final String? data; + final Map? headers; +} + +class RestApiResponseMessage extends Message { + RestApiResponseMessage({ + this.method, + this.uri, + this.statusCode, + this.data, + }) : super(text: 'response-uri: $uri\ncode: $statusCode\ndata: $data'); + + final String? method; + final Uri? uri; + final int? statusCode; + final String? data; +} + +class GraphQlResponseMessage extends Message { + GraphQlResponseMessage({ + this.data, + this.errors, + this.context, + }) : super(text: 'GraphQL Response\ndata: $data'); + + final Map? data; + final List? errors; + final Context? context; +} + +class GraphQlRequestMessage extends Message { + GraphQlRequestMessage({ + this.operation, + this.variables, + this.context, + }) : super(text: 'GraphQL Request\noperation: $operation'); + + final Operation? operation; + final Map? variables; + final Context? context; +} diff --git a/lib/logic/models/metrics.dart b/lib/logic/models/metrics.dart new file mode 100644 index 00000000..4f5d3efc --- /dev/null +++ b/lib/logic/models/metrics.dart @@ -0,0 +1,30 @@ +class TimeSeriesData { + TimeSeriesData( + this.secondsSinceEpoch, + this.value, + ); + + final int secondsSinceEpoch; + DateTime get time => + DateTime.fromMillisecondsSinceEpoch(secondsSinceEpoch * 1000); + final double value; +} + +class ServerMetrics { + ServerMetrics({ + required this.stepsInSecond, + required this.cpu, + required this.bandwidthIn, + required this.bandwidthOut, + required this.start, + required this.end, + }); + + final num stepsInSecond; + final List cpu; + final List bandwidthIn; + final List bandwidthOut; + + final DateTime start; + final DateTime end; +} diff --git a/lib/logic/models/price.dart b/lib/logic/models/price.dart new file mode 100644 index 00000000..494b1511 --- /dev/null +++ b/lib/logic/models/price.dart @@ -0,0 +1,64 @@ +class Price { + Price({ + required this.value, + required this.currency, + }); + + final double value; + final Currency currency; +} + +enum CurrencyType { + eur, + usd, + unkown, +} + +class Currency { + Currency({ + required this.type, + required this.shortcode, + this.fontcode, + this.symbol, + }); + + factory Currency.fromType(final CurrencyType type) { + switch (type) { + case CurrencyType.eur: + return Currency( + type: type, + shortcode: 'EUR', + fontcode: 'euro', + symbol: '€', + ); + case CurrencyType.usd: + return Currency( + type: type, + shortcode: 'USD', + fontcode: 'attach_money', + symbol: '\$', + ); + default: + return Currency( + type: type, + shortcode: '?', + fontcode: 'payments', + symbol: '?', + ); + } + } + + final CurrencyType type; + final String shortcode; + final String? fontcode; + final String? symbol; +} + +class AdditionalPricing { + AdditionalPricing({ + required this.perVolumeGb, + required this.perPublicIpv4, + }); + final Price perVolumeGb; + final Price perPublicIpv4; +} diff --git a/lib/logic/models/server_basic_info.dart b/lib/logic/models/server_basic_info.dart index 8670dc8c..3037a2d5 100644 --- a/lib/logic/models/server_basic_info.dart +++ b/lib/logic/models/server_basic_info.dart @@ -5,14 +5,12 @@ class ServerBasicInfo { required this.reverseDns, required this.ip, required this.created, - required this.volumeId, }); final int id; final String name; final String reverseDns; final String ip; final DateTime created; - final int volumeId; } class ServerBasicInfoWithValidators extends ServerBasicInfo { @@ -26,18 +24,16 @@ class ServerBasicInfoWithValidators extends ServerBasicInfo { reverseDns: serverBasicInfo.reverseDns, ip: serverBasicInfo.ip, created: serverBasicInfo.created, - volumeId: serverBasicInfo.volumeId, isIpValid: isIpValid, isReverseDnsValid: isReverseDnsValid, ); ServerBasicInfoWithValidators({ - required final super.id, - required final super.name, - required final super.reverseDns, - required final super.ip, - required final super.created, - required final super.volumeId, + required super.id, + required super.name, + required super.reverseDns, + required super.ip, + required super.created, required this.isIpValid, required this.isReverseDnsValid, }); diff --git a/lib/logic/models/server_metadata.dart b/lib/logic/models/server_metadata.dart new file mode 100644 index 00000000..553fcbb5 --- /dev/null +++ b/lib/logic/models/server_metadata.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; + +enum MetadataType { + id(icon: Icons.numbers_outlined), + status(icon: Icons.mode_standby_outlined), + cpu(icon: Icons.memory_outlined), + ram(icon: Icons.memory_outlined), + cost(icon: Icons.payments_outlined), + location(icon: Icons.location_on_outlined), + + other(icon: Icons.info_outlined); + + const MetadataType({ + required this.icon, + }); + + final IconData icon; +} + +class ServerMetadataEntity { + ServerMetadataEntity({ + required this.trId, + required this.value, + this.type = MetadataType.other, + }); + final MetadataType type; + final String trId; + final String value; +} diff --git a/lib/logic/models/server_provider_location.dart b/lib/logic/models/server_provider_location.dart new file mode 100644 index 00000000..688eb972 --- /dev/null +++ b/lib/logic/models/server_provider_location.dart @@ -0,0 +1,13 @@ +class ServerProviderLocation { + ServerProviderLocation({ + required this.title, + required this.identifier, + this.description, + this.flag = '', + }); + + final String title; + final String identifier; + final String? description; + final String flag; +} diff --git a/lib/logic/models/server_type.dart b/lib/logic/models/server_type.dart new file mode 100644 index 00000000..3bdb9baa --- /dev/null +++ b/lib/logic/models/server_type.dart @@ -0,0 +1,22 @@ +import 'package:selfprivacy/logic/models/disk_size.dart'; +import 'package:selfprivacy/logic/models/price.dart'; +import 'package:selfprivacy/logic/models/server_provider_location.dart'; + +class ServerType { + ServerType({ + required this.title, + required this.identifier, + required this.ram, + required this.cores, + required this.disk, + required this.price, + required this.location, + }); + final String title; + final String identifier; + final double ram; // GB !! + final DiskSize disk; + final int cores; + final Price price; + final ServerProviderLocation location; +} diff --git a/lib/logic/models/service.dart b/lib/logic/models/service.dart new file mode 100644 index 00000000..b7fee609 --- /dev/null +++ b/lib/logic/models/service.dart @@ -0,0 +1,148 @@ +import 'dart:convert'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/schema.graphql.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/services.graphql.dart'; +import 'package:selfprivacy/logic/models/disk_size.dart'; +import 'package:selfprivacy/logic/models/json/dns_records.dart'; + +import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server_settings.graphql.dart'; + +class Service { + Service.fromGraphQL(final Query$AllServices$services$allServices service) + : this( + id: service.id, + displayName: service.displayName, + description: service.description, + isEnabled: service.isEnabled, + isRequired: service.isRequired, + isMovable: service.isMovable, + canBeBackedUp: service.canBeBackedUp, + backupDescription: service.backupDescription, + status: ServiceStatus.fromGraphQL(service.status), + storageUsage: ServiceStorageUsage( + used: DiskSize(byte: int.parse(service.storageUsage.usedSpace)), + volume: service.storageUsage.volume?.name, + ), + // Decode the base64 encoded svg icon to text. + svgIcon: utf8.decode(base64.decode(service.svgIcon)), + dnsRecords: service.dnsRecords + ?.map( + ( + final Fragment$fragmentDnsRecords record, + ) => + DnsRecord.fromGraphQL(record), + ) + .toList() ?? + [], + url: service.url, + ); + Service({ + required this.id, + required this.displayName, + required this.description, + required this.isEnabled, + required this.isRequired, + required this.isMovable, + required this.canBeBackedUp, + required this.backupDescription, + required this.status, + required this.storageUsage, + required this.svgIcon, + required this.dnsRecords, + this.url, + }); + + /// TODO Turn loginInfo into dynamic data, not static! + String get loginInfo { + switch (id) { + case 'email': + return 'mail.login_info'.tr(); + case 'bitwarden': + return 'password_manager.login_info'.tr(); + case 'jitsi': + return 'video.login_info'.tr(); + case 'nextcloud': + return 'cloud.login_info'.tr(); + case 'pleroma': + return 'social_network.login_info'.tr(); + case 'gitea': + return 'git.login_info'.tr(); + } + return ''; + } + + static Service empty = Service( + id: 'empty', + displayName: '', + description: '', + isEnabled: false, + isRequired: false, + isMovable: false, + canBeBackedUp: false, + backupDescription: '', + status: ServiceStatus.off, + storageUsage: ServiceStorageUsage( + used: const DiskSize(byte: 0), + volume: '', + ), + svgIcon: '', + dnsRecords: [], + url: '', + ); + + final String id; + final String displayName; + final String description; + final bool isEnabled; + final bool isRequired; + final bool isMovable; + final bool canBeBackedUp; + final String backupDescription; + final ServiceStatus status; + final ServiceStorageUsage storageUsage; + final String svgIcon; + final String? url; + final List dnsRecords; +} + +class ServiceStorageUsage { + ServiceStorageUsage({ + required this.used, + required this.volume, + }); + + final DiskSize used; + final String? volume; +} + +enum ServiceStatus { + failed, + reloading, + activating, + active, + deactivating, + inactive, + off; + + factory ServiceStatus.fromGraphQL(final Enum$ServiceStatusEnum graphQL) { + switch (graphQL) { + case Enum$ServiceStatusEnum.ACTIVATING: + return activating; + case Enum$ServiceStatusEnum.ACTIVE: + return active; + case Enum$ServiceStatusEnum.DEACTIVATING: + return deactivating; + case Enum$ServiceStatusEnum.FAILED: + return failed; + case Enum$ServiceStatusEnum.INACTIVE: + return inactive; + case Enum$ServiceStatusEnum.OFF: + return off; + case Enum$ServiceStatusEnum.RELOADING: + return reloading; + case Enum$ServiceStatusEnum.$unknown: + return inactive; + } + } +} diff --git a/lib/logic/models/ssh_settings.dart b/lib/logic/models/ssh_settings.dart new file mode 100644 index 00000000..008b07ee --- /dev/null +++ b/lib/logic/models/ssh_settings.dart @@ -0,0 +1,17 @@ +import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server_settings.graphql.dart'; + +class SshSettings { + SshSettings({ + required this.enable, + required this.passwordAuthentication, + }); + + SshSettings.fromGraphQL(final Query$SystemSettings$system$settings$ssh ssh) + : this( + enable: ssh.enable, + passwordAuthentication: ssh.passwordAuthentication, + ); + + final bool enable; + final bool passwordAuthentication; +} diff --git a/lib/logic/models/state_types.dart b/lib/logic/models/state_types.dart index cd79de6c..3fb110c0 100644 --- a/lib/logic/models/state_types.dart +++ b/lib/logic/models/state_types.dart @@ -1 +1 @@ -enum StateType { uninitialized, stable, warning } +enum StateType { uninitialized, stable, warning, error } diff --git a/lib/logic/models/system_settings.dart b/lib/logic/models/system_settings.dart new file mode 100644 index 00000000..88ef5c7a --- /dev/null +++ b/lib/logic/models/system_settings.dart @@ -0,0 +1,26 @@ +import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server_settings.graphql.dart'; +import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart'; +import 'package:selfprivacy/logic/models/ssh_settings.dart'; + +class SystemSettings { + SystemSettings({ + required this.sshSettings, + required this.autoUpgradeSettings, + required this.timezone, + }); + + SystemSettings.fromGraphQL(final Query$SystemSettings$system system) + : this( + sshSettings: SshSettings.fromGraphQL( + system.settings.ssh, + ), + autoUpgradeSettings: AutoUpgradeSettings.fromGraphQL( + system.settings.autoUpgrade, + ), + timezone: system.settings.timezone, + ); + + final SshSettings sshSettings; + final AutoUpgradeSettings autoUpgradeSettings; + final String timezone; +} diff --git a/lib/logic/models/timezone_settings.dart b/lib/logic/models/timezone_settings.dart index 22c84b44..4b4334af 100644 --- a/lib/logic/models/timezone_settings.dart +++ b/lib/logic/models/timezone_settings.dart @@ -3,13 +3,16 @@ import 'package:timezone/timezone.dart'; class TimeZoneSettings { factory TimeZoneSettings.fromString(final String string) { final Location location = timeZoneDatabase.locations[string]!; - return TimeZoneSettings(location); + return TimeZoneSettings(timezone: location); } - TimeZoneSettings(this.timezone); - final Location timezone; + TimeZoneSettings({this.timezone}); + final Location? timezone; Map toJson() => { - 'timezone': timezone.name, + 'timezone': timezone?.name ?? 'Unknown', }; + + @override + String toString() => timezone?.name ?? 'Unknown'; } diff --git a/lib/logic/providers/dns_providers/cloudflare.dart b/lib/logic/providers/dns_providers/cloudflare.dart new file mode 100644 index 00000000..6cf4a494 --- /dev/null +++ b/lib/logic/providers/dns_providers/cloudflare.dart @@ -0,0 +1,442 @@ +import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare_api.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/desired_dns_record.dart'; +import 'package:selfprivacy/logic/models/hive/server_domain.dart'; +import 'package:selfprivacy/logic/models/json/cloudflare_dns_info.dart'; +import 'package:selfprivacy/logic/models/json/dns_records.dart'; +import 'package:selfprivacy/logic/providers/dns_providers/dns_provider.dart'; + +class ApiAdapter { + ApiAdapter({ + final bool isWithToken = true, + this.cachedDomain = '', + this.cachedZoneId = '', + }) : _api = CloudflareApi( + isWithToken: isWithToken, + ); + + CloudflareApi api({final bool getInitialized = true}) => getInitialized + ? _api + : CloudflareApi( + isWithToken: false, + ); + + final CloudflareApi _api; + final String cachedZoneId; + final String cachedDomain; +} + +class CloudflareDnsProvider extends DnsProvider { + CloudflareDnsProvider() : _adapter = ApiAdapter(); + CloudflareDnsProvider.load( + final bool isAuthotized, + ) : _adapter = ApiAdapter( + isWithToken: isAuthotized, + ); + + ApiAdapter _adapter; + + @override + DnsProviderType get type => DnsProviderType.cloudflare; + + @override + Future> tryInitApiByToken(final String token) async { + final api = _adapter.api(getInitialized: false); + final result = await api.isApiTokenValid(token); + if (!result.data || !result.success) { + return result; + } + + _adapter = ApiAdapter(isWithToken: true); + return result; + } + + @override + Future>> domainList() async { + List domains = []; + final result = await _adapter.api().getZones(); + if (result.data.isEmpty || !result.success) { + return GenericResult( + success: result.success, + data: domains, + code: result.code, + message: result.message, + ); + } + + domains = result.data + .map( + (final el) => el.name, + ) + .toList(); + + return GenericResult( + success: true, + data: domains, + ); + } + + @override + Future> createDomainRecords({ + required final ServerDomain domain, + final String? ip4, + }) async { + final syncZoneIdResult = await syncZoneId(domain.domainName); + if (!syncZoneIdResult.success) { + return syncZoneIdResult; + } + + final records = getProjectDnsRecords(domain.domainName, ip4); + return _adapter.api().createMultipleDnsRecords( + zoneId: _adapter.cachedZoneId, + records: records + .map( + (final rec) => CloudflareDnsRecord( + content: rec.content, + name: rec.name, + type: rec.type, + zoneName: domain.domainName, + id: null, + ttl: rec.ttl, + ), + ) + .toList(), + ); + } + + @override + Future> removeDomainRecords({ + required final ServerDomain domain, + final String? ip4, + }) async { + final syncZoneIdResult = await syncZoneId(domain.domainName); + if (!syncZoneIdResult.success) { + return syncZoneIdResult; + } + + final result = + await _adapter.api().getDnsRecords(zoneId: _adapter.cachedZoneId); + if (result.data.isEmpty || !result.success) { + return GenericResult( + success: result.success, + data: null, + code: result.code, + message: result.message, + ); + } + + return _adapter.api().removeSimilarRecords( + zoneId: _adapter.cachedZoneId, + records: result.data, + ); + } + + @override + Future>> getDnsRecords({ + required final ServerDomain domain, + }) async { + final syncZoneIdResult = await syncZoneId(domain.domainName); + if (!syncZoneIdResult.success) { + return GenericResult( + success: syncZoneIdResult.success, + data: [], + code: syncZoneIdResult.code, + message: syncZoneIdResult.message, + ); + } + + final List records = []; + final result = + await _adapter.api().getDnsRecords(zoneId: _adapter.cachedZoneId); + if (result.data.isEmpty || !result.success) { + return GenericResult( + success: result.success, + data: records, + code: result.code, + message: result.message, + ); + } + + for (final rawRecord in result.data) { + records.add( + DnsRecord( + name: rawRecord.name, + type: rawRecord.type, + content: rawRecord.content, + ttl: rawRecord.ttl, + ), + ); + } + + return GenericResult( + success: result.success, + data: records, + ); + } + + @override + Future> setDnsRecord( + final DnsRecord record, + final ServerDomain domain, + ) async { + final syncZoneIdResult = await syncZoneId(domain.domainName); + if (!syncZoneIdResult.success) { + return syncZoneIdResult; + } + + return _adapter.api().createMultipleDnsRecords( + zoneId: _adapter.cachedZoneId, + records: [ + CloudflareDnsRecord( + content: record.content, + id: null, + name: record.name, + type: record.type, + zoneName: domain.domainName, + ttl: record.ttl, + ), + ], + ); + } + + @override + Future>> validateDnsRecords( + final ServerDomain domain, + final String ip4, + final String dkimPublicKey, + ) async { + final GenericResult> records = + await getDnsRecords(domain: domain); + final List foundRecords = []; + try { + final List desiredRecords = + getDesiredDnsRecords(domain.domainName, ip4, dkimPublicKey); + for (final DesiredDnsRecord record in desiredRecords) { + if (record.description == 'record.dkim') { + final DnsRecord foundRecord = records.data.firstWhere( + (final r) => (r.name == record.name) && r.type == record.type, + orElse: () => DnsRecord( + name: record.name, + type: record.type, + content: '', + ttl: 800, + proxied: false, + ), + ); + // remove all spaces and tabulators from + // the foundRecord.content and the record.content + // to compare them + final String? foundContent = + foundRecord.content?.replaceAll(RegExp(r'\s+'), ''); + final String content = record.content.replaceAll(RegExp(r'\s+'), ''); + if (foundContent == content) { + foundRecords.add(record.copyWith(isSatisfied: true)); + } else { + foundRecords.add(record.copyWith(isSatisfied: false)); + } + } else { + if (records.data.any( + (final r) => + (r.name == record.name) && + r.type == record.type && + r.content == record.content, + )) { + foundRecords.add(record.copyWith(isSatisfied: true)); + } else { + foundRecords.add(record.copyWith(isSatisfied: false)); + } + } + } + } catch (e) { + print(e); + return GenericResult( + data: [], + success: false, + message: e.toString(), + ); + } + return GenericResult( + data: foundRecords, + success: true, + ); + } + + @override + List getDesiredDnsRecords( + final String? domainName, + final String? ip4, + final String? dkimPublicKey, + ) { + if (domainName == null || ip4 == null) { + return []; + } + return [ + DesiredDnsRecord( + name: domainName, + content: ip4, + description: 'record.root', + ), + DesiredDnsRecord( + name: 'api.$domainName', + content: ip4, + description: 'record.api', + ), + DesiredDnsRecord( + name: 'cloud.$domainName', + content: ip4, + description: 'record.cloud', + ), + DesiredDnsRecord( + name: 'git.$domainName', + content: ip4, + description: 'record.git', + ), + DesiredDnsRecord( + name: 'meet.$domainName', + content: ip4, + description: 'record.meet', + ), + DesiredDnsRecord( + name: 'social.$domainName', + content: ip4, + description: 'record.social', + ), + DesiredDnsRecord( + name: 'password.$domainName', + content: ip4, + description: 'record.password', + ), + DesiredDnsRecord( + name: 'vpn.$domainName', + content: ip4, + description: 'record.vpn', + ), + DesiredDnsRecord( + name: domainName, + content: domainName, + description: 'record.mx', + type: 'MX', + category: DnsRecordsCategory.email, + ), + DesiredDnsRecord( + name: '_dmarc.$domainName', + content: 'v=DMARC1; p=none', + description: 'record.dmarc', + type: 'TXT', + category: DnsRecordsCategory.email, + ), + DesiredDnsRecord( + name: domainName, + content: 'v=spf1 a mx ip4:$ip4 -all', + description: 'record.spf', + type: 'TXT', + category: DnsRecordsCategory.email, + ), + if (dkimPublicKey != null) + DesiredDnsRecord( + name: 'selector._domainkey.$domainName', + content: dkimPublicKey, + description: 'record.dkim', + type: 'TXT', + category: DnsRecordsCategory.email, + ), + ]; + } + + List getProjectDnsRecords( + final String? domainName, + final String? ip4, + ) { + final DnsRecord domainA = + DnsRecord(type: 'A', name: domainName, content: ip4); + + final DnsRecord mx = DnsRecord(type: 'MX', name: '@', content: domainName); + final DnsRecord apiA = DnsRecord(type: 'A', name: 'api', content: ip4); + final DnsRecord cloudA = DnsRecord(type: 'A', name: 'cloud', content: ip4); + final DnsRecord gitA = DnsRecord(type: 'A', name: 'git', content: ip4); + final DnsRecord meetA = DnsRecord(type: 'A', name: 'meet', content: ip4); + final DnsRecord passwordA = + DnsRecord(type: 'A', name: 'password', content: ip4); + final DnsRecord socialA = + DnsRecord(type: 'A', name: 'social', content: ip4); + final DnsRecord vpn = DnsRecord(type: 'A', name: 'vpn', content: ip4); + + final DnsRecord txt1 = DnsRecord( + type: 'TXT', + name: '_dmarc', + content: 'v=DMARC1; p=none', + ttl: 18000, + ); + + final DnsRecord txt2 = DnsRecord( + type: 'TXT', + name: domainName, + content: 'v=spf1 a mx ip4:$ip4 -all', + ttl: 18000, + ); + + return [ + domainA, + apiA, + cloudA, + gitA, + meetA, + passwordA, + socialA, + mx, + txt1, + txt2, + vpn + ]; + } + + Future> syncZoneId(final String domain) async { + if (domain == _adapter.cachedDomain && _adapter.cachedZoneId.isNotEmpty) { + return GenericResult( + success: true, + data: null, + ); + } + + final getZoneIdResult = await getZoneId(domain); + if (!getZoneIdResult.success || getZoneIdResult.data == null) { + return GenericResult( + success: false, + data: null, + code: getZoneIdResult.code, + message: getZoneIdResult.message, + ); + } + + _adapter = ApiAdapter( + isWithToken: true, + cachedDomain: domain, + cachedZoneId: getZoneIdResult.data!, + ); + + return GenericResult( + success: true, + data: null, + ); + } + + Future> getZoneId(final String domain) async { + String? id; + final result = await _adapter.api().getZones(); + if (result.data.isEmpty || !result.success) { + return GenericResult( + success: result.success, + data: id, + code: result.code, + message: result.message, + ); + } + + for (final availableDomain in result.data) { + if (availableDomain.name == domain) { + id = availableDomain.id; + } + } + + return GenericResult(success: id != null, data: id); + } +} diff --git a/lib/logic/providers/dns_providers/desec.dart b/lib/logic/providers/dns_providers/desec.dart new file mode 100644 index 00000000..a5e3e8d7 --- /dev/null +++ b/lib/logic/providers/dns_providers/desec.dart @@ -0,0 +1,411 @@ +import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/desec/desec_api.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/desired_dns_record.dart'; +import 'package:selfprivacy/logic/models/hive/server_domain.dart'; +import 'package:selfprivacy/logic/models/json/desec_dns_info.dart'; +import 'package:selfprivacy/logic/models/json/dns_records.dart'; +import 'package:selfprivacy/logic/providers/dns_providers/dns_provider.dart'; + +class ApiAdapter { + ApiAdapter({final bool isWithToken = true}) + : _api = DesecApi( + isWithToken: isWithToken, + ); + + DesecApi api({final bool getInitialized = true}) => getInitialized + ? _api + : DesecApi( + isWithToken: false, + ); + + final DesecApi _api; +} + +class DesecDnsProvider extends DnsProvider { + DesecDnsProvider() : _adapter = ApiAdapter(); + DesecDnsProvider.load( + final bool isAuthotized, + ) : _adapter = ApiAdapter( + isWithToken: isAuthotized, + ); + + ApiAdapter _adapter; + + @override + DnsProviderType get type => DnsProviderType.desec; + + @override + Future> tryInitApiByToken(final String token) async { + final api = _adapter.api(getInitialized: false); + final result = await api.isApiTokenValid(token); + if (!result.data || !result.success) { + return result; + } + + _adapter = ApiAdapter(isWithToken: true); + return result; + } + + @override + Future>> domainList() async { + List domains = []; + final result = await _adapter.api().getDomains(); + if (result.data.isEmpty || !result.success) { + return GenericResult( + success: result.success, + data: domains, + code: result.code, + message: result.message, + ); + } + + domains = result.data + .map( + (final el) => el.name, + ) + .toList(); + + return GenericResult( + success: true, + data: domains, + ); + } + + @override + Future> createDomainRecords({ + required final ServerDomain domain, + final String? ip4, + }) async { + final List listDnsRecords = projectDnsRecords( + domain.domainName, + ip4, + ); + + final List bulkRecords = []; + for (final DnsRecord record in listDnsRecords) { + bulkRecords.add( + DesecDnsRecord( + subname: record.name ?? '', + type: record.type, + ttl: record.ttl, + records: [extractContent(record) ?? ''], + ), + ); + } + + return _adapter.api().createMultipleDnsRecords( + domainName: domain.domainName, + records: bulkRecords, + ); + } + + @override + Future> removeDomainRecords({ + required final ServerDomain domain, + final String? ip4, + }) async { + final List listDnsRecords = projectDnsRecords( + domain.domainName, + ip4, + ); + + final List bulkRecords = []; + for (final DnsRecord record in listDnsRecords) { + bulkRecords.add( + DesecDnsRecord( + subname: record.name ?? '', + type: record.type, + ttl: record.ttl, + records: [], + ), + ); + } + bulkRecords.add( + DesecDnsRecord( + subname: 'selector._domainkey', + type: 'TXT', + ttl: 18000, + records: [], + ), + ); + + return _adapter.api().removeSimilarRecords( + domainName: domain.domainName, + records: bulkRecords, + ); + } + + @override + Future>> getDnsRecords({ + required final ServerDomain domain, + }) async { + final List records = []; + final result = await _adapter.api().getDnsRecords(domain.domainName); + if (result.data.isEmpty || !result.success) { + return GenericResult( + success: result.success, + data: records, + code: result.code, + message: result.message, + ); + } + + try { + for (final record in result.data) { + final String? content = + record.records.isEmpty ? null : record.records[0]; + records.add( + DnsRecord( + name: record.subname, + type: record.type, + content: content, + ttl: record.ttl, + ), + ); + } + } catch (e) { + print(e); + return GenericResult( + success: false, + data: records, + message: e.toString(), + ); + } + + return GenericResult(success: true, data: records); + } + + @override + Future> setDnsRecord( + final DnsRecord record, + final ServerDomain domain, + ) async { + final result = await _adapter.api().createMultipleDnsRecords( + domainName: domain.domainName, + records: [ + DesecDnsRecord( + subname: record.name ?? '', + type: record.type, + ttl: record.ttl, + records: [extractContent(record) ?? ''], + ), + ], + ); + + return GenericResult( + success: result.success, + data: null, + ); + } + + String? extractContent(final DnsRecord record) { + String? content = record.content; + if (record.type == 'TXT' && content != null && !content.startsWith('"')) { + content = '"$content"'; + } + + return content; + } + + @override + Future>> validateDnsRecords( + final ServerDomain domain, + final String ip4, + final String dkimPublicKey, + ) async { + final result = await getDnsRecords(domain: domain); + if (result.data.isEmpty || !result.success) { + return GenericResult( + success: result.success, + data: [], + code: result.code, + message: result.message, + ); + } + + final records = result.data; + final List foundRecords = []; + try { + final List desiredRecords = + getDesiredDnsRecords(domain.domainName, ip4, dkimPublicKey); + for (final DesiredDnsRecord record in desiredRecords) { + if (record.description == 'record.dkim') { + final DnsRecord foundRecord = records.firstWhere( + (final r) => + ('${r.name}.${domain.domainName}' == record.name) && + r.type == record.type, + orElse: () => DnsRecord( + name: record.name, + type: record.type, + content: '', + ttl: 800, + proxied: false, + ), + ); + // remove all spaces and tabulators from + // the foundRecord.content and the record.content + // to compare them + final String? foundContent = + foundRecord.content?.replaceAll(RegExp(r'\s+'), ''); + final String content = record.content.replaceAll(RegExp(r'\s+'), ''); + if (foundContent == content) { + foundRecords.add(record.copyWith(isSatisfied: true)); + } else { + foundRecords.add(record.copyWith(isSatisfied: false)); + } + } else { + if (records.any( + (final r) => + ('${r.name}.${domain.domainName}' == record.name || + record.name == '') && + r.type == record.type && + r.content == record.content, + )) { + foundRecords.add(record.copyWith(isSatisfied: true)); + } else { + foundRecords.add(record.copyWith(isSatisfied: false)); + } + } + } + } catch (e) { + print(e); + return GenericResult( + data: [], + success: false, + message: e.toString(), + ); + } + return GenericResult( + data: foundRecords, + success: true, + ); + } + + List projectDnsRecords( + final String? domainName, + final String? ip4, + ) { + final DnsRecord domainA = DnsRecord(type: 'A', name: '', content: ip4); + + final DnsRecord mx = + DnsRecord(type: 'MX', name: '', content: '10 $domainName.'); + final DnsRecord apiA = DnsRecord(type: 'A', name: 'api', content: ip4); + final DnsRecord cloudA = DnsRecord(type: 'A', name: 'cloud', content: ip4); + final DnsRecord gitA = DnsRecord(type: 'A', name: 'git', content: ip4); + final DnsRecord meetA = DnsRecord(type: 'A', name: 'meet', content: ip4); + final DnsRecord passwordA = + DnsRecord(type: 'A', name: 'password', content: ip4); + final DnsRecord socialA = + DnsRecord(type: 'A', name: 'social', content: ip4); + final DnsRecord vpn = DnsRecord(type: 'A', name: 'vpn', content: ip4); + + final DnsRecord txt1 = DnsRecord( + type: 'TXT', + name: '_dmarc', + content: '"v=DMARC1; p=none"', + ttl: 18000, + ); + + final DnsRecord txt2 = DnsRecord( + type: 'TXT', + name: '', + content: '"v=spf1 a mx ip4:$ip4 -all"', + ttl: 18000, + ); + + return [ + domainA, + apiA, + cloudA, + gitA, + meetA, + passwordA, + socialA, + mx, + txt1, + txt2, + vpn + ]; + } + + @override + List getDesiredDnsRecords( + final String? domainName, + final String? ip4, + final String? dkimPublicKey, + ) { + if (domainName == null || ip4 == null) { + return []; + } + return [ + DesiredDnsRecord( + name: '', + content: ip4, + description: 'record.root', + ), + DesiredDnsRecord( + name: 'api.$domainName', + content: ip4, + description: 'record.api', + ), + DesiredDnsRecord( + name: 'cloud.$domainName', + content: ip4, + description: 'record.cloud', + ), + DesiredDnsRecord( + name: 'git.$domainName', + content: ip4, + description: 'record.git', + ), + DesiredDnsRecord( + name: 'meet.$domainName', + content: ip4, + description: 'record.meet', + ), + DesiredDnsRecord( + name: 'social.$domainName', + content: ip4, + description: 'record.social', + ), + DesiredDnsRecord( + name: 'password.$domainName', + content: ip4, + description: 'record.password', + ), + DesiredDnsRecord( + name: 'vpn.$domainName', + content: ip4, + description: 'record.vpn', + ), + DesiredDnsRecord( + name: '', + content: '10 $domainName.', + description: 'record.mx', + type: 'MX', + category: DnsRecordsCategory.email, + ), + DesiredDnsRecord( + name: '_dmarc.$domainName', + content: '"v=DMARC1; p=none"', + description: 'record.dmarc', + type: 'TXT', + category: DnsRecordsCategory.email, + ), + DesiredDnsRecord( + name: '', + content: '"v=spf1 a mx ip4:$ip4 -all"', + description: 'record.spf', + type: 'TXT', + category: DnsRecordsCategory.email, + ), + if (dkimPublicKey != null) + DesiredDnsRecord( + name: 'selector._domainkey.$domainName', + content: '"$dkimPublicKey"', + description: 'record.dkim', + type: 'TXT', + category: DnsRecordsCategory.email, + ), + ]; + } +} diff --git a/lib/logic/providers/dns_providers/digital_ocean_dns.dart b/lib/logic/providers/dns_providers/digital_ocean_dns.dart new file mode 100644 index 00000000..4fac4b65 --- /dev/null +++ b/lib/logic/providers/dns_providers/digital_ocean_dns.dart @@ -0,0 +1,372 @@ +import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/desired_dns_record.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/digital_ocean_dns/digital_ocean_dns_api.dart'; +import 'package:selfprivacy/logic/models/hive/server_domain.dart'; +import 'package:selfprivacy/logic/models/json/digital_ocean_dns_info.dart'; +import 'package:selfprivacy/logic/models/json/dns_records.dart'; +import 'package:selfprivacy/logic/providers/dns_providers/dns_provider.dart'; + +class ApiAdapter { + ApiAdapter({final bool isWithToken = true}) + : _api = DigitalOceanDnsApi( + isWithToken: isWithToken, + ); + + DigitalOceanDnsApi api({final bool getInitialized = true}) => getInitialized + ? _api + : DigitalOceanDnsApi( + isWithToken: false, + ); + + final DigitalOceanDnsApi _api; +} + +class DigitalOceanDnsProvider extends DnsProvider { + DigitalOceanDnsProvider() : _adapter = ApiAdapter(); + DigitalOceanDnsProvider.load( + final bool isAuthotized, + ) : _adapter = ApiAdapter( + isWithToken: isAuthotized, + ); + + ApiAdapter _adapter; + + @override + DnsProviderType get type => DnsProviderType.digitalOcean; + + @override + Future> tryInitApiByToken(final String token) async { + final api = _adapter.api(getInitialized: false); + final result = await api.isApiTokenValid(token); + if (!result.data || !result.success) { + return result; + } + + _adapter = ApiAdapter(isWithToken: true); + return result; + } + + @override + Future>> domainList() async { + List domains = []; + final result = await _adapter.api().getDomains(); + if (result.data.isEmpty || !result.success) { + return GenericResult( + success: result.success, + data: domains, + code: result.code, + message: result.message, + ); + } + + domains = result.data + .map( + (final el) => el.name, + ) + .toList(); + + return GenericResult( + success: true, + data: domains, + ); + } + + @override + Future> createDomainRecords({ + required final ServerDomain domain, + final String? ip4, + }) async => + _adapter.api().createMultipleDnsRecords( + domainName: domain.domainName, + records: getProjectDnsRecords( + domain.domainName, + ip4, + ) + .map( + (final e) => DigitalOceanDnsRecord( + name: e.name ?? '', + id: null, + data: e.content ?? '', + ttl: e.ttl, + type: e.type, + priority: e.priority, + ), + ) + .toList(), + ); + + @override + Future> removeDomainRecords({ + required final ServerDomain domain, + final String? ip4, + }) async { + final result = await _adapter.api().getDnsRecords(domain.domainName); + if (result.data.isEmpty || !result.success) { + return GenericResult( + success: result.success, + data: null, + code: result.code, + message: result.message, + ); + } + + const ignoreType = 'SOA'; + final List filteredRecords = []; + for (final record in result.data) { + if (record.type != ignoreType) { + filteredRecords.add(record); + } + } + + return _adapter.api().removeSimilarRecords( + domainName: domain.domainName, + records: filteredRecords, + ); + } + + @override + Future>> getDnsRecords({ + required final ServerDomain domain, + }) async { + final List records = []; + final result = await _adapter.api().getDnsRecords(domain.domainName); + if (result.data.isEmpty || !result.success) { + return GenericResult( + success: result.success, + data: records, + code: result.code, + message: result.message, + ); + } + + for (final rawRecord in result.data) { + records.add( + DnsRecord( + name: rawRecord.name, + type: rawRecord.type, + content: rawRecord.data, + ttl: rawRecord.ttl, + proxied: false, + ), + ); + } + + return GenericResult(data: records, success: true); + } + + @override + Future> setDnsRecord( + final DnsRecord record, + final ServerDomain domain, + ) async => + _adapter.api().createMultipleDnsRecords( + domainName: domain.domainName, + records: [ + DigitalOceanDnsRecord( + data: record.content ?? '', + id: null, + name: record.name ?? '', + ttl: record.ttl, + type: record.type, + priority: record.priority, + ), + ], + ); + + @override + Future>> validateDnsRecords( + final ServerDomain domain, + final String ip4, + final String dkimPublicKey, + ) async { + final GenericResult> records = + await getDnsRecords(domain: domain); + final List foundRecords = []; + try { + final List desiredRecords = + getDesiredDnsRecords(domain.domainName, ip4, dkimPublicKey); + for (final DesiredDnsRecord record in desiredRecords) { + if (record.description == 'record.dkim') { + final DnsRecord foundRecord = records.data.firstWhere( + (final r) => (r.name == record.name) && r.type == record.type, + orElse: () => DnsRecord( + name: record.name, + type: record.type, + content: '', + ttl: 800, + proxied: false, + ), + ); + // remove all spaces and tabulators from + // the foundRecord.content and the record.content + // to compare them + final String? foundContent = + foundRecord.content?.replaceAll(RegExp(r'\s+'), ''); + final String content = record.content.replaceAll(RegExp(r'\s+'), ''); + if (foundContent == content) { + foundRecords.add(record.copyWith(isSatisfied: true)); + } else { + foundRecords.add(record.copyWith(isSatisfied: false)); + } + } else { + if (records.data.any( + (final r) => + (r.name == record.name) && + r.type == record.type && + r.content == record.content, + )) { + foundRecords.add(record.copyWith(isSatisfied: true)); + } else { + foundRecords.add(record.copyWith(isSatisfied: false)); + } + } + } + } catch (e) { + print(e); + return GenericResult( + data: [], + success: false, + message: e.toString(), + ); + } + return GenericResult( + data: foundRecords, + success: true, + ); + } + + List getProjectDnsRecords( + final String? domainName, + final String? ip4, + ) { + final DnsRecord domainA = DnsRecord(type: 'A', name: '@', content: ip4); + + final DnsRecord mx = DnsRecord(type: 'MX', name: '@', content: '@'); + final DnsRecord apiA = DnsRecord(type: 'A', name: 'api', content: ip4); + final DnsRecord cloudA = DnsRecord(type: 'A', name: 'cloud', content: ip4); + final DnsRecord gitA = DnsRecord(type: 'A', name: 'git', content: ip4); + final DnsRecord meetA = DnsRecord(type: 'A', name: 'meet', content: ip4); + final DnsRecord passwordA = + DnsRecord(type: 'A', name: 'password', content: ip4); + final DnsRecord socialA = + DnsRecord(type: 'A', name: 'social', content: ip4); + final DnsRecord vpn = DnsRecord(type: 'A', name: 'vpn', content: ip4); + + final DnsRecord txt1 = DnsRecord( + type: 'TXT', + name: '_dmarc', + content: 'v=DMARC1; p=none', + ttl: 18000, + ); + + final DnsRecord txt2 = DnsRecord( + type: 'TXT', + name: '@', + content: 'v=spf1 a mx ip4:$ip4 -all', + ttl: 18000, + ); + + return [ + domainA, + apiA, + cloudA, + gitA, + meetA, + passwordA, + socialA, + mx, + txt1, + txt2, + vpn + ]; + } + + @override + List getDesiredDnsRecords( + final String? domainName, + final String? ip4, + final String? dkimPublicKey, + ) { + if (domainName == null || ip4 == null) { + return []; + } + return [ + DesiredDnsRecord( + name: '@', + content: ip4, + description: 'record.root', + displayName: domainName, + ), + DesiredDnsRecord( + name: 'api', + content: ip4, + description: 'record.api', + displayName: 'api.$domainName', + ), + DesiredDnsRecord( + name: 'cloud', + content: ip4, + description: 'record.cloud', + displayName: 'cloud.$domainName', + ), + DesiredDnsRecord( + name: 'git', + content: ip4, + description: 'record.git', + displayName: 'git.$domainName', + ), + DesiredDnsRecord( + name: 'meet', + content: ip4, + description: 'record.meet', + displayName: 'meet.$domainName', + ), + DesiredDnsRecord( + name: 'social', + content: ip4, + description: 'record.social', + displayName: 'social.$domainName', + ), + DesiredDnsRecord( + name: 'password', + content: ip4, + description: 'record.password', + displayName: 'password.$domainName', + ), + DesiredDnsRecord( + name: 'vpn', + content: ip4, + description: 'record.vpn', + displayName: 'vpn.$domainName', + ), + const DesiredDnsRecord( + name: '@', + content: '@', + description: 'record.mx', + type: 'MX', + category: DnsRecordsCategory.email, + ), + const DesiredDnsRecord( + name: '_dmarc', + content: 'v=DMARC1; p=none', + description: 'record.dmarc', + type: 'TXT', + category: DnsRecordsCategory.email, + ), + DesiredDnsRecord( + name: '@', + content: 'v=spf1 a mx ip4:$ip4 -all', + description: 'record.spf', + type: 'TXT', + category: DnsRecordsCategory.email, + ), + if (dkimPublicKey != null) + DesiredDnsRecord( + name: 'selector._domainkey', + content: dkimPublicKey, + description: 'record.dkim', + type: 'TXT', + category: DnsRecordsCategory.email, + ), + ]; + } +} diff --git a/lib/logic/providers/dns_providers/dns_provider.dart b/lib/logic/providers/dns_providers/dns_provider.dart new file mode 100644 index 00000000..647e84ea --- /dev/null +++ b/lib/logic/providers/dns_providers/dns_provider.dart @@ -0,0 +1,76 @@ +import 'package:selfprivacy/logic/api_maps/generic_result.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/desired_dns_record.dart'; +import 'package:selfprivacy/logic/models/hive/server_domain.dart'; +import 'package:selfprivacy/logic/models/json/dns_records.dart'; +export 'package:selfprivacy/logic/api_maps/generic_result.dart'; + +abstract class DnsProvider { + /// Returns an assigned enum value, respectively to which + /// provider implements [DnsProvider] interface. + DnsProviderType get type; + + /// Tries to access an account linked to the provided token. + /// + /// To generate a token for your account follow instructions of your + /// DNS provider respectfully. + /// + /// If success, saves it for future usage. + Future> tryInitApiByToken(final String token); + + /// Returns list of all available domain entries assigned to the account. + Future>> domainList(); + + /// Tries to create all main domain records needed + /// for SelfPrivacy to launch on requested domain by ip4. + /// + /// Doesn't check for duplication, cleaning has + /// to be done beforehand by [removeDomainRecords] + Future> createDomainRecords({ + required final ServerDomain domain, + final String? ip4, + }); + + /// Tries to remove all domain records of requested domain by ip4. + /// + /// Will remove all entries, including the ones + /// that weren't created by SelfPrivacy. + Future> removeDomainRecords({ + required final ServerDomain domain, + final String? ip4, + }); + + /// Returns list of all [DnsRecord] entries assigned to requested domain. + Future>> getDnsRecords({ + required final ServerDomain domain, + }); + + /// Tries to create or update a domain record needed + /// on requested domain. + /// + /// Doesn't check for duplication, cleaning has + /// to be done beforehand by [removeDomainRecords] + Future> setDnsRecord( + final DnsRecord record, + final ServerDomain domain, + ); + + /// Tries to check whether all known DNS records on the domain by ip4 + /// match expectations of SelfPrivacy in order to launch. + /// + /// Will return list of [DesiredDnsRecord] objects, which represent + /// only those records which have successfully passed validation. + Future>> validateDnsRecords( + final ServerDomain domain, + final String ip4, + final String dkimPublicKey, + ); + + /// Will return list of [DesiredDnsRecord] objects, which represent + /// samples of perfect DNS records we need to know about in order to launch + /// SelfPrivacy application correctly. + List getDesiredDnsRecords( + final String? domainName, + final String? ip4, + final String? dkimPublicKey, + ); +} diff --git a/lib/logic/providers/dns_providers/dns_provider_factory.dart b/lib/logic/providers/dns_providers/dns_provider_factory.dart new file mode 100644 index 00000000..e02928b6 --- /dev/null +++ b/lib/logic/providers/dns_providers/dns_provider_factory.dart @@ -0,0 +1,40 @@ +import 'package:selfprivacy/logic/models/hive/server_domain.dart'; +import 'package:selfprivacy/logic/providers/dns_providers/cloudflare.dart'; +import 'package:selfprivacy/logic/providers/dns_providers/desec.dart'; +import 'package:selfprivacy/logic/providers/dns_providers/digital_ocean_dns.dart'; +import 'package:selfprivacy/logic/providers/dns_providers/dns_provider.dart'; +import 'package:selfprivacy/logic/providers/provider_settings.dart'; + +class UnknownProviderException implements Exception { + UnknownProviderException(this.message); + final String message; +} + +class DnsProviderFactory { + static DnsProvider createDnsProviderInterface( + final DnsProviderSettings settings, + ) { + switch (settings.provider) { + case DnsProviderType.cloudflare: + return settings.isAuthorized + ? CloudflareDnsProvider.load( + settings.isAuthorized, + ) + : CloudflareDnsProvider(); + case DnsProviderType.digitalOcean: + return settings.isAuthorized + ? DigitalOceanDnsProvider.load( + settings.isAuthorized, + ) + : DigitalOceanDnsProvider(); + case DnsProviderType.desec: + return settings.isAuthorized + ? DesecDnsProvider.load( + settings.isAuthorized, + ) + : DesecDnsProvider(); + case DnsProviderType.unknown: + throw UnknownProviderException('Unknown server provider'); + } + } +} diff --git a/lib/logic/providers/provider_settings.dart b/lib/logic/providers/provider_settings.dart new file mode 100644 index 00000000..835697cd --- /dev/null +++ b/lib/logic/providers/provider_settings.dart @@ -0,0 +1,24 @@ +import 'package:selfprivacy/logic/models/hive/server_details.dart'; +import 'package:selfprivacy/logic/models/hive/server_domain.dart'; + +class ServerProviderSettings { + ServerProviderSettings({ + required this.provider, + this.isAuthorized = false, + this.location, + }); + + final bool isAuthorized; + final ServerProviderType provider; + final String? location; +} + +class DnsProviderSettings { + DnsProviderSettings({ + required this.provider, + this.isAuthorized = false, + }); + + final bool isAuthorized; + final DnsProviderType provider; +} diff --git a/lib/logic/providers/providers_controller.dart b/lib/logic/providers/providers_controller.dart new file mode 100644 index 00000000..dab74221 --- /dev/null +++ b/lib/logic/providers/providers_controller.dart @@ -0,0 +1,34 @@ +import 'package:selfprivacy/logic/providers/dns_providers/dns_provider.dart'; +import 'package:selfprivacy/logic/providers/dns_providers/dns_provider_factory.dart'; +import 'package:selfprivacy/logic/providers/provider_settings.dart'; +import 'package:selfprivacy/logic/providers/server_providers/server_provider.dart'; +import 'package:selfprivacy/logic/providers/server_providers/server_provider_factory.dart'; + +class ProvidersController { + static ServerProvider? get currentServerProvider => _serverProvider; + static DnsProvider? get currentDnsProvider => _dnsProvider; + + static void initServerProvider( + final ServerProviderSettings settings, + ) { + _serverProvider = ServerProviderFactory.createServerProviderInterface( + settings, + ); + } + + static void initDnsProvider( + final DnsProviderSettings settings, + ) { + _dnsProvider = DnsProviderFactory.createDnsProviderInterface( + settings, + ); + } + + static void clearProviders() { + _serverProvider = null; + _dnsProvider = null; + } + + static ServerProvider? _serverProvider; + static DnsProvider? _dnsProvider; +} diff --git a/lib/logic/providers/server_providers/digital_ocean.dart b/lib/logic/providers/server_providers/digital_ocean.dart new file mode 100644 index 00000000..6e699723 --- /dev/null +++ b/lib/logic/providers/server_providers/digital_ocean.dart @@ -0,0 +1,910 @@ +import 'dart:convert'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean_api.dart'; +import 'package:selfprivacy/logic/models/callback_dialogue_branching.dart'; +import 'package:selfprivacy/logic/models/disk_size.dart'; +import 'package:selfprivacy/logic/models/hive/server_details.dart'; +import 'package:selfprivacy/logic/models/json/digital_ocean_server_info.dart'; +import 'package:selfprivacy/logic/models/metrics.dart'; +import 'package:selfprivacy/logic/models/price.dart'; +import 'package:selfprivacy/logic/models/server_basic_info.dart'; +import 'package:selfprivacy/logic/models/server_metadata.dart'; +import 'package:selfprivacy/logic/models/server_provider_location.dart'; +import 'package:selfprivacy/logic/models/server_type.dart'; +import 'package:selfprivacy/logic/providers/server_providers/server_provider.dart'; +import 'package:selfprivacy/utils/extensions/string_extensions.dart'; +import 'package:selfprivacy/utils/network_utils.dart'; +import 'package:selfprivacy/utils/password_generator.dart'; + +class ApiAdapter { + ApiAdapter({final String? region, final bool isWithToken = true}) + : _api = DigitalOceanApi( + region: region, + isWithToken: isWithToken, + ); + + DigitalOceanApi api({final bool getInitialized = true}) => getInitialized + ? _api + : DigitalOceanApi( + region: _api.region, + isWithToken: false, + ); + + final DigitalOceanApi _api; +} + +class DigitalOceanServerProvider extends ServerProvider { + DigitalOceanServerProvider() : _adapter = ApiAdapter(); + DigitalOceanServerProvider.load( + final String? location, + final bool isAuthotized, + ) : _adapter = ApiAdapter( + isWithToken: isAuthotized, + region: location, + ); + + ApiAdapter _adapter; + final Currency currency = Currency.fromType(CurrencyType.usd); + + @override + ServerProviderType get type => ServerProviderType.digitalOcean; + + @override + Future>> getServers() async { + List servers = []; + final result = await _adapter.api().getServers(); + if (result.data.isEmpty || !result.success) { + return GenericResult( + success: result.success, + data: servers, + code: result.code, + message: result.message, + ); + } + + final List rawServers = result.data; + servers = rawServers.map( + (final server) { + String ipv4 = '0.0.0.0'; + if (server['networks']['v4'].isNotEmpty) { + for (final v4 in server['networks']['v4']) { + if (v4['type'].toString() == 'public') { + ipv4 = v4['ip_address'].toString(); + } + } + } + + return ServerBasicInfo( + id: server['id'], + reverseDns: server['name'], + created: DateTime.now(), + ip: ipv4, + name: server['name'], + ); + }, + ).toList(); + + return GenericResult(success: true, data: servers); + } + + @override + Future> getServerType(final int serverId) async { + ServerType? serverType; + dynamic server; + final result = await _adapter.api().getServers(); + if (result.data.isEmpty || !result.success) { + return GenericResult( + success: result.success, + data: serverType, + code: result.code, + message: result.message, + ); + } + + final List rawServers = result.data; + for (final rawServer in rawServers) { + if (rawServer['networks']['v4'].isNotEmpty) { + for (final v4 in rawServer['networks']['v4']) { + if (v4['type'].toString() == 'public') { + server = rawServer; + } + } + } + } + + if (server == null) { + const String msg = 'getServerType: no server!'; + print(msg); + return GenericResult( + success: false, + data: serverType, + message: msg, + ); + } + + final rawLocationsResult = await getAvailableLocations(); + if (rawLocationsResult.data.isEmpty || !rawLocationsResult.success) { + return GenericResult( + success: rawLocationsResult.success, + data: serverType, + code: rawLocationsResult.code, + message: rawLocationsResult.message, + ); + } + + ServerProviderLocation? location; + for (final rawLocation in rawLocationsResult.data) { + if (rawLocation.identifier == server['region']['slug']) { + location = rawLocation; + } + } + + if (location == null) { + const String msg = 'getServerType: no location!'; + print(msg); + return GenericResult( + success: false, + data: serverType, + message: msg, + ); + } + + ServerType? type; + final rawSize = DigitalOceanServerType.fromJson(server['size']); + for (final rawRegion in rawSize.regions) { + if (rawRegion == server['region']['slug']) { + type = ServerType( + title: rawSize.description, + identifier: rawSize.slug, + ram: rawSize.memory / 1024, + cores: rawSize.vcpus, + disk: DiskSize(byte: rawSize.disk * 1024 * 1024 * 1024), + price: Price( + value: rawSize.priceMonthly, + currency: currency, + ), + location: location, + ); + } + } + + return GenericResult( + success: true, + data: type, + ); + } + + @override + Future> launchInstallation( + final LaunchInstallationData installationData, + ) async { + ServerHostingDetails? serverDetails; + final serverApiToken = StringGenerators.apiToken(); + final hostname = getHostnameFromDomain( + installationData.serverDomain.domainName, + ); + final pastServers = await getServers(); + for (final pastServer in pastServers.data) { + if (pastServer.name == hostname) { + return GenericResult( + data: CallbackDialogueBranching( + choices: [ + CallbackDialogueChoice( + title: 'basis.cancel'.tr(), + callback: () async => installationData.errorCallback(), + ), + CallbackDialogueChoice( + title: 'modals.yes'.tr(), + callback: () async { + final deleting = await deleteServer(hostname); + if (deleting.success) { + await Future.delayed(const Duration(seconds: 20)); + return launchInstallation(installationData); + } + + return deleting; + }, + ), + ], + description: 'modals.destroy_server'.tr(), + title: 'modals.already_exists'.tr(), + ), + success: false, + message: 'Droplet "$hostname" already exists.', + ); + } + } + + final serverResult = await _adapter.api().createServer( + dnsApiToken: installationData.dnsApiToken, + rootUser: installationData.rootUser, + domainName: installationData.serverDomain.domainName, + serverType: installationData.serverTypeId, + dnsProviderType: installationData.dnsProviderType.toInfectName(), + hostName: hostname, + base64Password: base64.encode( + utf8.encode(installationData.rootUser.password ?? 'PASS'), + ), + databasePassword: StringGenerators.dbPassword(), + serverApiToken: serverApiToken, + ); + + if (!serverResult.success || serverResult.data == null) { + GenericResult( + data: CallbackDialogueBranching( + choices: [ + CallbackDialogueChoice( + title: 'basis.cancel'.tr(), + callback: () async => await installationData.errorCallback(), + ), + CallbackDialogueChoice( + title: 'modals.try_again'.tr(), + callback: () async => launchInstallation(installationData), + ), + ], + description: serverResult.message ?? 'recovering.generic_error'.tr(), + title: 'modals.unexpected_error'.tr(), + ), + success: false, + message: serverResult.message, + code: serverResult.code, + ); + } + + try { + final int dropletId = serverResult.data!; + final newVolume = + (await createVolume(installationData.storageSize.gibibyte.toInt())) + .data; + final bool attachedVolume = (await _adapter.api().attachVolume( + newVolume!.name, + dropletId, + )) + .data; + + String? ipv4; + int attempts = 0; + while (attempts < 10 && ipv4 == null) { + await Future.delayed(const Duration(seconds: 20)); + final servers = await getServers(); + for (final server in servers.data) { + if (server.name == hostname && server.ip != '0.0.0.0') { + ipv4 = server.ip; + break; + } + } + ++attempts; + } + + if (attachedVolume && ipv4 != null) { + serverDetails = ServerHostingDetails( + id: dropletId, + ip4: ipv4, + createTime: DateTime.now(), + volume: newVolume, + apiToken: serverApiToken, + provider: ServerProviderType.digitalOcean, + ); + } + } catch (e) { + return GenericResult( + success: false, + data: CallbackDialogueBranching( + choices: [ + CallbackDialogueChoice( + title: 'basis.cancel'.tr(), + callback: null, + ), + CallbackDialogueChoice( + title: 'modals.try_again'.tr(), + callback: () async { + await Future.delayed(const Duration(seconds: 5)); + final deletion = await deleteServer(hostname); + return deletion.success + ? await launchInstallation(installationData) + : deletion; + }, + ), + ], + description: 'modals.try_again'.tr(), + title: 'modals.server_deletion_error'.tr(), + ), + message: e.toString(), + ); + } + + await installationData.successCallback(serverDetails!); + return GenericResult(success: true, data: null); + } + + @override + Future> deleteServer( + final String hostname, + ) async { + final String deletionName = getHostnameFromDomain(hostname); + final serversResult = await getServers(); + try { + final servers = serversResult.data; + ServerBasicInfo? foundServer; + for (final server in servers) { + if (server.name == deletionName) { + foundServer = server; + break; + } + } + + final volumes = await getVolumes(); + final ServerVolume volumeToRemove; + volumeToRemove = volumes.data.firstWhere( + (final el) => el.serverId == foundServer!.id, + ); + + await _adapter.api().detachVolume( + volumeToRemove.name, + volumeToRemove.serverId!, + ); + + await Future.delayed(const Duration(seconds: 10)); + final List laterFutures = []; + laterFutures.add(_adapter.api().deleteVolume(volumeToRemove.uuid!)); + laterFutures.add(_adapter.api().deleteServer(foundServer!.id)); + + await Future.wait(laterFutures); + } catch (e) { + print(e); + return GenericResult( + success: false, + data: CallbackDialogueBranching( + choices: [ + CallbackDialogueChoice( + title: 'basis.cancel'.tr(), + callback: null, + ), + CallbackDialogueChoice( + title: 'modals.try_again'.tr(), + callback: () async { + await Future.delayed(const Duration(seconds: 5)); + return deleteServer(hostname); + }, + ), + ], + description: 'modals.try_again'.tr(), + title: 'modals.server_deletion_error'.tr(), + ), + message: e.toString(), + ); + } + + return GenericResult( + success: true, + data: null, + ); + } + + @override + Future> tryInitApiByToken(final String token) async { + final api = _adapter.api(getInitialized: false); + final result = await api.isApiTokenValid(token); + if (!result.data || !result.success) { + return result; + } + + _adapter = ApiAdapter(region: api.region, isWithToken: true); + return result; + } + + @override + Future> trySetServerLocation( + final String location, + ) async { + final bool apiInitialized = _adapter.api().isWithToken; + if (!apiInitialized) { + return GenericResult( + success: true, + data: false, + message: 'Not authorized!', + ); + } + + _adapter = ApiAdapter( + isWithToken: true, + region: location, + ); + return success; + } + + @override + Future>> + getAvailableLocations() async { + final List locations = []; + final result = await _adapter.api().getAvailableLocations(); + if (result.data.isEmpty || !result.success) { + return GenericResult( + success: result.success, + data: locations, + code: result.code, + message: result.message, + ); + } + + final List rawLocations = result.data; + for (final rawLocation in rawLocations) { + ServerProviderLocation? location; + try { + location = ServerProviderLocation( + title: rawLocation.slug, + description: rawLocation.name, + flag: rawLocation.flag, + identifier: rawLocation.slug, + ); + } catch (e) { + continue; + } + locations.add(location); + } + + return GenericResult(success: true, data: locations); + } + + @override + Future>> getServerTypes({ + required final ServerProviderLocation location, + }) async { + final List types = []; + final result = await _adapter.api().getAvailableServerTypes(); + if (result.data.isEmpty || !result.success) { + return GenericResult( + success: result.success, + data: types, + code: result.code, + message: result.message, + ); + } + + final List rawSizes = result.data; + for (final rawSize in rawSizes) { + for (final rawRegion in rawSize.regions) { + if (rawRegion == location.identifier && rawSize.memory > 1024) { + types.add( + ServerType( + title: rawSize.description, + identifier: rawSize.slug, + ram: rawSize.memory / 1024, + cores: rawSize.vcpus, + disk: DiskSize(byte: rawSize.disk * 1024 * 1024 * 1024), + price: Price( + value: rawSize.priceMonthly, + currency: currency, + ), + location: location, + ), + ); + } + } + } + + return GenericResult(success: true, data: types); + } + + @override + Future> powerOn(final int serverId) async { + DateTime? timestamp; + final result = await _adapter.api().powerOn(serverId); + if (!result.success) { + return GenericResult( + success: false, + data: timestamp, + code: result.code, + message: result.message, + ); + } + + timestamp = DateTime.now(); + + return GenericResult( + success: true, + data: timestamp, + ); + } + + @override + Future> restart(final int serverId) async { + DateTime? timestamp; + final result = await _adapter.api().restart(serverId); + if (!result.success) { + return GenericResult( + success: false, + data: timestamp, + code: result.code, + message: result.message, + ); + } + + timestamp = DateTime.now(); + + return GenericResult( + success: true, + data: timestamp, + ); + } + + @override + Future> getAdditionalPricing() async => + GenericResult( + success: true, + data: AdditionalPricing( + perVolumeGb: Price( + /// Hardcoded in their documentation and there is no pricing API + value: 0.10, + currency: currency, + ), + perPublicIpv4: Price( + value: 0, + currency: currency, + ), + ), + ); + + @override + Future>> getVolumes({ + final String? status, + }) async { + final List volumes = []; + + final result = await _adapter.api().getVolumes(); + + if (!result.success || result.data.isEmpty) { + return GenericResult( + data: [], + success: false, + code: result.code, + message: result.message, + ); + } + + try { + int id = 0; + for (final rawVolume in result.data) { + final String volumeName = rawVolume.name; + final volume = ServerVolume( + id: id++, + name: volumeName, + sizeByte: rawVolume.sizeGigabytes * 1024 * 1024 * 1024, + serverId: + (rawVolume.dropletIds != null && rawVolume.dropletIds!.isNotEmpty) + ? rawVolume.dropletIds![0] + : null, + linuxDevice: 'scsi-0DO_Volume_$volumeName', + uuid: rawVolume.id, + ); + volumes.add(volume); + } + } catch (e) { + print(e); + return GenericResult( + data: [], + success: false, + message: e.toString(), + ); + } + + return GenericResult( + data: volumes, + success: true, + ); + } + + @override + Future> createVolume(final int gb) async { + ServerVolume? volume; + + final result = await _adapter.api().createVolume(gb); + + if (!result.success || result.data == null) { + return GenericResult( + data: null, + success: false, + code: result.code, + message: result.message, + ); + } + + final getVolumesResult = await _adapter.api().getVolumes(); + + if (!getVolumesResult.success || getVolumesResult.data.isEmpty) { + return GenericResult( + data: null, + success: false, + code: result.code, + message: result.message, + ); + } + + final String volumeName = result.data!.name; + volume = ServerVolume( + id: getVolumesResult.data.length, + name: volumeName, + sizeByte: result.data!.sizeGigabytes, + serverId: null, + linuxDevice: '/dev/disk/by-id/scsi-0DO_Volume_$volumeName', + uuid: result.data!.id, + ); + + return GenericResult( + data: volume, + success: true, + ); + } + + Future> getVolume( + final String volumeUuid, + ) async { + ServerVolume? requestedVolume; + + final result = await getVolumes(); + + if (!result.success || result.data.isEmpty) { + return GenericResult( + data: null, + success: false, + code: result.code, + message: result.message, + ); + } + + for (final volume in result.data) { + if (volume.uuid == volumeUuid) { + requestedVolume = volume; + } + } + + return GenericResult( + data: requestedVolume, + success: true, + ); + } + + @override + Future> attachVolume( + final ServerVolume volume, + final int serverId, + ) async => + _adapter.api().attachVolume( + volume.name, + serverId, + ); + + @override + Future> detachVolume( + final ServerVolume volume, + ) async => + _adapter.api().detachVolume( + volume.name, + volume.serverId!, + ); + + @override + Future> deleteVolume( + final ServerVolume volume, + ) async => + _adapter.api().deleteVolume( + volume.uuid!, + ); + + @override + Future> resizeVolume( + final ServerVolume volume, + final DiskSize size, + ) async => + _adapter.api().resizeVolume( + volume.uuid!, + size.gibibyte.toInt(), + ); + + @override + Future>> getMetadata( + final int serverId, + ) async { + List metadata = []; + final result = await _adapter.api().getServers(); + if (result.data.isEmpty || !result.success) { + return GenericResult( + success: false, + data: metadata, + code: result.code, + message: result.message, + ); + } + final resultVolumes = await _adapter.api().getVolumes(); + if (resultVolumes.data.isEmpty || !resultVolumes.success) { + return GenericResult( + success: false, + data: metadata, + code: resultVolumes.code, + message: resultVolumes.message, + ); + } + final resultPricePerGb = await getAdditionalPricing(); + if (resultPricePerGb.data == null || !resultPricePerGb.success) { + return GenericResult( + success: false, + data: metadata, + code: resultPricePerGb.code, + message: resultPricePerGb.message, + ); + } + + final List servers = result.data; + final List volumes = resultVolumes.data; + try { + final Price pricePerGb = resultPricePerGb.data!.perVolumeGb; + final droplet = servers.firstWhere( + (final server) => server['id'] == serverId, + ); + + final volume = volumes.firstWhere( + (final volume) => droplet['volume_ids'].contains(volume.id), + ); + + metadata = [ + ServerMetadataEntity( + type: MetadataType.id, + trId: 'server.server_id', + value: droplet['id'].toString(), + ), + ServerMetadataEntity( + type: MetadataType.status, + trId: 'server.status', + value: droplet['status'].toString().capitalize(), + ), + ServerMetadataEntity( + type: MetadataType.cpu, + trId: 'server.cpu', + value: droplet['vcpus'].toString(), + ), + ServerMetadataEntity( + type: MetadataType.ram, + trId: 'server.ram', + value: "${droplet['memory'].toString()} MB", + ), + ServerMetadataEntity( + type: MetadataType.cost, + trId: 'server.monthly_cost', + value: + '${droplet['size']['price_monthly']} + ${(volume.sizeGigabytes * pricePerGb.value).toStringAsFixed(2)} ${currency.shortcode}', + ), + ServerMetadataEntity( + type: MetadataType.location, + trId: 'server.location', + value: '${droplet['region']['name']}', + ), + ServerMetadataEntity( + type: MetadataType.other, + trId: 'server.server_provider', + value: type.displayName, + ), + ]; + } catch (e) { + return GenericResult( + success: false, + data: [], + message: e.toString(), + ); + } + + return GenericResult(success: true, data: metadata); + } + + /// Digital Ocean returns a map of lists of /proc/stat values, + /// so here we are trying to implement average CPU + /// load calculation for each point in time on a given interval. + /// + /// For each point of time: + /// + /// `Average Load = 100 * (1 - (Idle Load / Total Load))` + /// + /// For more info please proceed to read: + /// https://rosettacode.org/wiki/Linux_CPU_utilization + List calculateCpuLoadMetrics(final List rawProcStatMetrics) { + final List cpuLoads = []; + + final int pointsInTime = (rawProcStatMetrics[0]['values'] as List).length; + for (int i = 0; i < pointsInTime; ++i) { + double currentMetricLoad = 0.0; + double? currentMetricIdle; + for (final rawProcStat in rawProcStatMetrics) { + final String rawProcValue = rawProcStat['values'][i][1]; + // Converting MBit into bit + final double procValue = double.parse(rawProcValue) * 1000000; + currentMetricLoad += procValue; + if (currentMetricIdle == null && + rawProcStat['metric']['mode'] == 'idle') { + currentMetricIdle = procValue; + } + } + currentMetricIdle ??= 0.0; + currentMetricLoad = 100.0 * (1 - (currentMetricIdle / currentMetricLoad)); + cpuLoads.add( + TimeSeriesData( + rawProcStatMetrics[0]['values'][i][0], + currentMetricLoad, + ), + ); + } + + return cpuLoads; + } + + @override + Future> getMetrics( + final int serverId, + final DateTime start, + final DateTime end, + ) async { + ServerMetrics? metrics; + + const int step = 15; + final inboundResult = await _adapter.api().getMetricsBandwidth( + serverId, + start, + end, + true, + ); + + if (inboundResult.data.isEmpty || !inboundResult.success) { + return GenericResult( + success: false, + data: null, + code: inboundResult.code, + message: inboundResult.message, + ); + } + + final outboundResult = await _adapter.api().getMetricsBandwidth( + serverId, + start, + end, + false, + ); + + if (outboundResult.data.isEmpty || !outboundResult.success) { + return GenericResult( + success: false, + data: null, + code: outboundResult.code, + message: outboundResult.message, + ); + } + + final cpuResult = await _adapter.api().getMetricsCpu(serverId, start, end); + + if (cpuResult.data.isEmpty || !cpuResult.success) { + return GenericResult( + success: false, + data: null, + code: cpuResult.code, + message: cpuResult.message, + ); + } + + metrics = ServerMetrics( + bandwidthIn: inboundResult.data + .map( + (final el) => TimeSeriesData(el[0], double.parse(el[1]) * 100000), + ) + .toList(), + bandwidthOut: outboundResult.data + .map( + (final el) => TimeSeriesData(el[0], double.parse(el[1]) * 100000), + ) + .toList(), + cpu: calculateCpuLoadMetrics(cpuResult.data), + start: start, + end: end, + stepsInSecond: step, + ); + + return GenericResult(success: true, data: metrics); + } +} diff --git a/lib/logic/providers/server_providers/hetzner.dart b/lib/logic/providers/server_providers/hetzner.dart new file mode 100644 index 00000000..ff8e04c8 --- /dev/null +++ b/lib/logic/providers/server_providers/hetzner.dart @@ -0,0 +1,918 @@ +import 'dart:convert'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/hetzner/hetzner_api.dart'; +import 'package:selfprivacy/logic/models/callback_dialogue_branching.dart'; +import 'package:selfprivacy/logic/models/disk_size.dart'; +import 'package:selfprivacy/logic/models/hive/server_details.dart'; +import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart'; +import 'package:selfprivacy/logic/models/metrics.dart'; +import 'package:selfprivacy/logic/models/price.dart'; +import 'package:selfprivacy/logic/models/server_basic_info.dart'; +import 'package:selfprivacy/logic/models/server_metadata.dart'; +import 'package:selfprivacy/logic/models/server_provider_location.dart'; +import 'package:selfprivacy/logic/models/server_type.dart'; +import 'package:selfprivacy/logic/providers/server_providers/server_provider.dart'; +import 'package:selfprivacy/utils/extensions/string_extensions.dart'; +import 'package:selfprivacy/utils/network_utils.dart'; +import 'package:selfprivacy/utils/password_generator.dart'; + +class ApiAdapter { + ApiAdapter({final String? region, final bool isWithToken = true}) + : _api = HetznerApi( + region: region, + isWithToken: isWithToken, + ); + + HetznerApi api({final bool getInitialized = true}) => getInitialized + ? _api + : HetznerApi( + region: _api.region, + isWithToken: false, + ); + + final HetznerApi _api; +} + +class HetznerServerProvider extends ServerProvider { + HetznerServerProvider() : _adapter = ApiAdapter(); + HetznerServerProvider.load( + final String? location, + final bool isAuthotized, + ) : _adapter = ApiAdapter( + isWithToken: isAuthotized, + region: location, + ); + + ApiAdapter _adapter; + final Currency currency = Currency.fromType(CurrencyType.eur); + int? cachedCoreAmount; + + @override + ServerProviderType get type => ServerProviderType.hetzner; + + @override + Future>> getServers() async { + final List servers = []; + final result = await _adapter.api().getServers(); + if (result.data.isEmpty || !result.success) { + return GenericResult( + success: result.success, + data: servers, + code: result.code, + message: result.message, + ); + } + + final List hetznerServers = result.data; + for (final hetznerServer in hetznerServers) { + if (hetznerServer.publicNet.ipv4 == null) { + continue; + } + + ServerBasicInfo? server; + try { + server = ServerBasicInfo( + id: hetznerServer.id, + name: hetznerServer.name, + ip: hetznerServer.publicNet.ipv4!.ip, + reverseDns: hetznerServer.publicNet.ipv4!.reverseDns, + created: hetznerServer.created, + ); + } catch (e) { + continue; + } + + servers.add(server); + } + + return GenericResult(success: true, data: servers); + } + + @override + Future> getServerType(final int serverId) async { + ServerType? serverType; + HetznerServerInfo? server; + final result = await _adapter.api().getServers(); + if (result.data.isEmpty || !result.success) { + return GenericResult( + success: result.success, + data: serverType, + code: result.code, + message: result.message, + ); + } + + final List hetznerServers = result.data; + for (final hetznerServer in hetznerServers) { + if (hetznerServer.publicNet.ipv4 != null || + hetznerServer.id == serverId) { + server = hetznerServer; + break; + } + } + + if (server == null) { + const String msg = 'getServerType: no server!'; + print(msg); + return GenericResult( + success: false, + data: serverType, + message: msg, + ); + } + + double? priceValue; + for (final price in server.serverType.prices) { + if (price.location == server.location.name) { + priceValue = price.monthly; + } + } + + if (priceValue == null) { + const String msg = 'getServerType: no price!'; + print(msg); + return GenericResult( + success: false, + data: serverType, + message: msg, + ); + } + + return GenericResult( + success: true, + data: ServerType( + title: server.serverType.description, + identifier: server.serverType.name, + ram: server.serverType.memory.toDouble(), + cores: server.serverType.cores, + disk: DiskSize(byte: server.serverType.disk * 1024 * 1024 * 1024), + price: Price( + value: priceValue, + currency: currency, + ), + location: ServerProviderLocation( + title: server.location.city, + description: server.location.description, + flag: server.location.flag, + identifier: server.location.name, + ), + ), + ); + } + + @override + Future> launchInstallation( + final LaunchInstallationData installationData, + ) async { + final volumeResult = await _adapter.api().createVolume( + installationData.storageSize.gibibyte.toInt(), + ); + + if (!volumeResult.success || volumeResult.data == null) { + return GenericResult( + data: CallbackDialogueBranching( + choices: [ + CallbackDialogueChoice( + title: 'basis.cancel'.tr(), + callback: () async => await installationData.errorCallback(), + ), + CallbackDialogueChoice( + title: 'modals.try_again'.tr(), + callback: () async => launchInstallation(installationData), + ), + ], + description: + volumeResult.message ?? 'modals.volume_creation_error'.tr(), + title: 'modals.unexpected_error'.tr(), + ), + success: false, + message: volumeResult.message, + code: volumeResult.code, + ); + } + + final volume = volumeResult.data!; + final serverApiToken = StringGenerators.apiToken(); + final hostname = getHostnameFromDomain( + installationData.serverDomain.domainName, + ); + + final serverResult = await _adapter.api().createServer( + dnsApiToken: installationData.dnsApiToken, + rootUser: installationData.rootUser, + domainName: installationData.serverDomain.domainName, + serverType: installationData.serverTypeId, + dnsProviderType: installationData.dnsProviderType.toInfectName(), + hostName: hostname, + volumeId: volume.id, + base64Password: base64.encode( + utf8.encode(installationData.rootUser.password ?? 'PASS'), + ), + databasePassword: StringGenerators.dbPassword(), + serverApiToken: serverApiToken, + ); + + if (!serverResult.success || serverResult.data == null) { + await _adapter.api().deleteVolume(volume.id); + await Future.delayed(const Duration(seconds: 5)); + if (serverResult.message != null && + serverResult.message == 'uniqueness_error') { + return GenericResult( + data: CallbackDialogueBranching( + choices: [ + CallbackDialogueChoice( + title: 'basis.cancel'.tr(), + callback: () async => installationData.errorCallback(), + ), + CallbackDialogueChoice( + title: 'modals.yes'.tr(), + callback: () async { + final deleting = await deleteServer(hostname); + if (deleting.success) { + return launchInstallation(installationData); + } + + return deleting; + }, + ), + ], + description: 'modals.destroy_server'.tr(), + title: 'modals.already_exists'.tr(), + ), + success: false, + message: volumeResult.message, + code: volumeResult.code, + ); + } else { + return GenericResult( + data: CallbackDialogueBranching( + choices: [ + CallbackDialogueChoice( + title: 'basis.cancel'.tr(), + callback: () async { + final deletion = await deleteServer(hostname); + if (deletion.success) { + installationData.errorCallback(); + } + + return deletion; + }, + ), + CallbackDialogueChoice( + title: 'modals.try_again'.tr(), + callback: () async => launchInstallation(installationData), + ), + ], + description: + volumeResult.message ?? 'recovering.generic_error'.tr(), + title: 'modals.unexpected_error'.tr(), + ), + success: false, + message: volumeResult.message, + code: volumeResult.code, + ); + } + } + + final serverDetails = ServerHostingDetails( + id: serverResult.data!.id, + ip4: serverResult.data!.publicNet.ipv4!.ip, + createTime: DateTime.now(), + volume: ServerVolume( + id: volume.id, + name: volume.name, + sizeByte: volume.size * 1024 * 1024 * 1024, + serverId: volume.serverId, + linuxDevice: volume.linuxDevice, + ), + apiToken: serverApiToken, + provider: ServerProviderType.hetzner, + ); + + cachedCoreAmount = serverResult.data!.serverType.cores; + + final createDnsResult = await _adapter.api().createReverseDns( + serverId: serverDetails.id, + ip4: serverDetails.ip4, + dnsPtr: installationData.serverDomain.domainName, + ); + + if (!createDnsResult.success) { + return GenericResult( + data: CallbackDialogueBranching( + choices: [ + CallbackDialogueChoice( + title: 'basis.cancel'.tr(), + callback: () async { + final deletion = await deleteServer(hostname); + if (deletion.success) { + installationData.errorCallback(); + } + + return deletion; + }, + ), + CallbackDialogueChoice( + title: 'modals.try_again'.tr(), + callback: () async { + await _adapter.api().deleteVolume(volume.id); + await Future.delayed(const Duration(seconds: 5)); + final deletion = await deleteServer(hostname); + if (deletion.success) { + await Future.delayed(const Duration(seconds: 5)); + return launchInstallation(installationData); + } + + return deletion; + }, + ), + ], + description: volumeResult.message ?? 'recovering.generic_error'.tr(), + title: 'modals.unexpected_error'.tr(), + ), + success: false, + message: volumeResult.message, + code: volumeResult.code, + ); + } + + await installationData.successCallback(serverDetails); + return GenericResult(success: true, data: null); + } + + @override + Future> deleteServer( + final String hostname, + ) async { + final serversResult = await _adapter.api().getServers(); + try { + final servers = serversResult.data; + HetznerServerInfo? foundServer; + for (final server in servers) { + if (server.name == hostname) { + foundServer = server; + break; + } + } + + for (final volumeId in foundServer!.volumes) { + await _adapter.api().detachVolume(volumeId); + } + + await Future.delayed(const Duration(seconds: 10)); + final List laterFutures = []; + + for (final volumeId in foundServer.volumes) { + laterFutures.add(_adapter.api().deleteVolume(volumeId)); + } + laterFutures.add(_adapter.api().deleteServer(serverId: foundServer.id)); + + await Future.wait(laterFutures); + } catch (e) { + print(e); + return GenericResult( + success: false, + data: CallbackDialogueBranching( + choices: [ + CallbackDialogueChoice( + title: 'basis.cancel'.tr(), + callback: null, + ), + CallbackDialogueChoice( + title: 'modals.try_again'.tr(), + callback: () async { + await Future.delayed(const Duration(seconds: 5)); + return deleteServer(hostname); + }, + ), + ], + description: 'modals.try_again'.tr(), + title: 'modals.server_deletion_error'.tr(), + ), + message: e.toString(), + ); + } + + return GenericResult( + success: true, + data: null, + ); + } + + @override + Future> tryInitApiByToken(final String token) async { + final api = _adapter.api(getInitialized: false); + final result = await api.isApiTokenValid(token); + if (!result.data || !result.success) { + return result; + } + + _adapter = ApiAdapter(region: api.region, isWithToken: true); + return result; + } + + @override + Future> trySetServerLocation( + final String location, + ) async { + final bool apiInitialized = _adapter.api().isWithToken; + if (!apiInitialized) { + return GenericResult( + success: true, + data: false, + message: 'Not authorized!', + ); + } + + _adapter = ApiAdapter( + isWithToken: true, + region: location, + ); + return success; + } + + @override + Future>> + getAvailableLocations() async { + final List locations = []; + final result = await _adapter.api().getAvailableLocations(); + if (result.data.isEmpty || !result.success) { + return GenericResult( + success: result.success, + data: locations, + code: result.code, + message: result.message, + ); + } + + final List rawLocations = result.data; + for (final rawLocation in rawLocations) { + ServerProviderLocation? location; + try { + location = ServerProviderLocation( + title: rawLocation.city, + description: rawLocation.description, + flag: rawLocation.flag, + identifier: rawLocation.name, + ); + } catch (e) { + continue; + } + locations.add(location); + } + + return GenericResult(success: true, data: locations); + } + + @override + Future>> getServerTypes({ + required final ServerProviderLocation location, + }) async { + final List types = []; + final result = await _adapter.api().getAvailableServerTypes(); + if (result.data.isEmpty || !result.success) { + return GenericResult( + success: result.success, + data: types, + code: result.code, + message: result.message, + ); + } + + final rawTypes = result.data; + for (final rawType in rawTypes) { + for (final rawPrice in rawType.prices) { + if (rawPrice.location == location.identifier) { + types.add( + ServerType( + title: rawType.description, + identifier: rawType.name, + ram: rawType.memory.toDouble(), + cores: rawType.cores, + disk: DiskSize(byte: rawType.disk * 1024 * 1024 * 1024), + price: Price( + value: rawPrice.monthly, + currency: currency, + ), + location: location, + ), + ); + } + } + } + + return GenericResult(success: true, data: types); + } + + @override + Future> powerOn(final int serverId) async { + DateTime? timestamp; + final result = await _adapter.api().powerOn(serverId); + if (!result.success) { + return GenericResult( + success: false, + data: timestamp, + code: result.code, + message: result.message, + ); + } + + timestamp = DateTime.now(); + + return GenericResult( + success: true, + data: timestamp, + ); + } + + @override + Future> restart(final int serverId) async { + DateTime? timestamp; + final result = await _adapter.api().restart(serverId); + if (!result.success) { + return GenericResult( + success: false, + data: timestamp, + code: result.code, + message: result.message, + ); + } + + timestamp = DateTime.now(); + + return GenericResult( + success: true, + data: timestamp, + ); + } + + @override + Future> getAdditionalPricing() async { + final result = await _adapter.api().getPricing(); + + if (!result.success || result.data == null) { + return GenericResult( + data: null, + success: false, + message: result.message, + code: result.code, + ); + } + + return GenericResult( + success: true, + data: AdditionalPricing( + perVolumeGb: Price( + value: result.data!.perVolumeGb, + currency: currency, + ), + perPublicIpv4: Price( + value: result.data!.perPublicIpv4, + currency: currency, + ), + ), + ); + } + + @override + Future>> getVolumes({ + final String? status, + }) async { + final List volumes = []; + + final result = await _adapter.api().getVolumes(); + + if (!result.success) { + return GenericResult( + data: [], + success: false, + message: result.message, + code: result.code, + ); + } + + try { + for (final rawVolume in result.data) { + final int volumeId = rawVolume.id; + final int volumeSize = rawVolume.size * 1024 * 1024 * 1024; + final volumeServer = rawVolume.serverId; + final String volumeName = rawVolume.name; + final volumeDevice = rawVolume.linuxDevice; + final volume = ServerVolume( + id: volumeId, + name: volumeName, + sizeByte: volumeSize, + serverId: volumeServer, + linuxDevice: volumeDevice, + ); + volumes.add(volume); + } + } catch (e) { + return GenericResult( + data: [], + success: false, + message: e.toString(), + ); + } + + return GenericResult( + data: volumes, + success: true, + code: result.code, + message: result.message, + ); + } + + @override + Future> createVolume(final int gb) async { + ServerVolume? volume; + + final result = await _adapter.api().createVolume(gb); + + if (!result.success || result.data == null) { + return GenericResult( + data: null, + success: false, + message: result.message, + code: result.code, + ); + } + + try { + volume = ServerVolume( + id: result.data!.id, + name: result.data!.name, + sizeByte: result.data!.size * 1024 * 1024 * 1024, + serverId: result.data!.serverId, + linuxDevice: result.data!.linuxDevice, + ); + } catch (e) { + print(e); + return GenericResult( + data: null, + success: false, + message: e.toString(), + ); + } + + return GenericResult( + data: volume, + success: true, + code: result.code, + message: result.message, + ); + } + + @override + Future> deleteVolume(final ServerVolume volume) async => + _adapter.api().deleteVolume(volume.id); + + @override + Future> resizeVolume( + final ServerVolume volume, + final DiskSize size, + ) async => + _adapter.api().resizeVolume( + HetznerVolume( + volume.id, + volume.sizeByte, + volume.serverId, + volume.name, + volume.linuxDevice, + ), + size, + ); + + @override + Future> attachVolume( + final ServerVolume volume, + final int serverId, + ) async => + _adapter.api().attachVolume( + HetznerVolume( + volume.id, + volume.sizeByte, + volume.serverId, + volume.name, + volume.linuxDevice, + ), + serverId, + ); + + @override + Future> detachVolume( + final ServerVolume volume, + ) async => + _adapter.api().detachVolume( + volume.id, + ); + + @override + Future>> getMetadata( + final int serverId, + ) async { + List metadata = []; + final resultServers = await _adapter.api().getServers(); + if (resultServers.data.isEmpty || !resultServers.success) { + return GenericResult( + success: false, + data: metadata, + code: resultServers.code, + message: resultServers.message, + ); + } + final resultVolumes = await _adapter.api().getVolumes(); + if (resultVolumes.data.isEmpty || !resultVolumes.success) { + return GenericResult( + success: false, + data: metadata, + code: resultVolumes.code, + message: resultVolumes.message, + ); + } + final resultPricePerGb = await getAdditionalPricing(); + if (resultPricePerGb.data == null || !resultPricePerGb.success) { + return GenericResult( + success: false, + data: metadata, + code: resultPricePerGb.code, + message: resultPricePerGb.message, + ); + } + + final List servers = resultServers.data; + final List volumes = resultVolumes.data; + + try { + final Price pricePerGb = resultPricePerGb.data!.perVolumeGb; + final Price pricePerIp = resultPricePerGb.data!.perPublicIpv4; + final HetznerServerInfo server = servers.firstWhere( + (final server) => server.id == serverId, + ); + final HetznerVolume volume = volumes.firstWhere( + (final volume) => server.volumes.contains(volume.id), + ); + + metadata = [ + ServerMetadataEntity( + type: MetadataType.id, + trId: 'server.server_id', + value: server.id.toString(), + ), + ServerMetadataEntity( + type: MetadataType.status, + trId: 'server.status', + value: server.status.toString().split('.')[1].capitalize(), + ), + ServerMetadataEntity( + type: MetadataType.cpu, + trId: 'server.cpu', + value: server.serverType.cores.toString(), + ), + ServerMetadataEntity( + type: MetadataType.ram, + trId: 'server.ram', + value: '${server.serverType.memory.toString()} GB', + ), + ServerMetadataEntity( + type: MetadataType.cost, + trId: 'server.monthly_cost', + value: + // TODO: Make more descriptive + '${server.serverType.prices[1].monthly.toStringAsFixed(2)} + ${(volume.size * pricePerGb.value).toStringAsFixed(2)} + ${pricePerIp.value.toStringAsFixed(2)} ${currency.shortcode}', + ), + ServerMetadataEntity( + type: MetadataType.location, + trId: 'server.location', + value: '${server.location.city}, ${server.location.country}', + ), + ServerMetadataEntity( + type: MetadataType.other, + trId: 'server.server_provider', + value: type.displayName, + ), + ]; + } catch (e) { + return GenericResult( + success: false, + data: [], + message: e.toString(), + ); + } + + return GenericResult(success: true, data: metadata); + } + + @override + Future> getMetrics( + final int serverId, + final DateTime start, + final DateTime end, + ) async { + ServerMetrics? metrics; + + List serializeTimeNetworkSeries( + final Map json, + final String type, + ) { + final List list = json['time_series'][type]['values']; + return list + .map((final el) => TimeSeriesData(el[0], double.parse(el[1]))) + .toList(); + } + + if (cachedCoreAmount == null) { + final serversResult = await _adapter.api().getServers(); + if (serversResult.data.isEmpty || !serversResult.success) { + return GenericResult( + success: false, + data: metrics, + code: serversResult.code, + message: serversResult.message, + ); + } + + for (final server in serversResult.data) { + if (server.id == serverId) { + cachedCoreAmount = server.serverType.cores; + } + } + + if (cachedCoreAmount == null) { + return GenericResult( + success: false, + data: metrics, + message: "Couldn't find active server to cache core amount", + ); + } + } + + List serializeTimeCpuSeries( + final Map json, + final String type, + ) { + final List list = json['time_series'][type]['values']; + return list + .map( + (final el) => TimeSeriesData( + el[0], + double.parse(el[1]) / cachedCoreAmount!, + ), + ) + .toList(); + } + + final cpuResult = await _adapter.api().getMetrics( + serverId, + start, + end, + 'cpu', + ); + + if (cpuResult.data.isEmpty || !cpuResult.success) { + return GenericResult( + success: false, + data: metrics, + code: cpuResult.code, + message: cpuResult.message, + ); + } + + final netResult = await _adapter.api().getMetrics( + serverId, + start, + end, + 'network', + ); + + if (cpuResult.data.isEmpty || !netResult.success) { + return GenericResult( + success: false, + data: metrics, + code: netResult.code, + message: netResult.message, + ); + } + + metrics = ServerMetrics( + cpu: serializeTimeCpuSeries( + cpuResult.data, + 'cpu', + ), + bandwidthIn: serializeTimeNetworkSeries( + netResult.data, + 'network.0.bandwidth.in', + ), + bandwidthOut: serializeTimeNetworkSeries( + netResult.data, + 'network.0.bandwidth.out', + ), + end: end, + start: start, + stepsInSecond: cpuResult.data['step'], + ); + + return GenericResult(data: metrics, success: true); + } +} diff --git a/lib/logic/providers/server_providers/server_provider.dart b/lib/logic/providers/server_providers/server_provider.dart new file mode 100644 index 00000000..ce7b6944 --- /dev/null +++ b/lib/logic/providers/server_providers/server_provider.dart @@ -0,0 +1,142 @@ +import 'package:selfprivacy/logic/api_maps/generic_result.dart'; +import 'package:selfprivacy/logic/models/callback_dialogue_branching.dart'; +import 'package:selfprivacy/logic/models/disk_size.dart'; +import 'package:selfprivacy/logic/models/hive/server_details.dart'; +import 'package:selfprivacy/logic/models/launch_installation_data.dart'; +import 'package:selfprivacy/logic/models/metrics.dart'; +import 'package:selfprivacy/logic/models/price.dart'; +import 'package:selfprivacy/logic/models/server_basic_info.dart'; +import 'package:selfprivacy/logic/models/server_metadata.dart'; +import 'package:selfprivacy/logic/models/server_provider_location.dart'; +import 'package:selfprivacy/logic/models/server_type.dart'; + +export 'package:selfprivacy/logic/api_maps/generic_result.dart'; +export 'package:selfprivacy/logic/models/launch_installation_data.dart'; + +abstract class ServerProvider { + /// Returns an assigned enum value, respectively to which + /// provider implements [ServerProvider] interface. + ServerProviderType get type; + + /// Returns [ServerBasicInfo] of all available machines + /// assigned to the authorized user. + /// + /// Only with public IPv4 addresses. + Future>> getServers(); + + /// Returns actual [ServerType] of the + /// requested server entry assigned + /// to the authorized user. + Future> getServerType(final int serverId); + + /// Tries to launch installation of SelfPrivacy on + /// the requested server entry for the authorized account. + /// Depending on a server provider, the algorithm + /// and instructions vary drastically. + /// + /// If successly launched, stores new server information. + /// + /// If failure, returns error dialogue information to + /// write in ServerInstallationState. + Future> launchInstallation( + final LaunchInstallationData installationData, + ); + + /// Tries to delete the requested server entry + /// from the authorized account, including all assigned + /// storage extensions. + /// + /// If failure, returns error dialogue information to + /// write in ServerInstallationState. + Future> deleteServer( + final String hostname, + ); + + /// Tries to access an account linked to the provided token. + /// + /// To generate a token for your account follow instructions of your + /// server provider respectfully. + /// + /// If success, saves it for future usage. + Future> tryInitApiByToken(final String token); + + /// Tries to assign the location shortcode for future usage. + /// + /// If API wasn't initialized with token by [tryInitApiByToken] beforehand, + /// returns 'Not authorized!' error. + Future> trySetServerLocation(final String location); + + /// Returns all available server locations + /// of the authorized user's server provider. + Future>> getAvailableLocations(); + + /// Returns [ServerType] of all available + /// machine configurations available to the authorized account + /// within the requested provider location, + /// accessed from [getAvailableLocations]. + Future>> getServerTypes({ + required final ServerProviderLocation location, + }); + + /// Tries to power on the requested accessible machine. + /// + /// If success, returns [DateTime] of when the server + /// answered the request. + Future> powerOn(final int serverId); + + /// Tries to restart the requested accessible machine. + /// + /// If success, returns [DateTime] of when the server + /// answered the request. + Future> restart(final int serverId); + + /// Returns [Price] information map of all additional resources, excluding + /// main server type pricing + Future> getAdditionalPricing(); + + /// Returns [ServerVolume] of all available volumes + /// assigned to the authorized user and attached to active machine. + Future>> getVolumes({final String? status}); + + /// Tries to create an empty unattached [ServerVolume]. + /// + /// If success, returns this volume information. + Future> createVolume(final int gb); + + /// Tries to delete the requested accessible [ServerVolume]. + Future> deleteVolume(final ServerVolume volume); + + /// Tries to resize the requested accessible [ServerVolume] + /// to the provided size **(not by!)**, must be greater than current size. + Future> resizeVolume( + final ServerVolume volume, + final DiskSize size, + ); + + /// Tries to attach the requested accessible [ServerVolume] + /// to an accessible machine by the provided identificator. + Future> attachVolume( + final ServerVolume volume, + final int serverId, + ); + + /// Tries to attach the requested accessible [ServerVolume] + /// from any machine. + Future> detachVolume(final ServerVolume volume); + + /// Returns metedata of an accessible machine by the provided identificator + /// to show on ServerDetailsScreen. + Future>> getMetadata( + final int serverId, + ); + + /// Returns information about cpu and bandwidth load within the provided + /// time period of the requested accessible machine. + Future> getMetrics( + final int serverId, + final DateTime start, + final DateTime end, + ); + + GenericResult get success => GenericResult(success: true, data: true); +} diff --git a/lib/logic/providers/server_providers/server_provider_factory.dart b/lib/logic/providers/server_providers/server_provider_factory.dart new file mode 100644 index 00000000..9aff1ab5 --- /dev/null +++ b/lib/logic/providers/server_providers/server_provider_factory.dart @@ -0,0 +1,35 @@ +import 'package:selfprivacy/logic/providers/provider_settings.dart'; +import 'package:selfprivacy/logic/models/hive/server_details.dart'; +import 'package:selfprivacy/logic/providers/server_providers/server_provider.dart'; +import 'package:selfprivacy/logic/providers/server_providers/digital_ocean.dart'; +import 'package:selfprivacy/logic/providers/server_providers/hetzner.dart'; + +class UnknownProviderException implements Exception { + UnknownProviderException(this.message); + final String message; +} + +class ServerProviderFactory { + static ServerProvider createServerProviderInterface( + final ServerProviderSettings settings, + ) { + switch (settings.provider) { + case ServerProviderType.hetzner: + return settings.isAuthorized + ? HetznerServerProvider.load( + settings.location, + settings.isAuthorized, + ) + : HetznerServerProvider(); + case ServerProviderType.digitalOcean: + return settings.isAuthorized + ? DigitalOceanServerProvider.load( + settings.location, + settings.isAuthorized, + ) + : DigitalOceanServerProvider(); + case ServerProviderType.unknown: + throw UnknownProviderException('Unknown server provider'); + } + } +} diff --git a/lib/main.dart b/lib/main.dart index f2c36392..7ba0114a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,14 +1,11 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:selfprivacy/config/brand_colors.dart'; import 'package:selfprivacy/config/hive_config.dart'; import 'package:selfprivacy/theming/factory/app_theme_factory.dart'; -import 'package:selfprivacy/ui/pages/setup/initializing.dart'; -import 'package:selfprivacy/ui/pages/onboarding/onboarding.dart'; -import 'package:selfprivacy/ui/pages/root_route.dart'; -import 'package:wakelock/wakelock.dart'; +import 'package:selfprivacy/ui/router/router.dart'; +// import 'package:wakelock/wakelock.dart'; import 'package:timezone/data/latest.dart' as tz; import 'package:selfprivacy/config/bloc_config.dart'; @@ -20,15 +17,15 @@ import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await HiveConfig.init(); - await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); + // await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); - try { - /// Wakelock support for Linux - /// desktop is not yet implemented - await Wakelock.enable(); - } on PlatformException catch (e) { - print(e); - } + // try { + // /// Wakelock support for Linux + // /// desktop is not yet implemented + // await Wakelock.enable(); + // } on PlatformException catch (e) { + // print(e); + // } await getItSetup(); await EasyLocalization.ensureInitialized(); @@ -43,43 +40,46 @@ void main() async { fallbackColor: BrandColors.primary, ); - BlocOverrides.runZoned( - () => runApp( - Localization( - child: MyApp( - lightThemeData: lightThemeData, - darkThemeData: darkThemeData, - ), + Bloc.observer = SimpleBlocObserver(); + + runApp( + Localization( + child: SelfprivacyApp( + lightThemeData: lightThemeData, + darkThemeData: darkThemeData, ), ), - blocObserver: SimpleBlocObserver(), ); } -class MyApp extends StatelessWidget { - const MyApp({ +class SelfprivacyApp extends StatelessWidget { + SelfprivacyApp({ required this.lightThemeData, required this.darkThemeData, - final super.key, + super.key, }); final ThemeData lightThemeData; final ThemeData darkThemeData; + final _appRouter = RootRouter(getIt.get().navigatorKey); + @override Widget build(final BuildContext context) => Localization( - child: AnnotatedRegion( - value: SystemUiOverlayStyle.light, // Manually changing appbar color - child: BlocAndProviderConfig( - child: BlocBuilder( - builder: ( - final BuildContext context, - final AppSettingsState appSettings, - ) => - MaterialApp( + child: BlocAndProviderConfig( + child: BlocBuilder( + builder: ( + final BuildContext context, + final AppSettingsState appSettings, + ) { + getIt.get().setLocaleCode( + context.locale.languageCode, + ); + return MaterialApp.router( + routeInformationParser: _appRouter.defaultRouteParser(), + routerDelegate: _appRouter.delegate(), scaffoldMessengerKey: getIt.get().scaffoldMessengerKey, - navigatorKey: getIt.get().navigatorKey, localizationsDelegates: context.localizationDelegates, supportedLocales: context.supportedLocales, locale: context.locale, @@ -87,11 +87,11 @@ class MyApp extends StatelessWidget { title: 'SelfPrivacy', theme: lightThemeData, darkTheme: darkThemeData, - themeMode: - appSettings.isDarkModeOn ? ThemeMode.dark : ThemeMode.light, - home: appSettings.isOnboardingShowing - ? const OnboardingPage(nextPage: InitializingPage()) - : const RootPage(), + themeMode: appSettings.isAutoDarkModeOn + ? ThemeMode.system + : appSettings.isDarkModeOn + ? ThemeMode.dark + : ThemeMode.light, builder: (final BuildContext context, final Widget? widget) { Widget error = const Text('...rendering error...'); if (widget is Scaffold || widget is Navigator) { @@ -101,8 +101,8 @@ class MyApp extends StatelessWidget { (final FlutterErrorDetails errorDetails) => error; return widget!; }, - ), - ), + ); + }, ), ), ); diff --git a/lib/theming/factory/app_theme_factory.dart b/lib/theming/factory/app_theme_factory.dart index 48f4b086..e3ce278a 100644 --- a/lib/theming/factory/app_theme_factory.dart +++ b/lib/theming/factory/app_theme_factory.dart @@ -1,10 +1,7 @@ -import 'dart:io'; - import 'package:dynamic_color/dynamic_color.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:system_theme/system_theme.dart'; -import 'package:gtk_theme_fl/gtk_theme_fl.dart'; +import 'package:material_color_utilities/palettes/core_palette.dart'; abstract class AppThemeFactory { AppThemeFactory._(); @@ -22,54 +19,27 @@ abstract class AppThemeFactory { required final Color fallbackColor, final bool isDark = false, }) async { - ColorScheme? gtkColorsScheme; final Brightness brightness = isDark ? Brightness.dark : Brightness.light; final ColorScheme? dynamicColorsScheme = await _getDynamicColors(brightness); - if (Platform.isLinux) { - final GtkThemeData themeData = await GtkThemeData.initialize(); - final bool isGtkDark = - Color(themeData.theme_bg_color).computeLuminance() < 0.5; - final bool isInverseNeeded = isGtkDark != isDark; - gtkColorsScheme = ColorScheme.fromSeed( - seedColor: Color(themeData.theme_selected_bg_color), - brightness: brightness, - background: isInverseNeeded ? null : Color(themeData.theme_bg_color), - surface: isInverseNeeded ? null : Color(themeData.theme_base_color), - ); - } - - final SystemAccentColor accentColor = SystemAccentColor(fallbackColor); - - try { - await accentColor.load(); - } on MissingPluginException catch (e) { - print('_createAppTheme: ${e.message}'); - } - final ColorScheme fallbackColorScheme = ColorScheme.fromSeed( - seedColor: accentColor.accent, + seedColor: fallbackColor, brightness: brightness, ); - final ColorScheme colorScheme = - dynamicColorsScheme ?? gtkColorsScheme ?? fallbackColorScheme; + final ColorScheme colorScheme = dynamicColorsScheme ?? fallbackColorScheme; final Typography appTypography = Typography.material2021(); final ThemeData materialThemeData = ThemeData( + visualDensity: VisualDensity.adaptivePlatformDensity, colorScheme: colorScheme, brightness: colorScheme.brightness, typography: appTypography, useMaterial3: true, scaffoldBackgroundColor: colorScheme.background, - appBarTheme: AppBarTheme( - elevation: 0, - backgroundColor: colorScheme.primary, - foregroundColor: colorScheme.onPrimary, - ), ); return materialThemeData; @@ -84,4 +54,12 @@ abstract class AppThemeFactory { return Future.value(null); } } + + static Future getCorePalette() async { + try { + return await DynamicColorPlugin.getCorePalette(); + } on PlatformException { + return Future.value(null); + } + } } diff --git a/lib/ui/components/brand_alert/brand_alert.dart b/lib/ui/components/brand_alert/brand_alert.dart deleted file mode 100644 index 0d673ded..00000000 --- a/lib/ui/components/brand_alert/brand_alert.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:flutter/material.dart'; - -class BrandAlert extends AlertDialog { - BrandAlert({ - final super.key, - final String? title, - final String? contentText, - final super.actions, - }) : super( - title: title != null ? Text(title) : null, - content: title != null ? Text(contentText!) : null, - ); -} diff --git a/lib/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart b/lib/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart deleted file mode 100644 index de322b05..00000000 --- a/lib/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/brand_colors.dart'; - -class BrandBottomSheet extends StatelessWidget { - const BrandBottomSheet({ - required this.child, - final super.key, - this.isExpended = false, - }); - - final Widget child; - final bool isExpended; - - @override - Widget build(final BuildContext context) { - final double mainHeight = MediaQuery.of(context).size.height - - MediaQuery.of(context).padding.top - - 100; - late Widget innerWidget; - if (isExpended) { - innerWidget = Scaffold( - body: child, - ); - } else { - final ThemeData themeData = Theme.of(context); - - innerWidget = Material( - color: themeData.scaffoldBackgroundColor, - child: IntrinsicHeight(child: child), - ); - } - return ConstrainedBox( - constraints: BoxConstraints(maxHeight: mainHeight + 4 + 6), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Center( - child: Container( - height: 4, - width: 30, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(2), - color: BrandColors.gray4, - ), - ), - ), - const SizedBox(height: 6), - ClipRRect( - borderRadius: const BorderRadius.vertical(top: Radius.circular(20)), - child: ConstrainedBox( - constraints: BoxConstraints(maxHeight: mainHeight), - child: innerWidget, - ), - ), - ], - ), - ); - } -} diff --git a/lib/ui/components/brand_button/brand_button.dart b/lib/ui/components/brand_button/brand_button.dart deleted file mode 100644 index 8951b70f..00000000 --- a/lib/ui/components/brand_button/brand_button.dart +++ /dev/null @@ -1,91 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:selfprivacy/ui/components/brand_button/filled_button.dart'; -import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; - -enum BrandButtonTypes { rised, text, iconText } - -class BrandButton { - static ConstrainedBox rised({ - required final VoidCallback? onPressed, - final Key? key, - final String? text, - final Widget? child, - }) { - assert(text == null || child == null, 'required title or child'); - assert(text != null || child != null, 'required title or child'); - return ConstrainedBox( - constraints: const BoxConstraints( - minHeight: 48, - minWidth: double.infinity, - ), - child: FilledButton( - key: key, - title: text, - onPressed: onPressed, - child: child, - ), - ); - } - - static ConstrainedBox text({ - required final VoidCallback onPressed, - required final String title, - final Key? key, - }) => - ConstrainedBox( - constraints: const BoxConstraints( - minHeight: 40, - minWidth: double.infinity, - ), - child: TextButton(onPressed: onPressed, child: Text(title)), - ); - - static IconTextButton emptyWithIconText({ - required final VoidCallback onPressed, - required final String title, - required final Icon icon, - final Key? key, - }) => - IconTextButton( - key: key, - title: title, - onPressed: onPressed, - icon: icon, - ); -} - -class IconTextButton extends StatelessWidget { - const IconTextButton({ - final super.key, - this.onPressed, - this.title, - this.icon, - }); - - final VoidCallback? onPressed; - final String? title; - final Icon? icon; - - @override - Widget build(final BuildContext context) => Material( - color: Colors.transparent, - child: InkWell( - onTap: onPressed, - child: Container( - height: 48, - width: double.infinity, - alignment: Alignment.center, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - BrandText.body1(title), - Padding( - padding: const EdgeInsets.all(12.0), - child: icon, - ) - ], - ), - ), - ), - ); -} diff --git a/lib/ui/components/brand_button/filled_button.dart b/lib/ui/components/brand_button/filled_button.dart deleted file mode 100644 index b3888f3c..00000000 --- a/lib/ui/components/brand_button/filled_button.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:flutter/material.dart'; - -class FilledButton extends StatelessWidget { - const FilledButton({ - final super.key, - this.onPressed, - this.title, - this.child, - this.disabled = false, - }); - - final VoidCallback? onPressed; - final String? title; - final Widget? child; - final bool disabled; - - @override - Widget build(final BuildContext context) { - final ButtonStyle enabledStyle = ElevatedButton.styleFrom( - onPrimary: Theme.of(context).colorScheme.onPrimary, - primary: Theme.of(context).colorScheme.primary, - ).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)); - - final ButtonStyle disabledStyle = ElevatedButton.styleFrom( - onPrimary: Theme.of(context).colorScheme.onSurface.withAlpha(30), - primary: Theme.of(context).colorScheme.onSurface.withAlpha(98), - ).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)); - - return ConstrainedBox( - constraints: const BoxConstraints( - minHeight: 40, - minWidth: double.infinity, - ), - child: ElevatedButton( - onPressed: onPressed, - style: disabled ? disabledStyle : enabledStyle, - child: child ?? Text(title ?? ''), - ), - ); - } -} diff --git a/lib/ui/components/brand_cards/brand_cards.dart b/lib/ui/components/brand_cards/brand_cards.dart deleted file mode 100644 index d8f48088..00000000 --- a/lib/ui/components/brand_cards/brand_cards.dart +++ /dev/null @@ -1,108 +0,0 @@ -import 'package:flutter/material.dart'; - -class BrandCards { - static Widget big({required final Widget child}) => _BrandCard( - padding: const EdgeInsets.symmetric( - horizontal: 20, - vertical: 15, - ), - shadow: bigShadow, - borderRadius: BorderRadius.circular(20), - child: child, - ); - static Widget small({required final Widget child}) => _BrandCard( - padding: const EdgeInsets.symmetric( - horizontal: 15, - vertical: 10, - ), - shadow: bigShadow, - borderRadius: BorderRadius.circular(10), - child: child, - ); - static Widget outlined({required final Widget child}) => _OutlinedCard( - child: child, - ); - static Widget filled({ - required final Widget child, - final bool tertiary = false, - }) => - _FilledCard( - tertiary: tertiary, - child: child, - ); -} - -class _BrandCard extends StatelessWidget { - const _BrandCard({ - required this.child, - required this.padding, - required this.shadow, - required this.borderRadius, - }); - - final Widget child; - final EdgeInsets padding; - final List shadow; - final BorderRadius borderRadius; - - @override - Widget build(final BuildContext context) => Container( - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surface, - borderRadius: borderRadius, - boxShadow: shadow, - ), - padding: padding, - child: child, - ); -} - -class _OutlinedCard extends StatelessWidget { - const _OutlinedCard({ - required this.child, - }); - - final Widget child; - @override - Widget build(final BuildContext context) => Card( - elevation: 0.0, - shape: RoundedRectangleBorder( - borderRadius: const BorderRadius.all(Radius.circular(12)), - side: BorderSide( - color: Theme.of(context).colorScheme.outline, - ), - ), - clipBehavior: Clip.antiAlias, - child: child, - ); -} - -class _FilledCard extends StatelessWidget { - const _FilledCard({ - required this.child, - required this.tertiary, - }); - - final Widget child; - final bool tertiary; - @override - Widget build(final BuildContext context) => Card( - elevation: 0.0, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(12)), - ), - clipBehavior: Clip.antiAlias, - color: tertiary - ? Theme.of(context).colorScheme.tertiaryContainer - : Theme.of(context).colorScheme.surfaceVariant, - child: child, - ); -} - -final List bigShadow = [ - BoxShadow( - offset: const Offset(0, 4), - blurRadius: 8, - color: Colors.black.withOpacity(.08), - ) -]; diff --git a/lib/ui/components/brand_divider/brand_divider.dart b/lib/ui/components/brand_divider/brand_divider.dart deleted file mode 100644 index 03e44653..00000000 --- a/lib/ui/components/brand_divider/brand_divider.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/brand_colors.dart'; - -class BrandDivider extends StatelessWidget { - const BrandDivider({final super.key}); - - @override - Widget build(final BuildContext context) => Container( - width: double.infinity, - height: 1, - color: BrandColors.dividerColor, - ); -} diff --git a/lib/ui/components/brand_header/brand_header.dart b/lib/ui/components/brand_header/brand_header.dart index 7366298b..3151aff7 100644 --- a/lib/ui/components/brand_header/brand_header.dart +++ b/lib/ui/components/brand_header/brand_header.dart @@ -1,10 +1,8 @@ import 'package:flutter/material.dart'; -import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; -import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; class BrandHeader extends StatelessWidget { const BrandHeader({ - final super.key, + super.key, this.title = '', this.hasBackButton = false, this.onBackButtonPressed, @@ -15,25 +13,20 @@ class BrandHeader extends StatelessWidget { final VoidCallback? onBackButtonPressed; @override - Widget build(final BuildContext context) => Container( - height: 52, - alignment: Alignment.centerLeft, - padding: EdgeInsets.only( - left: hasBackButton ? 1 : 15, + Widget build(final BuildContext context) => AppBar( + title: Padding( + padding: const EdgeInsets.only(top: 4.0), + child: Text(title), ), - child: Row( - children: [ - if (hasBackButton) ...[ - IconButton( - icon: const Icon(BrandIcons.arrowLeft), + leading: hasBackButton + ? IconButton( + icon: const Icon(Icons.arrow_back), onPressed: onBackButtonPressed ?? () => Navigator.of(context).pop(), - ), - const SizedBox(width: 10), - ], - BrandText.h4(title), - const Spacer(), - ], - ), + ) + : null, + actions: const [ + SizedBox.shrink(), + ], ); } diff --git a/lib/ui/components/brand_hero_screen/brand_hero_screen.dart b/lib/ui/components/brand_hero_screen/brand_hero_screen.dart deleted file mode 100644 index cdccc8d2..00000000 --- a/lib/ui/components/brand_hero_screen/brand_hero_screen.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; -import 'package:selfprivacy/ui/components/pre_styled_buttons/flash_fab.dart'; - -class BrandHeroScreen extends StatelessWidget { - const BrandHeroScreen({ - required this.children, - final super.key, - this.headerTitle = '', - this.hasBackButton = true, - this.hasFlashButton = true, - this.heroIcon, - this.heroTitle, - this.heroSubtitle, - this.onBackButtonPressed, - }); - - final List children; - final String headerTitle; - final bool hasBackButton; - final bool hasFlashButton; - final IconData? heroIcon; - final String? heroTitle; - final String? heroSubtitle; - final VoidCallback? onBackButtonPressed; - - @override - Widget build(final BuildContext context) => SafeArea( - child: Scaffold( - appBar: PreferredSize( - preferredSize: const Size.fromHeight(52.0), - child: BrandHeader( - title: headerTitle, - hasBackButton: hasBackButton, - onBackButtonPressed: onBackButtonPressed, - ), - ), - floatingActionButton: hasFlashButton ? const BrandFab() : null, - body: ListView( - padding: const EdgeInsets.all(16.0), - children: [ - if (heroIcon != null) - Container( - alignment: Alignment.bottomLeft, - child: Icon( - heroIcon, - size: 48.0, - ), - ), - const SizedBox(height: 8.0), - if (heroTitle != null) - Text( - heroTitle!, - style: Theme.of(context).textTheme.headlineMedium!.copyWith( - color: Theme.of(context).colorScheme.onBackground, - ), - textAlign: TextAlign.start, - ), - const SizedBox(height: 8.0), - if (heroSubtitle != null) - Text( - heroSubtitle!, - style: Theme.of(context).textTheme.bodyMedium!.copyWith( - color: Theme.of(context).colorScheme.onBackground, - ), - textAlign: TextAlign.start, - ), - const SizedBox(height: 16.0), - ...children, - ], - ), - ), - ); -} diff --git a/lib/ui/components/brand_linear_indicator/brand_linear_indicator.dart b/lib/ui/components/brand_linear_indicator/brand_linear_indicator.dart new file mode 100644 index 00000000..34fb9e10 --- /dev/null +++ b/lib/ui/components/brand_linear_indicator/brand_linear_indicator.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; + +class BrandLinearIndicator extends StatelessWidget { + const BrandLinearIndicator({ + required this.value, + required this.color, + required this.backgroundColor, + required this.height, + super.key, + }); + + final double value; + final Color color; + final Color backgroundColor; + final double height; + + @override + Widget build(final BuildContext context) => LayoutBuilder( + builder: (final context, final constraints) => Container( + height: height, + width: constraints.maxWidth, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + color: backgroundColor, + borderRadius: BorderRadius.circular(height), + ), + alignment: Alignment.centerLeft, + child: AnimatedSlide( + duration: const Duration(milliseconds: 400), + curve: Curves.easeInOutCubicEmphasized, + offset: Offset( + -(1 - value), + 0, + ), + child: AnimatedContainer( + duration: const Duration(milliseconds: 400), + curve: Curves.easeInOutCubicEmphasized, + width: constraints.maxWidth, + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(height), + ), + ), + ), + ), + ); +} diff --git a/lib/ui/components/brand_loader/brand_loader.dart b/lib/ui/components/brand_loader/brand_loader.dart index 59f1f177..63c7cccd 100644 --- a/lib/ui/components/brand_loader/brand_loader.dart +++ b/lib/ui/components/brand_loader/brand_loader.dart @@ -6,7 +6,7 @@ class BrandLoader { } class HorizontalLoader extends StatelessWidget { - const HorizontalLoader({final super.key}); + const HorizontalLoader({super.key}); @override Widget build(final BuildContext context) => Column( diff --git a/lib/ui/components/brand_md/brand_md.dart b/lib/ui/components/brand_md/brand_md.dart index 249895a9..67c3e2ea 100644 --- a/lib/ui/components/brand_md/brand_md.dart +++ b/lib/ui/components/brand_md/brand_md.dart @@ -2,14 +2,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:flutter/services.dart' show rootBundle; import 'package:easy_localization/easy_localization.dart'; -import 'package:selfprivacy/config/brand_colors.dart'; -import 'package:selfprivacy/config/text_themes.dart'; import 'package:url_launcher/url_launcher_string.dart'; class BrandMarkdown extends StatefulWidget { const BrandMarkdown({ required this.fileName, - final super.key, + super.key, }); final String fileName; @@ -37,24 +35,7 @@ class _BrandMarkdownState extends State { @override Widget build(final BuildContext context) { - final bool isDark = Theme.of(context).brightness == Brightness.dark; - final MarkdownStyleSheet markdown = MarkdownStyleSheet( - p: defaultTextStyle.copyWith( - color: isDark ? BrandColors.white : null, - ), - h1: headline1Style.copyWith( - color: isDark ? BrandColors.white : null, - ), - h2: headline2Style.copyWith( - color: isDark ? BrandColors.white : null, - ), - h3: headline3Style.copyWith( - color: isDark ? BrandColors.white : null, - ), - h4: headline4Style.copyWith( - color: isDark ? BrandColors.white : null, - ), - ); + final MarkdownStyleSheet markdown = MarkdownStyleSheet(); return MarkdownBody( shrinkWrap: true, styleSheet: markdown, diff --git a/lib/ui/components/brand_modal_sheet/brand_modal_sheet.dart b/lib/ui/components/brand_modal_sheet/brand_modal_sheet.dart deleted file mode 100644 index 6435f7bc..00000000 --- a/lib/ui/components/brand_modal_sheet/brand_modal_sheet.dart +++ /dev/null @@ -1,63 +0,0 @@ -// import 'package:flutter/material.dart'; - -// var navigatorKey = GlobalKey(); - -// class BrandModalSheet extends StatelessWidget { -// const BrandModalSheet({ -// Key? key, -// this.child, -// }) : super(key: key); - -// final Widget? child; -// @override -// Widget build(BuildContext context) { -// return DraggableScrollableSheet( -// minChildSize: 1, -// initialChildSize: 1, -// maxChildSize: 1, -// builder: (context, scrollController) { -// return SingleChildScrollView( -// controller: scrollController, -// physics: ClampingScrollPhysics(), -// child: Container( -// child: Column( -// children: [ -// GestureDetector( -// onTap: () => Navigator.of(context).pop(), -// behavior: HitTestBehavior.opaque, -// child: Container( -// width: double.infinity, -// child: Center( -// child: Padding( -// padding: EdgeInsets.only(top: 132, bottom: 6), -// child: Container( -// height: 4, -// width: 30, -// decoration: BoxDecoration( -// borderRadius: BorderRadius.circular(2), -// color: Color(0xFFE3E3E3).withOpacity(0.65), -// ), -// ), -// ), -// ), -// ), -// ), -// Container( -// constraints: BoxConstraints( -// minHeight: MediaQuery.of(context).size.height - 132, -// maxHeight: MediaQuery.of(context).size.height - 132, -// ), -// decoration: BoxDecoration( -// borderRadius: -// BorderRadius.vertical(top: Radius.circular(20)), -// color: Theme.of(context).scaffoldBackgroundColor, -// ), -// width: double.infinity, -// child: child), -// ], -// ), -// ), -// ); -// }); -// } -// } diff --git a/lib/ui/components/brand_radio/brand_radio.dart b/lib/ui/components/brand_radio/brand_radio.dart deleted file mode 100644 index 60f41fb5..00000000 --- a/lib/ui/components/brand_radio/brand_radio.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/brand_colors.dart'; - -class BrandRadio extends StatelessWidget { - const BrandRadio({ - required this.isChecked, - final super.key, - }); - - final bool isChecked; - - @override - Widget build(final BuildContext context) => Container( - height: 20, - width: 20, - alignment: Alignment.center, - padding: const EdgeInsets.all(2), - decoration: BoxDecoration( - shape: BoxShape.circle, - border: _getBorder(), - ), - child: isChecked - ? Container( - height: 10, - width: 10, - decoration: const BoxDecoration( - shape: BoxShape.circle, - color: BrandColors.primary, - ), - ) - : null, - ); - - BoxBorder? _getBorder() => Border.all( - color: isChecked ? BrandColors.primary : BrandColors.gray1, - width: 2, - ); -} diff --git a/lib/ui/components/brand_radio_tile/brand_radio_tile.dart b/lib/ui/components/brand_radio_tile/brand_radio_tile.dart deleted file mode 100644 index 5b18247d..00000000 --- a/lib/ui/components/brand_radio_tile/brand_radio_tile.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:selfprivacy/ui/components/brand_radio/brand_radio.dart'; -import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; - -class BrandRadioTile extends StatelessWidget { - const BrandRadioTile({ - required this.isChecked, - required this.text, - required this.onPress, - final super.key, - }); - - final bool isChecked; - - final String text; - final VoidCallback onPress; - - @override - Widget build(final BuildContext context) => GestureDetector( - onTap: onPress, - behavior: HitTestBehavior.translucent, - child: Padding( - padding: const EdgeInsets.all(2), - child: Row( - children: [ - BrandRadio( - isChecked: isChecked, - ), - const SizedBox(width: 9), - BrandText.h5(text) - ], - ), - ), - ); -} diff --git a/lib/ui/components/brand_span_button/brand_span_button.dart b/lib/ui/components/brand_span_button/brand_span_button.dart deleted file mode 100644 index de19730e..00000000 --- a/lib/ui/components/brand_span_button/brand_span_button.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/brand_colors.dart'; -import 'package:url_launcher/url_launcher.dart'; - -class BrandSpanButton extends TextSpan { - BrandSpanButton({ - required final String text, - required final VoidCallback onTap, - final TextStyle? style, - }) : super( - recognizer: TapGestureRecognizer()..onTap = onTap, - text: text, - style: (style ?? const TextStyle()).copyWith(color: BrandColors.blue), - ); - - BrandSpanButton.link({ - required final String text, - final String? urlString, - final TextStyle? style, - }) : super( - recognizer: TapGestureRecognizer() - ..onTap = () => _launchURL(urlString ?? text), - text: text, - style: (style ?? const TextStyle()).copyWith(color: BrandColors.blue), - ); - - static Future _launchURL(final String link) async { - if (await canLaunchUrl(Uri.parse(link))) { - await launchUrl(Uri.parse(link)); - } else { - throw 'Could not launch $link'; - } - } -} diff --git a/lib/ui/components/brand_switch/brand_switch.dart b/lib/ui/components/brand_switch/brand_switch.dart deleted file mode 100644 index 89396acc..00000000 --- a/lib/ui/components/brand_switch/brand_switch.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:flutter/material.dart'; - -class BrandSwitch extends StatelessWidget { - const BrandSwitch({ - required this.onChanged, - required this.value, - final super.key, - }); - - final ValueChanged onChanged; - final bool value; - - @override - Widget build(final BuildContext context) => Switch( - activeColor: Theme.of(context).colorScheme.primary, - value: value, - onChanged: onChanged, - ); -} diff --git a/lib/ui/components/brand_tab_bar/brand_tab_bar.dart b/lib/ui/components/brand_tab_bar/brand_tab_bar.dart deleted file mode 100644 index 194c0ac1..00000000 --- a/lib/ui/components/brand_tab_bar/brand_tab_bar.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/material.dart'; -import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; - -class BrandTabBar extends StatefulWidget { - const BrandTabBar({final super.key, this.controller}); - - final TabController? controller; - @override - State createState() => _BrandTabBarState(); -} - -class _BrandTabBarState extends State { - int? currentIndex; - @override - void initState() { - currentIndex = widget.controller!.index; - widget.controller!.addListener(_listener); - super.initState(); - } - - void _listener() { - if (currentIndex != widget.controller!.index) { - setState(() { - currentIndex = widget.controller!.index; - }); - } - } - - @override - void dispose() { - widget.controller ?? widget.controller!.removeListener(_listener); - super.dispose(); - } - - @override - Widget build(final BuildContext context) => NavigationBar( - destinations: [ - _getIconButton('basis.providers'.tr(), BrandIcons.server, 0), - _getIconButton('basis.services'.tr(), BrandIcons.box, 1), - _getIconButton('basis.users'.tr(), BrandIcons.users, 2), - _getIconButton('basis.more'.tr(), Icons.menu_rounded, 3), - ], - onDestinationSelected: (final index) { - widget.controller!.animateTo(index); - }, - selectedIndex: currentIndex ?? 0, - labelBehavior: NavigationDestinationLabelBehavior.onlyShowSelected, - ); - - NavigationDestination _getIconButton( - final String label, - final IconData iconData, - final int index, - ) => - NavigationDestination( - icon: Icon(iconData), - label: label, - ); -} diff --git a/lib/ui/components/brand_text/brand_text.dart b/lib/ui/components/brand_text/brand_text.dart deleted file mode 100644 index 544ffcec..00000000 --- a/lib/ui/components/brand_text/brand_text.dart +++ /dev/null @@ -1,236 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/text_themes.dart'; -export 'package:selfprivacy/utils/extensions/text_extensions.dart'; - -enum TextType { - h1, // right now only at onboarding and opened providers - h2, // cards titles - h3, // titles in about page - h4, // caption - h5, // Table data - body1, // normal - body2, // with opacity - medium, - small, - onboardingTitle, - buttonTitleText, // risen button title text, - h4Underlined, -} - -class BrandText extends StatelessWidget { - factory BrandText.h4( - final String? text, { - final TextStyle? style, - final TextAlign? textAlign, - }) => - BrandText( - text, - type: TextType.h4, - style: style, - softWrap: true, - overflow: TextOverflow.ellipsis, - maxLines: 2, - textAlign: textAlign, - ); - - factory BrandText.onboardingTitle( - final String text, { - final TextStyle? style, - }) => - BrandText( - text, - type: TextType.onboardingTitle, - style: style, - ); - factory BrandText.h3( - final String text, { - final TextStyle? style, - final TextAlign? textAlign, - }) => - BrandText( - text, - type: TextType.h3, - style: style, - textAlign: textAlign, - overflow: TextOverflow.ellipsis, - ); - - factory BrandText.h4Underlined( - final String? text, { - final TextStyle? style, - final TextAlign? textAlign, - }) => - BrandText( - text, - type: TextType.h4Underlined, - style: style, - softWrap: true, - overflow: TextOverflow.ellipsis, - maxLines: 2, - textAlign: textAlign, - ); - - factory BrandText.h1( - final String? text, { - final TextStyle? style, - final TextOverflow? overflow, - final bool? softWrap, - }) => - BrandText( - text, - type: TextType.h1, - style: style, - ); - factory BrandText.h2( - final String? text, { - final TextStyle? style, - final TextAlign? textAlign, - }) => - BrandText( - text, - type: TextType.h2, - style: style, - textAlign: textAlign, - ); - factory BrandText.body1(final String? text, {final TextStyle? style}) => - BrandText( - text, - type: TextType.body1, - style: style, - ); - factory BrandText.small(final String text, {final TextStyle? style}) => - BrandText( - text, - type: TextType.small, - style: style, - ); - factory BrandText.body2(final String? text, {final TextStyle? style}) => - BrandText( - text, - type: TextType.body2, - style: style, - ); - factory BrandText.buttonTitleText( - final String? text, { - final TextStyle? style, - }) => - BrandText( - text, - type: TextType.buttonTitleText, - style: style, - ); - - factory BrandText.h5( - final String? text, { - final TextStyle? style, - final TextAlign? textAlign, - }) => - BrandText( - text, - type: TextType.h5, - style: style, - textAlign: textAlign, - ); - factory BrandText.medium( - final String? text, { - final TextStyle? style, - final TextAlign? textAlign, - }) => - BrandText( - text, - type: TextType.medium, - style: style, - textAlign: textAlign, - ); - const BrandText( - this.text, { - required this.type, - final super.key, - this.style, - this.overflow, - this.softWrap, - this.textAlign, - this.maxLines, - }); - - final String? text; - final TextStyle? style; - final TextType type; - final TextOverflow? overflow; - final bool? softWrap; - final TextAlign? textAlign; - final int? maxLines; - @override - Text build(final BuildContext context) { - TextStyle style; - final bool isDark = Theme.of(context).brightness == Brightness.dark; - switch (type) { - case TextType.h1: - style = isDark - ? headline1Style.copyWith(color: Colors.white) - : headline1Style; - break; - case TextType.h2: - style = isDark - ? headline2Style.copyWith(color: Colors.white) - : headline2Style; - break; - case TextType.h3: - style = isDark - ? headline3Style.copyWith(color: Colors.white) - : headline3Style; - break; - case TextType.h4: - style = isDark - ? headline4Style.copyWith(color: Colors.white) - : headline4Style; - break; - case TextType.h4Underlined: - style = isDark - ? headline4UnderlinedStyle.copyWith(color: Colors.white) - : headline4UnderlinedStyle; - break; - case TextType.h5: - style = isDark - ? headline5Style.copyWith(color: Colors.white) - : headline5Style; - break; - case TextType.body1: - style = isDark ? body1Style.copyWith(color: Colors.white) : body1Style; - break; - case TextType.body2: - style = isDark - ? body2Style.copyWith(color: Colors.white.withOpacity(0.6)) - : body2Style; - break; - case TextType.small: - style = isDark ? smallStyle.copyWith(color: Colors.white) : smallStyle; - break; - case TextType.onboardingTitle: - style = isDark - ? onboardingTitle.copyWith(color: Colors.white) - : onboardingTitle; - break; - case TextType.medium: - style = - isDark ? mediumStyle.copyWith(color: Colors.white) : mediumStyle; - break; - case TextType.buttonTitleText: - style = !isDark - ? buttonTitleText.copyWith(color: Colors.white) - : buttonTitleText; - break; - } - if (this.style != null) { - style = style.merge(this.style); - } - return Text( - text!, - style: style, - maxLines: maxLines, - overflow: overflow, - softWrap: softWrap, - textAlign: textAlign, - ); - } -} diff --git a/lib/ui/components/brand_timer/brand_timer.dart b/lib/ui/components/brand_timer/brand_timer.dart index 5d76d57d..4d744cfc 100644 --- a/lib/ui/components/brand_timer/brand_timer.dart +++ b/lib/ui/components/brand_timer/brand_timer.dart @@ -2,14 +2,13 @@ import 'dart:async'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; import 'package:selfprivacy/utils/named_font_weight.dart'; class BrandTimer extends StatefulWidget { const BrandTimer({ required this.startDateTime, required this.duration, - final super.key, + super.key, }); final DateTime startDateTime; @@ -52,11 +51,12 @@ class _BrandTimerState extends State { } @override - Widget build(final BuildContext context) => BrandText.medium( - _timeString, - style: const TextStyle( - fontWeight: NamedFontWeight.demiBold, - ), + Widget build(final BuildContext context) => Text( + _timeString ?? '', + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + fontWeight: NamedFontWeight.demiBold, + color: Theme.of(context).colorScheme.onSurface, + ), ); void _getTime() { diff --git a/lib/ui/components/buttons/brand_button.dart b/lib/ui/components/buttons/brand_button.dart new file mode 100644 index 00000000..a07a1de0 --- /dev/null +++ b/lib/ui/components/buttons/brand_button.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; + +class BrandButton { + static ConstrainedBox rised({ + required final VoidCallback? onPressed, + final Key? key, + final String? text, + final Widget? child, + }) { + assert(text == null || child == null, 'required title or child'); + assert(text != null || child != null, 'required title or child'); + return ConstrainedBox( + constraints: const BoxConstraints( + minHeight: 48, + minWidth: double.infinity, + ), + child: FilledButton( + key: key, + onPressed: onPressed, + child: child ?? Text(text ?? ''), + ), + ); + } + + static ConstrainedBox filled({ + required final VoidCallback? onPressed, + final Key? key, + final String? text, + final Widget? child, + }) { + assert(text == null || child == null, 'required title or child'); + assert(text != null || child != null, 'required title or child'); + return ConstrainedBox( + constraints: const BoxConstraints( + minWidth: double.infinity, + ), + child: FilledButton( + key: key, + onPressed: onPressed, + style: ElevatedButton.styleFrom( + tapTargetSize: MaterialTapTargetSize.padded, + ), + child: child ?? Text(text ?? ''), + ), + ); + } + + static ConstrainedBox text({ + required final VoidCallback onPressed, + required final String title, + final Key? key, + }) => + ConstrainedBox( + constraints: const BoxConstraints( + minHeight: 40, + minWidth: double.infinity, + ), + child: TextButton(onPressed: onPressed, child: Text(title)), + ); +} diff --git a/lib/ui/components/action_button/action_button.dart b/lib/ui/components/buttons/dialog_action_button.dart similarity index 64% rename from lib/ui/components/action_button/action_button.dart rename to lib/ui/components/buttons/dialog_action_button.dart index 3a518496..260cd493 100644 --- a/lib/ui/components/action_button/action_button.dart +++ b/lib/ui/components/buttons/dialog_action_button.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/brand_colors.dart'; -class ActionButton extends StatelessWidget { - const ActionButton({ - final super.key, +/// Basically a [TextButton] to be used in dialogs +class DialogActionButton extends StatelessWidget { + const DialogActionButton({ + super.key, this.text, this.onPressed, this.isRed = false, @@ -20,7 +20,9 @@ class ActionButton extends StatelessWidget { return TextButton( child: Text( text!, - style: isRed ? const TextStyle(color: BrandColors.red1) : null, + style: isRed + ? TextStyle(color: Theme.of(context).colorScheme.error) + : null, ), onPressed: () { navigator.pop(); diff --git a/lib/ui/components/buttons/outlined_button.dart b/lib/ui/components/buttons/outlined_button.dart new file mode 100644 index 00000000..306d1085 --- /dev/null +++ b/lib/ui/components/buttons/outlined_button.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; + +class BrandOutlinedButton extends StatelessWidget { + const BrandOutlinedButton({ + super.key, + this.onPressed, + this.title, + this.child, + this.disabled = false, + }); + + final VoidCallback? onPressed; + final String? title; + final Widget? child; + final bool disabled; + + @override + Widget build(final BuildContext context) => ConstrainedBox( + constraints: const BoxConstraints( + minWidth: double.infinity, + ), + child: OutlinedButton( + onPressed: onPressed, + style: OutlinedButton.styleFrom( + tapTargetSize: MaterialTapTargetSize.padded, + ), + child: child ?? + Text( + title ?? '', + style: Theme.of(context).textTheme.labelLarge?.copyWith( + color: Theme.of(context).colorScheme.primary, + ), + ), + ), + ); +} diff --git a/lib/ui/components/buttons/segmented_buttons.dart b/lib/ui/components/buttons/segmented_buttons.dart new file mode 100644 index 00000000..b876f71d --- /dev/null +++ b/lib/ui/components/buttons/segmented_buttons.dart @@ -0,0 +1,86 @@ +import 'package:flutter/material.dart'; + +/// For some reason original [SegmentedButton] does not have animations. +/// +/// The [SegmentedButtons] was written for SelfPrivacy before [SegmentedButton] was introduced. +/// While it doesn't have that much options to pass, it has cute little animation. +/// It is based on [ToggleButtons]. +class SegmentedButtons extends StatelessWidget { + /// Creates a segmented buttons widget. This is a SelfPrivacy implementation. + /// + /// Provide the button titles in [titles] as a [List]. + /// Current selection is provided in [isSelected] as a [List]. + /// This widget will call [onPressed] with the index of the button that was pressed. + const SegmentedButtons({ + required this.isSelected, + required this.onPressed, + required this.titles, + super.key, + }); + + /// The current selection state of the buttons. + /// + /// The length of this list must be equal to the length of [titles]. + /// Several buttons can be selected at the same time. + final List isSelected; + + /// The callback that is called when a button is pressed. + /// It will be called with the index of the button that was pressed. + final Function(int)? onPressed; + + /// The titles of the buttons. + final List titles; + + @override + Widget build(final BuildContext context) => LayoutBuilder( + builder: (final context, final constraints) => ToggleButtons( + constraints: BoxConstraints( + minWidth: (constraints.maxWidth - 8) / titles.length, + minHeight: 40 + Theme.of(context).visualDensity.vertical * 4, + ), + borderRadius: BorderRadius.circular(48), + borderColor: Theme.of(context).colorScheme.outline, + selectedBorderColor: Theme.of(context).colorScheme.outline, + fillColor: Theme.of(context).colorScheme.secondaryContainer, + selectedColor: Theme.of(context).colorScheme.onSecondaryContainer, + color: Theme.of(context).colorScheme.onSurface, + isSelected: isSelected, + onPressed: onPressed, + children: titles.asMap().entries.map((final entry) { + final index = entry.key; + final title = entry.value; + return Stack( + alignment: Alignment.centerLeft, + children: [ + AnimatedOpacity( + duration: const Duration(milliseconds: 200), + opacity: isSelected[index] ? 1 : 0, + child: AnimatedScale( + duration: const Duration(milliseconds: 200), + curve: Curves.easeInOutCubicEmphasized, + alignment: Alignment.centerLeft, + scale: isSelected[index] ? 1 : 0, + child: Icon( + Icons.check, + size: 18, + color: Theme.of(context).colorScheme.onSecondaryContainer, + ), + ), + ), + AnimatedPadding( + padding: isSelected[index] + ? const EdgeInsets.only(left: 24) + : EdgeInsets.zero, + duration: const Duration(milliseconds: 200), + curve: Curves.easeInOutCubicEmphasized, + child: Text( + title, + style: Theme.of(context).textTheme.labelLarge, + ), + ), + ], + ); + }).toList(), + ), + ); +} diff --git a/lib/ui/components/cards/filled_card.dart b/lib/ui/components/cards/filled_card.dart new file mode 100644 index 00000000..497f3c77 --- /dev/null +++ b/lib/ui/components/cards/filled_card.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; + +class FilledCard extends StatelessWidget { + const FilledCard({ + required this.child, + this.secondary = false, + this.tertiary = false, + this.error = false, + this.clipped = true, + super.key, + }); + + final Widget child; + final bool tertiary; + final bool error; + final bool clipped; + final bool secondary; + @override + Widget build(final BuildContext context) => Card( + elevation: 0.0, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(12)), + ), + clipBehavior: clipped ? Clip.antiAlias : Clip.none, + color: error + ? Theme.of(context).colorScheme.errorContainer + : secondary + ? Theme.of(context).colorScheme.secondaryContainer + : tertiary + ? Theme.of(context).colorScheme.tertiaryContainer + : Theme.of(context).colorScheme.surfaceVariant, + child: child, + ); +} diff --git a/lib/ui/components/cards/outlined_card.dart b/lib/ui/components/cards/outlined_card.dart new file mode 100644 index 00000000..d60fa9f0 --- /dev/null +++ b/lib/ui/components/cards/outlined_card.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; + +class OutlinedCard extends StatelessWidget { + const OutlinedCard({ + required this.child, + this.borderColor, + this.borderWidth, + super.key, + }); + + final Widget child; + final Color? borderColor; + final double? borderWidth; + @override + Widget build(final BuildContext context) => Card( + elevation: 0.0, + shape: RoundedRectangleBorder( + borderRadius: const BorderRadius.all(Radius.circular(12)), + side: BorderSide( + color: borderColor ?? Theme.of(context).colorScheme.outline, + width: borderWidth ?? 1.0, + ), + ), + clipBehavior: Clip.antiAlias, + child: child, + ); +} diff --git a/lib/ui/components/dots_indicator/dots_indicator.dart b/lib/ui/components/dots_indicator/dots_indicator.dart deleted file mode 100644 index fff647b7..00000000 --- a/lib/ui/components/dots_indicator/dots_indicator.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/brand_colors.dart'; - -class DotsIndicator extends StatelessWidget { - const DotsIndicator({ - required this.activeIndex, - required this.count, - final super.key, - }); - - final int activeIndex; - final int count; - - @override - Widget build(final BuildContext context) { - final List dots = List.generate( - count, - (final index) => Container( - margin: const EdgeInsets.symmetric(horizontal: 5, vertical: 10), - height: 10, - width: 10, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: index == activeIndex ? BrandColors.blue : BrandColors.gray2, - ), - ), - ); - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: dots, - ); - } -} diff --git a/lib/ui/components/drawers/progress_drawer.dart b/lib/ui/components/drawers/progress_drawer.dart new file mode 100644 index 00000000..d886da02 --- /dev/null +++ b/lib/ui/components/drawers/progress_drawer.dart @@ -0,0 +1,113 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; + +class ProgressDrawer extends StatelessWidget { + /// A [Drawer] that displays a list of steps and the current step. + /// Used in setup wizards. The [trailing] widget is displayed at the bottom. + /// The [steps] are translated using [EasyLocalization]. + const ProgressDrawer({ + required this.steps, + required this.currentStep, + required this.constraints, + required this.trailing, + required this.title, + super.key, + }); + + final List steps; + final int currentStep; + final Widget trailing; + final BoxConstraints constraints; + final String title; + + @override + Widget build(final BuildContext context) => SizedBox( + width: 300, + height: constraints.maxHeight, + child: Drawer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Text( + title, + style: Theme.of(context).textTheme.titleLarge, + ), + ), + Flexible( + fit: FlexFit.tight, + child: SingleChildScrollView( + child: Column( + children: [ + ...steps.map((final step) { + final index = steps.indexOf(step); + return _StepIndicator( + title: step.tr(), + isCurrent: index == currentStep, + isCompleted: index < currentStep, + ); + }), + ], + ), + ), + ), + // const Spacer(), + Padding( + padding: const EdgeInsets.all(16.0), + child: trailing, + ), + ], + ), + ), + ); +} + +class _StepIndicator extends StatelessWidget { + const _StepIndicator({ + required this.title, + required this.isCompleted, + required this.isCurrent, + }); + + final String title; + final bool isCompleted; + final bool isCurrent; + + @override + Widget build(final BuildContext context) => ListTile( + selected: isCurrent, + leading: isCurrent + ? const _StepCurrentIcon() + : isCompleted + ? const _StepCompletedIcon() + : const _StepPendingIcon(), + title: Text( + title, + ), + textColor: Theme.of(context).colorScheme.onSurfaceVariant, + iconColor: Theme.of(context).colorScheme.onSurfaceVariant, + ); +} + +class _StepCompletedIcon extends StatelessWidget { + const _StepCompletedIcon(); + + @override + Widget build(final BuildContext context) => const Icon(Icons.check_circle); +} + +class _StepPendingIcon extends StatelessWidget { + const _StepPendingIcon(); + + @override + Widget build(final BuildContext context) => const Icon(Icons.circle_outlined); +} + +class _StepCurrentIcon extends StatelessWidget { + const _StepCurrentIcon(); + + @override + Widget build(final BuildContext context) => + const Icon(Icons.build_circle_outlined); +} diff --git a/lib/ui/components/drawers/support_drawer.dart b/lib/ui/components/drawers/support_drawer.dart new file mode 100644 index 00000000..01fc292d --- /dev/null +++ b/lib/ui/components/drawers/support_drawer.dart @@ -0,0 +1,52 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:selfprivacy/logic/cubit/support_system/support_system_cubit.dart'; +import 'package:selfprivacy/ui/components/brand_md/brand_md.dart'; + +class SupportDrawer extends StatelessWidget { + const SupportDrawer({ + super.key, + }); + + @override + Widget build(final BuildContext context) { + final currentArticle = + context.watch().state.currentArticle; + return Drawer( + width: 440, + child: SingleChildScrollView( + child: SafeArea( + minimum: const EdgeInsets.all(8.0), + child: Column( + children: [ + Row( + children: [ + const SizedBox(width: 8), + const Icon(Icons.help_outline), + const SizedBox(width: 16), + Text( + 'support.title'.tr(), + style: Theme.of(context).textTheme.titleLarge, + ), + const Spacer(), + IconButton( + onPressed: () => Scaffold.of(context).closeEndDrawer(), + icon: const Icon(Icons.chevron_right_outlined), + ), + ], + ), + const SizedBox(height: 8), + Padding( + padding: const EdgeInsets.all(8.0), + child: BrandMarkdown( + fileName: currentArticle, + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/ui/components/error/error.dart b/lib/ui/components/error/error.dart deleted file mode 100644 index d12af1a3..00000000 --- a/lib/ui/components/error/error.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:flutter/material.dart'; - -class BrandError extends StatelessWidget { - const BrandError({final super.key, this.error, this.stackTrace}); - - final Object? error; - final StackTrace? stackTrace; - - @override - Widget build(final BuildContext context) => SafeArea( - child: Scaffold( - body: Center( - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text(error.toString()), - const Text('stackTrace: '), - Text(stackTrace.toString()), - ], - ), - ), - ), - ), - ); -} diff --git a/lib/ui/components/icon_status_mask/icon_status_mask.dart b/lib/ui/components/icon_status_mask/icon_status_mask.dart index 0c507ede..54d02aaf 100644 --- a/lib/ui/components/icon_status_mask/icon_status_mask.dart +++ b/lib/ui/components/icon_status_mask/icon_status_mask.dart @@ -4,11 +4,11 @@ import 'package:selfprivacy/logic/models/state_types.dart'; class IconStatusMask extends StatelessWidget { const IconStatusMask({ - required this.child, + required this.icon, required this.status, - final super.key, + super.key, }); - final Icon child; + final Widget icon; final StateType status; @@ -28,6 +28,12 @@ class IconStatusMask extends StatelessWidget { case StateType.warning: colors = BrandColors.warningGradientColors; break; + case StateType.error: + colors = [ + Theme.of(context).colorScheme.error, + Theme.of(context).colorScheme.error, + ]; + break; } return ShaderMask( shaderCallback: (final bounds) => LinearGradient( @@ -36,7 +42,7 @@ class IconStatusMask extends StatelessWidget { colors: colors, tileMode: TileMode.mirror, ).createShader(bounds), - child: child, + child: icon, ); } } diff --git a/lib/ui/components/info_box/info_box.dart b/lib/ui/components/info_box/info_box.dart new file mode 100644 index 00000000..c2e67def --- /dev/null +++ b/lib/ui/components/info_box/info_box.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; + +class InfoBox extends StatelessWidget { + const InfoBox({ + required this.text, + this.isWarning = false, + super.key, + }); + + final String text; + final bool isWarning; + + @override + Widget build(final BuildContext context) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon( + isWarning ? Icons.warning_amber_outlined : Icons.info_outline, + size: 24, + color: Theme.of(context).colorScheme.onBackground, + ), + const SizedBox(height: 16), + Text( + text, + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + color: Theme.of(context).colorScheme.onBackground, + ), + ), + ], + ); +} diff --git a/lib/ui/components/jobs_content/jobs_content.dart b/lib/ui/components/jobs_content/jobs_content.dart index bd8166de..8db853ac 100644 --- a/lib/ui/components/jobs_content/jobs_content.dart +++ b/lib/ui/components/jobs_content/jobs_content.dart @@ -2,124 +2,184 @@ import 'package:flutter/material.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:selfprivacy/config/brand_theme.dart'; -import 'package:selfprivacy/config/get_it_config.dart'; -import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart'; +import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; -import 'package:selfprivacy/ui/components/action_button/action_button.dart'; -import 'package:selfprivacy/ui/components/brand_alert/brand_alert.dart'; -import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; -import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart'; +import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_cubit.dart'; +import 'package:selfprivacy/logic/models/json/server_job.dart'; +import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; import 'package:selfprivacy/ui/components/brand_loader/brand_loader.dart'; -import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; +import 'package:selfprivacy/ui/components/jobs_content/server_job_card.dart'; +import 'package:selfprivacy/ui/helpers/modals.dart'; class JobsContent extends StatelessWidget { - const JobsContent({final super.key}); + const JobsContent({ + required this.controller, + super.key, + }); + + final ScrollController controller; @override - Widget build(final BuildContext context) => BlocBuilder( - builder: (final context, final state) { - late List widgets; - final ServerInstallationState installationState = - context.read().state; - if (state is JobsStateEmpty) { - widgets = [ - const SizedBox(height: 80), - Center(child: BrandText.body1('jobs.empty'.tr())), - ]; + Widget build(final BuildContext context) { + final List serverJobs = + context.watch().state.serverJobList; - if (installationState is ServerInstallationFinished) { - widgets = [ - ...widgets, - const SizedBox(height: 80), - BrandButton.rised( - onPressed: () => context.read().upgradeServer(), - text: 'jobs.upgradeServer'.tr(), - ), - const SizedBox(height: 10), - BrandButton.text( - onPressed: () { - final NavigationService nav = getIt(); - nav.showPopUpDialog( - BrandAlert( - title: 'jobs.rebootServer'.tr(), - contentText: 'modals.3'.tr(), - actions: [ - ActionButton( - text: 'basis.cancel'.tr(), - ), - ActionButton( - onPressed: () => - {context.read().rebootServer()}, - text: 'modals.9'.tr(), - ) - ], - ), - ); - }, - title: 'jobs.rebootServer'.tr(), - ), - ]; - } - } else if (state is JobsStateLoading) { + final bool hasRemovableJobs = + context.watch().state.hasRemovableJobs; + + return BlocBuilder( + builder: (final context, final state) { + late List widgets; + final ServerInstallationState installationState = + context.read().state; + if (state is JobsStateEmpty) { + widgets = [ + const SizedBox(height: 80), + Center( + child: Text( + 'jobs.empty'.tr(), + style: Theme.of(context).textTheme.bodyLarge, + ), + ), + ]; + + if (installationState is ServerInstallationFinished) { widgets = [ + ...widgets, const SizedBox(height: 80), - BrandLoader.horizontal(), - ]; - } else if (state is JobsStateWithJobs) { - widgets = [ - ...state.jobList - .map( - (final j) => Row( - children: [ - Expanded( - child: BrandCards.small( - child: Text(j.title), - ), - ), - const SizedBox(width: 10), - ElevatedButton( - style: ElevatedButton.styleFrom( - primary: - Theme.of(context).colorScheme.errorContainer, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - ), - ), - onPressed: () => - context.read().removeJob(j.id), - child: Text( - 'basis.remove'.tr(), - style: TextStyle( - color: Theme.of(context) - .colorScheme - .onErrorContainer, - ), - ), - ), - ], - ), - ) - .toList(), - const SizedBox(height: 20), BrandButton.rised( - onPressed: () => context.read().applyAll(), - text: 'jobs.start'.tr(), + onPressed: () => context.read().upgradeServer(), + text: 'jobs.upgrade_server'.tr(), + ), + const SizedBox(height: 10), + BrandButton.text( + title: 'jobs.reboot_server'.tr(), + onPressed: () { + showPopUpAlert( + alertTitle: 'jobs.reboot_server'.tr(), + description: 'modals.are_you_sure'.tr(), + actionButtonTitle: 'modals.reboot'.tr(), + actionButtonOnPressed: () => + {context.read().rebootServer()}, + ); + }, ), ]; } - return ListView( - padding: paddingH15V0, - children: [ - const SizedBox(height: 15), - Center( - child: BrandText.h2( - 'jobs.title'.tr(), + } else if (state is JobsStateLoading) { + widgets = [ + const SizedBox(height: 80), + BrandLoader.horizontal(), + ]; + } else if (state is JobsStateWithJobs) { + widgets = [ + ...state.clientJobList.map( + (final j) => Row( + children: [ + Expanded( + child: Card( + color: Theme.of(context).colorScheme.surfaceVariant, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 15, + vertical: 10, + ), + child: Text( + j.title, + style: + Theme.of(context).textTheme.labelLarge?.copyWith( + color: Theme.of(context) + .colorScheme + .onSurfaceVariant, + ), + ), + ), + ), + ), + const SizedBox(width: 10), + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: + Theme.of(context).colorScheme.errorContainer, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + ), + onPressed: () => context.read().removeJob(j.id), + child: Text( + 'basis.remove'.tr(), + style: TextStyle( + color: Theme.of(context).colorScheme.onErrorContainer, + ), + ), + ), + ], + ), + ), + const SizedBox(height: 20), + BrandButton.rised( + onPressed: () => context.read().applyAll(), + text: 'jobs.start'.tr(), + ), + ]; + } + return ListView( + controller: controller, + padding: paddingH15V0, + children: [ + const SizedBox(height: 16), + Center( + child: Text( + 'jobs.title'.tr(), + style: Theme.of(context).textTheme.headlineSmall, + ), + ), + const SizedBox(height: 20), + ...widgets, + const SizedBox(height: 8), + const Divider(height: 0), + const SizedBox(height: 8), + if (serverJobs.isNotEmpty) + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'jobs.server_jobs'.tr(), + style: Theme.of(context).textTheme.titleMedium, + ), + IconButton( + onPressed: hasRemovableJobs + ? () => context + .read() + .removeAllFinishedJobs() + : null, + icon: const Icon(Icons.clear_all), + color: Theme.of(context).colorScheme.onBackground, + ), + ], ), ), - const SizedBox(height: 20), - ...widgets - ], - ); - }, - ); + ...serverJobs.map( + (final job) => Dismissible( + key: ValueKey(job.uid), + direction: job.status == JobStatusEnum.finished || + job.status == JobStatusEnum.error + ? DismissDirection.horizontal + : DismissDirection.none, + child: ServerJobCard( + serverJob: job, + ), + onDismissed: (final direction) { + context.read().removeServerJob(job.uid); + }, + ), + ), + const SizedBox(height: 24), + ], + ); + }, + ); + } } diff --git a/lib/ui/components/jobs_content/server_job_card.dart b/lib/ui/components/jobs_content/server_job_card.dart new file mode 100644 index 00000000..a772e3c6 --- /dev/null +++ b/lib/ui/components/jobs_content/server_job_card.dart @@ -0,0 +1,95 @@ +import 'package:flutter/material.dart'; +import 'package:selfprivacy/logic/models/json/server_job.dart'; +import 'package:selfprivacy/ui/components/brand_linear_indicator/brand_linear_indicator.dart'; + +class ServerJobCard extends StatelessWidget { + const ServerJobCard({ + required this.serverJob, + super.key, + }); + + final ServerJob serverJob; + + @override + Widget build(final BuildContext context) { + Color color; + IconData icon; + + switch (serverJob.status) { + case JobStatusEnum.created: + color = Theme.of(context).colorScheme.secondary; + icon = Icons.query_builder_outlined; + break; + case JobStatusEnum.running: + color = Theme.of(context).colorScheme.tertiary; + icon = Icons.pending_outlined; + break; + case JobStatusEnum.finished: + color = Theme.of(context).colorScheme.primary; + icon = Icons.check_circle_outline; + break; + case JobStatusEnum.error: + color = Theme.of(context).colorScheme.error; + icon = Icons.error_outline; + break; + } + + final String? statusString = + serverJob.error ?? serverJob.result ?? serverJob.statusText; + + return GestureDetector( + child: Card( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + serverJob.name, + style: Theme.of(context).textTheme.bodyMedium, + ), + Text( + serverJob.description, + style: Theme.of(context).textTheme.bodySmall, + ), + ], + ), + ), + Icon( + icon, + color: color, + ), + ], + ), + const SizedBox(height: 8), + BrandLinearIndicator( + value: serverJob.progress == null + ? 0.0 + : serverJob.progress! / 100.0, + color: color, + backgroundColor: Theme.of(context).colorScheme.surfaceVariant, + height: 7.0, + ), + const SizedBox(height: 8), + if (statusString != null) + Text( + statusString, + style: Theme.of(context).textTheme.labelSmall?.copyWith( + color: Theme.of(context).colorScheme.onSurface, + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/ui/components/list_tiles/list_tile_on_surface_variant.dart b/lib/ui/components/list_tiles/list_tile_on_surface_variant.dart new file mode 100644 index 00000000..3d8d7a84 --- /dev/null +++ b/lib/ui/components/list_tiles/list_tile_on_surface_variant.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; + +class ListTileOnSurfaceVariant extends StatelessWidget { + const ListTileOnSurfaceVariant({ + required this.title, + this.subtitle, + this.leadingIcon, + this.onTap, + this.disableSubtitleOverflow = false, + super.key, + }); + + final String title; + final String? subtitle; + final IconData? leadingIcon; + final Function()? onTap; + final bool disableSubtitleOverflow; + + Widget? getSubtitle() { + if (subtitle == null) { + return null; + } + if (disableSubtitleOverflow) { + return Text( + subtitle!, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ); + } + return Text( + subtitle!, + ); + } + + @override + Widget build(final BuildContext context) => ListTile( + title: Text(title), + subtitle: getSubtitle(), + onTap: onTap, + textColor: Theme.of(context).colorScheme.onSurfaceVariant, + leading: leadingIcon != null ? Icon(leadingIcon) : null, + iconColor: Theme.of(context).colorScheme.onSurfaceVariant, + ); +} diff --git a/lib/ui/components/list_tiles/log_list_tile.dart b/lib/ui/components/list_tiles/log_list_tile.dart new file mode 100644 index 00000000..e83765e9 --- /dev/null +++ b/lib/ui/components/list_tiles/log_list_tile.dart @@ -0,0 +1,304 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:selfprivacy/logic/models/message.dart'; +import 'package:selfprivacy/utils/platform_adapter.dart'; + +class LogListItem extends StatelessWidget { + const LogListItem({ + required this.message, + super.key, + }); + + final Message message; + + @override + Widget build(final BuildContext context) { + final messageItem = message; + if (messageItem is RestApiRequestMessage) { + return _RestApiRequestMessageItem(message: messageItem); + } else if (messageItem is RestApiResponseMessage) { + return _RestApiResponseMessageItem(message: messageItem); + } else if (messageItem is GraphQlResponseMessage) { + return _GraphQlResponseMessageItem(message: messageItem); + } else if (messageItem is GraphQlRequestMessage) { + return _GraphQlRequestMessageItem(message: messageItem); + } else { + return _DefaultMessageItem(message: messageItem); + } + } +} + +class _RestApiRequestMessageItem extends StatelessWidget { + const _RestApiRequestMessageItem({required this.message}); + + final RestApiRequestMessage message; + + @override + Widget build(final BuildContext context) => ListTile( + title: Text( + '${message.method}\n${message.uri}', + ), + subtitle: Text(message.timeString), + leading: const Icon(Icons.upload_outlined), + iconColor: Theme.of(context).colorScheme.secondary, + onTap: () => showDialog( + context: context, + builder: (final BuildContext context) => AlertDialog( + scrollable: true, + title: Text( + '${message.method}\n${message.uri}', + ), + content: Column( + children: [ + Text(message.timeString), + const SizedBox(height: 16), + // Headers is a map of key-value pairs + if (message.headers != null) const Text('Headers'), + if (message.headers != null) + Text( + message.headers!.entries + .map((final entry) => '${entry.key}: ${entry.value}') + .join('\n'), + ), + if (message.data != null && message.data != 'null') + const Text('Data'), + if (message.data != null && message.data != 'null') + Text(message.data!), + ], + ), + actions: [ + // A button to copy the request to the clipboard + if (message.text != null) + TextButton( + onPressed: () { + PlatformAdapter.setClipboard(message.text ?? ''); + }, + child: Text('console_page.copy'.tr()), + ), + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: Text('basis.close'.tr()), + ), + ], + ), + ), + ); +} + +class _RestApiResponseMessageItem extends StatelessWidget { + const _RestApiResponseMessageItem({required this.message}); + + final RestApiResponseMessage message; + + @override + Widget build(final BuildContext context) => ListTile( + title: Text( + '${message.statusCode} ${message.method}\n${message.uri}', + ), + subtitle: Text(message.timeString), + leading: const Icon(Icons.download_outlined), + iconColor: Theme.of(context).colorScheme.primary, + onTap: () => showDialog( + context: context, + builder: (final BuildContext context) => AlertDialog( + scrollable: true, + title: Text( + '${message.statusCode} ${message.method}\n${message.uri}', + ), + content: Column( + children: [ + Text(message.timeString), + const SizedBox(height: 16), + // Headers is a map of key-value pairs + if (message.data != null && message.data != 'null') + const Text('Data'), + if (message.data != null && message.data != 'null') + Text(message.data!), + ], + ), + actions: [ + // A button to copy the request to the clipboard + if (message.text != null) + TextButton( + onPressed: () { + PlatformAdapter.setClipboard(message.text ?? ''); + }, + child: Text('console_page.copy'.tr()), + ), + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: Text('basis.close'.tr()), + ), + ], + ), + ), + ); +} + +class _GraphQlResponseMessageItem extends StatelessWidget { + const _GraphQlResponseMessageItem({required this.message}); + + final GraphQlResponseMessage message; + + @override + Widget build(final BuildContext context) => ListTile( + title: Text( + 'GraphQL Response at ${message.timeString}', + ), + subtitle: Text( + message.data.toString(), + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + leading: const Icon(Icons.arrow_circle_down_outlined), + iconColor: Theme.of(context).colorScheme.tertiary, + onTap: () => showDialog( + context: context, + builder: (final BuildContext context) => AlertDialog( + scrollable: true, + title: Text( + 'GraphQL Response at ${message.timeString}', + ), + content: Column( + children: [ + Text(message.timeString), + const Divider(), + if (message.data != null) const Text('Data'), + // Data is a map of key-value pairs + if (message.data != null) + Text( + message.data!.entries + .map((final entry) => '${entry.key}: ${entry.value}') + .join('\n'), + ), + const Divider(), + if (message.errors != null) const Text('Errors'), + if (message.errors != null) + Text( + message.errors! + .map( + (final entry) => + '${entry.message} at ${entry.locations}', + ) + .join('\n'), + ), + const Divider(), + if (message.context != null) const Text('Context'), + if (message.context != null) + Text( + message.context!.toString(), + ), + ], + ), + actions: [ + // A button to copy the request to the clipboard + if (message.text != null) + TextButton( + onPressed: () { + PlatformAdapter.setClipboard(message.text ?? ''); + }, + child: Text('console_page.copy'.tr()), + ), + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: Text('basis.close'.tr()), + ), + ], + ), + ), + ); +} + +class _GraphQlRequestMessageItem extends StatelessWidget { + const _GraphQlRequestMessageItem({required this.message}); + + final GraphQlRequestMessage message; + + @override + Widget build(final BuildContext context) => ListTile( + title: Text( + 'GraphQL Request at ${message.timeString}', + ), + subtitle: Text( + message.operation.toString(), + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + leading: const Icon(Icons.arrow_circle_up_outlined), + iconColor: Theme.of(context).colorScheme.secondary, + onTap: () => showDialog( + context: context, + builder: (final BuildContext context) => AlertDialog( + scrollable: true, + title: Text( + 'GraphQL Response at ${message.timeString}', + ), + content: Column( + children: [ + Text(message.timeString), + const Divider(), + if (message.operation != null) const Text('Operation'), + // Data is a map of key-value pairs + if (message.operation != null) + Text( + message.operation!.toString(), + ), + const Divider(), + if (message.variables != null) const Text('Variables'), + if (message.variables != null) + Text( + message.variables!.entries + .map((final entry) => '${entry.key}: ${entry.value}') + .join('\n'), + ), + const Divider(), + if (message.context != null) const Text('Context'), + if (message.context != null) + Text( + message.context!.toString(), + ), + ], + ), + actions: [ + // A button to copy the request to the clipboard + if (message.text != null) + TextButton( + onPressed: () { + PlatformAdapter.setClipboard(message.text ?? ''); + }, + child: Text('console_page.copy'.tr()), + ), + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: Text('basis.close'.tr()), + ), + ], + ), + ), + ); +} + +class _DefaultMessageItem extends StatelessWidget { + const _DefaultMessageItem({required this.message}); + + final Message message; + + @override + Widget build(final BuildContext context) => Padding( + padding: const EdgeInsets.symmetric(vertical: 4), + child: RichText( + text: TextSpan( + style: DefaultTextStyle.of(context).style, + children: [ + TextSpan( + text: '${message.timeString}: \n', + style: const TextStyle( + fontWeight: FontWeight.bold, + ), + ), + TextSpan(text: message.text), + ], + ), + ), + ); +} diff --git a/lib/ui/components/not_ready_card/not_ready_card.dart b/lib/ui/components/not_ready_card/not_ready_card.dart index 49947c1b..4b174a41 100644 --- a/lib/ui/components/not_ready_card/not_ready_card.dart +++ b/lib/ui/components/not_ready_card/not_ready_card.dart @@ -1,53 +1,29 @@ +import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/brand_colors.dart'; -import 'package:selfprivacy/config/text_themes.dart'; -import 'package:selfprivacy/ui/pages/setup/initializing.dart'; -import 'package:selfprivacy/utils/route_transitions/basic.dart'; +import 'package:selfprivacy/ui/components/cards/filled_card.dart'; +import 'package:selfprivacy/ui/router/router.dart'; import 'package:easy_localization/easy_localization.dart'; class NotReadyCard extends StatelessWidget { - const NotReadyCard({final super.key}); + const NotReadyCard({super.key}); @override - Widget build(final BuildContext context) => Container( - padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(15), - color: BrandColors.gray6, - ), - child: RichText( - text: TextSpan( - children: [ - TextSpan( - text: 'not_ready_card.1'.tr(), - style: const TextStyle(color: BrandColors.white), - ), - WidgetSpan( - child: Padding( - padding: const EdgeInsets.only(bottom: 0.5), - child: GestureDetector( - onTap: () => Navigator.of(context).push( - materialRoute( - const InitializingPage(), - ), - ), - child: Text( - 'not_ready_card.2'.tr(), - style: body1Style.copyWith( - color: Colors.white, - fontWeight: FontWeight.bold, - decoration: TextDecoration.underline, - // height: 1.1, - ), - ), - ), + Widget build(final BuildContext context) => FilledCard( + tertiary: true, + child: ListTile( + contentPadding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + onTap: () => context.pushRoute(const InitializingRoute()), + title: Text( + 'not_ready_card.in_menu'.tr(), + style: Theme.of(context).textTheme.titleSmall?.copyWith( + color: Theme.of(context).colorScheme.onTertiaryContainer, ), - ), - TextSpan( - text: 'not_ready_card.3'.tr(), - style: const TextStyle(color: BrandColors.white), - ), - ], + ), + trailing: Icon( + Icons.arrow_forward_ios_outlined, + size: 16, + color: Theme.of(context).colorScheme.onTertiaryContainer, ), ), ); diff --git a/lib/ui/components/one_page/one_page.dart b/lib/ui/components/one_page/one_page.dart deleted file mode 100644 index d16dd5f3..00000000 --- a/lib/ui/components/one_page/one_page.dart +++ /dev/null @@ -1,48 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart'; -import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:selfprivacy/ui/components/pre_styled_buttons/pre_styled_buttons.dart'; - -class OnePage extends StatelessWidget { - const OnePage({ - required this.title, - required this.child, - final super.key, - }); - - final String title; - final Widget child; - - @override - Widget build(final BuildContext context) => Scaffold( - appBar: PreferredSize( - preferredSize: const Size.fromHeight(52), - child: Column( - children: [ - Container( - height: 51, - alignment: Alignment.center, - padding: const EdgeInsets.symmetric(horizontal: 15), - child: BrandText.h4('basis.details'.tr()), - ), - const BrandDivider(), - ], - ), - ), - body: child, - bottomNavigationBar: SafeArea( - child: Container( - decoration: BoxDecoration(boxShadow: kElevationToShadow[3]), - height: kBottomNavigationBarHeight, - child: Container( - color: Theme.of(context).scaffoldBackgroundColor, - alignment: Alignment.center, - child: PreStyledButtons.close( - onPress: () => Navigator.of(context).pop(), - ), - ), - ), - ), - ); -} diff --git a/lib/ui/components/pre_styled_buttons/close.dart b/lib/ui/components/pre_styled_buttons/close.dart deleted file mode 100644 index 48a1bddb..00000000 --- a/lib/ui/components/pre_styled_buttons/close.dart +++ /dev/null @@ -1,19 +0,0 @@ -part of 'pre_styled_buttons.dart'; - -class _CloseButton extends StatelessWidget { - const _CloseButton({required this.onPress}); - - final VoidCallback onPress; - - @override - Widget build(final BuildContext context) => OutlinedButton( - onPressed: () => Navigator.of(context).pop(), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - BrandText.h4('basis.close'.tr()), - const Icon(Icons.close), - ], - ), - ); -} diff --git a/lib/ui/components/pre_styled_buttons/flash.dart b/lib/ui/components/pre_styled_buttons/flash.dart deleted file mode 100644 index 3e780fd7..00000000 --- a/lib/ui/components/pre_styled_buttons/flash.dart +++ /dev/null @@ -1,87 +0,0 @@ -part of 'pre_styled_buttons.dart'; - -class _BrandFlashButton extends StatefulWidget { - @override - _BrandFlashButtonState createState() => _BrandFlashButtonState(); -} - -class _BrandFlashButtonState extends State<_BrandFlashButton> - with SingleTickerProviderStateMixin { - late AnimationController _animationController; - late Animation _colorTween; - - @override - void initState() { - _animationController = AnimationController( - vsync: this, - duration: const Duration(milliseconds: 800), - ); - _colorTween = ColorTween( - begin: BrandColors.black, - end: BrandColors.primary, - ).animate(_animationController); - - super.initState(); - WidgetsBinding.instance.addPostFrameCallback(_afterLayout); - } - - void _afterLayout(final _) { - if (Theme.of(context).brightness == Brightness.dark) { - setState(() { - _colorTween = ColorTween( - begin: BrandColors.white, - end: BrandColors.primary, - ).animate(_animationController); - }); - } - } - - @override - void dispose() { - _animationController.dispose(); - super.dispose(); - } - - bool wasPrevStateIsEmpty = true; - - @override - Widget build(final BuildContext context) => - BlocListener( - listener: (final context, final state) { - if (wasPrevStateIsEmpty && state is! JobsStateEmpty) { - wasPrevStateIsEmpty = false; - _animationController.forward(); - } else if (!wasPrevStateIsEmpty && state is JobsStateEmpty) { - wasPrevStateIsEmpty = true; - - _animationController.reverse(); - } - }, - child: IconButton( - onPressed: () { - showBrandBottomSheet( - context: context, - builder: (final context) => const BrandBottomSheet( - isExpended: true, - child: JobsContent(), - ), - ); - }, - icon: AnimatedBuilder( - animation: _colorTween, - builder: (final context, final child) { - final double v = _animationController.value; - final IconData icon = - v > 0.5 ? Ionicons.flash : Ionicons.flash_outline; - return Transform.scale( - scale: 1 + (v < 0.5 ? v : 1 - v) * 2, - child: Icon( - icon, - color: _colorTween.value, - ), - ); - }, - ), - ), - ); -} diff --git a/lib/ui/components/pre_styled_buttons/flash_fab.dart b/lib/ui/components/pre_styled_buttons/flash_fab.dart index 4ae29087..caffbfac 100644 --- a/lib/ui/components/pre_styled_buttons/flash_fab.dart +++ b/lib/ui/components/pre_styled_buttons/flash_fab.dart @@ -1,13 +1,17 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:ionicons/ionicons.dart'; -import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart'; -import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart'; +import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart'; import 'package:selfprivacy/ui/components/jobs_content/jobs_content.dart'; -import 'package:selfprivacy/ui/helpers/modals.dart'; class BrandFab extends StatefulWidget { - const BrandFab({final super.key}); + const BrandFab({ + this.extended = false, + super.key, + }); + + final bool extended; @override State createState() => _BrandFabState(); @@ -55,28 +59,50 @@ class _BrandFabState extends State }, child: FloatingActionButton( onPressed: () { - showBrandBottomSheet( + // TODO: Make a hero animation to the screen + showModalBottomSheet( context: context, - builder: (final BuildContext context) => const BrandBottomSheet( - isExpended: true, - child: JobsContent(), + useRootNavigator: true, + isScrollControlled: true, + builder: (final BuildContext context) => DraggableScrollableSheet( + expand: false, + maxChildSize: 0.9, + minChildSize: 0.4, + initialChildSize: 0.6, + builder: (final context, final scrollController) => + JobsContent(controller: scrollController), ), ); }, - child: AnimatedBuilder( - animation: _colorTween, - builder: (final BuildContext context, final Widget? child) { - final double v = _animationController.value; - final IconData icon = - v > 0.5 ? Ionicons.flash : Ionicons.flash_outline; - return Transform.scale( - scale: 1 + (v < 0.5 ? v : 1 - v) * 2, - child: Icon( - icon, - color: _colorTween.value, + isExtended: widget.extended, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + AnimatedBuilder( + animation: _colorTween, + builder: (final BuildContext context, final Widget? child) { + final double v = _animationController.value; + final IconData icon = + v > 0.5 ? Ionicons.flash : Ionicons.flash_outline; + return Transform.scale( + scale: 1 + (v < 0.5 ? v : 1 - v) * 2, + child: Icon( + icon, + color: _colorTween.value, + ), + ); + }, + ), + if (widget.extended) + const SizedBox( + width: 8, ), - ); - }, + if (widget.extended) + Text( + 'jobs.title'.tr(), + ), + ], ), ), ); diff --git a/lib/ui/components/pre_styled_buttons/pre_styled_buttons.dart b/lib/ui/components/pre_styled_buttons/pre_styled_buttons.dart deleted file mode 100644 index ad9105fb..00000000 --- a/lib/ui/components/pre_styled_buttons/pre_styled_buttons.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:ionicons/ionicons.dart'; -import 'package:selfprivacy/config/brand_colors.dart'; -import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart'; -import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart'; -import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:selfprivacy/ui/components/jobs_content/jobs_content.dart'; -import 'package:selfprivacy/ui/helpers/modals.dart'; - -part 'close.dart'; -part 'flash.dart'; - -class PreStyledButtons { - static Widget close({ - required final VoidCallback onPress, - }) => - _CloseButton(onPress: onPress); - - static Widget flash() => _BrandFlashButton(); -} diff --git a/lib/ui/components/progress_bar/progress_bar.dart b/lib/ui/components/progress_bar/progress_bar.dart index 4de729f7..7f743f44 100644 --- a/lib/ui/components/progress_bar/progress_bar.dart +++ b/lib/ui/components/progress_bar/progress_bar.dart @@ -1,15 +1,11 @@ import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/brand_colors.dart'; -import 'package:selfprivacy/config/text_themes.dart'; import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart'; -import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; -import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; class ProgressBar extends StatefulWidget { const ProgressBar({ required this.steps, required this.activeIndex, - final super.key, + super.key, }); final int activeIndex; @@ -64,17 +60,10 @@ class _ProgressBarState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - BrandText.h2('Progress'), - const SizedBox(height: 10), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: even, - ), - const SizedBox(height: 7), Container( alignment: Alignment.centerLeft, decoration: BoxDecoration( - color: BrandColors.gray4, + color: const Color(0xFFDDDDDD), borderRadius: BorderRadius.circular(5), ), child: LayoutBuilder( @@ -83,10 +72,14 @@ class _ProgressBarState extends State { height: 5, decoration: BoxDecoration( borderRadius: BorderRadius.circular(5), - gradient: const LinearGradient( + color: Theme.of(context).colorScheme.surfaceVariant, + gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, - colors: BrandColors.stableGradientColors, + colors: [ + Theme.of(context).colorScheme.primary, + Theme.of(context).colorScheme.secondary + ], ), ), duration: const Duration( @@ -95,11 +88,6 @@ class _ProgressBarState extends State { ), ), ), - const SizedBox(height: 5), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: odd, - ), ], ); } @@ -110,7 +98,6 @@ class _ProgressBarState extends State { final String? step, }) { final bool isActive = index == widget.activeIndex; - final bool checked = index < widget.activeIndex; style = isActive ? style!.copyWith(fontWeight: FontWeight.w700) : style; return Container( @@ -122,15 +109,7 @@ class _ProgressBarState extends State { text: TextSpan( style: progressTextStyleLight, children: [ - if (checked) - const WidgetSpan( - child: Padding( - padding: EdgeInsets.only(bottom: 2, right: 2), - child: Icon(BrandIcons.check, size: 11), - ), - ) - else - TextSpan(text: '${index + 1}.', style: style), + TextSpan(text: '${index + 1}.', style: style), TextSpan(text: step, style: style) ], ), @@ -138,3 +117,13 @@ class _ProgressBarState extends State { ); } } + +const TextStyle progressTextStyleLight = TextStyle( + fontSize: 11, + color: Colors.black, + height: 1.7, +); + +final TextStyle progressTextStyleDark = progressTextStyleLight.copyWith( + color: Colors.white, +); diff --git a/lib/ui/components/storage_list_items/server_storage_list_item.dart b/lib/ui/components/storage_list_items/server_storage_list_item.dart new file mode 100644 index 00000000..52164d78 --- /dev/null +++ b/lib/ui/components/storage_list_items/server_storage_list_item.dart @@ -0,0 +1,124 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:selfprivacy/ui/components/brand_linear_indicator/brand_linear_indicator.dart'; +import 'package:selfprivacy/logic/models/disk_status.dart'; + +class ServerStorageListItem extends StatelessWidget { + const ServerStorageListItem({ + required this.volume, + this.showIcon = true, + this.dense = false, + super.key, + }); + + final DiskVolume volume; + final bool showIcon; + final bool dense; + + @override + Widget build(final BuildContext context) => ConsumptionListItem( + title: 'storage.disk_usage'.tr( + args: [ + volume.sizeUsed.toString(), + ], + ), + subtitle: 'storage.disk_total'.tr( + args: [ + volume.sizeTotal.toString(), + volume.displayName, + ], + ), + dense: dense, + color: volume.root + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.secondary, + backgroundColor: Theme.of(context).colorScheme.surfaceVariant, + percentage: volume.percentage, + icon: Icon( + Icons.storage_outlined, + size: 24, + color: Theme.of(context).colorScheme.onBackground, + ), + ); +} + +class ConsumptionListItem extends StatelessWidget { + const ConsumptionListItem({ + required this.title, + required this.color, + required this.backgroundColor, + required this.percentage, + this.subtitle, + this.rightSideText, + this.icon, + this.dense = false, + super.key, + }); + + final String title; + final String? subtitle; + final String? rightSideText; + final Color color; + final Color backgroundColor; + final double percentage; + final Widget? icon; + final bool dense; + + @override + Widget build(final BuildContext context) { + final TextStyle? titleStyle = dense + ? Theme.of(context).textTheme.titleMedium + : Theme.of(context).textTheme.titleLarge; + + final TextStyle? subtitleStyle = dense + ? Theme.of(context).textTheme.bodySmall + : Theme.of(context).textTheme.bodyMedium; + + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + if (icon != null) icon!, + if (icon != null) const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: rightSideText != null + ? MainAxisAlignment.spaceBetween + : MainAxisAlignment.start, + children: [ + Text( + title, + style: titleStyle, + textAlign: TextAlign.start, + ), + if (rightSideText != null) + Text( + rightSideText!, + style: Theme.of(context).textTheme.bodyMedium, + textAlign: TextAlign.end, + ), + ], + ), + const SizedBox(height: 4), + BrandLinearIndicator( + value: percentage, + color: color, + backgroundColor: backgroundColor, + height: dense ? 8.0 : 14.0, + ), + const SizedBox(height: 4), + if (subtitle != null) + Text( + subtitle!, + style: subtitleStyle, + textAlign: TextAlign.start, + ), + ], + ), + ), + ], + ); + } +} diff --git a/lib/ui/components/storage_list_items/service_migration_list_item.dart b/lib/ui/components/storage_list_items/service_migration_list_item.dart new file mode 100644 index 00000000..5229658d --- /dev/null +++ b/lib/ui/components/storage_list_items/service_migration_list_item.dart @@ -0,0 +1,103 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:selfprivacy/logic/models/service.dart'; +import 'package:selfprivacy/logic/models/disk_status.dart'; + +class ServiceMigrationListItem extends StatelessWidget { + const ServiceMigrationListItem({ + required this.service, + required this.diskStatus, + required this.selectedVolume, + required this.onChange, + super.key, + }); + + final Service service; + final DiskStatus diskStatus; + final String selectedVolume; + final Function onChange; + + @override + Widget build(final BuildContext context) => Column( + children: [ + ServiceConsumptionTitle(service: service), + const SizedBox(height: 16), + ..._radioRows(context), + ], + ); + + List _radioRows(final BuildContext context) { + final List volumeRows = []; + + for (final DiskVolume volume in diskStatus.diskVolumes) { + volumeRows.add( + RadioListTile( + title: Text( + volume.displayName, + ), + contentPadding: EdgeInsets.zero, + activeColor: Theme.of(context).colorScheme.secondary, + dense: true, + value: volume.name, + groupValue: selectedVolume, + onChanged: (final value) { + onChange(value, service.id); + }, + ), + ); + } + + return volumeRows; + } +} + +class ServiceConsumptionTitle extends StatelessWidget { + const ServiceConsumptionTitle({ + required this.service, + super.key, + }); + + final Service service; + + @override + Widget build(final BuildContext context) => SizedBox( + height: 24, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Row( + children: [ + Container( + alignment: Alignment.topLeft, + child: SvgPicture.string( + service.svgIcon, + width: 24.0, + height: 24.0, + colorFilter: ColorFilter.mode( + Theme.of(context).colorScheme.onBackground, + BlendMode.srcIn, + ), + ), + ), + const SizedBox(width: 16), + Container( + alignment: Alignment.topLeft, + child: Text( + service.displayName, + style: Theme.of(context).textTheme.titleMedium, + ), + ), + const SizedBox(width: 16), + Expanded( + child: Container( + alignment: Alignment.centerRight, + child: Text( + service.storageUsage.used.toString(), + style: Theme.of(context).textTheme.bodyMedium, + ), + ), + ), + ], + ), + ), + ); +} diff --git a/lib/ui/components/storage_list_items/service_storage_consumption_list_item.dart b/lib/ui/components/storage_list_items/service_storage_consumption_list_item.dart new file mode 100644 index 00000000..ffdb4e34 --- /dev/null +++ b/lib/ui/components/storage_list_items/service_storage_consumption_list_item.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; +import 'package:selfprivacy/ui/components/brand_linear_indicator/brand_linear_indicator.dart'; + +class ServiceStorageConsumptionListItem extends StatelessWidget { + const ServiceStorageConsumptionListItem({ + required this.title, + required this.percentage, + required this.storageConsumptionText, + required this.color, + required this.icon, + super.key, + }); + + final String title; + final double percentage; + final String storageConsumptionText; + final Color color; + final IconData icon; + @override + Widget build(final BuildContext context) => Row( + children: [ + Icon( + icon, + ), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + title, + style: Theme.of(context).textTheme.titleMedium, + ), + Text( + storageConsumptionText, + style: Theme.of(context).textTheme.bodyMedium, + ), + ], + ), + const SizedBox(height: 4), + BrandLinearIndicator( + value: percentage, + color: color, + backgroundColor: Theme.of(context).colorScheme.surfaceVariant, + height: 7.0, + ), + ], + ), + ), + ], + ); +} diff --git a/lib/ui/components/switch_block/switch_bloc.dart b/lib/ui/components/switch_block/switch_bloc.dart deleted file mode 100644 index ae593f1e..00000000 --- a/lib/ui/components/switch_block/switch_bloc.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/brand_colors.dart'; - -class SwitcherBlock extends StatelessWidget { - const SwitcherBlock({ - required this.child, - required this.isActive, - required this.onChange, - final super.key, - }); - - final Widget child; - final bool isActive; - final ValueChanged onChange; - - @override - Widget build(final BuildContext context) => Container( - padding: const EdgeInsets.only(top: 20, bottom: 5), - decoration: const BoxDecoration( - border: Border( - bottom: BorderSide(width: 1, color: BrandColors.dividerColor), - ), - ), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible(child: child), - const SizedBox(width: 5), - Switch( - activeColor: BrandColors.green1, - activeTrackColor: BrandColors.green2, - onChanged: onChange, - value: isActive, - ), - ], - ), - ); -} diff --git a/lib/ui/helpers/empty_page_placeholder.dart b/lib/ui/helpers/empty_page_placeholder.dart new file mode 100644 index 00000000..e1bb0d7b --- /dev/null +++ b/lib/ui/helpers/empty_page_placeholder.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; +import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart'; + +class EmptyPagePlaceholder extends StatelessWidget { + const EmptyPagePlaceholder({ + required this.title, + required this.iconData, + required this.description, + this.showReadyCard = false, + super.key, + }); + + final String title; + final String description; + final IconData iconData; + final bool showReadyCard; + + @override + Widget build(final BuildContext context) => !showReadyCard + ? _expandedContent(context) + : Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Padding( + padding: EdgeInsets.symmetric(horizontal: 15), + child: NotReadyCard(), + ), + Expanded( + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: Center( + child: _expandedContent(context), + ), + ), + ) + ], + ); + + Widget _expandedContent(final BuildContext context) => Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + iconData, + size: 50, + color: Theme.of(context).colorScheme.onBackground, + ), + const SizedBox(height: 16), + Text( + title, + style: Theme.of(context).textTheme.headlineMedium?.copyWith( + color: Theme.of(context).colorScheme.onBackground, + ), + ), + const SizedBox(height: 8), + Text( + description, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.titleSmall?.copyWith( + color: Theme.of(context).colorScheme.onBackground, + ), + ), + ], + ), + ); +} diff --git a/lib/ui/helpers/modals.dart b/lib/ui/helpers/modals.dart index 8867885f..b744e323 100644 --- a/lib/ui/helpers/modals.dart +++ b/lib/ui/helpers/modals.dart @@ -1,14 +1,31 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/ui/components/buttons/dialog_action_button.dart'; -Future showBrandBottomSheet({ - required final BuildContext context, - required final WidgetBuilder builder, -}) => - showCupertinoModalBottomSheet( - builder: builder, - barrierColor: Colors.black45, - context: context, - shadow: const BoxShadow(color: Colors.transparent), - backgroundColor: Colors.transparent, - ); +void showPopUpAlert({ + required final String description, + required final String actionButtonTitle, + required final void Function() actionButtonOnPressed, + final void Function()? cancelButtonOnPressed, + final String? alertTitle, + final String? cancelButtonTitle, +}) { + getIt.get().showPopUpDialog( + AlertDialog( + title: Text(alertTitle ?? 'basis.alert'.tr()), + content: Text(description), + actions: [ + DialogActionButton( + text: actionButtonTitle, + isRed: true, + onPressed: actionButtonOnPressed, + ), + DialogActionButton( + text: cancelButtonTitle ?? 'basis.cancel'.tr(), + onPressed: cancelButtonOnPressed, + ), + ], + ), + ); +} diff --git a/lib/ui/helpers/widget_size.dart b/lib/ui/helpers/widget_size.dart new file mode 100644 index 00000000..bbd1529f --- /dev/null +++ b/lib/ui/helpers/widget_size.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; + +/// A helper widget that calls a callback when its size changes. +/// +/// This is useful when you want to know the size of a widget, and use it in +/// another leaf of the tree. +/// +/// The [onChange] callback is called after the widget is rendered, and the +/// size of the widget is different from the previous render. +class WidgetSize extends StatefulWidget { + /// Creates a helper widget that calls a callback when its size changes. + const WidgetSize({ + required this.onChange, + required this.child, + super.key, + }); + + /// The child widget, the size of which is to be measured. + final Widget child; + + /// The callback to be called when the size of the widget changes. + final Function(Size) onChange; + + @override + State createState() => _WidgetSizeState(); +} + +class _WidgetSizeState extends State { + @override + Widget build(final BuildContext context) { + SchedulerBinding.instance.addPostFrameCallback(postFrameCallback); + return Container( + key: widgetKey, + child: widget.child, + ); + } + + var widgetKey = GlobalKey(); + Size? oldSize; + + void postFrameCallback(final _) { + final context = widgetKey.currentContext; + if (context == null) { + return; + } + + final newSize = context.size; + + if (newSize == null) { + return; + } + + if (oldSize == newSize) { + return; + } + + oldSize = newSize; + widget.onChange(newSize); + } +} diff --git a/lib/ui/layouts/brand_hero_screen.dart b/lib/ui/layouts/brand_hero_screen.dart new file mode 100644 index 00000000..a7b8be74 --- /dev/null +++ b/lib/ui/layouts/brand_hero_screen.dart @@ -0,0 +1,205 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:ionicons/ionicons.dart'; +import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart'; +import 'package:selfprivacy/ui/components/jobs_content/jobs_content.dart'; +import 'package:selfprivacy/ui/components/drawers/support_drawer.dart'; +import 'package:selfprivacy/ui/helpers/widget_size.dart'; +import 'package:selfprivacy/utils/breakpoints.dart'; + +class BrandHeroScreen extends StatelessWidget { + const BrandHeroScreen({ + required this.children, + super.key, + this.hasBackButton = true, + this.hasFlashButton = false, + this.heroIcon, + this.heroIconWidget, + this.heroTitle = '', + this.heroSubtitle, + this.onBackButtonPressed, + this.bodyPadding = const EdgeInsets.all(16.0), + this.ignoreBreakpoints = false, + this.hasSupportDrawer = false, + }); + + final List children; + final bool hasBackButton; + final bool hasFlashButton; + final IconData? heroIcon; + final Widget? heroIconWidget; + final String heroTitle; + final String? heroSubtitle; + final VoidCallback? onBackButtonPressed; + final EdgeInsetsGeometry bodyPadding; + + /// On non-mobile screens the buttons of the app bar are hidden. + /// This is because this widget implies that it is nested inside a bigger layout. + /// If it is not nested, set this to true. + final bool ignoreBreakpoints; + + /// Usually support drawer is provided by the parent layout. + /// If it is not provided, set this to true. + final bool hasSupportDrawer; + + @override + Widget build(final BuildContext context) { + final Widget heroIconWidget = this.heroIconWidget ?? + Icon( + heroIcon ?? Icons.help, + size: 48.0, + color: Theme.of(context).colorScheme.onBackground, + ); + final bool hasHeroIcon = heroIcon != null || this.heroIconWidget != null; + + return Scaffold( + endDrawerEnableOpenDragGesture: false, + endDrawer: hasSupportDrawer ? const SupportDrawer() : null, + body: CustomScrollView( + slivers: [ + HeroSliverAppBar( + heroTitle: heroTitle, + hasHeroIcon: hasHeroIcon, + hasBackButton: hasBackButton, + onBackButtonPressed: onBackButtonPressed, + heroIconWidget: heroIconWidget, + hasFlashButton: hasFlashButton, + ignoreBreakpoints: ignoreBreakpoints, + ), + if (heroSubtitle != null) + SliverPadding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 4.0, + ), + sliver: SliverList( + delegate: SliverChildListDelegate([ + Text( + heroSubtitle!, + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + color: Theme.of(context).colorScheme.onBackground, + ), + textAlign: hasHeroIcon ? TextAlign.center : TextAlign.start, + ), + ]), + ), + ), + SliverPadding( + padding: bodyPadding, + sliver: SliverList( + delegate: SliverChildListDelegate(children), + ), + ), + ], + ), + ); + } +} + +class HeroSliverAppBar extends StatefulWidget { + const HeroSliverAppBar({ + required this.heroTitle, + required this.hasHeroIcon, + required this.hasBackButton, + required this.onBackButtonPressed, + required this.heroIconWidget, + required this.hasFlashButton, + required this.ignoreBreakpoints, + super.key, + }); + + final String heroTitle; + final bool hasHeroIcon; + final bool hasBackButton; + final bool hasFlashButton; + final VoidCallback? onBackButtonPressed; + final Widget heroIconWidget; + final bool ignoreBreakpoints; + + @override + State createState() => _HeroSliverAppBarState(); +} + +class _HeroSliverAppBarState extends State { + Size _size = Size.zero; + @override + Widget build(final BuildContext context) { + final isMobile = + widget.ignoreBreakpoints ? true : Breakpoints.small.isActive(context); + final isJobsListEmpty = context.watch().state is JobsStateEmpty; + return SliverAppBar( + expandedHeight: + widget.hasHeroIcon ? 148.0 + _size.height : 72.0 + _size.height, + primary: true, + pinned: isMobile, + stretch: true, + surfaceTintColor: isMobile ? null : Colors.transparent, + leading: (widget.hasBackButton && isMobile) + ? const AutoLeadingButton() + : const SizedBox.shrink(), + actions: [ + if (widget.hasFlashButton && isMobile) + AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + child: IconButton( + onPressed: () { + showModalBottomSheet( + context: context, + useRootNavigator: true, + isScrollControlled: true, + builder: (final BuildContext context) => + DraggableScrollableSheet( + expand: false, + maxChildSize: 0.9, + minChildSize: 0.4, + initialChildSize: 0.6, + builder: (final context, final scrollController) => + JobsContent(controller: scrollController), + ), + ); + }, + icon: Icon( + isJobsListEmpty ? Ionicons.flash_outline : Ionicons.flash, + ), + color: isJobsListEmpty + ? Theme.of(context).colorScheme.onBackground + : Theme.of(context).colorScheme.primary, + ), + ), + const SizedBox.shrink(), + ], + flexibleSpace: FlexibleSpaceBar( + title: LayoutBuilder( + builder: (final context, final constraints) => SizedBox( + width: constraints.maxWidth - 72.0, + child: WidgetSize( + onChange: (final Size size) => setState(() => _size = size), + child: Text( + widget.heroTitle, + style: Theme.of(context).textTheme.titleLarge?.copyWith( + color: Theme.of(context).colorScheme.onSurface, + ), + overflow: TextOverflow.fade, + textAlign: TextAlign.center, + ), + ), + ), + ), + expandedTitleScale: 1.2, + centerTitle: true, + collapseMode: CollapseMode.pin, + titlePadding: const EdgeInsets.only( + bottom: 12.0, + top: 16.0, + ), + background: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const SizedBox(height: 72.0), + if (widget.hasHeroIcon) widget.heroIconWidget, + ], + ), + ), + ); + } +} diff --git a/lib/ui/layouts/responsive_layout_with_infobox.dart b/lib/ui/layouts/responsive_layout_with_infobox.dart new file mode 100644 index 00000000..4ef467ab --- /dev/null +++ b/lib/ui/layouts/responsive_layout_with_infobox.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:selfprivacy/utils/breakpoints.dart'; + +class ResponsiveLayoutWithInfobox extends StatelessWidget { + const ResponsiveLayoutWithInfobox({ + required this.primaryColumn, + this.topChild, + this.secondaryColumn, + super.key, + }); + + final Widget? topChild; + final Widget primaryColumn; + final Widget? secondaryColumn; + + @override + Widget build(final BuildContext context) { + final hasSecondaryColumn = secondaryColumn != null; + final hasTopChild = topChild != null; + + if (Breakpoints.large.isActive(context)) { + return LayoutBuilder( + builder: (final context, final constraints) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (hasTopChild) + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + width: constraints.maxWidth * 0.9, + child: topChild, + ), + ], + ), + if (hasTopChild) const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: hasSecondaryColumn + ? constraints.maxWidth * 0.7 + : constraints.maxWidth * 0.9, + child: primaryColumn, + ), + if (hasSecondaryColumn) const SizedBox(width: 16), + if (hasSecondaryColumn) + SizedBox( + width: constraints.maxWidth * 0.2, + child: secondaryColumn, + ), + ], + ), + ], + ), + ); + } + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (hasTopChild) topChild!, + const SizedBox(height: 16), + primaryColumn, + const SizedBox(height: 32), + if (hasSecondaryColumn) secondaryColumn!, + ], + ); + } +} diff --git a/lib/ui/layouts/root_scaffold_with_navigation.dart b/lib/ui/layouts/root_scaffold_with_navigation.dart new file mode 100644 index 00000000..fcc58515 --- /dev/null +++ b/lib/ui/layouts/root_scaffold_with_navigation.dart @@ -0,0 +1,277 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:selfprivacy/ui/components/pre_styled_buttons/flash_fab.dart'; +import 'package:selfprivacy/ui/components/drawers/support_drawer.dart'; +import 'package:selfprivacy/ui/router/root_destinations.dart'; +import 'package:selfprivacy/utils/breakpoints.dart'; + +class RootScaffoldWithNavigation extends StatelessWidget { + const RootScaffoldWithNavigation({ + required this.child, + required this.title, + required this.destinations, + this.showBottomBar = true, + this.showFab = true, + super.key, + }); + + final Widget child; + final String title; + final bool showBottomBar; + final List destinations; + final bool showFab; + + @override + // ignore: prefer_expression_function_bodies + Widget build(final BuildContext context) { + return Scaffold( + appBar: Breakpoints.mediumAndUp.isActive(context) + ? PreferredSize( + preferredSize: const Size.fromHeight(52), + child: _RootAppBar(title: title), + ) + : null, + endDrawer: const SupportDrawer(), + endDrawerEnableOpenDragGesture: false, + body: Row( + children: [ + if (Breakpoints.medium.isActive(context)) + _MainScreenNavigationRail( + destinations: destinations, + showFab: showFab, + ), + if (Breakpoints.large.isActive(context)) + _MainScreenNavigationDrawer( + destinations: destinations, + showFab: showFab, + ), + Expanded(child: child), + ], + ), + bottomNavigationBar: _BottomBar( + destinations: destinations, + hidden: !(Breakpoints.small.isActive(context) && showBottomBar), + key: const Key('bottomBar'), + ), + floatingActionButton: + showFab && Breakpoints.small.isActive(context) && showBottomBar + ? const BrandFab() + : null, + ); + } +} + +class _RootAppBar extends StatelessWidget { + const _RootAppBar({ + required this.title, + }); + + final String title; + + @override + Widget build(final BuildContext context) => AppBar( + title: AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + transitionBuilder: + (final Widget child, final Animation animation) => + SlideTransition( + position: animation.drive( + Tween( + begin: const Offset(0.0, 0.2), + end: Offset.zero, + ), + ), + child: FadeTransition( + opacity: animation, + child: child, + ), + ), + child: SizedBox( + key: ValueKey(title), + width: double.infinity, + child: Text( + title, + ), + ), + ), + leading: context.router.pageCount > 1 + ? IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: () => context.router.pop(), + ) + : null, + actions: const [ + SizedBox.shrink(), + ], + ); +} + +class _MainScreenNavigationRail extends StatelessWidget { + const _MainScreenNavigationRail({ + required this.destinations, + this.showFab = true, + }); + + final List destinations; + final bool showFab; + + @override + Widget build(final BuildContext context) { + int? activeIndex = destinations.indexWhere( + (final destination) => + context.router.isRouteActive(destination.route.routeName), + ); + + final prevActiveIndex = destinations.indexWhere( + (final destination) => context.router.stack + .any((final route) => route.name == destination.route.routeName), + ); + + if (activeIndex == -1) { + if (prevActiveIndex != -1) { + activeIndex = prevActiveIndex; + } else { + activeIndex = 0; + } + } + + final isExtended = Breakpoints.large.isActive(context); + + return LayoutBuilder( + builder: (final context, final constraints) => SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints(minHeight: constraints.maxHeight), + child: IntrinsicHeight( + child: NavigationRail( + backgroundColor: Colors.transparent, + labelType: isExtended + ? NavigationRailLabelType.none + : NavigationRailLabelType.all, + extended: isExtended, + leading: showFab + ? const BrandFab( + extended: false, + ) + : null, + groupAlignment: 0.0, + destinations: destinations + .map( + (final destination) => NavigationRailDestination( + icon: Icon(destination.icon), + label: Text(destination.label), + ), + ) + .toList(), + selectedIndex: activeIndex, + onDestinationSelected: (final index) { + context.router.replaceAll([destinations[index].route]); + }, + ), + ), + ), + ), + ); + } +} + +class _BottomBar extends StatelessWidget { + const _BottomBar({ + required this.destinations, + required this.hidden, + super.key, + }); + + final List destinations; + final bool hidden; + + @override + Widget build(final BuildContext context) { + final prevActiveIndex = destinations.indexWhere( + (final destination) => context.router.stack + .any((final route) => route.name == destination.route.routeName), + ); + + return AnimatedContainer( + duration: const Duration(milliseconds: 500), + height: hidden ? 0 : 80, + curve: Curves.easeInOutCubicEmphasized, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + color: Theme.of(context).scaffoldBackgroundColor, + ), + child: NavigationBar( + selectedIndex: prevActiveIndex == -1 ? 0 : prevActiveIndex, + labelBehavior: NavigationDestinationLabelBehavior.onlyShowSelected, + onDestinationSelected: (final index) { + context.router.replaceAll([destinations[index].route]); + }, + destinations: destinations + .map( + (final destination) => NavigationDestination( + icon: Icon(destination.icon), + label: destination.label, + ), + ) + .toList(), + ), + ); + } +} + +class _MainScreenNavigationDrawer extends StatelessWidget { + const _MainScreenNavigationDrawer({ + required this.destinations, + this.showFab = true, + }); + + final List destinations; + final bool showFab; + + @override + Widget build(final BuildContext context) { + int? activeIndex = destinations.indexWhere( + (final destination) => + context.router.isRouteActive(destination.route.routeName), + ); + + final prevActiveIndex = destinations.indexWhere( + (final destination) => context.router.stack + .any((final route) => route.name == destination.route.routeName), + ); + + if (activeIndex == -1) { + if (prevActiveIndex != -1) { + activeIndex = prevActiveIndex; + } else { + activeIndex = 0; + } + } + + return SizedBox( + height: MediaQuery.of(context).size.height, + width: 296, + child: NavigationDrawer( + key: const Key('PrimaryNavigationDrawer'), + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + surfaceTintColor: Colors.transparent, + selectedIndex: activeIndex, + onDestinationSelected: (final index) { + context.router.replaceAll([destinations[index].route]); + }, + children: [ + const Padding( + padding: EdgeInsets.all(16.0), + child: BrandFab(extended: true), + ), + const SizedBox(height: 16), + ...destinations.map( + (final destination) => NavigationDrawerDestination( + icon: Icon(destination.icon), + label: Text(destination.label), + ), + ), + ], + ), + ); + } +} diff --git a/lib/ui/pages/backup_details/backup_details.dart b/lib/ui/pages/backup_details/backup_details.dart deleted file mode 100644 index 55a8dd12..00000000 --- a/lib/ui/pages/backup_details/backup_details.dart +++ /dev/null @@ -1,251 +0,0 @@ -import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/brand_colors.dart'; -import 'package:selfprivacy/config/get_it_config.dart'; -import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; -import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart'; -import 'package:selfprivacy/logic/models/json/backup.dart'; -import 'package:selfprivacy/logic/models/state_types.dart'; -import 'package:selfprivacy/ui/components/action_button/action_button.dart'; -import 'package:selfprivacy/ui/components/brand_alert/brand_alert.dart'; -import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; -import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart'; -import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; -import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; - -import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart'; - -GlobalKey navigatorKey = GlobalKey(); - -class BackupDetails extends StatefulWidget { - const BackupDetails({final super.key}); - - @override - State createState() => _BackupDetailsState(); -} - -class _BackupDetailsState extends State - with SingleTickerProviderStateMixin { - @override - Widget build(final BuildContext context) { - final bool isReady = context.watch().state - is ServerInstallationFinished; - final bool isBackupInitialized = - context.watch().state.isInitialized; - final BackupStatusEnum backupStatus = - context.watch().state.status; - final StateType providerState = isReady && isBackupInitialized - ? (backupStatus == BackupStatusEnum.error - ? StateType.warning - : StateType.stable) - : StateType.uninitialized; - final bool preventActions = - context.watch().state.preventActions; - final double backupProgress = context.watch().state.progress; - final String backupError = context.watch().state.error; - final List backups = context.watch().state.backups; - final bool refreshing = context.watch().state.refreshing; - - return BrandHeroScreen( - heroIcon: BrandIcons.save, - heroTitle: 'providers.backup.card_title'.tr(), - heroSubtitle: 'providers.backup.bottom_sheet.1'.tr(), - children: [ - if (isReady && !isBackupInitialized) - BrandButton.rised( - onPressed: preventActions - ? null - : () async { - await context.read().createBucket(); - }, - text: 'providers.backup.initialize'.tr(), - ), - if (backupStatus == BackupStatusEnum.initializing) - BrandText.body1('providers.backup.waitingForRebuild'.tr()), - if (backupStatus != BackupStatusEnum.initializing && - backupStatus != BackupStatusEnum.noKey) - BrandCards.outlined( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (backupStatus == BackupStatusEnum.initialized) - ListTile( - onTap: preventActions - ? null - : () async { - await context.read().createBackup(); - }, - leading: const Icon( - Icons.add_circle_outline_rounded, - ), - title: Text( - 'providers.backup.create_new'.tr(), - style: Theme.of(context).textTheme.headline6, - ), - ), - if (backupStatus == BackupStatusEnum.backingUp) - ListTile( - title: Text( - 'providers.backup.creating'.tr( - args: [(backupProgress * 100).round().toString()], - ), - style: Theme.of(context).textTheme.headline6, - ), - subtitle: LinearProgressIndicator( - value: backupProgress, - backgroundColor: Colors.grey.withOpacity(0.2), - ), - ), - if (backupStatus == BackupStatusEnum.restoring) - ListTile( - title: Text( - 'providers.backup.restoring'.tr( - args: [(backupProgress * 100).round().toString()], - ), - style: Theme.of(context).textTheme.headline6, - ), - subtitle: LinearProgressIndicator( - backgroundColor: Colors.grey.withOpacity(0.2), - ), - ), - if (backupStatus == BackupStatusEnum.error) - ListTile( - leading: const Icon( - Icons.error_outline, - color: BrandColors.red1, - ), - title: Text( - 'providers.backup.error_pending'.tr(), - style: Theme.of(context).textTheme.headline6, - ), - ), - ], - ), - ), - const SizedBox(height: 16), - // Card with a list of existing backups - // Each list item has a date - // When clicked, starts the restore action - if (backupStatus != BackupStatusEnum.initializing && - backupStatus != BackupStatusEnum.noKey) - BrandCards.outlined( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ListTile( - leading: const Icon( - Icons.refresh, - ), - title: Text( - 'providers.backup.restore'.tr(), - style: Theme.of(context).textTheme.headline6, - ), - ), - const Divider( - height: 1.0, - ), - if (backups.isEmpty) - ListTile( - leading: const Icon( - Icons.error_outline, - ), - title: Text('providers.backup.no_backups'.tr()), - ), - if (backups.isNotEmpty) - Column( - children: backups - .map( - (final Backup backup) => ListTile( - onTap: preventActions - ? null - : () { - final NavigationService nav = - getIt(); - nav.showPopUpDialog( - BrandAlert( - title: - 'providers.backup.restoring'.tr(), - contentText: - 'providers.backup.restore_alert'.tr( - args: [backup.time.toString()], - ), - actions: [ - ActionButton( - text: 'basis.cancel'.tr(), - ), - ActionButton( - onPressed: () => { - context - .read() - .restoreBackup(backup.id) - }, - text: 'modals.yes'.tr(), - ) - ], - ), - ); - }, - title: Text( - '${MaterialLocalizations.of(context).formatShortDate(backup.time)} ${TimeOfDay.fromDateTime(backup.time).format(context)}', - ), - ), - ) - .toList(), - ), - ], - ), - ), - const SizedBox(height: 16), - BrandCards.outlined( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ListTile( - title: Text( - 'providers.backup.refresh'.tr(), - ), - onTap: refreshing - ? null - : () => {context.read().updateBackups()}, - enabled: !refreshing, - ), - if (providerState != StateType.uninitialized) - Column( - children: [ - const Divider( - height: 1.0, - ), - ListTile( - title: Text( - 'providers.backup.refetchBackups'.tr(), - ), - onTap: preventActions - ? null - : () => { - context - .read() - .forceUpdateBackups() - }, - ), - const Divider( - height: 1.0, - ), - ListTile( - title: Text( - 'providers.backup.reuploadKey'.tr(), - ), - onTap: preventActions - ? null - : () => {context.read().reuploadKey()}, - ), - ], - ), - ], - ), - ), - if (backupStatus == BackupStatusEnum.error) - BrandText.body1(backupError.toString()), - ], - ); - } -} diff --git a/lib/ui/pages/backups/backup_details.dart b/lib/ui/pages/backups/backup_details.dart new file mode 100644 index 00000000..344876b1 --- /dev/null +++ b/lib/ui/pages/backups/backup_details.dart @@ -0,0 +1,459 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; +import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart'; +import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_cubit.dart'; +import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; +import 'package:selfprivacy/logic/models/backup.dart'; +import 'package:selfprivacy/logic/models/json/server_job.dart'; +import 'package:selfprivacy/logic/models/service.dart'; +import 'package:selfprivacy/logic/models/state_types.dart'; +import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; +import 'package:selfprivacy/ui/components/jobs_content/server_job_card.dart'; +import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; +import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; +import 'package:selfprivacy/ui/helpers/modals.dart'; +import 'package:selfprivacy/ui/pages/backups/change_period_modal.dart'; +import 'package:selfprivacy/ui/pages/backups/change_rotation_quotas_modal.dart'; +import 'package:selfprivacy/ui/pages/backups/copy_encryption_key_modal.dart'; +import 'package:selfprivacy/ui/pages/backups/create_backups_modal.dart'; +import 'package:selfprivacy/ui/pages/backups/snapshot_modal.dart'; +import 'package:selfprivacy/ui/router/router.dart'; +import 'package:selfprivacy/utils/extensions/duration.dart'; + +@RoutePage() +class BackupDetailsPage extends StatelessWidget { + const BackupDetailsPage({super.key}); + + @override + Widget build(final BuildContext context) { + final bool isReady = context.watch().state + is ServerInstallationFinished; + final BackupsState backupsState = context.watch().state; + final bool isBackupInitialized = backupsState.isInitialized; + final StateType providerState = isReady && isBackupInitialized + ? StateType.stable + : StateType.uninitialized; + final bool preventActions = backupsState.preventActions; + final List backups = backupsState.backups; + final bool refreshing = backupsState.refreshing; + final List services = + context.watch().state.servicesThatCanBeBackedUp; + final Duration? autobackupPeriod = backupsState.autobackupPeriod; + final List backupJobs = context + .watch() + .state + .backupJobList + .where((final job) => job.status != JobStatusEnum.finished) + .toList(); + + if (!isReady) { + return BrandHeroScreen( + heroIcon: BrandIcons.save, + heroTitle: 'backup.card_title'.tr(), + heroSubtitle: 'not_ready_card.in_menu'.tr(), + children: const [], + ); + } + + if (!isBackupInitialized) { + return BrandHeroScreen( + heroIcon: BrandIcons.save, + heroTitle: 'backup.card_title'.tr(), + heroSubtitle: 'backup.description'.tr(), + children: [ + if (preventActions) + const Center( + child: Padding( + padding: EdgeInsets.all(16.0), + child: CircularProgressIndicator(), + ), + ), + if (!preventActions) + BrandButton.rised( + onPressed: preventActions + ? null + : () async { + await context.read().initializeBackups(); + }, + text: 'backup.initialize'.tr(), + ), + ], + ); + } + + final Color? overrideColor = + preventActions ? Theme.of(context).colorScheme.secondary : null; + + return BrandHeroScreen( + heroIcon: BrandIcons.save, + heroTitle: 'backup.card_title'.tr(), + heroSubtitle: 'backup.description'.tr(), + children: [ + ListTile( + onTap: preventActions + ? null + : () { + showModalBottomSheet( + useRootNavigator: true, + context: context, + isScrollControlled: true, + builder: (final BuildContext context) => + DraggableScrollableSheet( + expand: false, + maxChildSize: 0.9, + minChildSize: 0.4, + initialChildSize: 0.6, + builder: (final context, final scrollController) => + CreateBackupsModal( + services: services, + scrollController: scrollController, + ), + ), + ); + }, + leading: Icon( + Icons.add_circle_outline_rounded, + color: overrideColor, + ), + title: Text( + 'backup.create_new'.tr(), + style: TextStyle( + color: overrideColor, + ), + ), + ), + ListTile( + onTap: preventActions + ? null + : () { + showModalBottomSheet( + useRootNavigator: true, + context: context, + isScrollControlled: true, + builder: (final BuildContext context) => + DraggableScrollableSheet( + expand: false, + maxChildSize: 0.9, + minChildSize: 0.4, + initialChildSize: 0.6, + builder: (final context, final scrollController) => + ChangeAutobackupsPeriodModal( + scrollController: scrollController, + ), + ), + ); + }, + leading: Icon( + Icons.manage_history_outlined, + color: overrideColor, + ), + title: Text( + 'backup.autobackup_period_title'.tr(), + style: TextStyle( + color: overrideColor, + ), + ), + subtitle: Text( + style: TextStyle( + color: overrideColor, + ), + autobackupPeriod != null + ? 'backup.autobackup_period_subtitle'.tr( + namedArgs: { + 'period': autobackupPeriod.toPrettyString(context.locale) + }, + ) + : 'backup.autobackup_period_never'.tr(), + ), + ), + ListTile( + onTap: preventActions + ? null + : () { + showModalBottomSheet( + useRootNavigator: true, + context: context, + isScrollControlled: true, + builder: (final BuildContext context) => + DraggableScrollableSheet( + expand: false, + maxChildSize: 0.9, + minChildSize: 0.4, + initialChildSize: 0.6, + builder: (final context, final scrollController) => + ChangeRotationQuotasModal( + scrollController: scrollController, + ), + ), + ); + }, + leading: Icon( + Icons.auto_delete_outlined, + color: overrideColor, + ), + title: Text( + 'backup.rotation_quotas_title'.tr(), + style: TextStyle( + color: overrideColor, + ), + ), + ), + ListTile( + onTap: preventActions + ? null + : () { + showModalBottomSheet( + useRootNavigator: true, + context: context, + isScrollControlled: true, + builder: (final BuildContext context) => + DraggableScrollableSheet( + expand: false, + maxChildSize: 0.9, + minChildSize: 0.5, + initialChildSize: 0.7, + builder: (final context, final scrollController) => + CopyEncryptionKeyModal( + scrollController: scrollController, + ), + ), + ); + }, + leading: Icon( + Icons.key_outlined, + color: overrideColor, + ), + title: Text( + 'backup.backups_encryption_key'.tr(), + style: TextStyle( + color: overrideColor, + ), + ), + subtitle: Text( + 'backup.backups_encryption_key_subtitle'.tr(), + style: TextStyle( + color: overrideColor, + ), + ), + ), + const SizedBox(height: 8), + const Divider(), + const SizedBox(height: 8), + if (backupJobs.isNotEmpty) + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListTile( + title: Text( + 'backup.pending_jobs'.tr(), + style: Theme.of(context).textTheme.headlineSmall!.copyWith( + color: Theme.of(context).colorScheme.secondary, + ), + ), + ), + for (final job in backupJobs) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: ServerJobCard( + serverJob: job, + ), + ), + ], + ), + if (isBackupInitialized) + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListTile( + title: Text( + 'backup.latest_snapshots'.tr(), + style: Theme.of(context).textTheme.headlineSmall!.copyWith( + color: Theme.of(context).colorScheme.secondary, + ), + ), + subtitle: Text( + 'backup.latest_snapshots_subtitle'.tr(), + style: Theme.of(context).textTheme.labelMedium, + ), + ), + if (backups.isEmpty) + ListTile( + leading: Icon( + Icons.error_outline, + color: overrideColor, + ), + title: Text( + 'backup.no_backups'.tr(), + style: TextStyle( + color: overrideColor, + ), + ), + ), + if (backups.isNotEmpty) + Column( + children: backups.take(15).map( + (final Backup backup) { + final service = context + .read() + .state + .getServiceById(backup.serviceId); + return ListTile( + onTap: preventActions + ? null + : () { + showModalBottomSheet( + useRootNavigator: true, + context: context, + isScrollControlled: true, + builder: (final BuildContext context) => + DraggableScrollableSheet( + expand: false, + maxChildSize: 0.9, + minChildSize: 0.5, + initialChildSize: 0.7, + builder: ( + final context, + final scrollController, + ) => + SnapshotModal( + snapshot: backup, + scrollController: scrollController, + ), + ), + ); + }, + onLongPress: preventActions + ? null + : () { + showPopUpAlert( + alertTitle: 'backup.forget_snapshot'.tr(), + description: + 'backup.forget_snapshot_alert'.tr(), + actionButtonTitle: + 'backup.forget_snapshot'.tr(), + actionButtonOnPressed: () => { + context.read().forgetSnapshot( + backup.id, + ) + }, + ); + }, + title: Text( + style: TextStyle( + color: overrideColor, + ), + '${MaterialLocalizations.of(context).formatShortDate(backup.time)} ${TimeOfDay.fromDateTime(backup.time).format(context)}', + ), + subtitle: Text( + style: TextStyle( + color: overrideColor, + ), + service?.displayName ?? backup.fallbackServiceName, + ), + leading: service != null + ? SvgPicture.string( + service.svgIcon, + height: 24, + width: 24, + colorFilter: ColorFilter.mode( + overrideColor ?? + Theme.of(context) + .colorScheme + .onBackground, + BlendMode.srcIn, + ), + ) + : Icon( + Icons.question_mark_outlined, + color: overrideColor, + ), + ); + }, + ).toList(), + ), + if (backups.isNotEmpty && backups.length > 15) + ListTile( + title: Text( + 'backup.show_more'.tr(), + style: Theme.of(context).textTheme.labelMedium, + ), + leading: const Icon( + Icons.arrow_drop_down, + ), + onTap: () => + context.pushRoute(BackupsListRoute(service: null)), + ) + ], + ), + const SizedBox(height: 8), + const Divider(), + const SizedBox(height: 8), + ListTile( + title: Text( + 'backup.refresh'.tr(), + ), + onTap: refreshing + ? null + : () => {context.read().updateBackups()}, + enabled: !refreshing, + leading: const Icon( + Icons.refresh_outlined, + ), + ), + if (providerState != StateType.uninitialized) + Column( + children: [ + ListTile( + title: Text( + 'backup.refetch_backups'.tr(), + style: TextStyle( + color: overrideColor, + ), + ), + subtitle: Text( + 'backup.refetch_backups_subtitle'.tr(), + style: TextStyle( + color: overrideColor, + ), + ), + leading: Icon( + Icons.cached_outlined, + color: overrideColor, + ), + onTap: preventActions + ? null + : () => {context.read().forceUpdateBackups()}, + ), + const SizedBox(height: 8), + const Divider(), + const SizedBox(height: 8), + ListTile( + title: Text( + 'backup.reupload_key'.tr(), + style: TextStyle( + color: overrideColor, + ), + ), + subtitle: Text( + 'backup.reupload_key_subtitle'.tr(), + style: TextStyle( + color: overrideColor, + ), + ), + leading: Icon( + Icons.warning_amber_outlined, + color: overrideColor, + ), + onTap: preventActions + ? null + : () => {context.read().reuploadKey()}, + ), + ], + ), + ], + ); + } +} diff --git a/lib/ui/pages/backups/backups_list.dart b/lib/ui/pages/backups/backups_list.dart new file mode 100644 index 00000000..4af870ef --- /dev/null +++ b/lib/ui/pages/backups/backups_list.dart @@ -0,0 +1,107 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart'; +import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; +import 'package:selfprivacy/logic/models/backup.dart'; +import 'package:selfprivacy/logic/models/service.dart'; +import 'package:selfprivacy/ui/helpers/modals.dart'; +import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; +import 'package:selfprivacy/ui/pages/backups/snapshot_modal.dart'; + +@RoutePage() +class BackupsListPage extends StatelessWidget { + const BackupsListPage({ + required this.service, + super.key, + }); + + final Service? service; + + @override + Widget build(final BuildContext context) { + // If the service is null, get all backups from state. If not null, call the + // serviceBackups(serviceId) on the backups state. + final List backups = service == null + ? context.watch().state.backups + : context.watch().state.serviceBackups(service!.id); + final bool preventActions = + context.watch().state.preventActions; + return BrandHeroScreen( + heroTitle: 'backup.snapshots_title'.tr(), + children: [ + if (backups.isEmpty) + Center( + child: Text( + 'backup.no_backups'.tr(), + ), + ) + else + ...backups.map((final Backup backup) { + final service = context + .read() + .state + .getServiceById(backup.serviceId); + return ListTile( + onTap: preventActions + ? null + : () { + showModalBottomSheet( + useRootNavigator: true, + context: context, + isScrollControlled: true, + builder: (final BuildContext context) => + DraggableScrollableSheet( + expand: false, + maxChildSize: 0.9, + minChildSize: 0.5, + initialChildSize: 0.7, + builder: (final context, final scrollController) => + SnapshotModal( + snapshot: backup, + scrollController: scrollController, + ), + ), + ); + }, + onLongPress: preventActions + ? null + : () { + showPopUpAlert( + alertTitle: 'backup.forget_snapshot'.tr(), + description: 'backup.forget_snapshot_alert'.tr(), + actionButtonTitle: 'backup.forget_snapshot'.tr(), + actionButtonOnPressed: () => { + context.read().forgetSnapshot( + backup.id, + ) + }, + ); + }, + title: Text( + '${MaterialLocalizations.of(context).formatShortDate(backup.time)} ${TimeOfDay.fromDateTime(backup.time).format(context)}', + ), + subtitle: Text( + service?.displayName ?? backup.fallbackServiceName, + ), + leading: service != null + ? SvgPicture.string( + service.svgIcon, + height: 24, + width: 24, + colorFilter: ColorFilter.mode( + Theme.of(context).colorScheme.onBackground, + BlendMode.srcIn, + ), + ) + : const Icon( + Icons.question_mark_outlined, + ), + ); + }) + ], + ); + } +} diff --git a/lib/ui/pages/backups/change_period_modal.dart b/lib/ui/pages/backups/change_period_modal.dart new file mode 100644 index 00000000..dbee981e --- /dev/null +++ b/lib/ui/pages/backups/change_period_modal.dart @@ -0,0 +1,105 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; +import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart'; +import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_cubit.dart'; +import 'package:selfprivacy/utils/extensions/duration.dart'; + +class ChangeAutobackupsPeriodModal extends StatefulWidget { + const ChangeAutobackupsPeriodModal({ + required this.scrollController, + super.key, + }); + + final ScrollController scrollController; + + @override + State createState() => + _ChangeAutobackupsPeriodModalState(); +} + +class _ChangeAutobackupsPeriodModalState + extends State { + Duration? selectedPeriod; + + static const List autobackupPeriods = [ + Duration(hours: 12), + Duration(days: 1), + Duration(days: 2), + Duration(days: 3), + Duration(days: 7), + ]; + + // Set initial period to the one currently set + @override + void initState() { + super.initState(); + selectedPeriod = context.read().state.autobackupPeriod; + } + + @override + Widget build(final BuildContext context) { + final Duration? initialAutobackupPeriod = + context.watch().state.autobackupPeriod; + return ListView( + controller: widget.scrollController, + padding: const EdgeInsets.all(16), + children: [ + const SizedBox(height: 16), + Text( + 'backup.autobackup_period_title'.tr(), + style: Theme.of(context).textTheme.headlineSmall, + textAlign: TextAlign.center, + ), + const SizedBox(height: 16), + // Select all services tile + RadioListTile( + onChanged: (final Duration? value) { + setState(() { + selectedPeriod = value; + }); + }, + title: Text( + 'backup.autobackup_period_disable'.tr(), + ), + value: null, + groupValue: selectedPeriod, + ), + const Divider( + height: 1.0, + ), + ...autobackupPeriods.map( + (final Duration period) => RadioListTile( + onChanged: (final Duration? value) { + setState(() { + selectedPeriod = value; + }); + }, + title: Text( + 'backup.autobackup_period_every'.tr( + namedArgs: {'period': period.toPrettyString(context.locale)}, + ), + ), + value: period, + groupValue: selectedPeriod, + ), + ), + const SizedBox(height: 16), + // Create backup button + FilledButton( + onPressed: selectedPeriod == initialAutobackupPeriod + ? null + : () { + context + .read() + .setAutobackupPeriod(selectedPeriod); + Navigator.of(context).pop(); + }, + child: Text( + 'backup.autobackup_set_period'.tr(), + ), + ), + ], + ); + } +} diff --git a/lib/ui/pages/backups/change_rotation_quotas_modal.dart b/lib/ui/pages/backups/change_rotation_quotas_modal.dart new file mode 100644 index 00000000..0ce97eee --- /dev/null +++ b/lib/ui/pages/backups/change_rotation_quotas_modal.dart @@ -0,0 +1,250 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; +import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart'; +import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_cubit.dart'; +import 'package:selfprivacy/logic/models/backup.dart'; + +class ChangeRotationQuotasModal extends StatefulWidget { + const ChangeRotationQuotasModal({ + required this.scrollController, + super.key, + }); + + final ScrollController scrollController; + + @override + State createState() => + _ChangeRotationQuotasModalState(); +} + +enum QuotaUnits { + last, + daily, + weekly, + monthly, + yearly, +} + +class _ChangeRotationQuotasModalState extends State { + AutobackupQuotas selectedQuotas = AutobackupQuotas( + last: 3, + daily: 7, + weekly: 4, + monthly: 6, + yearly: -1, + ); + + // Set initial period to the one currently set + @override + void initState() { + super.initState(); + selectedQuotas = + context.read().state.autobackupQuotas ?? selectedQuotas; + } + + String generateSubtitle(final int value, final QuotaUnits unit) { + switch (unit) { + case QuotaUnits.last: + return value == -1 + ? 'backup.quota_subtitles.last_infinite'.tr() + : 'backup.quota_subtitles.last'.plural(value); + case QuotaUnits.daily: + if (selectedQuotas.last == -1) { + return 'backup.quota_subtitles.no_effect'.tr(); + } + return value == -1 + ? 'backup.quota_subtitles.daily_infinite'.tr() + : 'backup.quota_subtitles.daily'.plural(value); + case QuotaUnits.weekly: + if (selectedQuotas.last == -1 || selectedQuotas.daily == -1) { + return 'backup.quota_subtitles.no_effect'.tr(); + } + return value == -1 + ? 'backup.quota_subtitles.weekly_infinite'.tr() + : 'backup.quota_subtitles.weekly'.plural(value); + case QuotaUnits.monthly: + if (selectedQuotas.last == -1 || selectedQuotas.daily == -1) { + return 'backup.quota_subtitles.no_effect'.tr(); + } + return value == -1 + ? 'backup.quota_subtitles.monthly_infinite'.tr() + : 'backup.quota_subtitles.monthly'.plural(value); + case QuotaUnits.yearly: + if (selectedQuotas.last == -1 || selectedQuotas.daily == -1) { + return 'backup.quota_subtitles.no_effect'.tr(); + } + return value == -1 + ? 'backup.quota_subtitles.yearly_infinite'.tr() + : 'backup.quota_subtitles.yearly'.plural(value); + } + } + + @override + Widget build(final BuildContext context) { + final AutobackupQuotas? initialAutobackupQuotas = + context.watch().state.autobackupQuotas; + return ListView( + controller: widget.scrollController, + padding: const EdgeInsets.all(16), + children: [ + const SizedBox(height: 16), + Text( + 'backup.rotation_quotas_title'.tr(), + style: Theme.of(context).textTheme.headlineSmall, + textAlign: TextAlign.center, + ), + const SizedBox(height: 8), + Text( + 'backup.quotas_only_applied_to_autobackups'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + textAlign: TextAlign.center, + ), + const SizedBox(height: 16), + // Accordions for each quota type. When tapped allows to enter a new int value + // for the quota. + QuotaSelectionTile( + title: 'backup.quota_titles.last'.tr(), + subtitle: generateSubtitle(selectedQuotas.last, QuotaUnits.last), + value: selectedQuotas.last, + min: 1, + max: 30, + callback: (final double value) { + setState(() { + if (value == 31) { + selectedQuotas = selectedQuotas.copyWith(last: -1); + return; + } + selectedQuotas = selectedQuotas.copyWith(last: value.toInt()); + }); + }, + ), + QuotaSelectionTile( + title: 'backup.quota_titles.daily'.tr(), + subtitle: generateSubtitle(selectedQuotas.daily, QuotaUnits.daily), + value: selectedQuotas.daily, + min: 0, + max: 30, + callback: (final double value) { + setState(() { + if (value == 31) { + selectedQuotas = selectedQuotas.copyWith(daily: -1); + return; + } + selectedQuotas = selectedQuotas.copyWith(daily: value.toInt()); + }); + }, + ), + QuotaSelectionTile( + title: 'backup.quota_titles.weekly'.tr(), + subtitle: generateSubtitle(selectedQuotas.weekly, QuotaUnits.weekly), + value: selectedQuotas.weekly, + min: 0, + max: 15, + callback: (final double value) { + setState(() { + if (value == 16) { + selectedQuotas = selectedQuotas.copyWith(weekly: -1); + return; + } + selectedQuotas = selectedQuotas.copyWith(weekly: value.toInt()); + }); + }, + ), + QuotaSelectionTile( + title: 'backup.quota_titles.monthly'.tr(), + subtitle: + generateSubtitle(selectedQuotas.monthly, QuotaUnits.monthly), + value: selectedQuotas.monthly, + min: 0, + max: 24, + callback: (final double value) { + setState(() { + if (value == 25) { + selectedQuotas = selectedQuotas.copyWith(monthly: -1); + return; + } + selectedQuotas = selectedQuotas.copyWith(monthly: value.toInt()); + }); + }, + ), + QuotaSelectionTile( + title: 'backup.quota_titles.yearly'.tr(), + subtitle: generateSubtitle(selectedQuotas.yearly, QuotaUnits.yearly), + value: selectedQuotas.yearly, + min: 0, + max: 5, + callback: (final double value) { + setState(() { + if (value == 6) { + selectedQuotas = selectedQuotas.copyWith(yearly: -1); + return; + } + selectedQuotas = selectedQuotas.copyWith(yearly: value.toInt()); + }); + }, + ), + const SizedBox(height: 16), + FilledButton( + onPressed: selectedQuotas == initialAutobackupQuotas + ? null + : () { + context + .read() + .setAutobackupQuotas(selectedQuotas); + Navigator.of(context).pop(); + }, + child: Text( + 'backup.set_rotation_quotas'.tr(), + ), + ), + ], + ); + } +} + +class QuotaSelectionTile extends StatelessWidget { + const QuotaSelectionTile({ + required this.title, + required this.subtitle, + required this.value, + required this.min, + required this.max, + required this.callback, + super.key, + }); + + final String title; + final String subtitle; + final int value; + final int min; + final int max; + final void Function(double) callback; + + @override + Widget build(final BuildContext context) => ExpansionTile( + title: Text(title), + subtitle: Text(subtitle), + trailing: Text( + value == -1 ? '∞' : value.toString(), + style: Theme.of(context).textTheme.headlineMedium, + ), + children: [ + // Discrete slider to select the new value + if (value >= -1 && value <= max) + Slider( + value: value == -1 ? max + 1 : value.toDouble(), + min: min.toDouble(), + max: (max + 1).toDouble(), + divisions: max - min + 1, + label: value == -1 ? '∞' : value.toString(), + onChanged: callback, + ), + if (value < -1 || value > max) + Text( + 'Manually set to $value', + style: Theme.of(context).textTheme.headlineSmall, + ), + ], + ); +} diff --git a/lib/ui/pages/backups/copy_encryption_key_modal.dart b/lib/ui/pages/backups/copy_encryption_key_modal.dart new file mode 100644 index 00000000..fc16c356 --- /dev/null +++ b/lib/ui/pages/backups/copy_encryption_key_modal.dart @@ -0,0 +1,159 @@ +import 'dart:async'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; +import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart'; +import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_cubit.dart'; +import 'package:selfprivacy/ui/components/info_box/info_box.dart'; +import 'package:selfprivacy/utils/platform_adapter.dart'; + +class CopyEncryptionKeyModal extends StatefulWidget { + const CopyEncryptionKeyModal({ + required this.scrollController, + super.key, + }); + + final ScrollController scrollController; + + @override + State createState() => _CopyEncryptionKeyModalState(); +} + +class _CopyEncryptionKeyModalState extends State { + bool isKeyVisible = false; + bool copiedToClipboard = false; + Timer? copyToClipboardTimer; + + @override + void dispose() { + copyToClipboardTimer?.cancel(); + super.dispose(); + } + + @override + Widget build(final BuildContext context) { + final String? encryptionKey = + context.watch().state.backblazeBucket?.encryptionKey; + if (encryptionKey == null) { + return ListView( + controller: widget.scrollController, + padding: const EdgeInsets.all(16), + children: [ + const SizedBox(height: 16), + Text( + 'backup.backups_encryption_key'.tr(), + style: Theme.of(context).textTheme.headlineSmall, + textAlign: TextAlign.center, + ), + const SizedBox(height: 8), + InfoBox( + text: 'backup.backups_encryption_key_not_found'.tr(), + isWarning: true, + ), + ], + ); + } + return ListView( + controller: widget.scrollController, + padding: const EdgeInsets.all(16), + children: [ + const SizedBox(height: 16), + Text( + 'backup.backups_encryption_key'.tr(), + style: Theme.of(context).textTheme.headlineSmall, + textAlign: TextAlign.center, + ), + const SizedBox(height: 8), + Text( + 'backup.backups_encryption_key_description'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + textAlign: TextAlign.center, + ), + const SizedBox(height: 8), + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + color: Theme.of(context).colorScheme.surfaceVariant, + ), + padding: const EdgeInsets.all(16), + child: Stack( + children: [ + SelectableText( + encryptionKey, + style: Theme.of(context).textTheme.titleMedium?.copyWith( + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + Positioned.fill( + child: InkWell( + onTap: () { + setState( + () { + isKeyVisible = !isKeyVisible; + }, + ); + }, + child: AnimatedOpacity( + duration: const Duration(milliseconds: 200), + opacity: isKeyVisible ? 0 : 1, + child: Container( + color: Theme.of(context).colorScheme.surfaceVariant, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(Icons.visibility_outlined), + const SizedBox(width: 8), + Text( + 'backup.backups_encryption_key_show'.tr(), + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith( + color: Theme.of(context) + .colorScheme + .onSurfaceVariant, + ), + ), + ], + ), + ), + ), + ), + ), + ], + ), + ), + const SizedBox(height: 8), + FilledButton.icon( + onPressed: () { + setState( + () { + copiedToClipboard = true; + }, + ); + setState(() { + copyToClipboardTimer?.cancel(); + copyToClipboardTimer = Timer( + const Duration(seconds: 5), + () { + setState(() { + copiedToClipboard = false; + }); + }, + ); + }); + PlatformAdapter.setClipboard(encryptionKey); + }, + icon: const Icon(Icons.copy_all_outlined), + label: Text( + copiedToClipboard + ? 'basis.copied_to_clipboard'.tr() + : 'backup.backups_encryption_key_copy'.tr(), + ), + ), + ], + ); + } +} diff --git a/lib/ui/pages/backups/create_backups_modal.dart b/lib/ui/pages/backups/create_backups_modal.dart new file mode 100644 index 00000000..3f461da1 --- /dev/null +++ b/lib/ui/pages/backups/create_backups_modal.dart @@ -0,0 +1,161 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart'; +import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_cubit.dart'; +import 'package:selfprivacy/logic/models/json/server_job.dart'; +import 'package:selfprivacy/logic/models/service.dart'; + +class CreateBackupsModal extends StatefulWidget { + const CreateBackupsModal({ + required this.services, + required this.scrollController, + super.key, + }); + + final List services; + final ScrollController scrollController; + + @override + State createState() => _CreateBackupsModalState(); +} + +class _CreateBackupsModalState extends State { + // Store in state the selected services to backup + List selectedServices = []; + + // Select all services on modal open + @override + void initState() { + super.initState(); + final List busyServices = context + .read() + .state + .backupJobList + .where( + (final ServerJob job) => + job.status == JobStatusEnum.running || + job.status == JobStatusEnum.created, + ) + .map((final ServerJob job) => job.typeId.split('.')[1]) + .toList(); + selectedServices.addAll( + widget.services + .where((final Service service) => !busyServices.contains(service.id)), + ); + } + + @override + Widget build(final BuildContext context) { + final List busyServices = context + .watch() + .state + .backupJobList + .where( + (final ServerJob job) => + job.status == JobStatusEnum.running || + job.status == JobStatusEnum.created, + ) + .map((final ServerJob job) => job.typeId.split('.')[1]) + .toList(); + + return ListView( + controller: widget.scrollController, + padding: const EdgeInsets.all(16), + children: [ + const SizedBox(height: 16), + Text( + 'backup.create_new_select_heading'.tr(), + style: Theme.of(context).textTheme.headlineSmall, + textAlign: TextAlign.center, + ), + const SizedBox(height: 16), + // Select all services tile + CheckboxListTile( + onChanged: (final bool? value) { + setState(() { + if (value ?? true) { + setState(() { + selectedServices.clear(); + selectedServices.addAll( + widget.services.where( + (final service) => !busyServices.contains(service.id), + ), + ); + }); + } else { + selectedServices.clear(); + } + }); + }, + title: Text( + 'backup.select_all'.tr(), + ), + secondary: const Icon( + Icons.checklist_outlined, + ), + value: selectedServices.length >= + widget.services.length - busyServices.length, + ), + const Divider( + height: 1.0, + ), + ...widget.services.map( + (final Service service) { + final bool busy = busyServices.contains(service.id); + return CheckboxListTile( + onChanged: !busy + ? (final bool? value) { + setState(() { + if (value ?? true) { + setState(() { + selectedServices.add(service); + }); + } else { + setState(() { + selectedServices.remove(service); + }); + } + }); + } + : null, + title: Text( + service.displayName, + ), + subtitle: Text( + busy ? 'backup.service_busy'.tr() : service.backupDescription, + ), + secondary: SvgPicture.string( + service.svgIcon, + height: 24, + width: 24, + colorFilter: ColorFilter.mode( + busy + ? Theme.of(context).colorScheme.outlineVariant + : Theme.of(context).colorScheme.onBackground, + BlendMode.srcIn, + ), + ), + value: selectedServices.contains(service), + ); + }, + ), + const SizedBox(height: 16), + // Create backup button + FilledButton( + onPressed: selectedServices.isEmpty + ? null + : () { + context + .read() + .createMultipleBackups(selectedServices); + Navigator.of(context).pop(); + }, + child: Text( + 'backup.start'.tr(), + ), + ), + ], + ); + } +} diff --git a/lib/ui/pages/backups/snapshot_id_list_tile.dart b/lib/ui/pages/backups/snapshot_id_list_tile.dart new file mode 100644 index 00000000..802b90ee --- /dev/null +++ b/lib/ui/pages/backups/snapshot_id_list_tile.dart @@ -0,0 +1,30 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/utils/platform_adapter.dart'; + +class SnapshotIdListTile extends StatelessWidget { + const SnapshotIdListTile({ + required this.snapshotId, + super.key, + }); + + final String snapshotId; + + @override + Widget build(final BuildContext context) => ListTile( + onLongPress: () { + PlatformAdapter.setClipboard(snapshotId); + getIt().showSnackBar( + 'basis.copied_to_clipboard'.tr(), + behavior: SnackBarBehavior.floating, + ); + }, + leading: Icon( + Icons.numbers_outlined, + color: Theme.of(context).colorScheme.onSurface, + ), + title: Text('backup.snapshot_id_title'.tr()), + subtitle: Text(snapshotId), + ); +} diff --git a/lib/ui/pages/backups/snapshot_modal.dart b/lib/ui/pages/backups/snapshot_modal.dart new file mode 100644 index 00000000..9d714c25 --- /dev/null +++ b/lib/ui/pages/backups/snapshot_modal.dart @@ -0,0 +1,237 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart'; +import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_cubit.dart'; +import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; +import 'package:selfprivacy/logic/models/backup.dart'; +import 'package:selfprivacy/logic/models/json/server_job.dart'; +import 'package:selfprivacy/logic/models/service.dart'; +import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; +import 'package:selfprivacy/ui/components/cards/outlined_card.dart'; +import 'package:selfprivacy/ui/components/info_box/info_box.dart'; +import 'package:selfprivacy/ui/pages/backups/snapshot_id_list_tile.dart'; + +class SnapshotModal extends StatefulWidget { + const SnapshotModal({ + required this.snapshot, + required this.scrollController, + super.key, + }); + + final Backup snapshot; + final ScrollController scrollController; + + @override + State createState() => _SnapshotModalState(); +} + +class _SnapshotModalState extends State { + BackupRestoreStrategy selectedStrategy = + BackupRestoreStrategy.downloadVerifyOverwrite; + + @override + Widget build(final BuildContext context) { + final List busyServices = context + .watch() + .state + .backupJobList + .where( + (final ServerJob job) => + job.status == JobStatusEnum.running || + job.status == JobStatusEnum.created, + ) + .map((final ServerJob job) => job.typeId.split('.')[1]) + .toList(); + + final bool isServiceBusy = busyServices.contains(widget.snapshot.serviceId); + + final Service? service = context + .read() + .state + .getServiceById(widget.snapshot.serviceId); + + return Scaffold( + backgroundColor: Colors.transparent, + body: ListView( + controller: widget.scrollController, + padding: const EdgeInsets.all(16), + children: [ + const SizedBox(height: 16), + Text( + 'backup.snapshot_modal_heading'.tr(), + style: Theme.of(context).textTheme.headlineSmall, + textAlign: TextAlign.center, + ), + const SizedBox(height: 16), + ListTile( + leading: service != null + ? SvgPicture.string( + service.svgIcon, + height: 24, + width: 24, + colorFilter: ColorFilter.mode( + Theme.of(context).colorScheme.onSurface, + BlendMode.srcIn, + ), + ) + : const Icon( + Icons.question_mark_outlined, + ), + title: Text( + 'backup.snapshot_service_title'.tr(), + ), + subtitle: Text( + service?.displayName ?? widget.snapshot.fallbackServiceName, + ), + ), + ListTile( + leading: Icon( + Icons.access_time_outlined, + color: Theme.of(context).colorScheme.onSurface, + ), + title: Text( + 'backup.snapshot_creation_time_title'.tr(), + ), + subtitle: Text( + '${MaterialLocalizations.of(context).formatShortDate(widget.snapshot.time)} ${TimeOfDay.fromDateTime(widget.snapshot.time).format(context)}', + ), + ), + SnapshotIdListTile(snapshotId: widget.snapshot.id), + ListTile( + leading: Icon( + Icons.info_outline, + color: Theme.of(context).colorScheme.onSurface, + ), + title: Text( + 'backup.snapshot_reason_title'.tr(), + ), + subtitle: Text( + widget.snapshot.reason.displayName.tr(), + ), + ), + if (service != null) + Column( + children: [ + const SizedBox(height: 8), + Text( + 'backup.snapshot_modal_select_strategy'.tr(), + style: Theme.of(context).textTheme.titleMedium, + ), + const SizedBox(height: 8), + _BackupStrategySelectionCard( + isSelected: selectedStrategy == + BackupRestoreStrategy.downloadVerifyOverwrite, + onTap: () { + setState(() { + selectedStrategy = + BackupRestoreStrategy.downloadVerifyOverwrite; + }); + }, + title: + 'backup.snapshot_modal_download_verify_option_title'.tr(), + subtitle: + 'backup.snapshot_modal_download_verify_option_description' + .tr(), + ), + const SizedBox(height: 8), + _BackupStrategySelectionCard( + isSelected: selectedStrategy == BackupRestoreStrategy.inplace, + onTap: () { + setState(() { + selectedStrategy = BackupRestoreStrategy.inplace; + }); + }, + title: 'backup.snapshot_modal_inplace_option_title'.tr(), + subtitle: + 'backup.snapshot_modal_inplace_option_description'.tr(), + ), + const SizedBox(height: 8), + // Restore backup button + BrandButton.filled( + onPressed: isServiceBusy + ? null + : () { + context.read().restoreBackup( + widget.snapshot.id, + selectedStrategy, + ); + Navigator.of(context).pop(); + getIt() + .showSnackBar('backup.restore_started'.tr()); + }, + text: 'backup.restore'.tr(), + ), + ], + ) + else + Padding( + padding: const EdgeInsets.all(16.0), + child: InfoBox( + isWarning: true, + text: 'backup.snapshot_modal_service_not_found'.tr(), + ), + ) + ], + ), + ); + } +} + +class _BackupStrategySelectionCard extends StatelessWidget { + const _BackupStrategySelectionCard({ + required this.isSelected, + required this.title, + required this.subtitle, + required this.onTap, + }); + + final bool isSelected; + final String title; + final String subtitle; + final void Function() onTap; + + @override + Widget build(final BuildContext context) => OutlinedCard( + borderColor: isSelected ? Theme.of(context).colorScheme.primary : null, + borderWidth: isSelected ? 3 : 1, + child: InkResponse( + highlightShape: BoxShape.rectangle, + onTap: onTap, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + children: [ + if (isSelected) + Icon( + Icons.radio_button_on_outlined, + color: Theme.of(context).colorScheme.primary, + ) + else + Icon( + Icons.radio_button_off_outlined, + color: Theme.of(context).colorScheme.outline, + ), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: Theme.of(context).textTheme.bodyLarge, + ), + Text( + subtitle, + style: Theme.of(context).textTheme.bodyMedium, + ), + ], + ), + ), + ], + ), + ), + ), + ); +} diff --git a/lib/ui/pages/devices/devices.dart b/lib/ui/pages/devices/devices.dart index ad48096e..52fffdbe 100644 --- a/lib/ui/pages/devices/devices.dart +++ b/lib/ui/pages/devices/devices.dart @@ -1,3 +1,4 @@ +import 'package:auto_route/auto_route.dart'; import 'package:cubit_form/cubit_form.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; @@ -5,12 +6,14 @@ import 'package:selfprivacy/logic/common_enum/common_enum.dart'; import 'package:selfprivacy/logic/cubit/devices/devices_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/models/json/api_token.dart'; -import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart'; +import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; +import 'package:selfprivacy/ui/components/info_box/info_box.dart'; import 'package:selfprivacy/ui/pages/devices/new_device.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart'; +@RoutePage() class DevicesScreen extends StatefulWidget { - const DevicesScreen({final super.key}); + const DevicesScreen({super.key}); @override State createState() => _DevicesScreenState(); @@ -24,7 +27,7 @@ class _DevicesScreenState extends State { return RefreshIndicator( onRefresh: () async { - context.read().refresh(); + await context.read().refresh(); }, child: BrandHeroScreen( heroTitle: 'devices.main_screen.header'.tr(), @@ -51,19 +54,8 @@ class _DevicesScreenState extends State { const SizedBox(height: 16), const Divider(height: 1), const SizedBox(height: 16), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Icon( - Icons.info_outline, - color: Theme.of(context).colorScheme.onBackground, - ), - const SizedBox(height: 16), - Text( - 'devices.main_screen.tip'.tr(), - style: Theme.of(context).textTheme.bodyMedium, - ), - ], + InfoBox( + text: 'devices.main_screen.tip'.tr(), ), ], const SizedBox(height: 24), @@ -100,8 +92,7 @@ class _DevicesInfo extends StatelessWidget { ), ), ...devicesStatus.otherDevices - .map((final device) => _DeviceTile(device: device)) - .toList(), + .map((final device) => _DeviceTile(device: device)), ], ); } diff --git a/lib/ui/pages/devices/new_device.dart b/lib/ui/pages/devices/new_device.dart index e8173db0..9a64fa72 100644 --- a/lib/ui/pages/devices/new_device.dart +++ b/lib/ui/pages/devices/new_device.dart @@ -3,11 +3,11 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:selfprivacy/logic/cubit/devices/devices_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; -import 'package:selfprivacy/ui/components/brand_button/filled_button.dart'; -import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart'; +import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; +import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; class NewDeviceScreen extends StatelessWidget { - const NewDeviceScreen({final super.key}); + const NewDeviceScreen({super.key}); @override Widget build(final BuildContext context) => BrandHeroScreen( @@ -71,7 +71,7 @@ class _KeyDisplay extends StatelessWidget { ], ), const SizedBox(height: 16), - FilledButton( + BrandButton.filled( child: Text( 'basis.done'.tr(), ), diff --git a/lib/ui/pages/dns_details/dns_details.dart b/lib/ui/pages/dns_details/dns_details.dart index 44d6db0d..98e567a9 100644 --- a/lib/ui/pages/dns_details/dns_details.dart +++ b/lib/ui/pages/dns_details/dns_details.dart @@ -1,14 +1,17 @@ +import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/desired_dns_record.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/cubit/dns_records/dns_records_cubit.dart'; -import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart'; -import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart'; +import 'package:selfprivacy/ui/components/cards/filled_card.dart'; +import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; +@RoutePage() class DnsDetailsPage extends StatefulWidget { - const DnsDetailsPage({final super.key}); + const DnsDetailsPage({super.key}); @override State createState() => _DnsDetailsPageState(); @@ -22,46 +25,59 @@ class _DnsDetailsPageState extends State { String description = ''; String subtitle = ''; Icon icon = const Icon( - Icons.check, - color: Colors.green, + Icons.check_circle_outline, + size: 24.0, ); + bool isError = false; switch (dnsState) { case DnsRecordsStatus.uninitialized: - description = 'providers.domain.states.uninitialized'.tr(); + description = 'domain.uninitialized'.tr(); icon = const Icon( Icons.refresh, + size: 24.0, ); + isError = false; break; case DnsRecordsStatus.refreshing: - description = 'providers.domain.states.refreshing'.tr(); + description = 'domain.refreshing'.tr(); icon = const Icon( Icons.refresh, + size: 24.0, ); + isError = false; break; case DnsRecordsStatus.good: - description = 'providers.domain.states.ok'.tr(); + description = 'domain.ok'.tr(); icon = const Icon( - Icons.check, - color: Colors.green, + Icons.check_circle_outline, + size: 24.0, ); + isError = false; break; case DnsRecordsStatus.error: - description = 'providers.domain.states.error'.tr(); - subtitle = 'providers.domain.states.error_subtitle'.tr(); + description = 'domain.error'.tr(); + subtitle = 'domain.error_subtitle'.tr(); icon = const Icon( - Icons.error, - color: Colors.red, + Icons.error_outline, + size: 24.0, ); + isError = true; break; } - return ListTile( - onTap: dnsState == DnsRecordsStatus.error ? () => fixCallback() : null, - title: Text( - description, - style: Theme.of(context).textTheme.headline6, + return FilledCard( + error: isError, + child: ListTile( + onTap: dnsState == DnsRecordsStatus.error ? () => fixCallback() : null, + leading: icon, + title: Text(description), + subtitle: subtitle != '' ? Text(subtitle) : null, + textColor: isError + ? Theme.of(context).colorScheme.error + : Theme.of(context).colorScheme.onSurfaceVariant, + iconColor: isError + ? Theme.of(context).colorScheme.error + : Theme.of(context).colorScheme.onSurfaceVariant, ), - subtitle: subtitle != '' ? Text(subtitle) : null, - leading: icon, ); } @@ -78,148 +94,114 @@ class _DnsDetailsPageState extends State { if (!isReady) { return BrandHeroScreen( hasBackButton: true, - headerTitle: '', heroIcon: BrandIcons.globe, - heroTitle: 'providers.domain.screen_title'.tr(), - children: [ - BrandCards.outlined( - child: ListTile( - title: Text( - 'not_ready_card.in_menu'.tr(), - style: Theme.of(context).textTheme.headline6, - ), - ), - ), - ], + heroTitle: 'domain.screen_title'.tr(), + heroSubtitle: 'not_ready_card.in_menu'.tr(), + children: const [], ); } + final Color goodColor = Theme.of(context).colorScheme.onBackground; + final Color errorColor = Theme.of(context).colorScheme.error; + final Color neutralColor = Theme.of(context).colorScheme.onBackground; + return BrandHeroScreen( hasBackButton: true, heroSubtitle: domain, heroIcon: BrandIcons.globe, - heroTitle: 'providers.domain.screen_title'.tr(), + heroTitle: 'domain.screen_title'.tr(), children: [ - BrandCards.outlined( - child: Column( - children: [ - _getStateCard(dnsCubit.dnsState, () { - context.read().fix(); - }), - ], - ), - ), - - const SizedBox(height: 16.0), - // Outlined card with a list of A records and their - // status. - BrandCards.outlined( - child: Column( - children: [ - ListTile( - title: Text( - 'providers.domain.cards.services.title'.tr(), - style: Theme.of(context).textTheme.headline6, - ), - subtitle: Text( - 'providers.domain.cards.services.subtitle'.tr(), - style: Theme.of(context).textTheme.caption, - ), - ), - ...dnsCubit.dnsRecords - .where( - (final dnsRecord) => - dnsRecord.category == DnsRecordsCategory.services, - ) - .map( - (final dnsRecord) => Column( - children: [ - const Divider( - height: 1.0, - ), - ListTile( - leading: Icon( - dnsRecord.isSatisfied - ? Icons.check - : dnsCubit.dnsState == - DnsRecordsStatus.refreshing - ? Icons.refresh - : Icons.error, - color: dnsRecord.isSatisfied - ? Colors.green - : dnsCubit.dnsState == - DnsRecordsStatus.refreshing - ? Colors.grey - : Colors.red, - ), - title: Text( - dnsRecord.description.tr(), - style: Theme.of(context).textTheme.labelLarge, - ), - subtitle: Text( - dnsRecord.name, - style: Theme.of(context).textTheme.caption, - ), - ), - ], - ), - ) - .toList(), - ], - ), + _getStateCard( + dnsCubit.dnsState, + () { + context.read().fix(); + }, ), const SizedBox(height: 16.0), - BrandCards.outlined( - child: Column( - children: [ - ListTile( - title: Text( - 'providers.domain.cards.email.title'.tr(), - style: Theme.of(context).textTheme.headline6, + ListTile( + title: Text( + 'domain.services_title'.tr(), + style: Theme.of(context).textTheme.headlineSmall!.copyWith( + color: Theme.of(context).colorScheme.secondary, ), - subtitle: Text( - 'providers.domain.cards.email.subtitle'.tr(), - style: Theme.of(context).textTheme.caption, - ), - ), - ...dnsCubit.dnsRecords - .where( - (final dnsRecord) => - dnsRecord.category == DnsRecordsCategory.email, - ) - .map( - (final dnsRecord) => Column( - children: [ - const Divider( - height: 1.0, - ), - ListTile( - leading: Icon( - dnsRecord.isSatisfied - ? Icons.check - : dnsCubit.dnsState == - DnsRecordsStatus.refreshing - ? Icons.refresh - : Icons.error, - color: dnsRecord.isSatisfied - ? Colors.green - : dnsCubit.dnsState == - DnsRecordsStatus.refreshing - ? Colors.grey - : Colors.red, - ), - title: Text( - dnsRecord.description.tr(), - style: Theme.of(context).textTheme.labelLarge, - ), - ), - ], - ), - ) - .toList(), - ], + ), + subtitle: Text( + 'domain.services_subtitle'.tr(), + style: Theme.of(context).textTheme.labelMedium, ), ), + ...dnsCubit.dnsRecords + .where( + (final dnsRecord) => + dnsRecord.category == DnsRecordsCategory.services, + ) + .map( + (final dnsRecord) => Column( + children: [ + ListTile( + leading: Icon( + dnsRecord.isSatisfied + ? Icons.check_circle_outline + : dnsCubit.dnsState == DnsRecordsStatus.refreshing + ? Icons.refresh + : Icons.error_outline, + color: dnsRecord.isSatisfied + ? goodColor + : dnsCubit.dnsState == DnsRecordsStatus.refreshing + ? neutralColor + : errorColor, + ), + title: Text( + dnsRecord.description.tr(), + ), + subtitle: Text( + dnsRecord.displayName ?? dnsRecord.name, + ), + ), + ], + ), + ), + const SizedBox(height: 16.0), + ListTile( + title: Text( + 'domain.email_title'.tr(), + style: Theme.of(context).textTheme.headlineSmall!.copyWith( + color: Theme.of(context).colorScheme.secondary, + ), + ), + subtitle: Text( + 'domain.email_subtitle'.tr(), + style: Theme.of(context).textTheme.labelMedium, + ), + ), + ...dnsCubit.dnsRecords + .where( + (final dnsRecord) => + dnsRecord.category == DnsRecordsCategory.email, + ) + .map( + (final dnsRecord) => Column( + children: [ + ListTile( + leading: Icon( + dnsRecord.isSatisfied + ? Icons.check_circle_outline + : dnsCubit.dnsState == DnsRecordsStatus.refreshing + ? Icons.refresh + : Icons.error_outline, + color: dnsRecord.isSatisfied + ? goodColor + : dnsCubit.dnsState == DnsRecordsStatus.refreshing + ? neutralColor + : errorColor, + ), + title: Text( + dnsRecord.description.tr(), + ), + ), + ], + ), + ), ], ); } diff --git a/lib/ui/pages/more/about_application.dart b/lib/ui/pages/more/about_application.dart new file mode 100644 index 00000000..1fa5c932 --- /dev/null +++ b/lib/ui/pages/more/about_application.dart @@ -0,0 +1,94 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart'; +import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; +import 'package:selfprivacy/ui/components/brand_md/brand_md.dart'; +import 'package:package_info/package_info.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; +import 'package:url_launcher/url_launcher.dart'; + +@RoutePage() +class AboutApplicationPage extends StatelessWidget { + const AboutApplicationPage({super.key}); + + @override + Widget build(final BuildContext context) { + final bool isReady = context.watch().state + is ServerInstallationFinished; + + return BrandHeroScreen( + hasBackButton: true, + hasFlashButton: false, + heroTitle: 'about_application_page.title'.tr(), + children: [ + FutureBuilder( + future: _packageVersion(), + builder: (final context, final snapshot) => Text( + 'about_application_page.application_version_text' + .tr(args: [snapshot.data.toString()]), + style: Theme.of(context).textTheme.bodyLarge, + ), + ), + if (isReady) + FutureBuilder( + future: _apiVersion(), + builder: (final context, final snapshot) => Text( + 'about_application_page.api_version_text' + .tr(args: [snapshot.data.toString()]), + style: Theme.of(context).textTheme.bodyLarge, + ), + ), + const SizedBox(height: 10), + // Button to call showAboutDialog + TextButton( + onPressed: () => showAboutDialog( + context: context, + applicationName: 'SelfPrivacy', + applicationLegalese: '© 2022 SelfPrivacy', + // Link to privacy policy + children: [ + TextButton( + onPressed: () => launchUrl( + Uri.parse('https://selfprivacy.org/privacy-policy/'), + mode: LaunchMode.externalApplication, + ), + child: Text('about_application_page.privacy_policy'.tr()), + ), + ], + ), + child: const Text('Show about dialog'), + ), + const SizedBox(height: 8), + const Divider(height: 0), + const SizedBox(height: 8), + const BrandMarkdown( + fileName: 'about', + ), + ], + ); + } + + Future _packageVersion() async { + String packageVersion = 'unknown'; + try { + final PackageInfo packageInfo = await PackageInfo.fromPlatform(); + packageVersion = packageInfo.version; + } catch (e) { + print(e); + } + + return packageVersion; + } + + Future _apiVersion() async { + String apiVersion = 'unknown'; + try { + apiVersion = await ServerApi().getApiVersion() ?? apiVersion; + } catch (e) { + print(e); + } + + return apiVersion; + } +} diff --git a/lib/ui/pages/more/about/about.dart b/lib/ui/pages/more/about_us.dart similarity index 60% rename from lib/ui/pages/more/about/about.dart rename to lib/ui/pages/more/about_us.dart index 3d642adc..1a1279d7 100644 --- a/lib/ui/pages/more/about/about.dart +++ b/lib/ui/pages/more/about_us.dart @@ -1,10 +1,11 @@ import 'package:flutter/material.dart'; +import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:selfprivacy/ui/components/brand_md/brand_md.dart'; -class AboutPage extends StatelessWidget { - const AboutPage({final super.key}); +class AboutUsPage extends StatelessWidget { + const AboutUsPage({super.key}); @override Widget build(final BuildContext context) => SafeArea( @@ -12,12 +13,17 @@ class AboutPage extends StatelessWidget { appBar: PreferredSize( preferredSize: const Size.fromHeight(52), child: BrandHeader( - title: 'more.about_project'.tr(), + title: 'about_us_page.title'.tr(), hasBackButton: true, ), ), - body: const BrandMarkdown( - fileName: 'about', + body: ListView( + padding: paddingH15V0, + children: const [ + BrandMarkdown( + fileName: 'about', + ), + ], ), ), ); diff --git a/lib/ui/pages/more/app_settings/app_setting.dart b/lib/ui/pages/more/app_settings/app_setting.dart deleted file mode 100644 index 862815c1..00000000 --- a/lib/ui/pages/more/app_settings/app_setting.dart +++ /dev/null @@ -1,243 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/brand_colors.dart'; -import 'package:selfprivacy/config/brand_theme.dart'; -import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart'; -import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; -import 'package:selfprivacy/ui/components/action_button/action_button.dart'; -import 'package:selfprivacy/ui/components/brand_alert/brand_alert.dart'; -import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart'; -import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; -import 'package:selfprivacy/ui/components/brand_switch/brand_switch.dart'; -import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; -import 'package:selfprivacy/utils/named_font_weight.dart'; -import 'package:easy_localization/easy_localization.dart'; - -class AppSettingsPage extends StatefulWidget { - const AppSettingsPage({final super.key}); - - @override - State createState() => _AppSettingsPageState(); -} - -class _AppSettingsPageState extends State { - @override - Widget build(final BuildContext context) { - final bool isDarkModeOn = - context.watch().state.isDarkModeOn; - - return SafeArea( - child: Builder( - builder: (final context) => Scaffold( - appBar: PreferredSize( - preferredSize: const Size.fromHeight(52), - child: BrandHeader( - title: 'more.settings.title'.tr(), - hasBackButton: true, - ), - ), - body: ListView( - padding: paddingH15V0, - children: [ - const BrandDivider(), - Container( - padding: const EdgeInsets.only(top: 20, bottom: 5), - decoration: const BoxDecoration( - border: Border( - bottom: - BorderSide(width: 1, color: BrandColors.dividerColor), - ), - ), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible( - child: _TextColumn( - title: 'more.settings.1'.tr(), - value: 'more.settings.2'.tr(), - hasWarning: false, - ), - ), - const SizedBox(width: 5), - BrandSwitch( - value: Theme.of(context).brightness == Brightness.dark, - onChanged: (final value) => context - .read() - .updateDarkMode(isDarkModeOn: !isDarkModeOn), - ), - ], - ), - ), - Container( - padding: const EdgeInsets.only(top: 20, bottom: 5), - decoration: const BoxDecoration( - border: Border( - bottom: - BorderSide(width: 1, color: BrandColors.dividerColor), - ), - ), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible( - child: _TextColumn( - title: 'more.settings.3'.tr(), - value: 'more.settings.4'.tr(), - hasWarning: false, - ), - ), - const SizedBox(width: 5), - ElevatedButton( - style: ElevatedButton.styleFrom( - primary: BrandColors.red1, - ), - child: Text( - 'basis.reset'.tr(), - style: const TextStyle( - color: BrandColors.white, - fontWeight: NamedFontWeight.demiBold, - ), - ), - onPressed: () { - showDialog( - context: context, - builder: (final _) => BrandAlert( - title: 'modals.3'.tr(), - contentText: 'modals.4'.tr(), - actions: [ - ActionButton( - text: 'modals.5'.tr(), - isRed: true, - onPressed: () { - context - .read() - .clearAppConfig(); - Navigator.of(context).pop(); - }, - ), - ActionButton( - text: 'basis.cancel'.tr(), - ), - ], - ), - ); - }, - ), - ], - ), - ), - deleteServer(context) - ], - ), - ), - ), - ); - } - - Widget deleteServer(final BuildContext context) { - final bool isDisabled = - context.watch().state.serverDetails == null; - return Container( - padding: const EdgeInsets.only(top: 20, bottom: 5), - decoration: const BoxDecoration( - border: Border( - bottom: BorderSide(width: 1, color: BrandColors.dividerColor), - ), - ), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible( - child: _TextColumn( - title: 'more.settings.5'.tr(), - value: 'more.settings.6'.tr(), - hasWarning: false, - ), - ), - const SizedBox(width: 5), - ElevatedButton( - style: ElevatedButton.styleFrom( - primary: BrandColors.red1, - ), - onPressed: isDisabled - ? null - : () { - showDialog( - context: context, - builder: (final _) => BrandAlert( - title: 'modals.3'.tr(), - contentText: 'modals.6'.tr(), - actions: [ - ActionButton( - text: 'modals.7'.tr(), - isRed: true, - onPressed: () async { - showDialog( - context: context, - builder: (final context) => Container( - alignment: Alignment.center, - child: const CircularProgressIndicator(), - ), - ); - await context - .read() - .serverDelete(); - if (!mounted) { - return; - } - Navigator.of(context).pop(); - }, - ), - ActionButton( - text: 'basis.cancel'.tr(), - ), - ], - ), - ); - }, - child: Text( - 'basis.delete'.tr(), - style: const TextStyle( - color: BrandColors.white, - fontWeight: NamedFontWeight.demiBold, - ), - ), - ), - ], - ), - ); - } -} - -class _TextColumn extends StatelessWidget { - const _TextColumn({ - required this.title, - required this.value, - this.hasWarning = false, - }); - - final String title; - final String value; - final bool hasWarning; - @override - Widget build(final BuildContext context) => Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - BrandText.body1( - title, - style: TextStyle(color: hasWarning ? BrandColors.warning : null), - ), - const SizedBox(height: 5), - BrandText.body1( - value, - style: const TextStyle( - fontSize: 13, - height: 1.53, - color: BrandColors.gray1, - ).merge(TextStyle(color: hasWarning ? BrandColors.warning : null)), - ), - ], - ); -} diff --git a/lib/ui/pages/more/app_settings/app_settings.dart b/lib/ui/pages/more/app_settings/app_settings.dart new file mode 100644 index 00000000..6ab69f40 --- /dev/null +++ b/lib/ui/pages/more/app_settings/app_settings.dart @@ -0,0 +1,151 @@ +import 'dart:async'; + +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart'; +import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; +import 'package:selfprivacy/ui/components/buttons/dialog_action_button.dart'; +import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; +import 'package:easy_localization/easy_localization.dart'; + +@RoutePage() +class AppSettingsPage extends StatefulWidget { + const AppSettingsPage({super.key}); + + @override + State createState() => _AppSettingsPageState(); +} + +class _AppSettingsPageState extends State { + @override + Widget build(final BuildContext context) { + final bool isDarkModeOn = + context.watch().state.isDarkModeOn; + + final bool isSystemDarkModeOn = + context.watch().state.isAutoDarkModeOn; + + return BrandHeroScreen( + hasBackButton: true, + hasFlashButton: false, + bodyPadding: const EdgeInsets.symmetric(vertical: 16), + heroTitle: 'application_settings.title'.tr(), + children: [ + SwitchListTile( + title: Text('application_settings.system_dark_theme_title'.tr()), + subtitle: + Text('application_settings.system_dark_theme_description'.tr()), + value: isSystemDarkModeOn, + onChanged: (final value) => context + .read() + .updateAutoDarkMode(isAutoDarkModeOn: !isSystemDarkModeOn), + ), + SwitchListTile( + title: Text('application_settings.dark_theme_title'.tr()), + subtitle: Text('application_settings.dark_theme_description'.tr()), + value: Theme.of(context).brightness == Brightness.dark, + onChanged: isSystemDarkModeOn + ? null + : (final value) => context + .read() + .updateDarkMode(isDarkModeOn: !isDarkModeOn), + ), + const Divider(height: 0), + Padding( + padding: const EdgeInsets.all(16), + child: Text( + 'application_settings.dangerous_settings'.tr(), + style: Theme.of(context).textTheme.labelLarge!.copyWith( + color: Theme.of(context).colorScheme.error, + ), + ), + ), + const _ResetAppTile(), + // const Divider(height: 0), + _deleteServer(context) + ], + ); + } + + Widget _deleteServer(final BuildContext context) { + final bool isDisabled = + context.watch().state.serverDetails == null; + return ListTile( + title: Text('application_settings.delete_server_title'.tr()), + subtitle: Text('application_settings.delete_server_description'.tr()), + textColor: isDisabled + ? Theme.of(context).colorScheme.onBackground.withOpacity(0.5) + : Theme.of(context).colorScheme.onBackground, + onTap: isDisabled + ? null + : () { + showDialog( + context: context, + builder: (final _) => AlertDialog( + title: Text('modals.are_you_sure'.tr()), + content: Text('modals.delete_server_volume'.tr()), + actions: [ + DialogActionButton( + text: 'modals.yes'.tr(), + isRed: true, + onPressed: () async { + unawaited( + showDialog( + context: context, + builder: (final context) => Container( + alignment: Alignment.center, + child: const CircularProgressIndicator(), + ), + ), + ); + await context + .read() + .serverDelete(); + if (!mounted) { + return; + } + Navigator.of(context).pop(); + }, + ), + DialogActionButton( + text: 'basis.cancel'.tr(), + ), + ], + ), + ); + }, + ); + } +} + +class _ResetAppTile extends StatelessWidget { + const _ResetAppTile(); + + @override + Widget build(final BuildContext context) => ListTile( + title: Text('application_settings.reset_config_title'.tr()), + subtitle: Text('application_settings.reset_config_description'.tr()), + onTap: () { + showDialog( + context: context, + builder: (final _) => AlertDialog( + title: Text('modals.are_you_sure'.tr()), + content: Text('modals.purge_all_keys'.tr()), + actions: [ + DialogActionButton( + text: 'modals.purge_all_keys_confirm'.tr(), + isRed: true, + onPressed: () { + context.read().clearAppConfig(); + Navigator.of(context).pop(); + }, + ), + DialogActionButton( + text: 'basis.cancel'.tr(), + ), + ], + ), + ); + }, + ); +} diff --git a/lib/ui/pages/more/app_settings/developer_settings.dart b/lib/ui/pages/more/app_settings/developer_settings.dart new file mode 100644 index 00000000..3c3addb4 --- /dev/null +++ b/lib/ui/pages/more/app_settings/developer_settings.dart @@ -0,0 +1,95 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:selfprivacy/logic/api_maps/tls_options.dart'; +import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart'; +import 'package:selfprivacy/logic/cubit/devices/devices_cubit.dart'; +import 'package:selfprivacy/logic/cubit/recovery_key/recovery_key_cubit.dart'; +import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; +import 'package:easy_localization/easy_localization.dart'; + +@RoutePage() +class DeveloperSettingsPage extends StatefulWidget { + const DeveloperSettingsPage({super.key}); + + @override + State createState() => _DeveloperSettingsPageState(); +} + +class _DeveloperSettingsPageState extends State { + @override + Widget build(final BuildContext context) => BrandHeroScreen( + hasBackButton: true, + hasFlashButton: false, + bodyPadding: const EdgeInsets.symmetric(vertical: 16), + heroTitle: 'developer_settings.title'.tr(), + heroSubtitle: 'developer_settings.subtitle'.tr(), + children: [ + Padding( + padding: const EdgeInsets.all(16), + child: Text( + 'developer_settings.server_setup'.tr(), + style: Theme.of(context).textTheme.labelLarge!.copyWith( + color: Theme.of(context).colorScheme.secondary, + ), + ), + ), + SwitchListTile( + title: Text('developer_settings.use_staging_acme'.tr()), + subtitle: + Text('developer_settings.use_staging_acme_description'.tr()), + value: TlsOptions.stagingAcme, + onChanged: (final bool value) => setState( + () => TlsOptions.stagingAcme = value, + ), + ), + SwitchListTile( + title: Text('developer_settings.ignore_tls'.tr()), + subtitle: Text('developer_settings.ignore_tls_description'.tr()), + value: TlsOptions.verifyCertificate, + onChanged: (final bool value) => setState( + () => TlsOptions.verifyCertificate = value, + ), + ), + Padding( + padding: const EdgeInsets.all(16), + child: Text( + 'developer_settings.routing'.tr(), + style: Theme.of(context).textTheme.labelLarge!.copyWith( + color: Theme.of(context).colorScheme.secondary, + ), + ), + ), + ListTile( + title: Text('developer_settings.reset_onboarding'.tr()), + subtitle: + Text('developer_settings.reset_onboarding_description'.tr()), + enabled: + !context.watch().state.isOnboardingShowing, + onTap: () => context + .read() + .turnOffOnboarding(isOnboardingShowing: true), + ), + Padding( + padding: const EdgeInsets.all(16), + child: Text( + 'developer_settings.cubit_statuses'.tr(), + style: Theme.of(context).textTheme.labelLarge!.copyWith( + color: Theme.of(context).colorScheme.secondary, + ), + ), + ), + ListTile( + title: const Text('ApiDevicesCubit'), + subtitle: Text( + context.watch().state.status.toString(), + ), + ), + ListTile( + title: const Text('RecoveryKeyCubit'), + subtitle: Text( + context.watch().state.loadingStatus.toString(), + ), + ), + ], + ); +} diff --git a/lib/ui/pages/more/console.dart b/lib/ui/pages/more/console.dart new file mode 100644 index 00000000..f338b580 --- /dev/null +++ b/lib/ui/pages/more/console.dart @@ -0,0 +1,98 @@ +import 'package:auto_route/auto_route.dart'; +import 'dart:collection'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/models/message.dart'; +import 'package:selfprivacy/ui/components/list_tiles/log_list_tile.dart'; + +@RoutePage() +class ConsolePage extends StatefulWidget { + const ConsolePage({super.key}); + + @override + State createState() => _ConsolePageState(); +} + +class _ConsolePageState extends State { + @override + void initState() { + getIt.get().addListener(update); + + super.initState(); + } + + @override + void dispose() { + getIt().removeListener(update); + super.dispose(); + } + + bool paused = false; + + void update() { + if (!paused) { + setState(() => {}); + } + } + + @override + Widget build(final BuildContext context) => SafeArea( + child: Scaffold( + appBar: AppBar( + title: Text('console_page.title'.tr()), + leading: IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: () => Navigator.of(context).pop(), + ), + actions: [ + IconButton( + icon: Icon( + paused ? Icons.play_arrow_outlined : Icons.pause_outlined, + ), + onPressed: () => setState(() => paused = !paused), + ), + ], + ), + body: FutureBuilder( + future: getIt.allReady(), + builder: ( + final BuildContext context, + final AsyncSnapshot snapshot, + ) { + if (snapshot.hasData) { + final List messages = + getIt.get().messages; + + return ListView( + reverse: true, + shrinkWrap: true, + children: [ + const SizedBox(height: 20), + ...UnmodifiableListView( + messages + .map((final message) => LogListItem(message: message)) + .toList() + .reversed, + ), + ], + ); + } else { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Text('console_page.waiting'.tr()), + const SizedBox( + height: 16, + ), + const CircularProgressIndicator(), + ], + ); + } + }, + ), + ), + ); +} diff --git a/lib/ui/pages/more/console/console.dart b/lib/ui/pages/more/console/console.dart deleted file mode 100644 index 76703444..00000000 --- a/lib/ui/pages/more/console/console.dart +++ /dev/null @@ -1,108 +0,0 @@ -import 'dart:collection'; - -import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/brand_colors.dart'; -import 'package:selfprivacy/config/get_it_config.dart'; -import 'package:selfprivacy/logic/models/message.dart'; -import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart'; -import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; - -class Console extends StatefulWidget { - const Console({final super.key}); - - @override - State createState() => _ConsoleState(); -} - -class _ConsoleState extends State { - @override - void initState() { - getIt.get().addListener(update); - - super.initState(); - } - - @override - void dispose() { - getIt().removeListener(update); - super.dispose(); - } - - void update() => setState(() => {}); - - @override - Widget build(final BuildContext context) => SafeArea( - child: Scaffold( - appBar: PreferredSize( - preferredSize: const Size.fromHeight(53), - child: Column( - children: const [ - BrandHeader(title: 'Console', hasBackButton: true), - BrandDivider(), - ], - ), - ), - body: FutureBuilder( - future: getIt.allReady(), - builder: ( - final BuildContext context, - final AsyncSnapshot snapshot, - ) { - if (snapshot.hasData) { - final List messages = - getIt.get().messages; - - return ListView( - reverse: true, - shrinkWrap: true, - children: [ - const SizedBox(height: 20), - ...UnmodifiableListView( - messages - .map((final message) { - final bool isError = - message.type == MessageType.warning; - return Padding( - padding: const EdgeInsets.symmetric(vertical: 4), - child: RichText( - text: TextSpan( - style: DefaultTextStyle.of(context).style, - children: [ - TextSpan( - text: - '${message.timeString}${isError ? '(Error)' : ''}: \n', - style: TextStyle( - fontWeight: FontWeight.bold, - color: - isError ? BrandColors.red1 : null, - ), - ), - TextSpan(text: message.text), - ], - ), - ), - ); - }) - .toList() - .reversed, - ), - ], - ); - } else { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: const [ - Text('Waiting for initialisation'), - SizedBox( - height: 16, - ), - CircularProgressIndicator(), - ], - ); - } - }, - ), - ), - ); -} diff --git a/lib/ui/pages/more/info/info.dart b/lib/ui/pages/more/info/info.dart deleted file mode 100644 index d4c4863b..00000000 --- a/lib/ui/pages/more/info/info.dart +++ /dev/null @@ -1,48 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/brand_theme.dart'; -import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart'; -import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; -import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; -import 'package:package_info/package_info.dart'; -import 'package:easy_localization/easy_localization.dart'; - -class InfoPage extends StatelessWidget { - const InfoPage({final super.key}); - - @override - Widget build(final BuildContext context) => SafeArea( - child: Scaffold( - appBar: PreferredSize( - preferredSize: const Size.fromHeight(52), - child: - BrandHeader(title: 'more.about_app'.tr(), hasBackButton: true), - ), - body: ListView( - padding: paddingH15V0, - children: [ - const BrandDivider(), - const SizedBox(height: 10), - FutureBuilder( - future: _version(), - builder: (final context, final snapshot) => BrandText.body1( - 'more.about_app_page.text' - .tr(args: [snapshot.data.toString()]), - ), - ), - ], - ), - ), - ); - - Future _version() async { - String packageVersion = 'unknown'; - try { - final PackageInfo packageInfo = await PackageInfo.fromPlatform(); - packageVersion = packageInfo.version; - } catch (e) { - print(e); - } - - return packageVersion; - } -} diff --git a/lib/ui/pages/more/more.dart b/lib/ui/pages/more/more.dart index 5a02da3c..3d85b093 100644 --- a/lib/ui/pages/more/more.dart +++ b/lib/ui/pages/more/more.dart @@ -1,99 +1,120 @@ +import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:ionicons/ionicons.dart'; import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; -import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart'; +import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart'; +import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; +import 'package:selfprivacy/ui/components/cards/filled_card.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; -import 'package:selfprivacy/ui/pages/devices/devices.dart'; -import 'package:selfprivacy/ui/pages/recovery_key/recovery_key.dart'; -import 'package:selfprivacy/ui/pages/setup/initializing.dart'; -import 'package:selfprivacy/ui/pages/onboarding/onboarding.dart'; -import 'package:selfprivacy/ui/pages/root_route.dart'; -import 'package:selfprivacy/ui/pages/ssh_keys/ssh_keys.dart'; -import 'package:selfprivacy/utils/route_transitions/basic.dart'; - -import 'package:selfprivacy/logic/cubit/users/users_cubit.dart'; -import 'package:selfprivacy/ui/pages/more/about/about.dart'; -import 'package:selfprivacy/ui/pages/more/app_settings/app_setting.dart'; -import 'package:selfprivacy/ui/pages/more/console/console.dart'; -import 'package:selfprivacy/ui/pages/more/info/info.dart'; +import 'package:selfprivacy/utils/breakpoints.dart'; +import 'package:selfprivacy/ui/router/router.dart'; +@RoutePage() class MorePage extends StatelessWidget { - const MorePage({final super.key}); + const MorePage({super.key}); @override Widget build(final BuildContext context) { final bool isReady = context.watch().state is ServerInstallationFinished; + final bool? usesBinds = + context.watch().state.usesBinds; + return Scaffold( - appBar: PreferredSize( - preferredSize: const Size.fromHeight(52), - child: BrandHeader( - title: 'basis.more'.tr(), - ), - ), + appBar: Breakpoints.small.isActive(context) + ? PreferredSize( + preferredSize: const Size.fromHeight(52), + child: BrandHeader( + title: 'basis.more'.tr(), + ), + ) + : null, body: ListView( children: [ Padding( padding: paddingH15V0, child: Column( children: [ + if (isReady && usesBinds != null && !usesBinds) + _MoreMenuItem( + title: 'storage.start_migration_button'.tr(), + iconData: Icons.drive_file_move_outline, + goTo: () => ServicesMigrationRoute( + diskStatus: context + .watch() + .state + .diskStatus, + services: context + .read() + .state + .services + .where( + (final service) => + service.id == 'bitwarden' || + service.id == 'gitea' || + service.id == 'pleroma' || + service.id == 'mailserver' || + service.id == 'nextcloud', + ) + .toList(), + isMigration: true, + ), + subtitle: 'storage.data_migration_notice'.tr(), + accent: true, + ), if (!isReady) _MoreMenuItem( - title: 'more.configuration_wizard'.tr(), + title: 'more_page.configuration_wizard'.tr(), iconData: Icons.change_history_outlined, - goTo: const InitializingPage(), + goTo: () => const InitializingRoute(), subtitle: 'not_ready_card.in_menu'.tr(), accent: true, ), if (isReady) _MoreMenuItem( - title: 'more.create_ssh_key'.tr(), + title: 'more_page.create_ssh_key'.tr(), iconData: Ionicons.key_outline, - goTo: SshKeysPage( - user: context.read().state.rootUser, + goTo: () => UserDetailsRoute( + login: 'root', ), ), if (isReady) _MoreMenuItem( iconData: Icons.password_outlined, - goTo: const RecoveryKey(), + goTo: () => const RecoveryKeyRoute(), title: 'recovery_key.key_main_header'.tr(), ), if (isReady) _MoreMenuItem( iconData: Icons.devices_outlined, - goTo: const DevicesScreen(), + goTo: () => const DevicesRoute(), title: 'devices.main_screen.header'.tr(), ), _MoreMenuItem( - title: 'more.settings.title'.tr(), + title: 'more_page.application_settings'.tr(), iconData: Icons.settings_outlined, - goTo: const AppSettingsPage(), + goTo: () => const AppSettingsRoute(), ), _MoreMenuItem( - title: 'more.about_project'.tr(), - iconData: BrandIcons.engineer, - goTo: const AboutPage(), - ), - _MoreMenuItem( - title: 'more.about_app'.tr(), + title: 'more_page.about_application'.tr(), iconData: BrandIcons.fire, - goTo: const InfoPage(), + goTo: () => const AboutApplicationRoute(), + longGoTo: const DeveloperSettingsRoute(), ), if (!isReady) _MoreMenuItem( - title: 'more.onboarding'.tr(), + title: 'more_page.onboarding'.tr(), iconData: BrandIcons.start, - goTo: const OnboardingPage(nextPage: RootPage()), + goTo: () => const OnboardingRoute(), ), _MoreMenuItem( - title: 'more.console'.tr(), + title: 'more_page.console'.tr(), iconData: BrandIcons.terminal, - goTo: const Console(), + goTo: () => const ConsoleRoute(), ), ], ), @@ -108,14 +129,16 @@ class _MoreMenuItem extends StatelessWidget { const _MoreMenuItem({ required this.iconData, required this.title, + required this.goTo, this.subtitle, - this.goTo, + this.longGoTo, this.accent = false, }); final IconData iconData; final String title; - final Widget? goTo; + final PageRouteInfo Function() goTo; + final PageRouteInfo? longGoTo; final String? subtitle; final bool accent; @@ -124,13 +147,13 @@ class _MoreMenuItem extends StatelessWidget { final Color color = accent ? Theme.of(context).colorScheme.onTertiaryContainer : Theme.of(context).colorScheme.onSurface; - return BrandCards.filled( + return FilledCard( tertiary: accent, child: ListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - onTap: goTo != null - ? () => Navigator.of(context).push(materialRoute(goTo!)) - : null, + onTap: () => context.pushRoute(goTo()), + onLongPress: + longGoTo != null ? () => context.pushRoute(longGoTo!) : null, leading: Icon( iconData, size: 24, diff --git a/lib/ui/pages/onboarding/onboarding.dart b/lib/ui/pages/onboarding/onboarding.dart index dc5c8763..2e035d53 100644 --- a/lib/ui/pages/onboarding/onboarding.dart +++ b/lib/ui/pages/onboarding/onboarding.dart @@ -1,14 +1,14 @@ +import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart'; -import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; -import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; -import 'package:selfprivacy/utils/route_transitions/basic.dart'; +import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; +import 'package:selfprivacy/ui/router/router.dart'; import 'package:easy_localization/easy_localization.dart'; +@RoutePage() class OnboardingPage extends StatefulWidget { - const OnboardingPage({required this.nextPage, final super.key}); + const OnboardingPage({super.key}); - final Widget nextPage; @override State createState() => _OnboardingPageState(); } @@ -22,15 +22,13 @@ class _OnboardingPageState extends State { } @override - Widget build(final BuildContext context) => SafeArea( - child: Scaffold( - body: PageView( - controller: pageController, - children: [ - _withPadding(firstPage()), - _withPadding(secondPage()), - ], - ), + Widget build(final BuildContext context) => Scaffold( + body: PageView( + controller: pageController, + children: [ + _withPadding(firstPage()), + _withPadding(secondPage()), + ], ), ); @@ -46,24 +44,32 @@ class _OnboardingPageState extends State { maxHeight: MediaQuery.of(context).size.height, ), child: Column( - crossAxisAlignment: CrossAxisAlignment.start, children: [ - const SizedBox(height: 30), - BrandText.h2( - 'onboarding.page1_title'.tr(), - ), - const SizedBox(height: 20), - BrandText.body2('onboarding.page1_text'.tr()), - Flexible( - child: Center( - child: Image.asset( - _fileName( - context: context, - path: 'assets/images/onboarding', - fileExtention: 'png', - fileName: 'onboarding1', + Expanded( + child: ListView( + children: [ + const SizedBox(height: 30), + Text( + 'onboarding.page1_title'.tr(), + style: Theme.of(context).textTheme.headlineSmall, ), - ), + const SizedBox(height: 16), + Text( + 'onboarding.page1_text'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + ), + const SizedBox(height: 32), + Center( + child: Image.asset( + _fileName( + context: context, + path: 'assets/images/onboarding', + fileExtention: 'png', + fileName: 'onboarding1', + ), + ), + ), + ], ), ), BrandButton.rised( @@ -71,7 +77,7 @@ class _OnboardingPageState extends State { pageController.animateToPage( 1, duration: const Duration(milliseconds: 300), - curve: Curves.easeIn, + curve: Curves.easeInOutCubicEmphasized, ); }, text: 'basis.next'.tr(), @@ -87,40 +93,60 @@ class _OnboardingPageState extends State { ), child: Column( children: [ - const SizedBox(height: 30), - BrandText.h2('onboarding.page2_title'.tr()), - const SizedBox(height: 20), - BrandText.body2('onboarding.page2_text'.tr()), - const SizedBox(height: 20), - Center( - child: Image.asset( - _fileName( - context: context, - path: 'assets/images/onboarding', - fileExtention: 'png', - fileName: 'logos_line', - ), - ), - ), - Flexible( - child: Center( - child: Image.asset( - _fileName( - context: context, - path: 'assets/images/onboarding', - fileExtention: 'png', - fileName: 'onboarding2', + Expanded( + child: ListView( + children: [ + const SizedBox(height: 30), + Text( + 'onboarding.page2_title'.tr(), + style: Theme.of(context).textTheme.headlineSmall, ), - ), + const SizedBox(height: 16), + Text( + 'onboarding.page2_text'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + ), + const SizedBox(height: 16), + Text( + 'onboarding.page2_server_provider_title'.tr(), + style: Theme.of(context).textTheme.titleLarge, + ), + const SizedBox(height: 16), + Text( + 'onboarding.page2_server_provider_text'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + ), + const SizedBox(height: 16), + Text( + 'onboarding.page2_dns_provider_title'.tr(), + style: Theme.of(context).textTheme.titleLarge, + ), + const SizedBox(height: 16), + Text( + 'onboarding.page2_dns_provider_text'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + ), + const SizedBox(height: 16), + Text( + 'onboarding.page2_backup_provider_title'.tr(), + style: Theme.of(context).textTheme.titleLarge, + ), + const SizedBox(height: 16), + Text( + 'onboarding.page2_backup_provider_text'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + ), + const SizedBox(height: 16), + ], ), ), BrandButton.rised( onPressed: () { context.read().turnOffOnboarding(); - Navigator.of(context).pushAndRemoveUntil( - materialRoute(widget.nextPage), - (final route) => false, - ); + context.router.replaceAll([ + const RootRoute(), + const InitializingRoute(), + ]); }, text: 'basis.got_it'.tr(), ), diff --git a/lib/ui/pages/providers/providers.dart b/lib/ui/pages/providers/providers.dart index 97e4aeeb..5b2285f3 100644 --- a/lib/ui/pages/providers/providers.dart +++ b/lib/ui/pages/providers/providers.dart @@ -1,3 +1,4 @@ +import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:selfprivacy/config/brand_theme.dart'; @@ -5,23 +6,19 @@ import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart'; import 'package:selfprivacy/logic/cubit/dns_records/dns_records_cubit.dart'; import 'package:selfprivacy/logic/cubit/providers/providers_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; -import 'package:selfprivacy/logic/models/provider.dart'; -import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart'; -import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart'; +import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; -import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; +import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart'; import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart'; -import 'package:selfprivacy/ui/helpers/modals.dart'; -import 'package:selfprivacy/ui/pages/backup_details/backup_details.dart'; -import 'package:selfprivacy/ui/pages/dns_details/dns_details.dart'; -import 'package:selfprivacy/ui/pages/server_details/server_details_screen.dart'; -import 'package:selfprivacy/utils/route_transitions/basic.dart'; +import 'package:selfprivacy/ui/router/router.dart'; +import 'package:selfprivacy/utils/breakpoints.dart'; GlobalKey navigatorKey = GlobalKey(); +@RoutePage() class ProvidersPage extends StatefulWidget { - const ProvidersPage({final super.key}); + const ProvidersPage({super.key}); @override State createState() => _ProvidersPageState(); @@ -37,6 +34,23 @@ class _ProvidersPageState extends State { final DnsRecordsStatus dnsStatus = context.watch().state.dnsState; + final diskStatus = context.watch().state.diskStatus; + + final ServerInstallationState appConfig = + context.watch().state; + + StateType getServerStatus() { + if (!isReady) { + return StateType.uninitialized; + } + if (!diskStatus.isDiskOkay) { + return StateType.warning; + } + return StateType.stable; + } + + bool isClickable() => getServerStatus() != StateType.uninitialized; + StateType getDnsStatus() { if (dnsStatus == DnsRecordsStatus.uninitialized || dnsStatus == DnsRecordsStatus.refreshing) { @@ -48,40 +62,57 @@ class _ProvidersPageState extends State { return StateType.stable; } - final List cards = ProviderType.values - .map( - (final ProviderType type) => Padding( - padding: const EdgeInsets.only(bottom: 30), - child: _Card( - provider: ProviderModel( - state: isReady - ? (type == ProviderType.backup && !isBackupInitialized - ? StateType.uninitialized - : (type == ProviderType.domain) - ? getDnsStatus() - : StateType.stable) - : StateType.uninitialized, - type: type, - ), - ), - ), - ) - .toList(); return Scaffold( - appBar: PreferredSize( - preferredSize: const Size.fromHeight(52), - child: BrandHeader( - title: 'providers.page_title'.tr(), - ), - ), + appBar: Breakpoints.small.isActive(context) + ? PreferredSize( + preferredSize: const Size.fromHeight(52), + child: BrandHeader( + title: 'basis.providers_title'.tr(), + ), + ) + : null, body: ListView( padding: paddingH15V0, children: [ if (!isReady) ...[ const NotReadyCard(), - const SizedBox(height: 24), + const SizedBox(height: 16), ], - ...cards, + _Card( + state: getServerStatus(), + icon: BrandIcons.server, + title: 'server.card_title'.tr(), + subtitle: diskStatus.isDiskOkay + ? 'storage.status_ok'.tr() + : 'storage.status_error'.tr(), + onTap: isClickable() + ? () => context.pushRoute(const ServerDetailsRoute()) + : null, + ), + const SizedBox(height: 16), + _Card( + state: getDnsStatus(), + icon: BrandIcons.globe, + title: 'domain.screen_title'.tr(), + subtitle: appConfig.isDomainSelected + ? appConfig.serverDomain!.domainName + : '', + onTap: isClickable() + ? () => context.pushRoute(const DnsDetailsRoute()) + : null, + ), + const SizedBox(height: 16), + _Card( + state: isBackupInitialized + ? StateType.stable + : StateType.uninitialized, + icon: BrandIcons.save, + title: 'backup.card_title'.tr(), + subtitle: isBackupInitialized ? 'backup.card_subtitle'.tr() : '', + onTap: isClickable() + ? () => context.pushRoute(const BackupDetailsRoute()) + : null, + ), ], ), ); @@ -89,79 +120,65 @@ class _ProvidersPageState extends State { } class _Card extends StatelessWidget { - const _Card({required this.provider}); + const _Card({ + required this.state, + required this.icon, + required this.title, + required this.subtitle, + this.onTap, + }); + + final Function()? onTap; + final StateType state; + final IconData icon; + final String title; + final String subtitle; - final ProviderModel provider; @override - Widget build(final BuildContext context) { - late String title; - String? message; - late String stableText; - late VoidCallback onTap; - final bool isReady = context.watch().state - is ServerInstallationFinished; - final ServerInstallationState appConfig = - context.watch().state; - - final String domainName = - appConfig.isDomainFilled ? appConfig.serverDomain!.domainName : ''; - - switch (provider.type) { - case ProviderType.server: - title = 'providers.server.card_title'.tr(); - stableText = 'providers.server.status'.tr(); - onTap = () => showBrandBottomSheet( - context: context, - builder: (final BuildContext context) => const BrandBottomSheet( - isExpended: true, - child: ServerDetailsScreen(), - ), - ); - - break; - case ProviderType.domain: - title = 'providers.domain.screen_title'.tr(); - message = domainName; - stableText = 'providers.domain.status'.tr(); - - onTap = () => Navigator.of(context).push( - materialRoute( - const DnsDetailsPage(), - ), - ); - break; - case ProviderType.backup: - title = 'providers.backup.card_title'.tr(); - stableText = 'providers.backup.status'.tr(); - - onTap = () => Navigator.of(context).push( - materialRoute( - const BackupDetails(), - ), - ); - break; - } - return GestureDetector( - onTap: isReady ? onTap : null, - child: BrandCards.big( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - IconStatusMask( - status: provider.state, - child: Icon(provider.icon, size: 30, color: Colors.white), + Widget build(final BuildContext context) => Card( + clipBehavior: Clip.antiAlias, + child: InkResponse( + highlightShape: BoxShape.rectangle, + onTap: onTap, + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + IconStatusMask( + status: state, + icon: Icon(icon, size: 30, color: Colors.white), + ), + if (state != StateType.uninitialized) + IconStatusMask( + status: state, + icon: Icon( + state == StateType.stable + ? Icons.check_circle_outline + : state == StateType.warning + ? Icons.warning_amber_outlined + : Icons.error_outline, + color: Colors.white, + ), + ), + ], + ), + const SizedBox(height: 8), + Text( + title, + style: Theme.of(context).textTheme.titleLarge, + ), + if (state != StateType.uninitialized) + Text( + subtitle, + style: Theme.of(context).textTheme.bodyLarge, + ), + ], ), - const SizedBox(height: 10), - BrandText.h2(title), - const SizedBox(height: 10), - if (message != null) ...[ - BrandText.body2(message), - const SizedBox(height: 10), - ], - if (provider.state == StateType.stable) BrandText.body2(stableText), - ], + ), ), - ), - ); - } + ); } diff --git a/lib/ui/pages/recovery_key/recovery_key.dart b/lib/ui/pages/recovery_key/recovery_key.dart index 44147f57..86d678bb 100644 --- a/lib/ui/pages/recovery_key/recovery_key.dart +++ b/lib/ui/pages/recovery_key/recovery_key.dart @@ -1,4 +1,4 @@ -import 'package:cubit_form/cubit_form.dart'; +import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -6,21 +6,27 @@ import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/common_enum/common_enum.dart'; import 'package:selfprivacy/logic/cubit/recovery_key/recovery_key_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; -import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; -import 'package:selfprivacy/ui/components/brand_button/filled_button.dart'; -import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart'; -import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart'; +import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; +import 'package:selfprivacy/ui/components/cards/filled_card.dart'; +import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; import 'package:selfprivacy/ui/pages/recovery_key/recovery_key_receiving.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart'; -class RecoveryKey extends StatefulWidget { - const RecoveryKey({final super.key}); +@RoutePage() +class RecoveryKeyPage extends StatefulWidget { + const RecoveryKeyPage({super.key}); @override - State createState() => _RecoveryKeyState(); + State createState() => _RecoveryKeyPageState(); } -class _RecoveryKeyState extends State { +class _RecoveryKeyPageState extends State { + @override + void initState() { + super.initState(); + context.read().load(); + } + @override Widget build(final BuildContext context) { final RecoveryKeyState keyStatus = context.watch().state; @@ -50,18 +56,23 @@ class _RecoveryKeyState extends State { break; } - return BrandHeroScreen( - heroTitle: 'recovery_key.key_main_header'.tr(), - heroSubtitle: subtitle, - hasBackButton: true, - hasFlashButton: false, - children: widgets, + return RefreshIndicator( + onRefresh: () async { + context.read().load(); + }, + child: BrandHeroScreen( + heroTitle: 'recovery_key.key_main_header'.tr(), + heroSubtitle: subtitle, + hasBackButton: true, + hasFlashButton: false, + children: widgets, + ), ); } } class RecoveryKeyContent extends StatefulWidget { - const RecoveryKeyContent({final super.key}); + const RecoveryKeyContent({super.key}); @override State createState() => _RecoveryKeyContentState(); @@ -93,8 +104,8 @@ class _RecoveryKeyContentState extends State { }, ), if (!_isConfigurationVisible && !keyStatus.isValid && keyStatus.exists) - FilledButton( - title: 'recovery_key.key_replace_button'.tr(), + BrandButton.filled( + child: Text('recovery_key.key_replace_button'.tr()), onPressed: () { setState(() { _isConfigurationVisible = true; @@ -107,12 +118,12 @@ class _RecoveryKeyContentState extends State { } class RecoveryKeyStatusCard extends StatelessWidget { - const RecoveryKeyStatusCard({required this.isValid, final super.key}); + const RecoveryKeyStatusCard({required this.isValid, super.key}); final bool isValid; @override - Widget build(final BuildContext context) => BrandCards.filled( + Widget build(final BuildContext context) => FilledCard( child: ListTile( title: isValid ? Text( @@ -144,7 +155,7 @@ class RecoveryKeyStatusCard extends StatelessWidget { } class RecoveryKeyInformation extends StatelessWidget { - const RecoveryKeyInformation({required this.state, final super.key}); + const RecoveryKeyInformation({required this.state, super.key}); final RecoveryKeyState state; @@ -201,7 +212,7 @@ class RecoveryKeyInformation extends StatelessWidget { } class RecoveryKeyConfiguration extends StatefulWidget { - const RecoveryKeyConfiguration({final super.key}); + const RecoveryKeyConfiguration({super.key}); @override State createState() => _RecoveryKeyConfigurationState(); @@ -240,7 +251,7 @@ class _RecoveryKeyConfigurationState extends State { setState(() { _isLoading = false; }); - Navigator.of(context).push( + await Navigator.of(context).push( materialRoute( RecoveryKeyReceiving(recoveryKey: token), // TO DO ), @@ -294,7 +305,6 @@ class _RecoveryKeyConfigurationState extends State { } _amountController.addListener(_updateErrorStatuses); - _expirationController.addListener(_updateErrorStatuses); return Column( @@ -321,6 +331,7 @@ class _RecoveryKeyConfigurationState extends State { children: [ const SizedBox(height: 8), TextField( + textInputAction: TextInputAction.next, enabled: _isAmountToggled, controller: _amountController, decoration: InputDecoration( @@ -360,6 +371,7 @@ class _RecoveryKeyConfigurationState extends State { children: [ const SizedBox(height: 8), TextField( + textInputAction: TextInputAction.next, enabled: _isExpirationToggled, controller: _expirationController, onTap: () { @@ -381,12 +393,11 @@ class _RecoveryKeyConfigurationState extends State { secondChild: Container(), ), const SizedBox(height: 16), - FilledButton( - title: 'recovery_key.key_receive_button'.tr(), - disabled: _isAmountError || _isExpirationError || _isLoading, - onPressed: !_isAmountError && !_isExpirationError + BrandButton.filled( + onPressed: !_isAmountError && !_isExpirationError && !_isLoading ? _generateRecoveryToken : null, + child: Text('recovery_key.key_receive_button'.tr()), ), ], ); diff --git a/lib/ui/pages/recovery_key/recovery_key_receiving.dart b/lib/ui/pages/recovery_key/recovery_key_receiving.dart index 7ae6adaf..10f2e7c0 100644 --- a/lib/ui/pages/recovery_key/recovery_key_receiving.dart +++ b/lib/ui/pages/recovery_key/recovery_key_receiving.dart @@ -1,10 +1,11 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:selfprivacy/ui/components/brand_button/filled_button.dart'; -import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart'; +import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; +import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; +import 'package:selfprivacy/ui/components/info_box/info_box.dart'; class RecoveryKeyReceiving extends StatelessWidget { - const RecoveryKeyReceiving({required this.recoveryKey, final super.key}); + const RecoveryKeyReceiving({required this.recoveryKey, super.key}); final String recoveryKey; @@ -28,17 +29,12 @@ class RecoveryKeyReceiving extends StatelessWidget { const SizedBox(height: 16), const Divider(), const SizedBox(height: 16), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Icon(Icons.info_outlined, size: 24), - const SizedBox(height: 16), - Text('recovery_key.key_receiving_info'.tr()), - ], + InfoBox( + text: 'recovery_key.key_receiving_info'.tr(), ), const SizedBox(height: 16), - FilledButton( - title: 'recovery_key.key_receiving_done'.tr(), + BrandButton.filled( + child: Text('recovery_key.key_receiving_done'.tr()), onPressed: () { Navigator.of(context).popUntil((final route) => route.isFirst); }, diff --git a/lib/ui/pages/root_route.dart b/lib/ui/pages/root_route.dart index d68e4a0e..b50f453b 100644 --- a/lib/ui/pages/root_route.dart +++ b/lib/ui/pages/root_route.dart @@ -1,90 +1,150 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; -import 'package:selfprivacy/ui/components/brand_tab_bar/brand_tab_bar.dart'; -import 'package:selfprivacy/ui/pages/more/more.dart'; -import 'package:selfprivacy/ui/pages/providers/providers.dart'; -import 'package:selfprivacy/ui/pages/services/services.dart'; -import 'package:selfprivacy/ui/pages/users/users.dart'; +import 'package:selfprivacy/ui/layouts/root_scaffold_with_navigation.dart'; +import 'package:selfprivacy/ui/router/root_destinations.dart'; -import 'package:selfprivacy/ui/components/pre_styled_buttons/flash_fab.dart'; +import 'package:selfprivacy/ui/router/router.dart'; -class RootPage extends StatefulWidget { - const RootPage({final super.key}); +@RoutePage() +class RootPage extends StatefulWidget implements AutoRouteWrapper { + const RootPage({super.key}); @override State createState() => _RootPageState(); + + @override + Widget wrappedRoute(final BuildContext context) => this; } class _RootPageState extends State with TickerProviderStateMixin { - late TabController tabController; + bool shouldUseSplitView() => false; - late final AnimationController _controller = AnimationController( - duration: const Duration(milliseconds: 400), - vsync: this, - ); - late final Animation _animation = CurvedAnimation( - parent: _controller, - curve: Curves.fastOutSlowIn, - ); - - @override - void initState() { - tabController = TabController(length: 4, vsync: this); - tabController.addListener(() { - setState(() { - tabController.index == 2 - ? _controller.forward() - : _controller.reverse(); - }); - }); - super.initState(); - } - - @override - void dispose() { - tabController.dispose(); - _controller.dispose(); - super.dispose(); - } + final destinations = rootDestinations; @override Widget build(final BuildContext context) { final bool isReady = context.watch().state is ServerInstallationFinished; - return SafeArea( - child: Provider( - create: (final _) => ChangeTab(tabController.animateTo), - child: Scaffold( - body: TabBarView( - controller: tabController, - children: const [ - ProvidersPage(), - ServicesPage(), - UsersPage(), - MorePage(), - ], + if (context.read().state.isOnboardingShowing) { + context.router.replace(const OnboardingRoute()); + } + + return AutoRouter( + builder: (final context, final child) { + final currentDestinationIndex = destinations.indexWhere( + (final destination) => + context.router.isRouteActive(destination.route.routeName), + ); + final isOtherRouterActive = + context.router.root.current.name != RootRoute.name; + final routeName = getRouteTitle(context.router.current.name).tr(); + return RootScaffoldWithNavigation( + title: routeName, + destinations: destinations, + showBottomBar: + !(currentDestinationIndex == -1 && !isOtherRouterActive), + showFab: isReady, + child: child, + ); + }, + ); + } +} + +class MainScreenNavigationRail extends StatelessWidget { + const MainScreenNavigationRail({ + required this.destinations, + super.key, + }); + + final List destinations; + + @override + Widget build(final BuildContext context) { + int? activeIndex = destinations.indexWhere( + (final destination) => + context.router.isRouteActive(destination.route.routeName), + ); + if (activeIndex == -1) { + activeIndex = null; + } + + return Padding( + padding: const EdgeInsets.all(8.0), + child: SizedBox( + height: MediaQuery.of(context).size.height, + width: 72, + child: LayoutBuilder( + builder: (final context, final constraints) => SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints(minHeight: constraints.maxHeight), + child: IntrinsicHeight( + child: NavigationRail( + backgroundColor: Colors.transparent, + labelType: NavigationRailLabelType.all, + destinations: destinations + .map( + (final destination) => NavigationRailDestination( + icon: Icon(destination.icon), + label: Text(destination.label), + ), + ) + .toList(), + selectedIndex: activeIndex, + onDestinationSelected: (final index) { + context.router.replaceAll([destinations[index].route]); + }, + ), + ), + ), ), - bottomNavigationBar: BrandTabBar( - controller: tabController, - ), - floatingActionButton: isReady - ? SizedBox( - height: 104 + 16, - child: Column( - crossAxisAlignment: CrossAxisAlignment.end, - mainAxisAlignment: MainAxisAlignment.end, - children: [ - ScaleTransition( - scale: _animation, - child: const AddUserFab(), - ), - const SizedBox(height: 16), - const BrandFab(), - ], - ), - ) - : null, + ), + ), + ); + } +} + +class MainScreenNavigationDrawer extends StatelessWidget { + const MainScreenNavigationDrawer({ + required this.destinations, + super.key, + }); + + final List destinations; + + @override + Widget build(final BuildContext context) { + int? activeIndex = destinations.indexWhere( + (final destination) => + context.router.isRouteActive(destination.route.routeName), + ); + if (activeIndex == -1) { + activeIndex = null; + } + + return SizedBox( + height: MediaQuery.of(context).size.height, + width: 296, + child: LayoutBuilder( + builder: (final context, final constraints) => NavigationDrawer( + key: const Key('PrimaryNavigationDrawer'), + selectedIndex: activeIndex, + onDestinationSelected: (final index) { + context.router.replaceAll([destinations[index].route]); + }, + children: [ + const SizedBox(height: 18), + ...destinations.map( + (final destination) => NavigationDrawerDestination( + icon: Icon(destination.icon), + label: Text(destination.label), + ), + ), + ], ), ), ); diff --git a/lib/ui/pages/server_details/chart.dart b/lib/ui/pages/server_details/chart.dart deleted file mode 100644 index f7972e9c..00000000 --- a/lib/ui/pages/server_details/chart.dart +++ /dev/null @@ -1,166 +0,0 @@ -part of 'server_details_screen.dart'; - -class _Chart extends StatelessWidget { - @override - Widget build(final BuildContext context) { - final HetznerMetricsCubit cubit = context.watch(); - final Period period = cubit.state.period; - final HetznerMetricsState state = cubit.state; - List charts; - if (state is HetznerMetricsLoading) { - charts = [ - Container( - height: 200, - alignment: Alignment.center, - child: Text('basis.loading'.tr()), - ) - ]; - } else if (state is HetznerMetricsLoaded) { - charts = [ - const Legend(color: Colors.red, text: 'CPU %'), - const SizedBox(height: 20), - getCpuChart(state), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - BrandText.small('Public Network interface packets per sec'), - const SizedBox(width: 10), - const Legend(color: Colors.red, text: 'IN'), - const SizedBox(width: 5), - const Legend(color: Colors.green, text: 'OUT'), - ], - ), - const SizedBox(height: 20), - getPpsChart(state), - const SizedBox(height: 1), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - BrandText.small('Public Network interface bytes per sec'), - const SizedBox(width: 10), - const Legend(color: Colors.red, text: 'IN'), - const SizedBox(width: 5), - const Legend(color: Colors.green, text: 'OUT'), - ], - ), - const SizedBox(height: 20), - getBandwidthChart(state), - ]; - } else { - throw 'wrong state'; - } - - return Container( - padding: const EdgeInsets.symmetric(horizontal: 5), - child: Column( - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - BrandRadioTile( - isChecked: period == Period.month, - text: 'providers.server.chart.month'.tr(), - onPress: () => cubit.changePeriod(Period.month), - ), - BrandRadioTile( - isChecked: period == Period.day, - text: 'providers.server.chart.day'.tr(), - onPress: () => cubit.changePeriod(Period.day), - ), - BrandRadioTile( - isChecked: period == Period.hour, - text: 'providers.server.chart.hour'.tr(), - onPress: () => cubit.changePeriod(Period.hour), - ), - ], - ), - ), - ...charts, - ], - ), - ); - } - - Widget getCpuChart(final HetznerMetricsLoaded state) { - final data = state.cpu; - - return SizedBox( - height: 200, - child: CpuChart( - data: data, - period: state.period, - start: state.start, - ), - ); - } - - Widget getPpsChart(final HetznerMetricsLoaded state) { - final ppsIn = state.ppsIn; - final ppsOut = state.ppsOut; - - return SizedBox( - height: 200, - child: NetworkChart( - listData: [ppsIn, ppsOut], - period: state.period, - start: state.start, - ), - ); - } - - Widget getBandwidthChart(final HetznerMetricsLoaded state) { - final ppsIn = state.bandwidthIn; - final ppsOut = state.bandwidthOut; - - return SizedBox( - height: 200, - child: NetworkChart( - listData: [ppsIn, ppsOut], - period: state.period, - start: state.start, - ), - ); - } -} - -class Legend extends StatelessWidget { - const Legend({ - required this.color, - required this.text, - final super.key, - }); - - final String text; - final Color color; - @override - Widget build(final BuildContext context) => Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - _ColoredBox(color: color), - const SizedBox(width: 5), - BrandText.small(text), - ], - ); -} - -class _ColoredBox extends StatelessWidget { - const _ColoredBox({ - required this.color, - }); - - final Color color; - - @override - Widget build(final BuildContext context) => Container( - width: 10, - height: 10, - decoration: BoxDecoration( - color: color.withOpacity(0.3), - border: Border.all( - color: color, - ), - ), - ); -} diff --git a/lib/ui/pages/server_details/charts/bottom_title.dart b/lib/ui/pages/server_details/charts/bottom_title.dart new file mode 100644 index 00000000..8d215d7e --- /dev/null +++ b/lib/ui/pages/server_details/charts/bottom_title.dart @@ -0,0 +1,29 @@ +import 'package:selfprivacy/logic/common_enum/common_enum.dart'; +import 'package:selfprivacy/logic/models/metrics.dart'; +import 'package:intl/intl.dart'; + +String bottomTitle( + final int value, + final List data, + final Period period, +) { + final hhmm = DateFormat('HH:mm'); + final day = DateFormat('MMMd'); + String res; + + if (value <= 0 || value >= data.length) { + return ''; + } + + final time = data[value].time; + switch (period) { + case Period.hour: + case Period.day: + res = hhmm.format(time); + break; + case Period.month: + res = day.format(time); + } + + return res; +} diff --git a/lib/ui/pages/server_details/charts/chart.dart b/lib/ui/pages/server_details/charts/chart.dart new file mode 100644 index 00000000..774dcf75 --- /dev/null +++ b/lib/ui/pages/server_details/charts/chart.dart @@ -0,0 +1,206 @@ +part of '../server_details_screen.dart'; + +class _Chart extends StatelessWidget { + @override + Widget build(final BuildContext context) { + final MetricsCubit cubit = context.watch(); + final Period period = cubit.state.period; + final MetricsState state = cubit.state; + List charts; + if (state is MetricsLoaded || state is MetricsLoading) { + charts = [ + FilledCard( + clipped: false, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'resource_chart.cpu_title'.tr(), + style: Theme.of(context).textTheme.titleMedium?.copyWith( + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + const SizedBox(height: 16), + Stack( + alignment: Alignment.center, + children: [ + if (state is MetricsLoaded) getCpuChart(state), + AnimatedOpacity( + duration: const Duration(milliseconds: 200), + opacity: state is MetricsLoading ? 1 : 0, + child: const _GraphLoadingCardContent(), + ), + ], + ), + ], + ), + ), + ), + const SizedBox(height: 8), + FilledCard( + clipped: false, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + 'resource_chart.network_title'.tr(), + style: Theme.of(context).textTheme.titleMedium?.copyWith( + color: + Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + const Spacer(), + Legend( + color: Theme.of(context).colorScheme.primary, + text: 'resource_chart.in'.tr(), + ), + const SizedBox(width: 5), + Legend( + color: Theme.of(context).colorScheme.tertiary, + text: 'resource_chart.out'.tr(), + ), + ], + ), + const SizedBox(height: 20), + Stack( + alignment: Alignment.center, + children: [ + if (state is MetricsLoaded) getBandwidthChart(state), + AnimatedOpacity( + duration: const Duration(milliseconds: 200), + opacity: state is MetricsLoading ? 1 : 0, + child: const _GraphLoadingCardContent(), + ), + ], + ), + ], + ), + ), + ), + ]; + } else { + throw 'wrong state'; + } + + return Column( + children: [ + SegmentedButtons( + isSelected: [ + period == Period.month, + period == Period.day, + period == Period.hour, + ], + onPressed: (final index) { + switch (index) { + case 0: + cubit.changePeriod(Period.month); + break; + case 1: + cubit.changePeriod(Period.day); + break; + case 2: + cubit.changePeriod(Period.hour); + break; + } + }, + titles: [ + 'resource_chart.month'.tr(), + 'resource_chart.day'.tr(), + 'resource_chart.hour'.tr() + ], + ), + const SizedBox(height: 8), + ...charts, + ], + ); + } + + Widget getCpuChart(final MetricsLoaded state) { + final data = state.metrics.cpu; + + return SizedBox( + height: 200, + child: CpuChart( + data: data, + period: state.period, + start: state.metrics.start, + ), + ); + } + + Widget getBandwidthChart(final MetricsLoaded state) { + final ppsIn = state.metrics.bandwidthIn; + final ppsOut = state.metrics.bandwidthOut; + + return SizedBox( + height: 200, + child: NetworkChart( + listData: [ppsIn, ppsOut], + period: state.period, + start: state.metrics.start, + ), + ); + } +} + +class _GraphLoadingCardContent extends StatelessWidget { + const _GraphLoadingCardContent(); + + @override + Widget build(final BuildContext context) => const SizedBox( + height: 200, + child: Center(child: CircularProgressIndicator()), + ); +} + +class Legend extends StatelessWidget { + const Legend({ + required this.color, + required this.text, + super.key, + }); + + final String text; + final Color color; + @override + Widget build(final BuildContext context) => Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + _ColoredBox(color: color), + const SizedBox(width: 5), + Text( + text, + style: Theme.of(context).textTheme.labelSmall, + ), + ], + ); +} + +class _ColoredBox extends StatelessWidget { + const _ColoredBox({ + required this.color, + }); + + final Color color; + + @override + Widget build(final BuildContext context) => Container( + width: 10, + height: 10, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: color.withOpacity(0.4), + border: Border.all( + color: color, + width: 1.5, + ), + ), + ); +} diff --git a/lib/ui/pages/server_details/charts/cpu_chart.dart b/lib/ui/pages/server_details/charts/cpu_chart.dart new file mode 100644 index 00000000..a11361d4 --- /dev/null +++ b/lib/ui/pages/server_details/charts/cpu_chart.dart @@ -0,0 +1,176 @@ +import 'package:flutter/material.dart'; +import 'package:fl_chart/fl_chart.dart'; +import 'package:selfprivacy/logic/common_enum/common_enum.dart'; +import 'package:selfprivacy/logic/models/metrics.dart'; +import 'package:intl/intl.dart'; +import 'package:selfprivacy/ui/pages/server_details/charts/bottom_title.dart'; + +class CpuChart extends StatelessWidget { + const CpuChart({ + required this.data, + required this.period, + required this.start, + super.key, + }); + + final List data; + final Period period; + final DateTime start; + + List getSpots() { + var i = 0; + final List res = []; + + for (final d in data) { + res.add(FlSpot(i.toDouble(), d.value)); + i++; + } + + return res; + } + + @override + Widget build(final BuildContext context) => LineChart( + LineChartData( + lineTouchData: LineTouchData( + enabled: true, + touchTooltipData: LineTouchTooltipData( + tooltipBgColor: Theme.of(context).colorScheme.surface, + tooltipPadding: const EdgeInsets.all(8), + getTooltipItems: (final List touchedBarSpots) { + final List res = []; + + for (final spot in touchedBarSpots) { + final value = spot.y; + final date = data[spot.x.toInt()].time; + + res.add( + LineTooltipItem( + '${value.toStringAsFixed(2)}% at ${DateFormat('HH:mm dd.MM.yyyy').format(date)}', + TextStyle( + color: Theme.of(context).colorScheme.onSurface, + fontWeight: FontWeight.bold, + ), + ), + ); + } + + return res; + }, + ), + ), + lineBarsData: [ + LineChartBarData( + spots: getSpots(), + isCurved: false, + barWidth: 2, + color: Theme.of(context).colorScheme.primary, + dotData: FlDotData( + show: false, + ), + belowBarData: BarAreaData( + show: true, + gradient: LinearGradient( + colors: [ + Theme.of(context).colorScheme.primary.withOpacity(0.5), + Theme.of(context).colorScheme.primary.withOpacity(0.0), + ], + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + ), + ), + ), + ], + minY: 0, + // Maximal value of data by 100 step + maxY: 100, + minX: 0, + titlesData: FlTitlesData( + topTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)), + bottomTitles: AxisTitles( + sideTitles: SideTitles( + interval: 40, + reservedSize: 30, + getTitlesWidget: (final value, final titleMeta) => Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + bottomTitle( + value.toInt(), + data, + period, + ), + style: Theme.of(context).textTheme.labelSmall?.copyWith( + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + ), + showTitles: true, + ), + ), + leftTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: false, + ), + ), + rightTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: false, + ), + ), + ), + gridData: FlGridData( + show: true, + drawVerticalLine: true, + horizontalInterval: 25, + verticalInterval: 40, + getDrawingHorizontalLine: (final value) => FlLine( + color: Theme.of(context).colorScheme.outline.withOpacity(0.3), + strokeWidth: 1, + ), + getDrawingVerticalLine: (final value) => FlLine( + color: Theme.of(context).colorScheme.outline.withOpacity(0.3), + strokeWidth: 1, + ), + ), + borderData: FlBorderData( + show: true, + border: Border( + bottom: BorderSide( + color: Theme.of(context).colorScheme.outline.withOpacity(0.3), + width: 1, + ), + left: BorderSide( + color: Theme.of(context).colorScheme.outline.withOpacity(0.3), + width: 1, + ), + right: BorderSide( + color: Theme.of(context).colorScheme.outline.withOpacity(0.3), + width: 1, + ), + top: BorderSide( + color: Theme.of(context).colorScheme.outline.withOpacity(0.3), + width: 1, + ), + ), + ), + ), + ); + + bool checkToShowTitle( + final double minValue, + final double maxValue, + final SideTitles sideTitles, + final double appliedInterval, + final double value, + ) { + if (value < 0) { + return false; + } else if (value == 0) { + return true; + } + + final localValue = value - minValue; + final v = localValue / 20; + return v - v.floor() == 0; + } +} diff --git a/lib/ui/pages/server_details/charts/network_charts.dart b/lib/ui/pages/server_details/charts/network_charts.dart new file mode 100644 index 00000000..946d0247 --- /dev/null +++ b/lib/ui/pages/server_details/charts/network_charts.dart @@ -0,0 +1,225 @@ +import 'dart:math'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:fl_chart/fl_chart.dart'; +import 'package:selfprivacy/logic/common_enum/common_enum.dart'; +import 'package:selfprivacy/logic/models/disk_size.dart'; +import 'package:selfprivacy/logic/models/metrics.dart'; +import 'package:selfprivacy/ui/pages/server_details/charts/bottom_title.dart'; + +class NetworkChart extends StatelessWidget { + const NetworkChart({ + required this.listData, + required this.period, + required this.start, + super.key, + }); + + final List> listData; + final Period period; + final DateTime start; + + List getSpots(final data) { + var i = 0; + final List res = []; + + for (final d in data) { + res.add(FlSpot(i.toDouble(), d.value)); + i++; + } + + return res; + } + + @override + Widget build(final BuildContext context) => LineChart( + LineChartData( + lineTouchData: LineTouchData( + enabled: true, + touchTooltipData: LineTouchTooltipData( + tooltipBgColor: Theme.of(context).colorScheme.surface, + tooltipPadding: const EdgeInsets.all(8), + getTooltipItems: (final List touchedBarSpots) { + final List res = []; + + bool timeShown = false; + + for (final spot in touchedBarSpots) { + final value = spot.y; + final date = listData[0][spot.x.toInt()].time; + + res.add( + LineTooltipItem( + '${timeShown ? '' : DateFormat('HH:mm dd.MM.yyyy').format(date)} ${spot.barIndex == 0 ? 'resource_chart.in'.tr() : 'resource_chart.out'.tr()} ${DiskSize(byte: value.toInt()).toString()}', + TextStyle( + color: Theme.of(context).colorScheme.onSurface, + fontWeight: FontWeight.bold, + ), + ), + ); + + timeShown = true; + } + + return res; + }, + ), + ), + lineBarsData: [ + // IN + LineChartBarData( + spots: getSpots(listData[0]), + isCurved: false, + barWidth: 2, + color: Theme.of(context).colorScheme.primary, + dotData: FlDotData( + show: false, + ), + belowBarData: BarAreaData( + show: true, + gradient: LinearGradient( + colors: [ + Theme.of(context).colorScheme.primary.withOpacity(0.5), + Theme.of(context).colorScheme.primary.withOpacity(0.0), + ], + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + ), + ), + ), + // OUT + LineChartBarData( + spots: getSpots(listData[1]), + isCurved: false, + barWidth: 2, + color: Theme.of(context).colorScheme.tertiary, + dotData: FlDotData( + show: false, + ), + belowBarData: BarAreaData( + show: true, + gradient: LinearGradient( + colors: [ + Theme.of(context).colorScheme.tertiary.withOpacity(0.5), + Theme.of(context).colorScheme.tertiary.withOpacity(0.0), + ], + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + ), + ), + ), + ], + minY: 0, + maxY: [ + ...listData[0].map((final e) => e.value), + ...listData[1].map((final e) => e.value) + ].reduce(max) * + 1.2, + minX: 0, + titlesData: FlTitlesData( + topTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)), + bottomTitles: AxisTitles( + sideTitles: SideTitles( + interval: 40, + reservedSize: 30, + getTitlesWidget: (final value, final titleMeta) => Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + bottomTitle( + value.toInt(), + listData[0], + period, + ), + style: Theme.of(context).textTheme.labelSmall?.copyWith( + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + ), + showTitles: true, + ), + ), + leftTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)), + rightTitles: AxisTitles( + sideTitles: SideTitles( + reservedSize: 50, + getTitlesWidget: (final value, final titleMeta) => Padding( + padding: const EdgeInsets.only(left: 5), + child: Text( + DiskSize(byte: value.toInt()).toString(), + style: Theme.of(context).textTheme.labelSmall?.copyWith( + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + ), + interval: [ + ...listData[0].map((final e) => e.value), + ...listData[1].map((final e) => e.value) + ].reduce(max) * + 2 / + 6.5, + showTitles: true, + ), + ), + ), + gridData: FlGridData( + show: true, + drawVerticalLine: true, + verticalInterval: 40, + horizontalInterval: [ + ...listData[0].map((final e) => e.value), + ...listData[1].map((final e) => e.value) + ].reduce(max) * + 2 / + 6.5, + getDrawingHorizontalLine: (final value) => FlLine( + color: Theme.of(context).colorScheme.outline.withOpacity(0.3), + strokeWidth: 1, + ), + getDrawingVerticalLine: (final value) => FlLine( + color: Theme.of(context).colorScheme.outline.withOpacity(0.3), + strokeWidth: 1, + ), + ), + borderData: FlBorderData( + show: true, + border: Border( + bottom: BorderSide( + color: Theme.of(context).colorScheme.outline.withOpacity(0.3), + width: 1, + ), + left: BorderSide( + color: Theme.of(context).colorScheme.outline.withOpacity(0.3), + width: 1, + ), + right: BorderSide( + color: Theme.of(context).colorScheme.outline.withOpacity(0.3), + width: 1, + ), + top: BorderSide( + color: Theme.of(context).colorScheme.outline.withOpacity(0.3), + width: 1, + ), + ), + ), + ), + ); + + bool checkToShowTitle( + final double minValue, + final double maxValue, + final SideTitles sideTitles, + final double appliedInterval, + final double value, + ) { + if (value < 0) { + return false; + } else if (value == 0) { + return true; + } + + final diff = value - minValue; + final finalValue = diff / 20; + return finalValue - finalValue.floor() == 0; + } +} diff --git a/lib/ui/pages/server_details/cpu_chart.dart b/lib/ui/pages/server_details/cpu_chart.dart deleted file mode 100644 index 11f1eaef..00000000 --- a/lib/ui/pages/server_details/cpu_chart.dart +++ /dev/null @@ -1,135 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:fl_chart/fl_chart.dart'; -import 'package:selfprivacy/config/brand_colors.dart'; -import 'package:selfprivacy/config/text_themes.dart'; -import 'package:selfprivacy/logic/common_enum/common_enum.dart'; -import 'package:selfprivacy/logic/models/hetzner_metrics.dart'; -import 'package:intl/intl.dart'; - -class CpuChart extends StatelessWidget { - const CpuChart({ - required this.data, - required this.period, - required this.start, - final super.key, - }); - - final List data; - final Period period; - final DateTime start; - - List getSpots() { - var i = 0; - final List res = []; - - for (final d in data) { - res.add(FlSpot(i.toDouble(), d.value)); - i++; - } - - return res; - } - - @override - Widget build(final BuildContext context) => LineChart( - LineChartData( - lineTouchData: LineTouchData(enabled: false), - lineBarsData: [ - LineChartBarData( - spots: getSpots(), - isCurved: true, - barWidth: 1, - color: Colors.red, - dotData: FlDotData( - show: false, - ), - ), - ], - minY: 0, - maxY: 100, - minX: data.length - 200, - titlesData: FlTitlesData( - topTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)), - bottomTitles: AxisTitles( - sideTitles: SideTitles( - interval: 20, - reservedSize: 50, - getTitlesWidget: (final value, final titleMeta) => Padding( - padding: const EdgeInsets.all(8.0), - child: RotatedBox( - quarterTurns: 1, - child: Text( - bottomTitle(value.toInt()), - style: const TextStyle( - fontSize: 10, - color: Colors.purple, - fontWeight: FontWeight.bold, - ), - ), - ), - ), - showTitles: true, - ), - ), - leftTitles: AxisTitles( - sideTitles: SideTitles( - getTitlesWidget: (final value, final titleMeta) => Padding( - padding: const EdgeInsets.only(right: 15), - child: Text( - value.toInt().toString(), - style: progressTextStyleLight.copyWith( - color: Theme.of(context).brightness == Brightness.dark - ? BrandColors.gray4 - : null, - ), - ), - ), - interval: 25, - showTitles: false, - ), - ), - ), - gridData: FlGridData(show: true), - ), - ); - - bool checkToShowTitle( - final double minValue, - final double maxValue, - final SideTitles sideTitles, - final double appliedInterval, - final double value, - ) { - if (value < 0) { - return false; - } else if (value == 0) { - return true; - } - - final localValue = value - minValue; - final v = localValue / 20; - return v - v.floor() == 0; - } - - String bottomTitle(final int value) { - final hhmm = DateFormat('HH:mm'); - final day = DateFormat('MMMd'); - String res; - - if (value <= 0) { - return ''; - } - - final time = data[value].time; - switch (period) { - case Period.hour: - case Period.day: - res = hhmm.format(time); - break; - case Period.month: - res = day.format(time); - } - - return res; - } -} diff --git a/lib/ui/pages/server_details/header.dart b/lib/ui/pages/server_details/header.dart deleted file mode 100644 index a10bc1cf..00000000 --- a/lib/ui/pages/server_details/header.dart +++ /dev/null @@ -1,58 +0,0 @@ -part of 'server_details_screen.dart'; - -class _Header extends StatelessWidget { - const _Header({ - required this.providerState, - required this.tabController, - }); - - final StateType providerState; - final TabController tabController; - - @override - Widget build(final BuildContext context) => Row( - children: [ - IconStatusMask( - status: providerState, - child: const Icon( - BrandIcons.server, - size: 40, - color: Colors.white, - ), - ), - const SizedBox(width: 10), - BrandText.h2('providers.server.card_title'.tr()), - const Spacer(), - Padding( - padding: const EdgeInsets.symmetric( - vertical: 4, - horizontal: 2, - ), - child: PopupMenuButton<_PopupMenuItemType>( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10.0), - ), - onSelected: (final _PopupMenuItemType result) { - switch (result) { - case _PopupMenuItemType.setting: - tabController.animateTo(1); - break; - } - }, - icon: const Icon(Icons.more_vert), - itemBuilder: (final BuildContext context) => [ - PopupMenuItem<_PopupMenuItemType>( - value: _PopupMenuItemType.setting, - child: Container( - padding: const EdgeInsets.only(left: 5), - child: Text('basis.settings'.tr()), - ), - ), - ], - ), - ), - ], - ); -} - -enum _PopupMenuItemType { setting } diff --git a/lib/ui/pages/server_details/network_charts.dart b/lib/ui/pages/server_details/network_charts.dart deleted file mode 100644 index d1375ae6..00000000 --- a/lib/ui/pages/server_details/network_charts.dart +++ /dev/null @@ -1,160 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; -import 'package:fl_chart/fl_chart.dart'; -import 'package:selfprivacy/config/brand_colors.dart'; -import 'package:selfprivacy/config/text_themes.dart'; -import 'package:selfprivacy/logic/common_enum/common_enum.dart'; -import 'package:selfprivacy/logic/models/hetzner_metrics.dart'; -import 'package:intl/intl.dart'; - -class NetworkChart extends StatelessWidget { - const NetworkChart({ - required this.listData, - required this.period, - required this.start, - final super.key, - }); - - final List> listData; - final Period period; - final DateTime start; - - List getSpots(final data) { - var i = 0; - final List res = []; - - for (final d in data) { - res.add(FlSpot(i.toDouble(), d.value)); - i++; - } - - return res; - } - - @override - Widget build(final BuildContext context) => SizedBox( - height: 150, - width: MediaQuery.of(context).size.width, - child: LineChart( - LineChartData( - lineTouchData: LineTouchData(enabled: false), - lineBarsData: [ - LineChartBarData( - spots: getSpots(listData[0]), - isCurved: true, - barWidth: 1, - color: Colors.red, - dotData: FlDotData( - show: false, - ), - ), - LineChartBarData( - spots: getSpots(listData[1]), - isCurved: true, - barWidth: 1, - color: Colors.green, - dotData: FlDotData( - show: false, - ), - ), - ], - minY: 0, - maxY: [ - ...listData[0].map((final e) => e.value), - ...listData[1].map((final e) => e.value) - ].reduce(max) * - 1.2, - minX: listData[0].length - 200, - titlesData: FlTitlesData( - topTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)), - bottomTitles: AxisTitles( - sideTitles: SideTitles( - interval: 20, - reservedSize: 50, - getTitlesWidget: (final value, final titleMeta) => Padding( - padding: const EdgeInsets.all(8.0), - child: RotatedBox( - quarterTurns: 1, - child: Text( - bottomTitle(value.toInt()), - style: const TextStyle( - fontSize: 10, - color: Colors.purple, - fontWeight: FontWeight.bold, - ), - ), - ), - ), - showTitles: true, - ), - ), - leftTitles: AxisTitles( - sideTitles: SideTitles( - reservedSize: 50, - getTitlesWidget: (final value, final titleMeta) => Padding( - padding: const EdgeInsets.only(right: 5), - child: Text( - value.toInt().toString(), - style: progressTextStyleLight.copyWith( - color: Theme.of(context).brightness == Brightness.dark - ? BrandColors.gray4 - : null, - ), - ), - ), - interval: [ - ...listData[0].map((final e) => e.value), - ...listData[1].map((final e) => e.value) - ].reduce(max) * - 2 / - 10, - showTitles: false, - ), - ), - ), - gridData: FlGridData(show: true), - ), - ), - ); - - bool checkToShowTitle( - final double minValue, - final double maxValue, - final SideTitles sideTitles, - final double appliedInterval, - final double value, - ) { - if (value < 0) { - return false; - } else if (value == 0) { - return true; - } - - final diff = value - minValue; - final finalValue = diff / 20; - return finalValue - finalValue.floor() == 0; - } - - String bottomTitle(final int value) { - final hhmm = DateFormat('HH:mm'); - final day = DateFormat('MMMd'); - String res; - - if (value <= 0) { - return ''; - } - - final time = listData[0][value].time; - switch (period) { - case Period.hour: - case Period.day: - res = hhmm.format(time); - break; - case Period.month: - res = day.format(time); - } - - return res; - } -} diff --git a/lib/ui/pages/server_details/server_details_screen.dart b/lib/ui/pages/server_details/server_details_screen.dart index 1f442220..a3a521b5 100644 --- a/lib/ui/pages/server_details/server_details_screen.dart +++ b/lib/ui/pages/server_details/server_details_screen.dart @@ -1,40 +1,39 @@ +import 'package:auto_route/auto_route.dart'; import 'package:cubit_form/cubit_form.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/brand_colors.dart'; -import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/logic/common_enum/common_enum.dart'; -import 'package:selfprivacy/logic/cubit/hetzner_metrics/hetzner_metrics_cubit.dart'; +import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart'; +import 'package:selfprivacy/logic/cubit/metrics/metrics_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; -import 'package:selfprivacy/logic/models/state_types.dart'; -import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart'; -import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; +import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart'; +import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart'; +import 'package:selfprivacy/logic/models/job.dart'; +import 'package:selfprivacy/ui/components/buttons/segmented_buttons.dart'; +import 'package:selfprivacy/ui/components/cards/filled_card.dart'; +import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; import 'package:selfprivacy/ui/components/brand_loader/brand_loader.dart'; -import 'package:selfprivacy/ui/components/brand_radio_tile/brand_radio_tile.dart'; -import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; -import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart'; -import 'package:selfprivacy/ui/components/switch_block/switch_bloc.dart'; -import 'package:selfprivacy/ui/pages/server_details/time_zone/lang.dart'; +import 'package:selfprivacy/ui/components/list_tiles/list_tile_on_surface_variant.dart'; +import 'package:selfprivacy/ui/pages/server_details/charts/cpu_chart.dart'; +import 'package:selfprivacy/ui/pages/server_details/charts/network_charts.dart'; +import 'package:selfprivacy/ui/pages/server_storage/storage_card.dart'; +import 'package:selfprivacy/utils/breakpoints.dart'; import 'package:selfprivacy/utils/extensions/duration.dart'; -import 'package:selfprivacy/utils/named_font_weight.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart'; import 'package:timezone/timezone.dart'; -import 'package:selfprivacy/ui/pages/server_details/cpu_chart.dart'; -import 'package:selfprivacy/ui/pages/server_details/network_charts.dart'; - -part 'chart.dart'; -part 'header.dart'; +part 'charts/chart.dart'; part 'server_settings.dart'; part 'text_details.dart'; part 'time_zone/time_zone.dart'; var navigatorKey = GlobalKey(); +@RoutePage() class ServerDetailsScreen extends StatefulWidget { - const ServerDetailsScreen({final super.key}); + const ServerDetailsScreen({super.key}); @override State createState() => _ServerDetailsScreenState(); @@ -63,60 +62,42 @@ class _ServerDetailsScreenState extends State Widget build(final BuildContext context) { final bool isReady = context.watch().state is ServerInstallationFinished; - final providerState = isReady ? StateType.stable : StateType.uninitialized; + + if (!isReady) { + return BrandHeroScreen( + heroIcon: BrandIcons.server, + heroTitle: 'server.card_title'.tr(), + heroSubtitle: 'not_ready_card.in_menu'.tr(), + children: const [], + ); + } return BlocProvider( - create: (final context) => ServerDetailsCubit()..check(), - child: Scaffold( - appBar: PreferredSize( - preferredSize: const Size.fromHeight(52), - child: Column( - children: [ - Container( - height: 51, - alignment: Alignment.center, - padding: const EdgeInsets.symmetric(horizontal: 15), - child: BrandText.h4('basis.details'.tr()), - ), - const BrandDivider(), - ], + create: (final context) => context.read()..check(), + child: BrandHeroScreen( + hasFlashButton: true, + heroIcon: BrandIcons.server, + heroTitle: 'server.card_title'.tr(), + heroSubtitle: 'server.description'.tr(), + children: [ + StorageCard( + diskStatus: context.watch().state.diskStatus, ), - ), - body: TabBarView( - physics: const NeverScrollableScrollPhysics(), - controller: tabController, - children: [ - SingleChildScrollView( - physics: const ClampingScrollPhysics(), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: paddingH15V0, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _Header( - providerState: providerState, - tabController: tabController, - ), - BrandText.body1('providers.server.bottom_sheet.1'.tr()), - ], - ), - ), - const SizedBox(height: 10), - BlocProvider( - create: (final context) => HetznerMetricsCubit()..restart(), - child: _Chart(), - ), - const SizedBox(height: 20), - _TextDetails(), - ], - ), - ), - _ServerSettings(tabController: tabController), - ], - ), + const SizedBox(height: 16), + const _ServerSettings(), + const Divider(height: 32), + Text( + 'server.resource_usage'.tr(), + style: Theme.of(context).textTheme.titleLarge, + ), + const SizedBox(height: 8), + BlocProvider( + create: (final context) => MetricsCubit()..restart(), + child: _Chart(), + ), + const SizedBox(height: 8), + _TextDetails(), + ], ), ); } diff --git a/lib/ui/pages/server_details/server_settings.dart b/lib/ui/pages/server_details/server_settings.dart index 18d425e6..a9facd5d 100644 --- a/lib/ui/pages/server_details/server_settings.dart +++ b/lib/ui/pages/server_details/server_settings.dart @@ -1,124 +1,98 @@ part of 'server_details_screen.dart'; -class _ServerSettings extends StatelessWidget { - const _ServerSettings({ - required this.tabController, - }); +class _ServerSettings extends StatefulWidget { + const _ServerSettings(); - final TabController tabController; + @override + State<_ServerSettings> createState() => _ServerSettingsState(); +} + +class _ServerSettingsState extends State<_ServerSettings> { + bool? allowAutoUpgrade; + bool? rebootAfterUpgrade; @override Widget build(final BuildContext context) { final serverDetailsState = context.watch().state; if (serverDetailsState is ServerDetailsNotReady) { - return const Text('not ready'); + return Text('basis.loading'.tr()); } else if (serverDetailsState is! Loaded) { return BrandLoader.horizontal(); } - return ListView( - padding: paddingH15V0, + if (allowAutoUpgrade == null || rebootAfterUpgrade == null) { + allowAutoUpgrade = serverDetailsState.autoUpgradeSettings.enable; + rebootAfterUpgrade = serverDetailsState.autoUpgradeSettings.allowReboot; + } + + return Column( children: [ - const SizedBox(height: 10), - Container( - height: 52, - alignment: Alignment.centerLeft, - padding: const EdgeInsets.only(left: 1), - child: Row( - children: [ - IconButton( - icon: const Icon(BrandIcons.arrowLeft), - onPressed: () => tabController.animateTo(0), - ), - const SizedBox(width: 10), - BrandText.h4('basis.settings'.tr()), - ], - ), - ), - const BrandDivider(), - SwitcherBlock( - onChange: (final _) {}, - isActive: serverDetailsState.autoUpgradeSettings.enable, - child: const _TextColumn( - title: 'Allow Auto-upgrade', - value: 'Wether to allow automatic packages upgrades', - hasWarning: false, - ), - ), - SwitcherBlock( - onChange: (final _) {}, - isActive: serverDetailsState.autoUpgradeSettings.allowReboot, - child: const _TextColumn( - title: 'Reboot after upgrade', - value: 'Reboot without prompt after applying updates', - hasWarning: false, - ), - ), - _Button( - onTap: () { - Navigator.of(context).push(materialRoute(const SelectTimezone())); + SwitchListTile( + value: allowAutoUpgrade ?? false, + onChanged: (final switched) { + context.read().addJob( + RebuildServerJob(title: 'jobs.upgrade_server'.tr()), + ); + context + .read() + .repository + .setAutoUpgradeSettings( + AutoUpgradeSettings( + enable: switched, + allowReboot: rebootAfterUpgrade ?? false, + ), + ); + setState(() { + allowAutoUpgrade = switched; + }); }, - child: _TextColumn( - title: 'Server Timezone', - value: serverDetailsState.serverTimezone.timezone.name, - hasWarning: false, + title: Text('server.allow_autoupgrade'.tr()), + subtitle: Text( + 'server.allow_autoupgrade_hint'.tr(), ), + activeColor: Theme.of(context).colorScheme.primary, + ), + SwitchListTile( + value: rebootAfterUpgrade ?? false, + onChanged: (final switched) { + context.read().addJob( + RebuildServerJob(title: 'jobs.upgrade_server'.tr()), + ); + context + .read() + .repository + .setAutoUpgradeSettings( + AutoUpgradeSettings( + enable: allowAutoUpgrade ?? false, + allowReboot: switched, + ), + ); + setState(() { + rebootAfterUpgrade = switched; + }); + }, + title: Text('server.reboot_after_upgrade'.tr()), + subtitle: Text( + 'server.reboot_after_upgrade_hint'.tr(), + ), + activeColor: Theme.of(context).colorScheme.primary, + ), + ListTile( + title: Text('server.server_timezone'.tr()), + subtitle: Text( + serverDetailsState.serverTimezone.toString(), + ), + onTap: () { + context.read().addJob( + RebuildServerJob(title: 'jobs.upgrade_server'.tr()), + ); + Navigator.of(context).push( + materialRoute( + const SelectTimezone(), + ), + ); + }, ), ], ); } } - -class _Button extends StatelessWidget { - const _Button({ - required this.onTap, - required this.child, - }); - - final Widget child; - final VoidCallback onTap; - - @override - Widget build(final BuildContext context) => InkWell( - onTap: onTap, - child: Container( - padding: const EdgeInsets.only(top: 20, bottom: 5), - decoration: const BoxDecoration( - border: Border( - bottom: BorderSide(width: 1, color: BrandColors.dividerColor), - ), - ), - child: child, - ), - ); -} - -class _TextColumn extends StatelessWidget { - const _TextColumn({ - required this.title, - required this.value, - this.hasWarning = false, - }); - - final String title; - final String value; - final bool hasWarning; - @override - Widget build(final BuildContext context) => Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - BrandText.body1( - title, - style: TextStyle(color: hasWarning ? BrandColors.warning : null), - ), - const SizedBox(height: 5), - BrandText.body1( - value, - style: TextStyle( - fontSize: 13, - height: 1.53, - color: hasWarning ? BrandColors.warning : BrandColors.gray1, - ), - ), - ], - ); -} diff --git a/lib/ui/pages/server_details/text_details.dart b/lib/ui/pages/server_details/text_details.dart index 2285d305..2fef5440 100644 --- a/lib/ui/pages/server_details/text_details.dart +++ b/lib/ui/pages/server_details/text_details.dart @@ -10,139 +10,33 @@ class _TextDetails extends StatelessWidget { } else if (details is ServerDetailsNotReady) { return _TempMessage(message: 'basis.no_data'.tr()); } else if (details is Loaded) { - final data = details.serverInfo; - final checkTime = details.checkTime; - return Column( - children: [ - Center(child: BrandText.h3('providers.server.bottom_sheet.2'.tr())), - const SizedBox(height: 10), - Table( - columnWidths: const { - 0: FractionColumnWidth(.5), - 1: FractionColumnWidth(.5), - }, - defaultVerticalAlignment: TableCellVerticalAlignment.middle, - children: [ - TableRow( - children: [ - getRowTitle('Last check:'), - getRowValue(formatter.format(checkTime)), - ], + return FilledCard( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Text( + 'server.general_information'.tr(), + style: Theme.of(context).textTheme.titleMedium?.copyWith( + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), ), - TableRow( - children: [ - getRowTitle('Server Id:'), - getRowValue(data.id.toString()), - ], + ), + ...details.metadata.map( + (final metadata) => ListTileOnSurfaceVariant( + leadingIcon: metadata.type.icon, + title: metadata.trId.tr(), + subtitle: metadata.value, ), - TableRow( - children: [ - getRowTitle('Status:'), - getRowValue( - data.status.toString().split('.')[1].toUpperCase(), - isBold: true, - ), - ], - ), - TableRow( - children: [ - getRowTitle('CPU:'), - getRowValue( - data.serverType.cores.toString(), - ), - ], - ), - TableRow( - children: [ - getRowTitle('Memory:'), - getRowValue( - '${data.serverType.memory.toString()} GB', - ), - ], - ), - TableRow( - children: [ - getRowTitle('Disk Local:'), - getRowValue( - '${data.serverType.disk.toString()} GB', - ), - ], - ), - TableRow( - children: [ - getRowTitle('Price monthly:'), - getRowValue( - data.serverType.prices[1].monthly.toString(), - ), - ], - ), - TableRow( - children: [ - getRowTitle('Price hourly:'), - getRowValue( - data.serverType.prices[1].hourly.toString(), - ), - ], - ), - ], - ), - const SizedBox(height: 30), - Center(child: BrandText.h3('providers.server.bottom_sheet.3'.tr())), - const SizedBox(height: 10), - Table( - columnWidths: const { - 0: FractionColumnWidth(.5), - 1: FractionColumnWidth(.5), - }, - defaultVerticalAlignment: TableCellVerticalAlignment.middle, - children: [ - TableRow( - children: [ - getRowTitle('Country:'), - getRowValue( - data.location.country, - ), - ], - ), - TableRow( - children: [ - getRowTitle('City:'), - getRowValue(data.location.city), - ], - ), - TableRow( - children: [ - getRowTitle('Description:'), - getRowValue(data.location.description), - ], - ), - ], - ), - const SizedBox(height: 20), - ], + ), + ], + ), ); } else { throw Exception('wrong state'); } } - - Widget getRowTitle(final String title) => Padding( - padding: const EdgeInsets.only(right: 10), - child: BrandText.h5( - title, - textAlign: TextAlign.right, - ), - ); - - Widget getRowValue(final String title, {final bool isBold = false}) => - BrandText.body1( - title, - style: isBold - ? const TextStyle( - fontWeight: NamedFontWeight.demiBold, - ) - : null, - ); } class _TempMessage extends StatelessWidget { @@ -155,7 +49,10 @@ class _TempMessage extends StatelessWidget { Widget build(final BuildContext context) => SizedBox( height: MediaQuery.of(context).size.height - 100, child: Center( - child: BrandText.body2(message), + child: Text( + message, + style: Theme.of(context).textTheme.bodyMedium, + ), ), ); } diff --git a/lib/ui/pages/server_details/time_zone/time_zone.dart b/lib/ui/pages/server_details/time_zone/time_zone.dart index 33799c35..5eb369bc 100644 --- a/lib/ui/pages/server_details/time_zone/time_zone.dart +++ b/lib/ui/pages/server_details/time_zone/time_zone.dart @@ -7,18 +7,28 @@ final List locations = timeZoneDatabase.locations.values.toList() ); class SelectTimezone extends StatefulWidget { - const SelectTimezone({final super.key}); + const SelectTimezone({super.key}); @override State createState() => _SelectTimezoneState(); } class _SelectTimezoneState extends State { - final ScrollController controller = ScrollController(); + final ScrollController scrollController = ScrollController(); + final TextEditingController searchController = TextEditingController(); + + String? timezoneFilterValue; + bool isSearching = false; @override void initState() { WidgetsBinding.instance.addPostFrameCallback(_afterLayout); + searchController.addListener(() { + setState(() { + timezoneFilterValue = + searchController.text.isNotEmpty ? searchController.text : null; + }); + }); super.initState(); } @@ -31,7 +41,7 @@ class _SelectTimezoneState extends State { print(t); if (index >= 0) { - controller.animateTo( + scrollController.animateTo( 60.0 * index, duration: const Duration(milliseconds: 300), curve: Curves.easeIn, @@ -41,74 +51,101 @@ class _SelectTimezoneState extends State { @override void dispose() { - controller.dispose(); + scrollController.dispose(); + searchController.dispose(); super.dispose(); } @override - Widget build(final BuildContext context) => Scaffold( - appBar: const PreferredSize( - preferredSize: Size.fromHeight(52), - child: BrandHeader( - title: 'select timezone', - hasBackButton: true, - ), - ), - body: ListView( - controller: controller, + Widget build(final BuildContext context) { + final isDesktop = Breakpoints.mediumAndUp.isActive(context); + return Scaffold( + appBar: AppBar( + automaticallyImplyLeading: false, + title: (isDesktop || isSearching) + ? TextField( + readOnly: false, + textAlign: TextAlign.start, + textInputAction: TextInputAction.next, + enabled: true, + controller: searchController, + decoration: InputDecoration( + errorText: null, + hintText: 'server.timezone_search_bar'.tr(), + ), + ) + : Padding( + padding: const EdgeInsets.only(top: 4.0), + child: Text('server.select_timezone'.tr()), + ), + leading: !isDesktop + ? IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: isSearching + ? () => setState(() => isSearching = false) + : () => Navigator.of(context).pop(), + ) + : null, + actions: [ + if (!isSearching && !isDesktop) + IconButton( + icon: const Icon(Icons.search), + onPressed: () => setState(() => isSearching = true), + ), + ], + ), + body: SafeArea( + child: ListView( + controller: scrollController, children: locations + .where( + (final Location location) => timezoneFilterValue == null + ? true + : location.name + .toLowerCase() + .contains(timezoneFilterValue!) || + Duration( + milliseconds: location.currentTimeZone.offset, + ) + .toTimezoneOffsetFormat() + .contains(timezoneFilterValue!), + ) + .toList() .asMap() - .map((final key, final value) { - final duration = - Duration(milliseconds: value.currentTimeZone.offset); - final area = value.currentTimeZone.abbreviation - .replaceAll(RegExp(r'[\d+()-]'), ''); - - String timezoneName = value.name; - if (context.locale.toString() == 'ru') { - timezoneName = russian[value.name] ?? - () { - final arr = value.name.split('/')..removeAt(0); - return arr.join('/'); - }(); - } - - return MapEntry( - key, - Container( - height: 60, - padding: const EdgeInsets.symmetric(horizontal: 20), - decoration: const BoxDecoration( - border: Border( - bottom: BorderSide( - color: BrandColors.dividerColor, - ), - ), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - BrandText.body1( - timezoneName, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ), - BrandText.small( - 'GMT ${duration.toDayHourMinuteFormat()} ${area.isNotEmpty ? '($area)' : ''}', - style: const TextStyle( - fontSize: 13, - ), - ), - ], - ), - ), - ); - }) + .map( + (final key, final value) => locationToListTile(key, value), + ) .values .toList(), ), - ); + ), + ); + } + + MapEntry locationToListTile( + final int key, + final Location location, + ) { + final duration = Duration(milliseconds: location.currentTimeZone.offset); + final area = location.currentTimeZone.abbreviation + .replaceAll(RegExp(r'[\d+()-]'), ''); + + return MapEntry( + key, + ListTile( + title: Text( + location.name, + ), + subtitle: Text( + 'GMT ${duration.toTimezoneOffsetFormat()} ${area.isNotEmpty ? '($area)' : ''}', + ), + onTap: () { + context.read().repository.setTimezone( + location.name, + ); + Navigator.of(context).pop(); + }, + ), + ); + } } diff --git a/lib/ui/pages/server_storage/binds_migration/migration_process_page.dart b/lib/ui/pages/server_storage/binds_migration/migration_process_page.dart new file mode 100644 index 00000000..8d42cb6f --- /dev/null +++ b/lib/ui/pages/server_storage/binds_migration/migration_process_page.dart @@ -0,0 +1,79 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_cubit.dart'; +import 'package:selfprivacy/logic/models/json/server_job.dart'; +import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; +import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; +import 'package:selfprivacy/ui/components/brand_linear_indicator/brand_linear_indicator.dart'; +import 'package:selfprivacy/ui/pages/root_route.dart'; +import 'package:selfprivacy/utils/route_transitions/basic.dart'; + +class MigrationProcessPage extends StatefulWidget { + const MigrationProcessPage({super.key}); + + @override + State createState() => _MigrationProcessPageState(); +} + +class _MigrationProcessPageState extends State { + @override + void initState() { + super.initState(); + } + + @override + Widget build(final BuildContext context) { + ServerJob? job; + String? subtitle = ''; + double value = 0.0; + List children = []; + + final serverJobsState = context.watch().state; + if (serverJobsState.migrationJobUid != null) { + job = context.read().getServerJobByUid( + serverJobsState.migrationJobUid!, + ); + } + + if (job == null) { + subtitle = 'basis.loading'.tr(); + } else { + value = job.progress == null ? 0.0 : job.progress! / 100; + subtitle = job.statusText; + children = [ + ...children, + const SizedBox(height: 16), + if (job.finishedAt != null) + Text( + job.result!, + style: Theme.of(context).textTheme.titleMedium, + ), + if (job.finishedAt != null) const SizedBox(height: 16), + if (job.finishedAt != null) + BrandButton.filled( + child: Text('storage.migration_done'.tr()), + onPressed: () { + Navigator.of(context).pushAndRemoveUntil( + materialRoute(const RootPage()), + (final predicate) => false, + ); + }, + ), + ]; + } + return BrandHeroScreen( + hasBackButton: false, + heroTitle: 'storage.migration_process'.tr(), + heroSubtitle: subtitle, + children: [ + BrandLinearIndicator( + value: value, + color: Theme.of(context).colorScheme.primary, + backgroundColor: Theme.of(context).colorScheme.surfaceVariant, + height: 4.0, + ), + ...children, + ], + ); + } +} diff --git a/lib/ui/pages/server_storage/binds_migration/services_migration.dart b/lib/ui/pages/server_storage/binds_migration/services_migration.dart new file mode 100644 index 00000000..edb7474e --- /dev/null +++ b/lib/ui/pages/server_storage/binds_migration/services_migration.dart @@ -0,0 +1,215 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_cubit.dart'; +import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; +import 'package:selfprivacy/logic/models/disk_size.dart'; +import 'package:selfprivacy/logic/models/service.dart'; +import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; +import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; +import 'package:selfprivacy/ui/components/info_box/info_box.dart'; +import 'package:selfprivacy/logic/models/disk_status.dart'; +import 'package:selfprivacy/ui/components/jobs_content/jobs_content.dart'; +import 'package:selfprivacy/ui/components/storage_list_items/server_storage_list_item.dart'; +import 'package:selfprivacy/ui/components/storage_list_items/service_migration_list_item.dart'; + +@RoutePage() +class ServicesMigrationPage extends StatefulWidget { + const ServicesMigrationPage({ + required this.services, + required this.diskStatus, + required this.isMigration, + super.key, + }); + + final DiskStatus diskStatus; + final List services; + final bool isMigration; + + @override + State createState() => _ServicesMigrationPageState(); +} + +class _ServicesMigrationPageState extends State { + /// Service id to target migration disk name + final Map serviceToDisk = {}; + + static const headerHeight = 52.0; + static const headerVerticalPadding = 8.0; + static const listItemHeight = 62.0; + + @override + void initState() { + super.initState(); + + for (final Service service in widget.services) { + if (service.storageUsage.volume != null) { + serviceToDisk[service.id] = service.storageUsage.volume!; + } + } + } + + void onChange(final String volumeName, final String serviceId) { + setState(() { + serviceToDisk[serviceId] = volumeName; + }); + } + + bool get isVolumePicked { + bool isChangeFound = false; + for (final Service service in widget.services) { + for (final String serviceId in serviceToDisk.keys) { + if (serviceId == service.id && + serviceToDisk[serviceId] != service.storageUsage.volume!) { + isChangeFound = true; + } + } + } + + return isChangeFound; + } + + /// Check the services and if a service is moved (in a serviceToDisk entry) + /// subtract the used storage from the old volume and add it to the new volume. + /// The old volume is the volume the service is currently on, shown in services list. + DiskVolume recalculatedDiskUsages( + final DiskVolume volume, + final List services, + ) { + DiskSize used = volume.sizeUsed; + + for (final Service service in services) { + if (service.storageUsage.volume != null) { + if (service.storageUsage.volume == volume.name) { + if (serviceToDisk[service.id] != null && + serviceToDisk[service.id] != volume.name) { + used -= service.storageUsage.used; + } + } else { + if (serviceToDisk[service.id] != null && + serviceToDisk[service.id] == volume.name) { + used += service.storageUsage.used; + } + } + } + } + + return volume.copyWith(sizeUsed: used); + } + + @override + Widget build(final BuildContext context) { + final Size appBarHeight = Size.fromHeight( + headerHeight + + headerVerticalPadding * 2 + + listItemHeight * widget.diskStatus.diskVolumes.length + + headerVerticalPadding * widget.diskStatus.diskVolumes.length, + ); + return SafeArea( + child: Scaffold( + appBar: PreferredSize( + preferredSize: appBarHeight, + child: Column( + children: [ + BrandHeader( + title: 'storage.data_migration_title'.tr(), + hasBackButton: true, + ), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: headerVerticalPadding, + ), + child: Column( + children: [ + ...widget.diskStatus.diskVolumes.map( + (final volume) => Column( + children: [ + ServerStorageListItem( + volume: recalculatedDiskUsages( + volume, + widget.services, + ), + dense: true, + ), + const SizedBox(height: headerVerticalPadding), + ], + ), + ), + ], + ), + ), + const Divider(height: 0), + ], + ), + ), + body: ListView( + padding: const EdgeInsets.all(16.0), + children: [ + if (widget.services.isEmpty) + const Center(child: CircularProgressIndicator()), + ...widget.services.map( + (final service) => Column( + children: [ + const SizedBox(height: 8), + ServiceMigrationListItem( + service: service, + diskStatus: widget.diskStatus, + selectedVolume: serviceToDisk[service.id]!, + onChange: onChange, + ), + const SizedBox(height: 4), + const Divider(), + ], + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: InfoBox( + text: 'storage.data_migration_notice'.tr(), + isWarning: true, + ), + ), + const SizedBox(height: 16), + if (widget.isMigration || (!widget.isMigration && isVolumePicked)) + BrandButton.filled( + child: Text('storage.start_migration_button'.tr()), + onPressed: () { + if (widget.isMigration) { + context.read().migrateToBinds( + serviceToDisk, + ); + } else { + for (final service in widget.services) { + if (serviceToDisk[service.id] != null) { + context.read().moveService( + service.id, + serviceToDisk[service.id]!, + ); + } + } + } + context.router.popUntilRoot(); + showModalBottomSheet( + context: context, + useRootNavigator: true, + isScrollControlled: true, + builder: (final BuildContext context) => + DraggableScrollableSheet( + expand: false, + maxChildSize: 0.9, + minChildSize: 0.4, + initialChildSize: 0.6, + builder: (final context, final scrollController) => + JobsContent(controller: scrollController), + ), + ); + }, + ), + const SizedBox(height: 32), + ], + ), + ), + ); + } +} diff --git a/lib/ui/pages/server_storage/extending_volume.dart b/lib/ui/pages/server_storage/extending_volume.dart new file mode 100644 index 00000000..fd8213b1 --- /dev/null +++ b/lib/ui/pages/server_storage/extending_volume.dart @@ -0,0 +1,193 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; +import 'package:selfprivacy/logic/cubit/provider_volumes/provider_volume_cubit.dart'; +import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart'; +import 'package:selfprivacy/logic/models/disk_size.dart'; +import 'package:selfprivacy/logic/models/price.dart'; +import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; +import 'package:selfprivacy/ui/components/info_box/info_box.dart'; +import 'package:selfprivacy/ui/helpers/modals.dart'; +import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; +import 'package:selfprivacy/logic/models/disk_status.dart'; + +@RoutePage() +class ExtendingVolumePage extends StatefulWidget { + const ExtendingVolumePage({ + required this.diskVolumeToResize, + required this.diskStatus, + super.key, + }); + + final DiskVolume diskVolumeToResize; + final DiskStatus diskStatus; + + @override + State createState() => _ExtendingVolumePageState(); +} + +class _ExtendingVolumePageState extends State { + @override + void initState() { + minSize = widget.diskVolumeToResize.sizeTotal + DiskSize.fromGibibyte(3); + _currentSliderGbValue = minSize.gibibyte; + super.initState(); + } + + @override + void dispose() { + _sizeController.dispose(); + _priceController.dispose(); + super.dispose(); + } + + bool _isError = false; + + late double _currentSliderGbValue; + double _pricePerGb = 1.0; + + final DiskSize maxSize = const DiskSize(byte: 500000000000); + late DiskSize minSize; + + final TextEditingController _sizeController = TextEditingController(); + final TextEditingController _priceController = TextEditingController(); + + void _updateErrorStatuses() { + _isError = minSize.gibibyte > _currentSliderGbValue; + } + + @override + Widget build(final BuildContext context) => FutureBuilder( + future: context.read().getPricePerGb(), + builder: ( + final BuildContext context, + final AsyncSnapshot snapshot, + ) { + if (!snapshot.hasData) { + return BrandHeroScreen( + hasBackButton: true, + heroTitle: 'storage.extending_volume_title'.tr(), + heroSubtitle: 'storage.extending_volume_description'.tr(), + children: const [ + SizedBox(height: 16), + Center( + child: CircularProgressIndicator(), + ), + ], + ); + } + final price = snapshot.data as Price; + _pricePerGb = price.value; + final currentSizeValue = _currentSliderGbValue.truncate().toString(); + _sizeController.text = 'storage.gb'.tr(args: [currentSizeValue]); + _priceController.text = + '${(_pricePerGb * double.parse(currentSizeValue)).toStringAsFixed(2)}' + ' ' + '${price.currency.shortcode}'; + minSize = + widget.diskVolumeToResize.sizeTotal + DiskSize.fromGibibyte(3); + if (_currentSliderGbValue < 0) { + _currentSliderGbValue = minSize.gibibyte; + } + + final isAlreadyResizing = + context.watch().state.isResizing; + + return BrandHeroScreen( + hasBackButton: true, + heroTitle: 'storage.extending_volume_title'.tr(), + heroSubtitle: 'storage.extending_volume_description'.tr(), + children: [ + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 130), + child: TextField( + readOnly: true, + textAlign: TextAlign.start, + textInputAction: TextInputAction.next, + enabled: true, + controller: _sizeController, + decoration: InputDecoration( + border: const OutlineInputBorder(), + errorText: _isError ? ' ' : null, + labelText: 'storage.size'.tr(), + ), + ), + ), + const SizedBox(width: 16), + ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 130), + child: TextField( + readOnly: true, + textAlign: TextAlign.start, + textInputAction: TextInputAction.next, + enabled: true, + controller: _priceController, + decoration: InputDecoration( + border: const OutlineInputBorder(), + errorText: _isError ? ' ' : null, + labelText: 'storage.price'.tr(), + ), + ), + ), + ], + ), + const SizedBox(height: 16), + Slider( + min: minSize.gibibyte, + value: _currentSliderGbValue, + max: maxSize.gibibyte, + onChanged: (final double value) { + setState(() { + _currentSliderGbValue = value; + _updateErrorStatuses(); + }); + }, + ), + const SizedBox(height: 16), + BrandButton.filled( + onPressed: _isError || isAlreadyResizing + ? null + : () { + showPopUpAlert( + alertTitle: 'storage.extending_volume_title'.tr(), + description: + 'storage.extending_volume_modal_description'.tr( + args: [_sizeController.text, _priceController.text], + ), + actionButtonTitle: 'basis.continue'.tr(), + actionButtonOnPressed: () { + context.read().resizeVolume( + widget.diskVolumeToResize, + DiskSize.fromGibibyte( + _currentSliderGbValue.truncate().toDouble(), + ), + context.read().reload, + ); + context.router.popUntilRoot(); + }, + ); + }, + child: Text('storage.extend_volume_button.title'.tr()), + ), + const SizedBox(height: 16), + const Divider( + height: 1.0, + ), + const SizedBox(height: 16), + InfoBox( + text: 'storage.extending_volume_price_info'.tr(), + isWarning: false, + ), + const SizedBox(height: 16), + ], + ); + }, + ); +} diff --git a/lib/ui/pages/server_storage/server_storage.dart b/lib/ui/pages/server_storage/server_storage.dart new file mode 100644 index 00000000..eb7a586b --- /dev/null +++ b/lib/ui/pages/server_storage/server_storage.dart @@ -0,0 +1,150 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; +import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; +import 'package:selfprivacy/logic/models/service.dart'; +import 'package:selfprivacy/ui/components/buttons/outlined_button.dart'; +import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; +import 'package:selfprivacy/logic/models/disk_status.dart'; +import 'package:selfprivacy/ui/components/storage_list_items/server_storage_list_item.dart'; +import 'package:selfprivacy/ui/router/router.dart'; + +@RoutePage() +class ServerStoragePage extends StatefulWidget { + const ServerStoragePage({ + required this.diskStatus, + super.key, + }); + + final DiskStatus diskStatus; + + @override + State createState() => _ServerStoragePageState(); +} + +class _ServerStoragePageState extends State { + @override + Widget build(final BuildContext context) { + final bool isReady = context.watch().state + is ServerInstallationFinished; + + final List services = + context.watch().state.services; + + if (!isReady) { + return BrandHeroScreen( + hasBackButton: true, + heroTitle: 'storage.card_title'.tr(), + children: const [], + ); + } + + return BrandHeroScreen( + hasBackButton: true, + heroTitle: 'storage.card_title'.tr(), + children: [ + // ...sections, + ...widget.diskStatus.diskVolumes.map( + (final volume) => Column( + mainAxisSize: MainAxisSize.min, + children: [ + ServerStorageSection( + volume: volume, + diskStatus: widget.diskStatus, + services: services + .where( + (final service) => + service.storageUsage.volume == volume.name, + ) + .toList(), + ), + const SizedBox(height: 16), + const Divider(), + const SizedBox(height: 16), + ], + ), + ), + const SizedBox(height: 8), + ], + ); + } +} + +class ServerStorageSection extends StatelessWidget { + const ServerStorageSection({ + required this.volume, + required this.diskStatus, + required this.services, + super.key, + }); + + final DiskVolume volume; + final DiskStatus diskStatus; + final List services; + + @override + Widget build(final BuildContext context) => Column( + mainAxisSize: MainAxisSize.min, + children: [ + ServerStorageListItem( + volume: volume, + ), + const SizedBox(height: 16), + ...services.map( + (final service) => ServerConsumptionListTile( + service: service, + volume: volume, + ), + ), + if (volume.isResizable) ...[ + const SizedBox(height: 16), + BrandOutlinedButton( + title: 'storage.extend_volume_button.title'.tr(), + onPressed: () => context.pushRoute( + ExtendingVolumeRoute( + diskVolumeToResize: volume, + diskStatus: diskStatus, + ), + ), + ), + ], + ], + ); +} + +class ServerConsumptionListTile extends StatelessWidget { + const ServerConsumptionListTile({ + required this.service, + required this.volume, + super.key, + }); + + final Service service; + final DiskVolume volume; + + @override + Widget build(final BuildContext context) => Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: ConsumptionListItem( + title: service.displayName, + icon: SvgPicture.string( + service.svgIcon, + width: 24.0, + height: 24.0, + colorFilter: ColorFilter.mode( + Theme.of(context).colorScheme.onBackground, + BlendMode.srcIn, + ), + ), + rightSideText: service.storageUsage.used.toString(), + percentage: service.storageUsage.used.byte / volume.sizeTotal.byte, + color: volume.root + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.secondary, + backgroundColor: Theme.of(context).colorScheme.surfaceVariant, + dense: true, + ), + ); +} diff --git a/lib/ui/pages/server_storage/storage_card.dart b/lib/ui/pages/server_storage/storage_card.dart new file mode 100644 index 00000000..fbda9bee --- /dev/null +++ b/lib/ui/pages/server_storage/storage_card.dart @@ -0,0 +1,99 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; +import 'package:selfprivacy/logic/cubit/providers/providers_cubit.dart'; +import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart'; +import 'package:selfprivacy/logic/models/disk_status.dart'; +import 'package:selfprivacy/ui/components/storage_list_items/server_storage_list_item.dart'; +import 'package:selfprivacy/ui/router/router.dart'; + +class StorageCard extends StatelessWidget { + const StorageCard({ + required this.diskStatus, + super.key, + }); + + final DiskStatus diskStatus; + + @override + Widget build(final BuildContext context) { + final List sections = []; + for (final DiskVolume volume in diskStatus.diskVolumes) { + sections.add( + const SizedBox(height: 16), + ); + sections.add( + ServerStorageListItem( + volume: volume, + dense: true, + showIcon: false, + ), + ); + } + + StateType state = context.watch().state + is ServerInstallationFinished + ? StateType.stable + : StateType.uninitialized; + + if (state == StateType.stable && !diskStatus.isDiskOkay) { + state = StateType.error; + } + + return Card( + clipBehavior: Clip.antiAlias, + child: InkResponse( + highlightShape: BoxShape.rectangle, + + /// TODO: when 'isEmpty' replace with a skeleton + onTap: () => diskStatus.diskVolumes.isEmpty + ? null + : context.pushRoute(ServerStorageRoute(diskStatus: diskStatus)), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'storage.card_title'.tr(), + style: Theme.of(context).textTheme.titleLarge, + ), + if (state != StateType.uninitialized) + Text( + diskStatus.isDiskOkay + ? 'storage.status_ok'.tr() + : 'storage.status_error'.tr(), + style: Theme.of(context).textTheme.bodyLarge, + ), + ], + ), + if (state != StateType.uninitialized) + IconStatusMask( + status: state, + icon: Icon( + diskStatus.isDiskOkay + ? Icons.check_circle_outline + : Icons.error_outline, + size: 24, + color: Colors.white, + ), + ), + ], + ), + ...sections, + const SizedBox(height: 8), + ], + ), + ), + ), + ); + } +} diff --git a/lib/ui/pages/services/service_page.dart b/lib/ui/pages/services/service_page.dart new file mode 100644 index 00000000..e1e00ec0 --- /dev/null +++ b/lib/ui/pages/services/service_page.dart @@ -0,0 +1,252 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart'; +import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart'; +import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; +import 'package:selfprivacy/logic/models/job.dart'; +import 'package:selfprivacy/logic/models/service.dart'; +import 'package:selfprivacy/ui/components/cards/filled_card.dart'; +import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; +import 'package:selfprivacy/ui/router/router.dart'; +import 'package:selfprivacy/utils/launch_url.dart'; + +@RoutePage() +class ServicePage extends StatefulWidget { + const ServicePage({required this.serviceId, super.key}); + + final String serviceId; + + @override + State createState() => _ServicePageState(); +} + +class _ServicePageState extends State { + @override + Widget build(final BuildContext context) { + final Service? service = + context.watch().state.getServiceById(widget.serviceId); + + if (service == null) { + return const BrandHeroScreen( + hasBackButton: true, + children: [ + Center( + child: CircularProgressIndicator(), + ), + ], + ); + } + + final bool serviceDisabled = service.status == ServiceStatus.inactive || + service.status == ServiceStatus.off; + + final bool serviceLocked = + context.watch().state.isServiceLocked(service.id); + + return BrandHeroScreen( + hasBackButton: true, + hasFlashButton: true, + heroIconWidget: SvgPicture.string( + service.svgIcon, + width: 48.0, + height: 48.0, + colorFilter: ColorFilter.mode( + Theme.of(context).colorScheme.onBackground, + BlendMode.srcIn, + ), + ), + heroTitle: service.displayName, + children: [ + ServiceStatusCard(status: service.status), + const SizedBox(height: 16), + if (service.url != null) + ListTile( + iconColor: Theme.of(context).colorScheme.onBackground, + onTap: () => launchURL(service.url), + leading: const Icon(Icons.open_in_browser), + title: Text( + 'service_page.open_in_browser'.tr(), + style: Theme.of(context).textTheme.titleMedium, + ), + subtitle: Text( + service.url!.replaceAll('https://', ''), + style: Theme.of(context).textTheme.bodyMedium, + ), + ), + const SizedBox(height: 8), + const Divider(), + const SizedBox(height: 8), + ListTile( + iconColor: Theme.of(context).colorScheme.onBackground, + onTap: () => { + context.read().restart(service.id), + }, + leading: const Icon(Icons.restart_alt_outlined), + title: Text( + 'service_page.restart'.tr(), + style: Theme.of(context).textTheme.titleMedium, + ), + enabled: !serviceDisabled && !serviceLocked, + ), + ListTile( + iconColor: Theme.of(context).colorScheme.onBackground, + onTap: () => { + context.read().addJob( + ServiceToggleJob( + service: service, + needToTurnOn: serviceDisabled, + ), + ), + }, + leading: const Icon(Icons.power_settings_new), + title: Text( + serviceDisabled + ? 'service_page.enable'.tr() + : 'service_page.disable'.tr(), + style: Theme.of(context).textTheme.titleMedium, + ), + enabled: !serviceLocked, + ), + if (service.isMovable) + ListTile( + iconColor: Theme.of(context).colorScheme.onBackground, + // Open page ServicesMigrationPage + onTap: () => context.pushRoute( + ServicesMigrationRoute( + services: [service], + diskStatus: + context.read().state.diskStatus, + isMigration: false, + ), + ), + leading: const Icon(Icons.drive_file_move_outlined), + title: Text( + 'service_page.move'.tr(), + style: Theme.of(context).textTheme.titleMedium, + ), + subtitle: Text( + 'service_page.uses'.tr( + namedArgs: { + 'usage': service.storageUsage.used.toString(), + 'volume': context + .read() + .state + .getVolume(service.storageUsage.volume ?? '') + .displayName + }, + ), + style: Theme.of(context).textTheme.bodyMedium, + ), + enabled: !serviceDisabled && + !serviceLocked && + service.storageUsage.volume != null, + ), + if (service.canBeBackedUp) + ListTile( + iconColor: Theme.of(context).colorScheme.onBackground, + // Open page ServicesMigrationPage + onTap: () => context.pushRoute( + BackupsListRoute(service: service), + ), + leading: const Icon(Icons.settings_backup_restore_outlined), + title: Text( + 'service_page.snapshots'.tr(), + style: Theme.of(context).textTheme.titleMedium, + ), + ), + ], + ); + } +} + +class ServiceStatusCard extends StatelessWidget { + const ServiceStatusCard({ + required this.status, + super.key, + }); + final ServiceStatus status; + + @override + Widget build(final BuildContext context) { + switch (status) { + case ServiceStatus.active: + return FilledCard( + child: ListTile( + leading: const Icon( + Icons.check_circle_outline, + size: 24, + ), + title: Text('service_page.status.active'.tr()), + ), + ); + case ServiceStatus.inactive: + return FilledCard( + tertiary: true, + child: ListTile( + leading: const Icon( + Icons.stop_circle_outlined, + size: 24, + ), + title: Text('service_page.status.inactive'.tr()), + ), + ); + case ServiceStatus.failed: + return FilledCard( + error: true, + child: ListTile( + leading: const Icon( + Icons.error_outline, + size: 24, + ), + title: Text('service_page.status.failed'.tr()), + ), + ); + case ServiceStatus.off: + return FilledCard( + tertiary: true, + child: ListTile( + leading: const Icon( + Icons.power_settings_new, + size: 24, + ), + title: Text('service_page.status.off'.tr()), + ), + ); + case ServiceStatus.activating: + return FilledCard( + tertiary: true, + child: ListTile( + leading: const Icon( + Icons.restart_alt_outlined, + size: 24, + ), + title: Text('service_page.status.activating'.tr()), + ), + ); + case ServiceStatus.deactivating: + return FilledCard( + tertiary: true, + child: ListTile( + leading: const Icon( + Icons.restart_alt_outlined, + size: 24, + ), + title: Text('service_page.status.deactivating'.tr()), + ), + ); + case ServiceStatus.reloading: + return FilledCard( + tertiary: true, + child: ListTile( + leading: const Icon( + Icons.restart_alt_outlined, + size: 24, + ), + title: Text('service_page.status.reloading'.tr()), + ), + ); + } + } +} diff --git a/lib/ui/pages/services/services.dart b/lib/ui/pages/services/services.dart index 0b8ea12d..36af585f 100644 --- a/lib/ui/pages/services/services.dart +++ b/lib/ui/pages/services/services.dart @@ -1,497 +1,184 @@ -import 'dart:ui'; - +import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/brand_colors.dart'; +import 'package:flutter_svg/flutter_svg.dart'; import 'package:selfprivacy/config/brand_theme.dart'; -import 'package:selfprivacy/config/text_themes.dart'; -import 'package:selfprivacy/logic/common_enum/common_enum.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; -import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart'; import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; -import 'package:selfprivacy/logic/models/job.dart'; +import 'package:selfprivacy/logic/models/service.dart'; import 'package:selfprivacy/logic/models/state_types.dart'; -import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; -import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; -import 'package:selfprivacy/ui/components/brand_switch/brand_switch.dart'; -import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; +import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart'; -import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:selfprivacy/ui/helpers/empty_page_placeholder.dart'; +import 'package:selfprivacy/ui/router/router.dart'; +import 'package:selfprivacy/utils/breakpoints.dart'; +import 'package:selfprivacy/utils/launch_url.dart'; import 'package:selfprivacy/utils/ui_helpers.dart'; -import 'package:url_launcher/url_launcher_string.dart'; - -import 'package:selfprivacy/ui/pages/root_route.dart'; - -const switchableServices = [ - ServiceTypes.passwordManager, - ServiceTypes.cloud, - ServiceTypes.socialNetwork, - ServiceTypes.git, - ServiceTypes.vpn, -]; +@RoutePage() class ServicesPage extends StatefulWidget { - const ServicesPage({final super.key}); + const ServicesPage({super.key}); @override State createState() => _ServicesPageState(); } -void _launchURL(final url) async { - final canLaunch = await canLaunchUrlString(url); - - if (canLaunch) { - try { - await launchUrlString( - url, - ); - } catch (e) { - print(e); - } - } else { - throw 'Could not launch $url'; - } -} - class _ServicesPageState extends State { @override Widget build(final BuildContext context) { final isReady = context.watch().state is ServerInstallationFinished; + final services = [...context.watch().state.services]; + services + .sort((final a, final b) => a.status.index.compareTo(b.status.index)); + return Scaffold( - appBar: PreferredSize( - preferredSize: const Size.fromHeight(52), - child: BrandHeader( - title: 'basis.services'.tr(), - ), - ), - body: ListView( - padding: paddingH15V0, - children: [ - BrandText.body1('services.title'.tr()), - const SizedBox(height: 24), - if (!isReady) ...[const NotReadyCard(), const SizedBox(height: 24)], - ...ServiceTypes.values - .map( - (final t) => Padding( - padding: const EdgeInsets.only( - bottom: 30, + appBar: Breakpoints.small.isActive(context) + ? PreferredSize( + preferredSize: const Size.fromHeight(52), + child: BrandHeader( + title: 'basis.services'.tr(), + ), + ) + : null, + body: !isReady + ? EmptyPagePlaceholder( + showReadyCard: true, + title: 'service_page.nothing_here'.tr(), + description: 'basis.please_connect'.tr(), + iconData: BrandIcons.box, + ) + : RefreshIndicator( + onRefresh: () async { + await context.read().reload(); + }, + child: ListView( + padding: paddingH15V0, + children: [ + Text( + 'basis.services_title'.tr(), + style: Theme.of(context).textTheme.bodyLarge, ), - child: _Card(serviceType: t), - ), - ) - .toList() - ], - ), + const SizedBox(height: 24), + ...services.map( + (final service) => Padding( + padding: const EdgeInsets.only( + bottom: 30, + ), + child: _Card(service: service), + ), + ) + ], + ), + ), ); } } class _Card extends StatelessWidget { - const _Card({required this.serviceType}); + const _Card({required this.service}); - final ServiceTypes serviceType; + final Service service; @override Widget build(final BuildContext context) { final isReady = context.watch().state is ServerInstallationFinished; - final changeTab = context.read().onPress; - - final serviceState = context.watch().state; - final jobsCubit = context.watch(); - final jobState = jobsCubit.state; - - final switchableService = switchableServices.contains(serviceType); - final hasSwitchJob = switchableService && - jobState is JobsStateWithJobs && - jobState.jobList.any( - (final el) => el is ServiceToggleJob && el.type == serviceType, - ); - - final isSwitchOn = isReady && - (!switchableServices.contains(serviceType) || - serviceState.isEnableByType(serviceType)); final config = context.watch().state; final domainName = UiHelpers.getDomainName(config); - return GestureDetector( - onTap: isSwitchOn - ? () => showDialog( - context: context, - // isScrollControlled: true, - // backgroundColor: Colors.transparent, - builder: (final BuildContext context) => _ServiceDetails( - serviceType: serviceType, - status: - isSwitchOn ? StateType.stable : StateType.uninitialized, - title: serviceType.title, - icon: serviceType.icon, - changeTab: changeTab, - ), - ) - : null, - child: BrandCards.big( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - IconStatusMask( - status: - isSwitchOn ? StateType.stable : StateType.uninitialized, - child: Icon(serviceType.icon, size: 30, color: Colors.white), - ), - if (isReady && switchableService) ...[ - const Spacer(), - Builder( - builder: (final context) { - late bool isActive; - if (hasSwitchJob) { - isActive = (jobState.jobList.firstWhere( - (final el) => - el is ServiceToggleJob && el.type == serviceType, - ) as ServiceToggleJob) - .needToTurnOn; - } else { - isActive = serviceState.isEnableByType(serviceType); - } - - return BrandSwitch( - value: isActive, - onChanged: (final value) => - jobsCubit.createOrRemoveServiceToggleJob( - ServiceToggleJob( - type: serviceType, - needToTurnOn: value, - ), - ), - ); - }, - ), - ] - ], - ), - ClipRect( - child: Stack( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 10), - BrandText.h2(serviceType.title), - const SizedBox(height: 10), - if (serviceType.subdomain != '') - Column( - children: [ - GestureDetector( - onTap: () => _launchURL( - 'https://${serviceType.subdomain}.$domainName', - ), - child: Text( - '${serviceType.subdomain}.$domainName', - style: TextStyle( - color: - Theme.of(context).colorScheme.secondary, - decoration: TextDecoration.underline, - ), - ), - ), - const SizedBox(height: 10), - ], - ), - if (serviceType == ServiceTypes.mail) - Column( - children: [ - Text( - domainName, - style: TextStyle( - color: Theme.of(context).colorScheme.primary, - decoration: TextDecoration.underline, - ), - ), - const SizedBox(height: 10), - ], - ), - BrandText.body2(serviceType.loginInfo), - const SizedBox(height: 10), - BrandText.body2(serviceType.subtitle), - const SizedBox(height: 10), - ], - ), - if (hasSwitchJob) - Positioned( - bottom: 24, - left: 0, - right: 0, - child: BackdropFilter( - filter: ImageFilter.blur( - sigmaX: 3, - sigmaY: 2, - ), - child: BrandText.h2( - 'jobs.runJobs'.tr(), - textAlign: TextAlign.center, - ), - ), - ) - ], - ), - ) - ], - ), - ), - ); - } -} - -class _ServiceDetails extends StatelessWidget { - const _ServiceDetails({ - required this.serviceType, - required this.icon, - required this.status, - required this.title, - required this.changeTab, - }); - - final ServiceTypes serviceType; - final IconData icon; - final StateType status; - final String title; - final ValueChanged changeTab; - - @override - Widget build(final BuildContext context) { - late Widget child; - - final config = context.watch().state; - final domainName = UiHelpers.getDomainName(config); - - final linksStyle = body1Style.copyWith( - fontSize: 15, - color: Theme.of(context).brightness == Brightness.dark - ? Colors.white - : BrandColors.black, - fontWeight: FontWeight.bold, - decoration: TextDecoration.underline, - ); - - final textStyle = body1Style.copyWith( - color: Theme.of(context).brightness == Brightness.dark - ? Colors.white - : BrandColors.black, - ); - switch (serviceType) { - case ServiceTypes.mail: - child = RichText( - text: TextSpan( - children: [ - TextSpan( - text: 'services.mail.bottom_sheet.1'.tr(args: [domainName]), - style: textStyle, - ), - const WidgetSpan(child: SizedBox(width: 5)), - WidgetSpan( - child: Padding( - padding: const EdgeInsets.only(bottom: 0.8), - child: GestureDetector( - child: Text( - 'services.mail.bottom_sheet.2'.tr(), - style: linksStyle, - ), - onTap: () { - Navigator.of(context).pop(); - changeTab(2); - }, - ), - ), - ), - ], - ), - ); - break; - case ServiceTypes.messenger: - child = RichText( - text: TextSpan( - children: [ - TextSpan( - text: - 'services.messenger.bottom_sheet.1'.tr(args: [domainName]), - style: textStyle, - ) - ], - ), - ); - break; - case ServiceTypes.passwordManager: - child = RichText( - text: TextSpan( - children: [ - TextSpan( - text: 'services.password_manager.bottom_sheet.1' - .tr(args: [domainName]), - style: textStyle, - ), - const WidgetSpan(child: SizedBox(width: 5)), - WidgetSpan( - child: Padding( - padding: const EdgeInsets.only(bottom: 0.8), - child: GestureDetector( - onTap: () => _launchURL('https://password.$domainName'), - child: Text( - 'password.$domainName', - style: linksStyle, - ), - ), - ), - ), - ], - ), - ); - break; - case ServiceTypes.video: - child = RichText( - text: TextSpan( - children: [ - TextSpan( - text: 'services.video.bottom_sheet.1'.tr(args: [domainName]), - style: textStyle, - ), - const WidgetSpan(child: SizedBox(width: 5)), - WidgetSpan( - child: Padding( - padding: const EdgeInsets.only(bottom: 0.8), - child: GestureDetector( - onTap: () => _launchURL('https://meet.$domainName'), - child: Text( - 'meet.$domainName', - style: linksStyle, - ), - ), - ), - ), - ], - ), - ); - break; - case ServiceTypes.cloud: - child = RichText( - text: TextSpan( - children: [ - TextSpan( - text: 'services.cloud.bottom_sheet.1'.tr(args: [domainName]), - style: textStyle, - ), - const WidgetSpan(child: SizedBox(width: 5)), - WidgetSpan( - child: Padding( - padding: const EdgeInsets.only(bottom: 0.8), - child: GestureDetector( - onTap: () => _launchURL('https://cloud.$domainName'), - child: Text( - 'cloud.$domainName', - style: linksStyle, - ), - ), - ), - ), - ], - ), - ); - break; - case ServiceTypes.socialNetwork: - child = RichText( - text: TextSpan( - children: [ - TextSpan( - text: 'services.social_network.bottom_sheet.1' - .tr(args: [domainName]), - style: textStyle, - ), - const WidgetSpan(child: SizedBox(width: 5)), - WidgetSpan( - child: Padding( - padding: const EdgeInsets.only(bottom: 0.8), - child: GestureDetector( - onTap: () => _launchURL('https://social.$domainName'), - child: Text( - 'social.$domainName', - style: linksStyle, - ), - ), - ), - ), - ], - ), - ); - break; - case ServiceTypes.git: - child = RichText( - text: TextSpan( - children: [ - TextSpan( - text: 'services.git.bottom_sheet.1'.tr(args: [domainName]), - style: textStyle, - ), - const WidgetSpan(child: SizedBox(width: 5)), - WidgetSpan( - child: Padding( - padding: const EdgeInsets.only(bottom: 0.8), - child: GestureDetector( - onTap: () => _launchURL('https://git.$domainName'), - child: Text( - 'git.$domainName', - style: linksStyle, - ), - ), - ), - ), - ], - ), - ); - break; - case ServiceTypes.vpn: - child = Text( - 'services.vpn.bottom_sheet.1'.tr(), - ); + StateType getStatus(final ServiceStatus status) { + switch (status) { + case ServiceStatus.active: + return StateType.stable; + case ServiceStatus.activating: + return StateType.stable; + case ServiceStatus.deactivating: + return StateType.uninitialized; + case ServiceStatus.inactive: + return StateType.uninitialized; + case ServiceStatus.failed: + return StateType.error; + case ServiceStatus.off: + return StateType.uninitialized; + case ServiceStatus.reloading: + return StateType.stable; + } } - return Dialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20), - ), - child: SingleChildScrollView( - child: SizedBox( - width: 350, + return Card( + clipBehavior: Clip.antiAlias, + child: InkResponse( + highlightShape: BoxShape.rectangle, + onTap: isReady + ? () => context.pushRoute( + ServiceRoute(serviceId: service.id), + ) + : null, + child: Padding( + padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Padding( - padding: paddingH15V30, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - IconStatusMask( - status: status, - child: Icon(icon, size: 40, color: Colors.white), - ), - const SizedBox(height: 10), - BrandText.h2(title), - const SizedBox(height: 10), - child, - const SizedBox(height: 40), - Center( - child: Container( - child: BrandButton.rised( - onPressed: () => Navigator.of(context).pop(), - text: 'basis.close'.tr(), - ), + Row( + children: [ + IconStatusMask( + status: getStatus(service.status), + icon: SvgPicture.string( + service.svgIcon, + width: 30.0, + height: 30.0, + colorFilter: const ColorFilter.mode( + Colors.white, + BlendMode.srcIn, ), ), - ], - ), + ), + const SizedBox(width: 8), + Text( + service.displayName, + style: Theme.of(context).textTheme.headlineMedium, + ), + ], + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 8), + if (service.url != '' && service.url != null) + Column( + children: [ + _ServiceLink( + url: service.url ?? '', + ), + const SizedBox(height: 8), + ], + ), + if (service.id == 'mailserver') + Column( + children: [ + _ServiceLink( + url: domainName, + isActive: false, + ), + const SizedBox(height: 8), + ], + ), + Text( + service.description, + style: Theme.of(context).textTheme.bodyMedium, + ), + const SizedBox(height: 8), + Text( + service.loginInfo, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Theme.of(context).colorScheme.secondary, + ), + ), + const SizedBox(height: 8), + ], ) ], ), @@ -500,3 +187,29 @@ class _ServiceDetails extends StatelessWidget { ); } } + +class _ServiceLink extends StatelessWidget { + const _ServiceLink({ + required this.url, + this.isActive = true, + }); + + final String url; + final bool isActive; + + @override + Widget build(final BuildContext context) => GestureDetector( + onTap: isActive + ? () => launchURL( + url, + ) + : null, + child: Text( + url, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Theme.of(context).colorScheme.primary, + decoration: TextDecoration.underline, + ), + ), + ); +} diff --git a/lib/ui/pages/setup/initializing.dart b/lib/ui/pages/setup/initializing.dart deleted file mode 100644 index 9c92f161..00000000 --- a/lib/ui/pages/setup/initializing.dart +++ /dev/null @@ -1,580 +0,0 @@ -import 'package:cubit_form/cubit_form.dart'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/brand_theme.dart'; -import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; -import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart'; -import 'package:selfprivacy/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart'; -import 'package:selfprivacy/logic/cubit/forms/setup/initializing/cloudflare_form_cubit.dart'; -import 'package:selfprivacy/logic/cubit/forms/setup/initializing/domain_cloudflare.dart'; -import 'package:selfprivacy/logic/cubit/forms/setup/initializing/hetzner_form_cubit.dart'; -import 'package:selfprivacy/logic/cubit/forms/setup/initializing/root_user_form_cubit.dart'; -import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart'; -import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; -import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart'; -import 'package:selfprivacy/ui/components/brand_md/brand_md.dart'; -import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; -import 'package:selfprivacy/ui/components/brand_timer/brand_timer.dart'; -import 'package:selfprivacy/ui/components/progress_bar/progress_bar.dart'; -import 'package:selfprivacy/ui/pages/root_route.dart'; -import 'package:selfprivacy/ui/pages/setup/recovering/recovery_routing.dart'; -import 'package:selfprivacy/utils/route_transitions/basic.dart'; - -class InitializingPage extends StatelessWidget { - const InitializingPage({final super.key}); - - @override - Widget build(final BuildContext context) { - final cubit = context.watch(); - - if (cubit.state is ServerInstallationRecovery) { - return const RecoveryRouting(); - } else { - final actualInitializingPage = [ - () => _stepHetzner(cubit), - () => _stepCloudflare(cubit), - () => _stepBackblaze(cubit), - () => _stepDomain(cubit), - () => _stepUser(cubit), - () => _stepServer(cubit), - () => _stepCheck(cubit), - () => _stepCheck(cubit), - () => _stepCheck(cubit), - () => Center(child: Text('initializing.finish'.tr())) - ][cubit.state.progress.index](); - - return BlocListener( - listener: (final context, final state) { - if (cubit.state is ServerInstallationFinished) { - Navigator.of(context) - .pushReplacement(materialRoute(const RootPage())); - } - }, - child: SafeArea( - child: Scaffold( - body: SingleChildScrollView( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: paddingH15V0.copyWith(top: 10, bottom: 10), - child: cubit.state.isFullyInitilized - ? const SizedBox( - height: 80, - ) - : ProgressBar( - steps: const [ - 'Hetzner', - 'CloudFlare', - 'Backblaze', - 'Domain', - 'User', - 'Server', - '✅ Check', - ], - activeIndex: cubit.state.porgressBar, - ), - ), - _addCard( - AnimatedSwitcher( - duration: const Duration(milliseconds: 300), - child: actualInitializingPage, - ), - ), - ConstrainedBox( - constraints: BoxConstraints( - minHeight: MediaQuery.of(context).size.height - - MediaQuery.of(context).padding.top - - MediaQuery.of(context).padding.bottom - - 566, - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - alignment: Alignment.center, - child: BrandButton.text( - title: cubit.state is ServerInstallationFinished - ? 'basis.close'.tr() - : 'basis.later'.tr(), - onPressed: () { - Navigator.of(context).pushAndRemoveUntil( - materialRoute(const RootPage()), - (final predicate) => false, - ); - }, - ), - ), - if (cubit.state is ServerInstallationFinished) - Container() - else - Container( - alignment: Alignment.center, - child: BrandButton.text( - title: 'basis.connect_to_existing'.tr(), - onPressed: () { - Navigator.of(context).push( - materialRoute( - const RecoveryRouting(), - ), - ); - }, - ), - ) - ], - ), - ), - ], - ), - ), - ), - ), - ); - } - } - - Widget _stepHetzner(final ServerInstallationCubit serverInstallationCubit) => - BlocProvider( - create: (final context) => HetznerFormCubit(serverInstallationCubit), - child: Builder( - builder: (final context) { - final formCubitState = context.watch().state; - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Image.asset( - 'assets/images/logos/hetzner.png', - width: 150, - ), - const SizedBox(height: 10), - BrandText.h2('initializing.1'.tr()), - const SizedBox(height: 10), - BrandText.body2('initializing.2'.tr()), - const Spacer(), - CubitFormTextField( - formFieldCubit: context.read().apiKey, - textAlign: TextAlign.center, - scrollPadding: const EdgeInsets.only(bottom: 70), - decoration: const InputDecoration( - hintText: 'Hetzner API Token', - ), - ), - const Spacer(), - BrandButton.rised( - onPressed: formCubitState.isSubmitting - ? null - : () => context.read().trySubmit(), - text: 'basis.connect'.tr(), - ), - const SizedBox(height: 10), - BrandButton.text( - onPressed: () => _showModal( - context, - const _HowTo(fileName: 'how_hetzner'), - ), - title: 'initializing.how'.tr(), - ), - ], - ); - }, - ), - ); - - void _showModal(final BuildContext context, final Widget widget) { - showModalBottomSheet( - context: context, - isScrollControlled: true, - backgroundColor: Colors.transparent, - builder: (final BuildContext context) => widget, - ); - } - - Widget _stepCloudflare(final ServerInstallationCubit initializingCubit) => - BlocProvider( - create: (final context) => CloudFlareFormCubit(initializingCubit), - child: Builder( - builder: (final context) { - final formCubitState = context.watch().state; - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Image.asset( - 'assets/images/logos/cloudflare.png', - width: 150, - ), - const SizedBox(height: 10), - BrandText.h2('initializing.3'.tr()), - const SizedBox(height: 10), - BrandText.body2('initializing.4'.tr()), - const Spacer(), - CubitFormTextField( - formFieldCubit: context.read().apiKey, - textAlign: TextAlign.center, - scrollPadding: const EdgeInsets.only(bottom: 70), - decoration: InputDecoration( - hintText: 'initializing.5'.tr(), - ), - ), - const Spacer(), - BrandButton.rised( - onPressed: formCubitState.isSubmitting - ? null - : () => context.read().trySubmit(), - text: 'basis.connect'.tr(), - ), - const SizedBox(height: 10), - BrandButton.text( - onPressed: () => _showModal( - context, - const _HowTo( - fileName: 'how_cloudflare', - ), - ), - title: 'initializing.how'.tr(), - ), - ], - ); - }, - ), - ); - - Widget _stepBackblaze(final ServerInstallationCubit initializingCubit) => - BlocProvider( - create: (final context) => BackblazeFormCubit(initializingCubit), - child: Builder( - builder: (final context) { - final formCubitState = context.watch().state; - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Image.asset( - 'assets/images/logos/backblaze.png', - height: 50, - ), - const SizedBox(height: 10), - BrandText.h2('initializing.6'.tr()), - const SizedBox(height: 10), - const Spacer(), - CubitFormTextField( - formFieldCubit: context.read().keyId, - textAlign: TextAlign.center, - scrollPadding: const EdgeInsets.only(bottom: 70), - decoration: const InputDecoration( - hintText: 'KeyID', - ), - ), - const Spacer(), - CubitFormTextField( - formFieldCubit: - context.read().applicationKey, - textAlign: TextAlign.center, - scrollPadding: const EdgeInsets.only(bottom: 70), - decoration: const InputDecoration( - hintText: 'Master Application Key', - ), - ), - const Spacer(), - BrandButton.rised( - onPressed: formCubitState.isSubmitting - ? null - : () => context.read().trySubmit(), - text: 'basis.connect'.tr(), - ), - const SizedBox(height: 10), - BrandButton.text( - onPressed: () => _showModal( - context, - const _HowTo( - fileName: 'how_backblaze', - ), - ), - title: 'initializing.how'.tr(), - ), - ], - ); - }, - ), - ); - - Widget _stepDomain(final ServerInstallationCubit initializingCubit) => - BlocProvider( - create: (final context) => DomainSetupCubit(initializingCubit)..load(), - child: Builder( - builder: (final context) { - final DomainSetupState state = - context.watch().state; - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Image.asset( - 'assets/images/logos/cloudflare.png', - width: 150, - ), - const SizedBox(height: 30), - BrandText.h2('basis.domain'.tr()), - const SizedBox(height: 10), - if (state is Empty) BrandText.body2('initializing.7'.tr()), - if (state is Loading) - BrandText.body2( - state.type == LoadingTypes.loadingDomain - ? 'initializing.8'.tr() - : 'basis.saving'.tr(), - ), - if (state is MoreThenOne) - BrandText.body2( - 'initializing.9'.tr(), - ), - if (state is Loaded) ...[ - const SizedBox(height: 10), - Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - child: BrandText.h3( - state.domain, - textAlign: TextAlign.center, - ), - ), - SizedBox( - width: 56, - child: BrandButton.rised( - onPressed: () => - context.read().load(), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: const [ - Icon( - Icons.refresh, - color: Colors.white, - ), - ], - ), - ), - ), - ], - ) - ], - if (state is Empty) ...[ - const SizedBox(height: 30), - BrandButton.rised( - onPressed: () => context.read().load(), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Icon( - Icons.refresh, - color: Colors.white, - ), - const SizedBox(width: 10), - BrandText.buttonTitleText('Обновить cписок'), - ], - ), - ), - ], - if (state is Loaded) ...[ - const SizedBox(height: 30), - BrandButton.rised( - onPressed: () => - context.read().saveDomain(), - text: 'initializing.10'.tr(), - ), - ], - const SizedBox( - height: 10, - width: double.infinity, - ), - ], - ); - }, - ), - ); - - Widget _stepUser(final ServerInstallationCubit initializingCubit) => - BlocProvider( - create: (final context) => - RootUserFormCubit(initializingCubit, FieldCubitFactory(context)), - child: Builder( - builder: (final context) { - final formCubitState = context.watch().state; - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - BrandText.h2('initializing.22'.tr()), - const SizedBox(height: 10), - BrandText.body2('initializing.23'.tr()), - const Spacer(), - CubitFormTextField( - formFieldCubit: context.read().userName, - textAlign: TextAlign.center, - scrollPadding: const EdgeInsets.only(bottom: 70), - decoration: InputDecoration( - hintText: 'basis.nickname'.tr(), - ), - ), - const SizedBox(height: 10), - BlocBuilder, FieldCubitState>( - bloc: context.read().isVisible, - builder: (final context, final state) { - final bool isVisible = state.value; - return CubitFormTextField( - obscureText: !isVisible, - formFieldCubit: - context.read().password, - textAlign: TextAlign.center, - scrollPadding: const EdgeInsets.only(bottom: 70), - decoration: InputDecoration( - hintText: 'basis.password'.tr(), - suffixIcon: IconButton( - icon: Icon( - isVisible ? Icons.visibility : Icons.visibility_off, - ), - onPressed: () => context - .read() - .isVisible - .setValue(!isVisible), - ), - suffixIconConstraints: - const BoxConstraints(minWidth: 60), - prefixIconConstraints: - const BoxConstraints(maxWidth: 60), - prefixIcon: Container(), - ), - ); - }, - ), - const Spacer(), - BrandButton.rised( - onPressed: formCubitState.isSubmitting - ? null - : () => context.read().trySubmit(), - text: 'basis.connect'.tr(), - ), - ], - ); - }, - ), - ); - - Widget _stepServer(final ServerInstallationCubit appConfigCubit) { - final bool isLoading = - (appConfigCubit.state as ServerInstallationNotFinished).isLoading; - return Builder( - builder: (final context) => Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Spacer(flex: 2), - BrandText.h2('initializing.final'.tr()), - const SizedBox(height: 10), - BrandText.body2('initializing.11'.tr()), - const Spacer(), - BrandButton.rised( - onPressed: - isLoading ? null : appConfigCubit.createServerAndSetDnsRecords, - text: isLoading ? 'basis.loading'.tr() : 'initializing.11'.tr(), - ), - ], - ), - ); - } - - Widget _stepCheck(final ServerInstallationCubit appConfigCubit) { - assert( - appConfigCubit.state is ServerInstallationNotFinished, - 'wrong state', - ); - final state = appConfigCubit.state as TimerState; - late int doneCount; - late String? text; - if (state.isServerResetedSecondTime) { - text = 'initializing.13'.tr(); - doneCount = 3; - } else if (state.isServerResetedFirstTime) { - text = 'initializing.21'.tr(); - doneCount = 2; - } else if (state.isServerStarted) { - text = 'initializing.14'.tr(); - doneCount = 1; - } else if (state.isServerCreated) { - text = 'initializing.15'.tr(); - doneCount = 0; - } - return Builder( - builder: (final context) => Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 15), - BrandText.h4( - 'initializing.checks'.tr(args: [doneCount.toString(), '4']), - ), - const Spacer(flex: 2), - const SizedBox(height: 10), - BrandText.body2(text), - const SizedBox(height: 10), - if (doneCount == 0 && state.dnsMatches != null) - Column( - children: state.dnsMatches!.entries.map((final entry) { - final String domain = entry.key; - final bool isCorrect = entry.value; - return Row( - children: [ - if (isCorrect) const Icon(Icons.check, color: Colors.green), - if (!isCorrect) - const Icon(Icons.schedule, color: Colors.amber), - const SizedBox(width: 10), - Text(domain), - ], - ); - }).toList(), - ), - const SizedBox(height: 10), - if (!state.isLoading) - Row( - children: [ - BrandText.body2('initializing.16'.tr()), - BrandTimer( - startDateTime: state.timerStart!, - duration: state.duration!, - ) - ], - ), - if (state.isLoading) BrandText.body2('initializing.17'.tr()), - ], - ), - ); - } - - Widget _addCard(final Widget child) => Container( - height: 450, - padding: paddingH15V0, - child: BrandCards.big(child: child), - ); -} - -class _HowTo extends StatelessWidget { - const _HowTo({ - required this.fileName, - }); - - final String fileName; - - @override - Widget build(final BuildContext context) => BrandBottomSheet( - isExpended: true, - child: Padding( - padding: paddingH15V0, - child: ListView( - padding: const EdgeInsets.symmetric(vertical: 16), - children: [ - BrandMarkdown( - fileName: fileName, - ), - ], - ), - ), - ); -} diff --git a/lib/ui/pages/setup/initializing/dns_provider_picker.dart b/lib/ui/pages/setup/initializing/dns_provider_picker.dart new file mode 100644 index 00000000..69560f5c --- /dev/null +++ b/lib/ui/pages/setup/initializing/dns_provider_picker.dart @@ -0,0 +1,333 @@ +import 'package:cubit_form/cubit_form.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; +import 'package:selfprivacy/logic/cubit/forms/setup/initializing/dns_provider_form_cubit.dart'; +import 'package:selfprivacy/logic/cubit/support_system/support_system_cubit.dart'; +import 'package:selfprivacy/logic/models/hive/server_domain.dart'; +import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; +import 'package:selfprivacy/ui/components/buttons/outlined_button.dart'; +import 'package:selfprivacy/ui/components/cards/outlined_card.dart'; +import 'package:url_launcher/url_launcher_string.dart'; + +class DnsProviderPicker extends StatefulWidget { + const DnsProviderPicker({ + required this.formCubit, + required this.serverInstallationCubit, + super.key, + }); + + final DnsProviderFormCubit formCubit; + final ServerInstallationCubit serverInstallationCubit; + + @override + State createState() => _DnsProviderPickerState(); +} + +class _DnsProviderPickerState extends State { + DnsProviderType selectedProvider = DnsProviderType.unknown; + + void setProvider(final DnsProviderType provider) { + setState(() { + selectedProvider = provider; + }); + } + + @override + Widget build(final BuildContext context) { + switch (selectedProvider) { + case DnsProviderType.unknown: + return ProviderSelectionPage( + serverInstallationCubit: widget.serverInstallationCubit, + callback: setProvider, + ); + + case DnsProviderType.cloudflare: + return ProviderInputDataPage( + providerCubit: widget.formCubit, + providerInfo: const ProviderPageInfo( + providerType: DnsProviderType.cloudflare, + pathToHow: 'how_cloudflare', + ), + ); + + case DnsProviderType.digitalOcean: + return ProviderInputDataPage( + providerCubit: widget.formCubit, + providerInfo: const ProviderPageInfo( + providerType: DnsProviderType.digitalOcean, + pathToHow: 'how_digital_ocean_dns', + ), + ); + + case DnsProviderType.desec: + return ProviderInputDataPage( + providerCubit: widget.formCubit, + providerInfo: const ProviderPageInfo( + providerType: DnsProviderType.desec, + pathToHow: 'how_desec', + ), + ); + } + } +} + +class ProviderPageInfo { + const ProviderPageInfo({ + required this.providerType, + required this.pathToHow, + }); + + final String pathToHow; + final DnsProviderType providerType; +} + +class ProviderInputDataPage extends StatelessWidget { + const ProviderInputDataPage({ + required this.providerInfo, + required this.providerCubit, + super.key, + }); + + final ProviderPageInfo providerInfo; + final DnsProviderFormCubit providerCubit; + + @override + Widget build(final BuildContext context) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'initializing.connect_to_dns'.tr(), + style: Theme.of(context).textTheme.headlineSmall, + ), + const SizedBox(height: 16), + Text( + 'initializing.connect_to_server_provider_text'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + ), + const SizedBox(height: 32), + CubitFormTextField( + autofocus: true, + formFieldCubit: providerCubit.apiKey, + textAlign: TextAlign.center, + scrollPadding: const EdgeInsets.only(bottom: 70), + decoration: const InputDecoration( + hintText: 'Provider API Token', + ), + ), + const SizedBox(height: 32), + BrandButton.rised( + text: 'basis.connect'.tr(), + onPressed: () => providerCubit.trySubmit(), + ), + const SizedBox(height: 10), + BrandOutlinedButton( + child: Text('initializing.how'.tr()), + onPressed: () => context.read().showArticle( + article: providerInfo.pathToHow, + context: context, + ), + ), + ], + ); +} + +class ProviderSelectionPage extends StatelessWidget { + const ProviderSelectionPage({ + required this.callback, + required this.serverInstallationCubit, + super.key, + }); + + final Function callback; + final ServerInstallationCubit serverInstallationCubit; + + @override + Widget build(final BuildContext context) => SizedBox( + width: double.infinity, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + + /// TODO: Remove obvious repetition + children: [ + Text( + 'initializing.select_dns'.tr(), + style: Theme.of(context).textTheme.headlineSmall, + ), + const SizedBox(height: 10), + Text( + 'initializing.select_provider'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + ), + const SizedBox(height: 10), + OutlinedCard( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + width: 40, + height: 40, + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(40), + color: const Color.fromARGB(255, 245, 229, 82), + ), + child: SvgPicture.asset( + 'assets/images/logos/desec.svg', + ), + ), + const SizedBox(width: 16), + Text( + 'deSEC', + style: Theme.of(context).textTheme.titleMedium, + ), + ], + ), + const SizedBox(height: 16), + Text( + 'initializing.select_provider_price_title'.tr(), + style: Theme.of(context).textTheme.bodyLarge, + ), + Text( + 'initializing.select_provider_price_free'.tr(), + style: Theme.of(context).textTheme.bodySmall, + ), + const SizedBox(height: 16), + BrandButton.rised( + text: 'basis.select'.tr(), + onPressed: () { + serverInstallationCubit + .setDnsProviderType(DnsProviderType.desec); + callback(DnsProviderType.desec); + }, + ), + // Outlined button that will open website + BrandOutlinedButton( + onPressed: () => launchUrlString('https://desec.io/'), + title: 'initializing.select_provider_site_button'.tr(), + ), + ], + ), + ), + ), + const SizedBox(height: 16), + OutlinedCard( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + width: 40, + height: 40, + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(40), + color: const Color.fromARGB(255, 244, 128, 31), + ), + child: SvgPicture.asset( + 'assets/images/logos/cloudflare.svg', + ), + ), + const SizedBox(width: 16), + Text( + 'Cloudflare', + style: Theme.of(context).textTheme.titleMedium, + ), + ], + ), + const SizedBox(height: 16), + Text( + 'initializing.select_provider_price_title'.tr(), + style: Theme.of(context).textTheme.bodyLarge, + ), + Text( + 'initializing.select_provider_price_free'.tr(), + style: Theme.of(context).textTheme.bodySmall, + ), + const SizedBox(height: 16), + BrandButton.rised( + text: 'basis.select'.tr(), + onPressed: () { + serverInstallationCubit + .setDnsProviderType(DnsProviderType.cloudflare); + callback(DnsProviderType.cloudflare); + }, + ), + // Outlined button that will open website + BrandOutlinedButton( + onPressed: () => + launchUrlString('https://dash.cloudflare.com/'), + title: 'initializing.select_provider_site_button'.tr(), + ), + ], + ), + ), + ), + const SizedBox(height: 16), + OutlinedCard( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + width: 40, + height: 40, + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(40), + color: const Color.fromARGB(255, 1, 126, 251), + ), + child: SvgPicture.asset( + 'assets/images/logos/digital_ocean.svg', + ), + ), + const SizedBox(width: 16), + Text( + 'Digital Ocean', + style: Theme.of(context).textTheme.titleMedium, + ), + ], + ), + const SizedBox(height: 16), + Text( + 'initializing.select_provider_price_title'.tr(), + style: Theme.of(context).textTheme.bodyLarge, + ), + Text( + 'initializing.select_provider_price_free'.tr(), + style: Theme.of(context).textTheme.bodySmall, + ), + const SizedBox(height: 16), + BrandButton.rised( + text: 'basis.select'.tr(), + onPressed: () { + serverInstallationCubit + .setDnsProviderType(DnsProviderType.digitalOcean); + callback(DnsProviderType.digitalOcean); + }, + ), + // Outlined button that will open website + BrandOutlinedButton( + onPressed: () => + launchUrlString('https://cloud.digitalocean.com/'), + title: 'initializing.select_provider_site_button'.tr(), + ), + ], + ), + ), + ), + ], + ), + ); +} diff --git a/lib/ui/pages/setup/initializing/domain_picker.dart b/lib/ui/pages/setup/initializing/domain_picker.dart new file mode 100644 index 00000000..1d64349c --- /dev/null +++ b/lib/ui/pages/setup/initializing/domain_picker.dart @@ -0,0 +1,163 @@ +import 'package:cubit_form/cubit_form.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; +import 'package:selfprivacy/logic/cubit/forms/setup/initializing/domain_setup_cubit.dart'; +import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; +import 'package:selfprivacy/ui/components/cards/outlined_card.dart'; +import 'package:selfprivacy/ui/layouts/responsive_layout_with_infobox.dart'; + +class DomainPicker extends StatefulWidget { + const DomainPicker({ + super.key, + }); + + @override + State createState() => _DomainPickerState(); +} + +class _DomainPickerState extends State { + String? selectedDomain; + + @override + Widget build(final BuildContext context) { + final DomainSetupState state = context.watch().state; + + return ResponsiveLayoutWithInfobox( + topChild: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + (state is MoreThenOne) + ? 'initializing.multiple_domains_found'.tr() + : 'initializing.use_this_domain'.tr(), + style: Theme.of(context).textTheme.headlineSmall, + ), + const SizedBox(height: 16), + Text( + (state is MoreThenOne) + ? 'initializing.multiple_domains_found_text'.tr() + : 'initializing.use_this_domain_text'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + ), + ], + ), + primaryColumn: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (state is Empty) + Text( + 'initializing.no_connected_domains'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + ), + if (state is Loading) + Text( + state.type == LoadingTypes.loadingDomain + ? 'initializing.loading_domain_list'.tr() + : 'basis.saving'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + ), + if (state is MoreThenOne) + ...state.domains.map( + (final domain) => Column( + children: [ + SizedBox( + width: double.infinity, + child: OutlinedCard( + borderColor: domain == selectedDomain + ? Theme.of(context).colorScheme.primary + : null, + borderWidth: domain == selectedDomain ? 3 : 1, + child: InkResponse( + highlightShape: BoxShape.rectangle, + onTap: () => setState(() { + selectedDomain = domain; + }), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Radio( + value: domain, + groupValue: selectedDomain, + onChanged: (final String? value) { + setState(() { + selectedDomain = value; + }); + }, + ), + Text( + domain, + style: Theme.of(context).textTheme.bodyLarge, + ), + ], + ), + ), + ), + ), + ), + const SizedBox(height: 8), + // Button to select and save domain + ], + ), + ), + if (state is MoreThenOne) + BrandButton.filled( + onPressed: (selectedDomain != null && + state.domains.contains(selectedDomain)) + ? () => context + .read() + .saveDomain(selectedDomain!) + : null, + child: Text('initializing.use_this_domain'.tr()), + ), + if (state is Loaded) ...[ + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + state.domain, + style: Theme.of(context).textTheme.headlineMedium?.copyWith( + color: Theme.of(context).colorScheme.onBackground, + ), + textAlign: TextAlign.center, + ), + ], + ), + ], + if (state is Empty) ...[ + const SizedBox(height: 30), + BrandButton.filled( + onPressed: () => context.read().load(), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon( + Icons.refresh, + color: Colors.white, + ), + const SizedBox(width: 10), + Text( + 'domain.update_list'.tr(), + style: Theme.of(context).textTheme.bodyLarge, + ), + ], + ), + ), + ], + if (state is Loaded) ...[ + const SizedBox(height: 32), + BrandButton.filled( + onPressed: () => + context.read().saveDomain(state.domain), + text: 'initializing.save_domain'.tr(), + ), + ], + ], + ), + ); + } +} diff --git a/lib/ui/pages/setup/initializing/initializing.dart b/lib/ui/pages/setup/initializing/initializing.dart new file mode 100644 index 00000000..a681c07a --- /dev/null +++ b/lib/ui/pages/setup/initializing/initializing.dart @@ -0,0 +1,534 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:cubit_form/cubit_form.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:selfprivacy/logic/cubit/forms/setup/initializing/server_provider_form_cubit.dart'; +import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; +import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart'; +import 'package:selfprivacy/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart'; +import 'package:selfprivacy/logic/cubit/forms/setup/initializing/dns_provider_form_cubit.dart'; +import 'package:selfprivacy/logic/cubit/forms/setup/initializing/domain_setup_cubit.dart'; +import 'package:selfprivacy/logic/cubit/forms/setup/initializing/root_user_form_cubit.dart'; +import 'package:selfprivacy/logic/cubit/support_system/support_system_cubit.dart'; +import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; +import 'package:selfprivacy/ui/components/brand_timer/brand_timer.dart'; +import 'package:selfprivacy/ui/components/buttons/outlined_button.dart'; +import 'package:selfprivacy/ui/components/drawers/progress_drawer.dart'; +import 'package:selfprivacy/ui/components/progress_bar/progress_bar.dart'; +import 'package:selfprivacy/ui/components/drawers/support_drawer.dart'; +import 'package:selfprivacy/ui/layouts/responsive_layout_with_infobox.dart'; +import 'package:selfprivacy/ui/pages/setup/initializing/dns_provider_picker.dart'; +import 'package:selfprivacy/ui/pages/setup/initializing/domain_picker.dart'; +import 'package:selfprivacy/ui/pages/setup/initializing/server_provider_picker.dart'; +import 'package:selfprivacy/ui/pages/setup/initializing/server_type_picker.dart'; +import 'package:selfprivacy/ui/pages/setup/recovering/recovery_routing.dart'; +import 'package:selfprivacy/ui/router/router.dart'; +import 'package:selfprivacy/utils/breakpoints.dart'; + +@RoutePage() +class InitializingPage extends StatelessWidget { + const InitializingPage({super.key}); + + @override + Widget build(final BuildContext context) { + final cubit = context.watch(); + + if (cubit.state is ServerInstallationRecovery) { + return const RecoveryRouting(); + } else { + Widget? actualInitializingPage; + if (cubit.state is! ServerInstallationFinished) { + actualInitializingPage = [ + () => _stepServerProviderToken(cubit), + () => _stepServerType(cubit), + () => _stepDnsProviderToken(cubit), + () => _stepBackblaze(cubit), + () => _stepDomain(cubit), + () => _stepUser(cubit), + () => _stepServer(cubit), + () => _stepCheck(cubit), + () => _stepCheck(cubit), + () => _stepCheck(cubit), + () => _stepCheck(cubit) + ][cubit.state.progress.index](); + } + + const steps = [ + 'initializing.steps.hosting', + 'initializing.steps.server_type', + 'initializing.steps.dns_provider', + 'initializing.steps.backups_provider', + 'initializing.steps.domain', + 'initializing.steps.master_account', + 'initializing.steps.server', + 'initializing.steps.dns_setup', + 'initializing.steps.nixos_installation', + 'initializing.steps.server_reboot', + 'initializing.steps.final_checks', + ]; + + return BlocListener( + listener: (final context, final state) { + if (cubit.state is ServerInstallationFinished) { + context.router.popUntilRoot(); + } + }, + child: Scaffold( + endDrawer: const SupportDrawer(), + endDrawerEnableOpenDragGesture: false, + appBar: Breakpoints.large.isActive(context) + ? null + : AppBar( + actions: [ + if (cubit.state is ServerInstallationFinished) + IconButton( + icon: const Icon(Icons.check), + onPressed: () { + context.router.popUntilRoot(); + }, + ), + const SizedBox.shrink(), + ], + title: Text( + 'more_page.configuration_wizard'.tr(), + ), + bottom: PreferredSize( + preferredSize: const Size.fromHeight(28), + child: Padding( + padding: const EdgeInsets.fromLTRB(16, 0, 16, 16), + child: ProgressBar( + steps: const [ + 'Hosting', + 'Server Type', + 'DNS Provider', + 'Backblaze', + 'Domain', + 'User', + 'Server', + 'Installation', + ], + activeIndex: cubit.state.porgressBar, + ), + ), + ), + ), + body: LayoutBuilder( + builder: (final context, final constraints) => Row( + children: [ + if (Breakpoints.large.isActive(context)) + ProgressDrawer( + steps: steps, + currentStep: cubit.state.progress.index, + title: 'more_page.configuration_wizard'.tr(), + constraints: constraints, + trailing: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (cubit.state is ServerInstallationEmpty || + cubit.state is ServerInstallationNotFinished) + Container( + alignment: Alignment.center, + child: BrandButton.filled( + text: 'basis.connect_to_existing'.tr(), + onPressed: () { + context.router.replace(const RecoveryRoute()); + }, + ), + ), + // const SizedBox(height: 8), + BrandOutlinedButton( + child: Text( + cubit.state is ServerInstallationFinished + ? 'basis.close'.tr() + : 'basis.later'.tr(), + ), + onPressed: () { + context.router.popUntilRoot(); + }, + ), + ], + ), + ), + SizedBox( + width: constraints.maxWidth - + (Breakpoints.large.isActive(context) ? 300 : 0), + height: constraints.maxHeight, + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: Breakpoints.large.isActive(context) + ? const EdgeInsets.all(16.0) + : const EdgeInsets.fromLTRB(16.0, 0, 16.0, 0.0), + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + child: actualInitializingPage, + ), + ), + if (!Breakpoints.large.isActive(context)) + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + alignment: Alignment.center, + child: BrandButton.text( + title: + cubit.state is ServerInstallationFinished + ? 'basis.close'.tr() + : 'basis.later'.tr(), + onPressed: () { + context.router.popUntilRoot(); + }, + ), + ), + if (cubit.state is ServerInstallationEmpty || + cubit.state is ServerInstallationNotFinished) + Container( + alignment: Alignment.center, + child: BrandButton.text( + title: 'basis.connect_to_existing'.tr(), + onPressed: () { + context.router + .replace(const RecoveryRoute()); + }, + ), + ) + ], + ), + ], + ), + ), + ), + ], + ), + ), + ), + ); + } + } + + Widget _stepServerProviderToken( + final ServerInstallationCubit serverInstallationCubit, + ) => + BlocProvider( + create: (final context) => + ServerProviderFormCubit(serverInstallationCubit), + child: Builder( + builder: (final context) { + final providerCubit = context.watch(); + return ServerProviderPicker( + formCubit: providerCubit, + serverInstallationCubit: serverInstallationCubit, + ); + }, + ), + ); + + Widget _stepServerType( + final ServerInstallationCubit serverInstallationCubit, + ) => + BlocProvider( + create: (final context) => + ServerProviderFormCubit(serverInstallationCubit), + child: Builder( + builder: (final context) => ServerTypePicker( + serverInstallationCubit: serverInstallationCubit, + ), + ), + ); + + Widget _stepDnsProviderToken( + final ServerInstallationCubit initializingCubit, + ) => + BlocProvider( + create: (final context) => DnsProviderFormCubit(initializingCubit), + child: Builder( + builder: (final context) { + final providerCubit = context.watch(); + return DnsProviderPicker( + formCubit: providerCubit, + serverInstallationCubit: initializingCubit, + ); + }, + ), + ); + + Widget _stepBackblaze(final ServerInstallationCubit initializingCubit) => + BlocProvider( + create: (final context) => BackblazeFormCubit(initializingCubit), + child: Builder( + builder: (final context) { + final formCubitState = context.watch().state; + return ResponsiveLayoutWithInfobox( + topChild: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '${'initializing.connect_to_server_provider'.tr()}Backblaze', + style: Theme.of(context).textTheme.headlineSmall, + ), + ], + ), + primaryColumn: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CubitFormTextField( + autofocus: true, + formFieldCubit: context.read().keyId, + textAlign: TextAlign.center, + scrollPadding: const EdgeInsets.only(bottom: 70), + decoration: const InputDecoration( + hintText: 'KeyID', + ), + ), + const SizedBox(height: 16), + CubitFormTextField( + formFieldCubit: + context.read().applicationKey, + textAlign: TextAlign.center, + scrollPadding: const EdgeInsets.only(bottom: 70), + decoration: const InputDecoration( + hintText: 'Master Application Key', + ), + ), + const SizedBox(height: 32), + BrandButton.rised( + onPressed: formCubitState.isSubmitting + ? null + : () => context.read().trySubmit(), + text: 'basis.connect'.tr(), + ), + const SizedBox(height: 10), + BrandButton.text( + onPressed: () { + context.read().showArticle( + article: 'how_backblaze', + context: context, + ); + Scaffold.of(context).openEndDrawer(); + }, + title: 'initializing.how'.tr(), + ), + ], + ), + ); + }, + ), + ); + + Widget _stepDomain(final ServerInstallationCubit initializingCubit) => + BlocProvider( + create: (final context) => DomainSetupCubit(initializingCubit)..load(), + child: const DomainPicker(), + ); + + Widget _stepUser(final ServerInstallationCubit initializingCubit) => + BlocProvider( + create: (final context) => + RootUserFormCubit(initializingCubit, FieldCubitFactory(context)), + child: Builder( + builder: (final context) { + final formCubitState = context.watch().state; + + return ResponsiveLayoutWithInfobox( + topChild: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'initializing.create_master_account'.tr(), + style: Theme.of(context).textTheme.headlineSmall, + ), + const SizedBox(height: 16), + Text( + 'initializing.enter_username_and_password'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + ), + ], + ), + primaryColumn: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (formCubitState.isErrorShown) const SizedBox(height: 16), + if (formCubitState.isErrorShown) + Text( + 'users.username_rule'.tr(), + style: TextStyle( + color: Theme.of(context).colorScheme.error, + ), + ), + const SizedBox(height: 32), + CubitFormTextField( + autofocus: true, + formFieldCubit: context.read().userName, + textAlign: TextAlign.center, + scrollPadding: const EdgeInsets.only(bottom: 70), + decoration: InputDecoration( + hintText: 'basis.username'.tr(), + ), + ), + const SizedBox(height: 16), + BlocBuilder, FieldCubitState>( + bloc: context.read().isVisible, + builder: (final context, final state) { + final bool isVisible = state.value; + return CubitFormTextField( + obscureText: !isVisible, + formFieldCubit: + context.read().password, + textAlign: TextAlign.center, + scrollPadding: const EdgeInsets.only(bottom: 70), + decoration: InputDecoration( + hintText: 'basis.password'.tr(), + suffixIcon: IconButton( + icon: Icon( + isVisible + ? Icons.visibility + : Icons.visibility_off, + ), + onPressed: () => context + .read() + .isVisible + .setValue(!isVisible), + ), + suffixIconConstraints: + const BoxConstraints(minWidth: 60), + prefixIconConstraints: + const BoxConstraints(maxWidth: 60), + prefixIcon: Container(), + ), + ); + }, + ), + const SizedBox(height: 32), + BrandButton.filled( + onPressed: formCubitState.isSubmitting + ? null + : () => context.read().trySubmit(), + text: 'basis.connect'.tr(), + ), + ], + ), + ); + }, + ), + ); + + Widget _stepServer(final ServerInstallationCubit appConfigCubit) { + final bool isLoading = + (appConfigCubit.state as ServerInstallationNotFinished).isLoading; + return Builder( + builder: (final context) => ResponsiveLayoutWithInfobox( + topChild: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'initializing.final'.tr(), + style: Theme.of(context).textTheme.headlineSmall, + ), + const SizedBox(height: 16), + Text( + 'initializing.create_server'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + ), + ], + ), + primaryColumn: BrandButton.filled( + onPressed: + isLoading ? null : appConfigCubit.createServerAndSetDnsRecords, + text: isLoading + ? 'basis.loading'.tr() + : 'initializing.create_server'.tr(), + ), + ), + ); + } + + Widget _stepCheck(final ServerInstallationCubit appConfigCubit) { + assert( + appConfigCubit.state is ServerInstallationNotFinished, + 'wrong state', + ); + final state = appConfigCubit.state as TimerState; + late int doneCount; + late String? text; + if (state.isServerResetedSecondTime) { + text = 'initializing.server_rebooted'.tr(); + doneCount = 3; + } else if (state.isServerResetedFirstTime) { + text = 'initializing.one_more_restart'.tr(); + doneCount = 2; + } else if (state.isServerStarted) { + text = 'initializing.server_started'.tr(); + doneCount = 1; + } else if (state.isServerCreated) { + text = 'initializing.server_created'.tr(); + doneCount = 0; + } + return Builder( + builder: (final context) => SizedBox( + width: double.infinity, + child: ResponsiveLayoutWithInfobox( + topChild: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'initializing.checks'.tr(args: [doneCount.toString(), '4']), + style: Theme.of(context).textTheme.headlineSmall, + ), + const SizedBox(height: 16), + if (text != null) + Text( + text, + style: Theme.of(context).textTheme.bodyMedium, + ), + ], + ), + primaryColumn: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 128), + const SizedBox(height: 10), + if (doneCount == 0 && state.dnsMatches != null) + Column( + children: state.dnsMatches!.entries.map((final entry) { + final String domain = entry.key; + final bool isCorrect = entry.value; + return Row( + children: [ + if (isCorrect) + const Icon(Icons.check, color: Colors.green), + if (!isCorrect) + const Icon(Icons.schedule, color: Colors.amber), + const SizedBox(width: 10), + Text(domain), + ], + ); + }).toList(), + ), + const SizedBox(height: 10), + if (!state.isLoading) + Row( + children: [ + Text( + 'initializing.until_the_next_check'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + ), + BrandTimer( + startDateTime: state.timerStart!, + duration: state.duration!, + ) + ], + ), + if (state.isLoading) + Text( + 'initializing.check'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/ui/pages/setup/initializing/server_provider_picker.dart b/lib/ui/pages/setup/initializing/server_provider_picker.dart new file mode 100644 index 00000000..41c4c9ea --- /dev/null +++ b/lib/ui/pages/setup/initializing/server_provider_picker.dart @@ -0,0 +1,339 @@ +import 'package:cubit_form/cubit_form.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; +import 'package:selfprivacy/logic/cubit/forms/setup/initializing/server_provider_form_cubit.dart'; +import 'package:selfprivacy/logic/cubit/support_system/support_system_cubit.dart'; +import 'package:selfprivacy/logic/models/hive/server_details.dart'; +import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; +import 'package:selfprivacy/ui/components/buttons/outlined_button.dart'; +import 'package:selfprivacy/ui/components/cards/outlined_card.dart'; +import 'package:selfprivacy/ui/components/info_box/info_box.dart'; +import 'package:selfprivacy/ui/layouts/responsive_layout_with_infobox.dart'; +import 'package:selfprivacy/utils/launch_url.dart'; + +class ServerProviderPicker extends StatefulWidget { + const ServerProviderPicker({ + required this.formCubit, + required this.serverInstallationCubit, + super.key, + }); + + final ServerProviderFormCubit formCubit; + final ServerInstallationCubit serverInstallationCubit; + + @override + State createState() => _ServerProviderPickerState(); +} + +class _ServerProviderPickerState extends State { + ServerProviderType selectedProvider = ServerProviderType.unknown; + + void setProvider(final ServerProviderType provider) { + setState(() { + selectedProvider = provider; + }); + } + + @override + Widget build(final BuildContext context) { + switch (selectedProvider) { + case ServerProviderType.unknown: + return ProviderSelectionPage( + serverInstallationCubit: widget.serverInstallationCubit, + callback: setProvider, + ); + + case ServerProviderType.hetzner: + return ProviderInputDataPage( + providerCubit: widget.formCubit, + providerInfo: ProviderPageInfo( + providerType: ServerProviderType.hetzner, + pathToHow: 'how_hetzner', + image: Image.asset( + 'assets/images/logos/hetzner.png', + width: 150, + ), + ), + ); + + case ServerProviderType.digitalOcean: + return ProviderInputDataPage( + providerCubit: widget.formCubit, + providerInfo: ProviderPageInfo( + providerType: ServerProviderType.digitalOcean, + pathToHow: 'how_digital_ocean', + image: Image.asset( + 'assets/images/logos/digital_ocean.png', + width: 150, + ), + ), + ); + } + } +} + +class ProviderPageInfo { + const ProviderPageInfo({ + required this.providerType, + required this.pathToHow, + required this.image, + }); + + final String pathToHow; + final Image image; + final ServerProviderType providerType; +} + +class ProviderInputDataPage extends StatelessWidget { + const ProviderInputDataPage({ + required this.providerInfo, + required this.providerCubit, + super.key, + }); + + final ProviderPageInfo providerInfo; + final ServerProviderFormCubit providerCubit; + + @override + Widget build(final BuildContext context) => ResponsiveLayoutWithInfobox( + topChild: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "${'initializing.connect_to_server_provider'.tr()}${providerInfo.providerType.displayName}", + style: Theme.of(context).textTheme.headlineSmall, + ), + const SizedBox(height: 16), + Text( + 'initializing.connect_to_server_provider_text'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + ), + ], + ), + primaryColumn: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CubitFormTextField( + autofocus: true, + formFieldCubit: providerCubit.apiKey, + textAlign: TextAlign.center, + scrollPadding: const EdgeInsets.only(bottom: 70), + decoration: const InputDecoration( + hintText: 'Provider API Token', + ), + ), + const SizedBox(height: 32), + BrandButton.filled( + child: Text('basis.connect'.tr()), + onPressed: () => providerCubit.trySubmit(), + ), + const SizedBox(height: 10), + BrandOutlinedButton( + child: Text('initializing.how'.tr()), + onPressed: () { + context.read().showArticle( + article: providerInfo.pathToHow, + context: context, + ); + }, + ), + ], + ), + ); +} + +class ProviderSelectionPage extends StatelessWidget { + const ProviderSelectionPage({ + required this.callback, + required this.serverInstallationCubit, + super.key, + }); + + final Function callback; + final ServerInstallationCubit serverInstallationCubit; + + @override + Widget build(final BuildContext context) => SizedBox( + width: double.infinity, + child: ResponsiveLayoutWithInfobox( + topChild: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'initializing.connect_to_server'.tr(), + style: Theme.of(context).textTheme.headlineSmall, + ), + const SizedBox(height: 10), + Text( + 'initializing.select_provider'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + ), + ], + ), + primaryColumn: Column( + children: [ + OutlinedCard( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + width: 40, + height: 40, + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(40), + color: const Color(0xFFD50C2D), + ), + child: SvgPicture.asset( + 'assets/images/logos/hetzner.svg', + ), + ), + const SizedBox(width: 16), + Text( + 'Hetzner Cloud', + style: Theme.of(context).textTheme.titleMedium, + ), + ], + ), + const SizedBox(height: 16), + Text( + 'initializing.select_provider_countries_title'.tr(), + style: Theme.of(context).textTheme.bodyLarge, + ), + Text( + 'initializing.select_provider_countries_text_hetzner' + .tr(), + style: Theme.of(context).textTheme.bodySmall, + ), + const SizedBox(height: 16), + Text( + 'initializing.select_provider_price_title'.tr(), + style: Theme.of(context).textTheme.bodyLarge, + ), + Text( + 'initializing.select_provider_price_text_hetzner'.tr(), + style: Theme.of(context).textTheme.bodySmall, + ), + const SizedBox(height: 16), + Text( + 'initializing.select_provider_payment_title'.tr(), + style: Theme.of(context).textTheme.bodyLarge, + ), + Text( + 'initializing.select_provider_payment_text_hetzner' + .tr(), + style: Theme.of(context).textTheme.bodySmall, + ), + const SizedBox(height: 16), + Text( + 'initializing.select_provider_email_notice'.tr(), + style: Theme.of(context).textTheme.bodySmall, + ), + const SizedBox(height: 16), + BrandButton.filled( + child: Text('basis.select'.tr()), + onPressed: () { + serverInstallationCubit.setServerProviderType( + ServerProviderType.hetzner, + ); + callback(ServerProviderType.hetzner); + }, + ), + // Outlined button that will open website + BrandOutlinedButton( + onPressed: () => + launchURL('https://www.hetzner.com/cloud'), + title: 'initializing.select_provider_site_button'.tr(), + ), + ], + ), + ), + ), + const SizedBox(height: 16), + OutlinedCard( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + width: 40, + height: 40, + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(40), + color: const Color(0xFF0080FF), + ), + child: SvgPicture.asset( + 'assets/images/logos/digital_ocean.svg', + ), + ), + const SizedBox(width: 16), + Text( + 'Digital Ocean', + style: Theme.of(context).textTheme.titleMedium, + ), + ], + ), + const SizedBox(height: 16), + Text( + 'initializing.select_provider_countries_title'.tr(), + style: Theme.of(context).textTheme.bodyLarge, + ), + Text( + 'initializing.select_provider_countries_text_do'.tr(), + style: Theme.of(context).textTheme.bodySmall, + ), + const SizedBox(height: 16), + Text( + 'initializing.select_provider_price_title'.tr(), + style: Theme.of(context).textTheme.bodyLarge, + ), + Text( + 'initializing.select_provider_price_text_do'.tr(), + style: Theme.of(context).textTheme.bodySmall, + ), + const SizedBox(height: 16), + Text( + 'initializing.select_provider_payment_title'.tr(), + style: Theme.of(context).textTheme.bodyLarge, + ), + Text( + 'initializing.select_provider_payment_text_do'.tr(), + style: Theme.of(context).textTheme.bodySmall, + ), + const SizedBox(height: 16), + BrandButton.filled( + child: Text('basis.select'.tr()), + onPressed: () { + serverInstallationCubit.setServerProviderType( + ServerProviderType.digitalOcean, + ); + callback(ServerProviderType.digitalOcean); + }, + ), + // Outlined button that will open website + BrandOutlinedButton( + onPressed: () => + launchURL('https://www.digitalocean.com'), + title: 'initializing.select_provider_site_button'.tr(), + ), + ], + ), + ), + ), + ], + ), + secondaryColumn: + InfoBox(text: 'initializing.select_provider_notice'.tr()), + ), + ); +} diff --git a/lib/ui/pages/setup/initializing/server_type_picker.dart b/lib/ui/pages/setup/initializing/server_type_picker.dart new file mode 100644 index 00000000..3ce0f94b --- /dev/null +++ b/lib/ui/pages/setup/initializing/server_type_picker.dart @@ -0,0 +1,478 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:selfprivacy/illustrations/stray_deer.dart'; +import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; +import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart'; +import 'package:selfprivacy/logic/models/price.dart'; +import 'package:selfprivacy/logic/models/server_provider_location.dart'; +import 'package:selfprivacy/logic/models/server_type.dart'; +import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; +import 'package:selfprivacy/ui/components/info_box/info_box.dart'; +import 'package:selfprivacy/ui/layouts/responsive_layout_with_infobox.dart'; + +class ServerTypePicker extends StatefulWidget { + const ServerTypePicker({ + required this.serverInstallationCubit, + super.key, + }); + + final ServerInstallationCubit serverInstallationCubit; + + @override + State createState() => _ServerTypePickerState(); +} + +class _ServerTypePickerState extends State { + ServerProviderLocation? serverProviderLocation; + ServerType? serverType; + + void setServerProviderLocation(final ServerProviderLocation? location) async { + if (location != null) { + await widget.serverInstallationCubit.setLocationIdentifier( + location.identifier, + ); + } + setState(() { + serverProviderLocation = location; + }); + } + + @override + Widget build(final BuildContext context) { + if (serverProviderLocation == null) { + return SelectLocationPage( + serverInstallationCubit: widget.serverInstallationCubit, + callback: setServerProviderLocation, + ); + } + + return SelectTypePage( + location: serverProviderLocation!, + serverInstallationCubit: widget.serverInstallationCubit, + backToLocationPickingCallback: () { + setServerProviderLocation(null); + }, + ); + } +} + +class SelectLocationPage extends StatelessWidget { + const SelectLocationPage({ + required this.serverInstallationCubit, + required this.callback, + super.key, + }); + + final Function callback; + final ServerInstallationCubit serverInstallationCubit; + + @override + Widget build(final BuildContext context) => FutureBuilder( + future: serverInstallationCubit.fetchAvailableLocations(), + builder: ( + final BuildContext context, + final AsyncSnapshot snapshot, + ) { + if (snapshot.hasData) { + if ((snapshot.data as List).isEmpty) { + return Text('initializing.no_locations_found'.tr()); + } + return ResponsiveLayoutWithInfobox( + topChild: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'initializing.choose_location_type'.tr(), + style: Theme.of(context).textTheme.headlineSmall, + ), + const SizedBox(height: 16), + Text( + 'initializing.choose_location_type_text'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + ), + ], + ), + primaryColumn: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ...(snapshot.data! as List).map( + (final location) => Column( + children: [ + SizedBox( + width: double.infinity, + child: Card( + clipBehavior: Clip.antiAlias, + child: InkResponse( + highlightShape: BoxShape.rectangle, + onTap: () { + callback(location); + }, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '${location.flag} ${location.title}', + style: Theme.of(context) + .textTheme + .titleMedium, + ), + const SizedBox(height: 8), + if (location.description != null) + Text( + location.description!, + style: Theme.of(context) + .textTheme + .bodyMedium, + ), + ], + ), + ), + ), + ), + ), + const SizedBox(height: 8), + ], + ), + ), + ], + ), + ); + } else { + return const Center(child: CircularProgressIndicator()); + } + }, + ); +} + +class SelectTypePage extends StatelessWidget { + const SelectTypePage({ + required this.backToLocationPickingCallback, + required this.location, + required this.serverInstallationCubit, + super.key, + }); + + final ServerProviderLocation location; + final ServerInstallationCubit serverInstallationCubit; + final Function backToLocationPickingCallback; + + @override + Widget build(final BuildContext context) { + final Future> serverTypes = + serverInstallationCubit.fetchAvailableTypesByLocation(location); + final Future prices = + serverInstallationCubit.fetchAvailableAdditionalPricing(); + return FutureBuilder( + future: Future.wait([ + serverTypes, + prices, + ]), + builder: ( + final BuildContext context, + final AsyncSnapshot> snapshot, + ) { + if (snapshot.hasData) { + if ((snapshot.data![0] as List).isEmpty || + (snapshot.data![1] as AdditionalPricing?) == null) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'initializing.locations_not_found'.tr(), + style: Theme.of(context).textTheme.headlineSmall, + ), + const SizedBox(height: 16), + Text( + 'initializing.locations_not_found_text'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + ), + LayoutBuilder( + builder: (final context, final constraints) => CustomPaint( + size: Size( + constraints.maxWidth, + (constraints.maxWidth * 1).toDouble(), + ), + painter: StrayDeerPainter( + colorScheme: Theme.of(context).colorScheme, + colorPalette: context + .read() + .state + .corePaletteOrDefault, + ), + ), + ), + const SizedBox(height: 16), + BrandButton.rised( + onPressed: () { + backToLocationPickingCallback(); + }, + text: 'initializing.back_to_locations'.tr(), + ), + ], + ); + } + final prices = snapshot.data![1] as AdditionalPricing; + final storagePrice = serverInstallationCubit.initialStorage.gibibyte * + prices.perVolumeGb.value; + final publicIpPrice = prices.perPublicIpv4.value; + return ResponsiveLayoutWithInfobox( + topChild: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'initializing.choose_server_type'.tr(), + style: Theme.of(context).textTheme.headlineSmall, + ), + const SizedBox(height: 16), + Text( + 'initializing.choose_server_type_text'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + ), + ], + ), + primaryColumn: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ...(snapshot.data![0] as List).map( + (final type) => Column( + children: [ + SizedBox( + width: double.infinity, + child: InkWell( + onTap: () { + serverInstallationCubit.setServerType(type); + }, + child: Card( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + type.title, + style: + Theme.of(context).textTheme.titleMedium, + ), + const SizedBox(height: 8), + Row( + children: [ + Icon( + Icons.memory_outlined, + color: Theme.of(context) + .colorScheme + .onSurface, + ), + const SizedBox(width: 8), + Text( + 'server.core_count'.plural(type.cores), + style: Theme.of(context) + .textTheme + .bodyMedium, + ), + ], + ), + const SizedBox(height: 8), + Row( + children: [ + Icon( + Icons.memory_outlined, + color: Theme.of(context) + .colorScheme + .onSurface, + ), + const SizedBox(width: 8), + Text( + 'initializing.choose_server_type_ram' + .tr(args: [type.ram.toString()]), + style: Theme.of(context) + .textTheme + .bodyMedium, + ), + ], + ), + const SizedBox(height: 8), + Row( + children: [ + Icon( + Icons.sd_card_outlined, + color: Theme.of(context) + .colorScheme + .onSurface, + ), + const SizedBox(width: 8), + Text( + 'initializing.choose_server_type_storage' + .tr( + args: [type.disk.gibibyte.toString()], + ), + style: Theme.of(context) + .textTheme + .bodyMedium, + ), + ], + ), + const SizedBox(height: 8), + const Divider(height: 8), + const SizedBox(height: 8), + Row( + children: [ + Icon( + Icons.payments_outlined, + color: Theme.of(context) + .colorScheme + .onSurface, + ), + const SizedBox(width: 8), + Text( + 'initializing.choose_server_type_payment_per_month' + .tr( + args: [ + '${(type.price.value + storagePrice + publicIpPrice).toStringAsFixed(4)} ${type.price.currency.shortcode}' + ], + ), + style: Theme.of(context) + .textTheme + .bodyLarge, + ), + ], + ), + IntrinsicHeight( + child: Row( + children: [ + VerticalDivider( + width: 24.0, + indent: 4.0, + endIndent: 4.0, + color: Theme.of(context) + .colorScheme + .onSurface + .withAlpha(128), + ), + const SizedBox(width: 8), + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon( + Icons.memory_outlined, + color: Theme.of(context) + .colorScheme + .onSurface + .withAlpha(128), + size: 16, + ), + const SizedBox(width: 8), + Text( + 'initializing.choose_server_type_payment_server' + .tr( + args: [ + type.price.value + .toString() + ], + ), + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith( + color: Theme.of(context) + .colorScheme + .onSurface + .withAlpha(128), + ), + ), + ], + ), + Row( + children: [ + Icon( + Icons.sd_card_outlined, + color: Theme.of(context) + .colorScheme + .onSurface + .withAlpha(128), + size: 16, + ), + const SizedBox(width: 8), + Text( + 'initializing.choose_server_type_payment_storage' + .tr( + args: [ + storagePrice.toString() + ], + ), + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith( + color: Theme.of(context) + .colorScheme + .onSurface + .withAlpha(128), + ), + ), + ], + ), + if (publicIpPrice != 0) + Row( + children: [ + Icon( + Icons.lan_outlined, + color: Theme.of(context) + .colorScheme + .onSurface + .withAlpha(128), + size: 16, + ), + const SizedBox(width: 8), + Text( + 'initializing.choose_server_type_payment_ip' + .tr( + args: [ + publicIpPrice.toString() + ], + ), + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith( + color: Theme.of( + context, + ) + .colorScheme + .onSurface + .withAlpha(128), + ), + ), + ], + ), + ], + ), + ], + ), + ), + ], + ), + ), + ), + ), + ), + const SizedBox(height: 8), + ], + ), + ), + ], + ), + secondaryColumn: + InfoBox(text: 'initializing.choose_server_type_notice'.tr()), + ); + } else { + return const Center(child: CircularProgressIndicator()); + } + }, + ); + } +} diff --git a/lib/ui/pages/setup/recovering/recover_by_new_device_key.dart b/lib/ui/pages/setup/recovering/recover_by_new_device_key.dart index a9f37b19..d1dce974 100644 --- a/lib/ui/pages/setup/recovering/recover_by_new_device_key.dart +++ b/lib/ui/pages/setup/recovering/recover_by_new_device_key.dart @@ -1,15 +1,15 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:selfprivacy/logic/cubit/forms/setup/recovering/recovery_device_form_cubit.dart'; -import 'package:selfprivacy/ui/components/brand_button/filled_button.dart'; -import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart'; +import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; +import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart'; import 'package:cubit_form/cubit_form.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart'; class RecoverByNewDeviceKeyInstruction extends StatelessWidget { - const RecoverByNewDeviceKeyInstruction({final super.key}); + const RecoverByNewDeviceKeyInstruction({super.key}); @override Widget build(final BuildContext context) => BrandHeroScreen( @@ -17,11 +17,12 @@ class RecoverByNewDeviceKeyInstruction extends StatelessWidget { heroSubtitle: 'recovering.method_device_description'.tr(), hasBackButton: true, hasFlashButton: false, + ignoreBreakpoints: true, onBackButtonPressed: context.read().revertRecoveryStep, children: [ - FilledButton( - title: 'recovering.method_device_button'.tr(), + BrandButton.filled( + child: Text('recovering.method_device_button'.tr()), onPressed: () => Navigator.of(context) .push(materialRoute(const RecoverByNewDeviceKeyInput())), ) @@ -30,7 +31,7 @@ class RecoverByNewDeviceKeyInstruction extends StatelessWidget { } class RecoverByNewDeviceKeyInput extends StatelessWidget { - const RecoverByNewDeviceKeyInput({final super.key}); + const RecoverByNewDeviceKeyInput({super.key}); @override Widget build(final BuildContext context) { @@ -61,8 +62,10 @@ class RecoverByNewDeviceKeyInput extends StatelessWidget { heroSubtitle: 'recovering.method_device_input_description'.tr(), hasBackButton: true, hasFlashButton: false, + ignoreBreakpoints: true, children: [ CubitFormTextField( + autofocus: true, formFieldCubit: context.read().tokenField, decoration: InputDecoration( @@ -73,11 +76,11 @@ class RecoverByNewDeviceKeyInput extends StatelessWidget { ), const SizedBox(height: 16), FilledButton( - title: 'more.continue'.tr(), onPressed: formCubitState.isSubmitting ? null : () => context.read().trySubmit(), + child: Text('basis.continue'.tr()), ) ], ); diff --git a/lib/ui/pages/setup/recovering/recover_by_old_token.dart b/lib/ui/pages/setup/recovering/recover_by_old_token.dart index e3507a0e..1a777f83 100644 --- a/lib/ui/pages/setup/recovering/recover_by_old_token.dart +++ b/lib/ui/pages/setup/recovering/recover_by_old_token.dart @@ -1,8 +1,8 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:selfprivacy/logic/cubit/forms/setup/recovering/recovery_device_form_cubit.dart'; -import 'package:selfprivacy/ui/components/brand_button/filled_button.dart'; -import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart'; +import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; +import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; import 'package:selfprivacy/ui/components/brand_md/brand_md.dart'; import 'package:cubit_form/cubit_form.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; @@ -12,7 +12,7 @@ class RecoverByOldTokenInstruction extends StatelessWidget { @override const RecoverByOldTokenInstruction({ required this.instructionFilename, - final super.key, + super.key, }); @override @@ -28,6 +28,7 @@ class RecoverByOldTokenInstruction extends StatelessWidget { heroTitle: 'recovering.recovery_main_header'.tr(), hasBackButton: true, hasFlashButton: false, + ignoreBreakpoints: true, onBackButtonPressed: context.read().revertRecoveryStep, children: [ @@ -35,8 +36,8 @@ class RecoverByOldTokenInstruction extends StatelessWidget { fileName: instructionFilename, ), const SizedBox(height: 16), - FilledButton( - title: 'recovering.method_device_button'.tr(), + BrandButton.filled( + child: Text('recovering.method_device_button'.tr()), onPressed: () => context .read() .selectRecoveryMethod(ServerRecoveryMethods.oldToken), @@ -49,7 +50,7 @@ class RecoverByOldTokenInstruction extends StatelessWidget { } class RecoverByOldToken extends StatelessWidget { - const RecoverByOldToken({final super.key}); + const RecoverByOldToken({super.key}); @override Widget build(final BuildContext context) { @@ -72,8 +73,10 @@ class RecoverByOldToken extends StatelessWidget { heroSubtitle: 'recovering.method_device_input_description'.tr(), hasBackButton: true, hasFlashButton: false, + ignoreBreakpoints: true, children: [ CubitFormTextField( + autofocus: true, formFieldCubit: context.read().tokenField, decoration: InputDecoration( @@ -82,11 +85,11 @@ class RecoverByOldToken extends StatelessWidget { ), ), const SizedBox(height: 16), - FilledButton( - title: 'more.continue'.tr(), + BrandButton.filled( onPressed: formCubitState.isSubmitting ? null : () => context.read().trySubmit(), + child: Text('basis.continue'.tr()), ) ], ); diff --git a/lib/ui/pages/setup/recovering/recover_by_recovery_key.dart b/lib/ui/pages/setup/recovering/recover_by_recovery_key.dart index f729524e..ad18bc95 100644 --- a/lib/ui/pages/setup/recovering/recover_by_recovery_key.dart +++ b/lib/ui/pages/setup/recovering/recover_by_recovery_key.dart @@ -4,11 +4,11 @@ import 'package:flutter/material.dart'; import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart'; import 'package:selfprivacy/logic/cubit/forms/setup/recovering/recovery_device_form_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; -import 'package:selfprivacy/ui/components/brand_button/filled_button.dart'; -import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart'; +import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; +import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; class RecoverByRecoveryKey extends StatelessWidget { - const RecoverByRecoveryKey({final super.key}); + const RecoverByRecoveryKey({super.key}); @override Widget build(final BuildContext context) { @@ -31,10 +31,12 @@ class RecoverByRecoveryKey extends StatelessWidget { heroSubtitle: 'recovering.method_recovery_input_description'.tr(), hasBackButton: true, hasFlashButton: false, + ignoreBreakpoints: true, onBackButtonPressed: context.read().revertRecoveryStep, children: [ CubitFormTextField( + autofocus: true, formFieldCubit: context.read().tokenField, decoration: InputDecoration( @@ -43,11 +45,11 @@ class RecoverByRecoveryKey extends StatelessWidget { ), ), const SizedBox(height: 16), - FilledButton( - title: 'more.continue'.tr(), + BrandButton.filled( onPressed: formCubitState.isSubmitting ? null : () => context.read().trySubmit(), + child: Text('basis.continue'.tr()), ) ], ); diff --git a/lib/ui/pages/setup/recovering/recovery_confirm_backblaze.dart b/lib/ui/pages/setup/recovering/recovery_confirm_backblaze.dart index 2b558727..4b27e3ad 100644 --- a/lib/ui/pages/setup/recovering/recovery_confirm_backblaze.dart +++ b/lib/ui/pages/setup/recovering/recovery_confirm_backblaze.dart @@ -1,16 +1,14 @@ import 'package:cubit_form/cubit_form.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; -import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart'; -import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; -import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart'; -import 'package:selfprivacy/ui/components/brand_md/brand_md.dart'; +import 'package:selfprivacy/logic/cubit/support_system/support_system_cubit.dart'; +import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; +import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; class RecoveryConfirmBackblaze extends StatelessWidget { - const RecoveryConfirmBackblaze({final super.key}); + const RecoveryConfirmBackblaze({super.key}); @override Widget build(final BuildContext context) { @@ -25,12 +23,20 @@ class RecoveryConfirmBackblaze extends StatelessWidget { context.watch().state; return BrandHeroScreen( - heroTitle: 'recovering.confirm_backblaze'.tr(), - heroSubtitle: 'recovering.confirm_backblaze_description'.tr(), + heroTitle: 'recovering.provider_connected'.tr(args: ['Backblaze']), + heroSubtitle: 'recovering.provider_connected_description'.tr( + args: ['Backblaze'], + ), hasBackButton: true, + ignoreBreakpoints: true, + hasSupportDrawer: true, + onBackButtonPressed: () { + Navigator.of(context).popUntil((final route) => route.isFirst); + }, hasFlashButton: false, children: [ CubitFormTextField( + autofocus: true, formFieldCubit: context.read().keyId, decoration: const InputDecoration( border: OutlineInputBorder(), @@ -54,27 +60,15 @@ class RecoveryConfirmBackblaze extends StatelessWidget { text: 'basis.connect'.tr(), ), const SizedBox(height: 16), - BrandButton.text( - onPressed: () => showModalBottomSheet( - context: context, - isScrollControlled: true, - backgroundColor: Colors.transparent, - builder: (final BuildContext context) => BrandBottomSheet( - isExpended: true, - child: Padding( - padding: paddingH15V0, - child: ListView( - padding: const EdgeInsets.symmetric(vertical: 16), - children: const [ - BrandMarkdown( - fileName: 'how_backblaze', + Builder( + builder: (final context) => BrandButton.text( + onPressed: () => + context.read().showArticle( + article: 'how_backblaze', + context: context, ), - ], - ), - ), - ), + title: 'initializing.how'.tr(), ), - title: 'initializing.how'.tr(), ), ], ); diff --git a/lib/ui/pages/setup/recovering/recovery_confirm_cloudflare.dart b/lib/ui/pages/setup/recovering/recovery_confirm_cloudflare.dart deleted file mode 100644 index 8cbdbe6c..00000000 --- a/lib/ui/pages/setup/recovering/recovery_confirm_cloudflare.dart +++ /dev/null @@ -1,78 +0,0 @@ -import 'package:cubit_form/cubit_form.dart'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/brand_theme.dart'; -import 'package:selfprivacy/logic/cubit/forms/setup/initializing/cloudflare_form_cubit.dart'; -import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; -import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart'; -import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; -import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart'; -import 'package:selfprivacy/ui/components/brand_md/brand_md.dart'; - -class RecoveryConfirmCloudflare extends StatelessWidget { - const RecoveryConfirmCloudflare({final super.key}); - - @override - Widget build(final BuildContext context) { - final ServerInstallationCubit appConfig = - context.watch(); - - return BlocProvider( - create: (final BuildContext context) => CloudFlareFormCubit(appConfig), - child: Builder( - builder: (final BuildContext context) { - final FormCubitState formCubitState = - context.watch().state; - - return BrandHeroScreen( - heroTitle: 'recovering.confirm_cloudflare'.tr(), - heroSubtitle: 'recovering.confirm_cloudflare_description'.tr( - args: [appConfig.state.serverDomain?.domainName ?? 'your domain'], - ), - hasBackButton: true, - hasFlashButton: false, - children: [ - CubitFormTextField( - formFieldCubit: context.read().apiKey, - decoration: InputDecoration( - border: const OutlineInputBorder(), - labelText: 'initializing.5'.tr(), - ), - ), - const SizedBox(height: 16), - BrandButton.rised( - onPressed: formCubitState.isSubmitting - ? null - : () => context.read().trySubmit(), - text: 'basis.connect'.tr(), - ), - const SizedBox(height: 16), - BrandButton.text( - onPressed: () => showModalBottomSheet( - context: context, - isScrollControlled: true, - backgroundColor: Colors.transparent, - builder: (final BuildContext context) => BrandBottomSheet( - isExpended: true, - child: Padding( - padding: paddingH15V0, - child: ListView( - padding: const EdgeInsets.symmetric(vertical: 16), - children: const [ - BrandMarkdown( - fileName: 'how_cloudflare', - ), - ], - ), - ), - ), - ), - title: 'initializing.how'.tr(), - ), - ], - ); - }, - ), - ); - } -} diff --git a/lib/ui/pages/setup/recovering/recovery_confirm_dns.dart b/lib/ui/pages/setup/recovering/recovery_confirm_dns.dart new file mode 100644 index 00000000..e49efe9e --- /dev/null +++ b/lib/ui/pages/setup/recovering/recovery_confirm_dns.dart @@ -0,0 +1,75 @@ +import 'package:cubit_form/cubit_form.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:selfprivacy/logic/cubit/forms/setup/initializing/dns_provider_form_cubit.dart'; +import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; +import 'package:selfprivacy/logic/cubit/support_system/support_system_cubit.dart'; +import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; +import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; + +class RecoveryConfirmDns extends StatelessWidget { + const RecoveryConfirmDns({super.key}); + + @override + Widget build(final BuildContext context) { + final ServerInstallationCubit appConfig = + context.watch(); + + return BlocProvider( + create: (final BuildContext context) => DnsProviderFormCubit(appConfig), + child: Builder( + builder: (final BuildContext context) { + final FormCubitState formCubitState = + context.watch().state; + final String providerDisplayName = + appConfig.state.serverDomain?.provider.displayName ?? + 'DNS Provider'; + return BrandHeroScreen( + heroTitle: 'recovering.provider_connected'.tr( + args: [providerDisplayName], + ), + heroSubtitle: 'recovering.provider_connected_description'.tr( + args: [appConfig.state.serverDomain?.domainName ?? 'your domain'], + ), + hasBackButton: true, + hasFlashButton: false, + ignoreBreakpoints: true, + hasSupportDrawer: true, + onBackButtonPressed: + context.read().revertRecoveryStep, + children: [ + CubitFormTextField( + autofocus: true, + formFieldCubit: context.read().apiKey, + decoration: InputDecoration( + border: const OutlineInputBorder(), + labelText: 'recovering.provider_connected_placeholder'.tr( + args: [providerDisplayName], + ), + ), + ), + const SizedBox(height: 16), + BrandButton.rised( + onPressed: formCubitState.isSubmitting + ? null + : () => context.read().trySubmit(), + text: 'basis.connect'.tr(), + ), + const SizedBox(height: 16), + Builder( + builder: (final context) => BrandButton.text( + onPressed: () => + context.read().showArticle( + article: 'how_cloudflare', + context: context, + ), + title: 'initializing.how'.tr(), + ), + ), + ], + ); + }, + ), + ); + } +} diff --git a/lib/ui/pages/setup/recovering/recovery_confirm_server.dart b/lib/ui/pages/setup/recovering/recovery_confirm_server.dart index 110425ef..2efcff87 100644 --- a/lib/ui/pages/setup/recovering/recovery_confirm_server.dart +++ b/lib/ui/pages/setup/recovering/recovery_confirm_server.dart @@ -2,13 +2,12 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; import 'package:selfprivacy/logic/models/server_basic_info.dart'; -import 'package:selfprivacy/ui/components/brand_button/filled_button.dart'; -import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; -import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart'; -import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart'; +import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; +import 'package:selfprivacy/ui/components/cards/filled_card.dart'; +import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; class RecoveryConfirmServer extends StatefulWidget { - const RecoveryConfirmServer({final super.key}); + const RecoveryConfirmServer({super.key}); @override State createState() => _RecoveryConfirmServerState(); @@ -39,12 +38,15 @@ class _RecoveryConfirmServerState extends State { ? 'recovering.choose_server_description'.tr() : 'recovering.confirm_server_description'.tr(), hasBackButton: true, + ignoreBreakpoints: true, + onBackButtonPressed: () { + Navigator.of(context).popUntil((final route) => route.isFirst); + }, hasFlashButton: false, children: [ FutureBuilder>( - future: context - .read() - .getServersOnHetznerAccount(), + future: + context.read().getAvailableServers(), builder: (final context, final snapshot) { if (snapshot.hasData) { final servers = snapshot.data; @@ -53,8 +55,7 @@ class _RecoveryConfirmServerState extends State { if (servers != null && servers.isNotEmpty) Column( children: [ - if (servers.length == 1 || - (!_isExtended && _isServerFound(servers))) + if (!_isExtended && _isServerFound(servers)) confirmServer( context, _firstValidServer(servers), @@ -69,7 +70,7 @@ class _RecoveryConfirmServerState extends State { Center( child: Text( 'recovering.no_servers'.tr(), - style: Theme.of(context).textTheme.headline6, + style: Theme.of(context).textTheme.titleLarge, ), ), ], @@ -96,8 +97,8 @@ class _RecoveryConfirmServerState extends State { server: server, ), const SizedBox(height: 16), - FilledButton( - title: 'recovering.confirm_server_accept'.tr(), + BrandButton.filled( + child: Text('recovering.confirm_server_accept'.tr()), onPressed: () => _showConfirmationDialog(context, server), ), const SizedBox(height: 16), @@ -132,7 +133,7 @@ class _RecoveryConfirmServerState extends State { required final ServerBasicInfoWithValidators server, final VoidCallback? onTap, }) => - BrandCards.filled( + FilledCard( child: ListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), @@ -262,7 +263,7 @@ class IsValidStringDisplay extends StatelessWidget { required this.isValid, required this.textIfValid, required this.textIfInvalid, - final super.key, + super.key, }); final bool isValid; diff --git a/lib/ui/pages/setup/recovering/recovery_hentzner_connected.dart b/lib/ui/pages/setup/recovering/recovery_hentzner_connected.dart deleted file mode 100644 index e1812b32..00000000 --- a/lib/ui/pages/setup/recovering/recovery_hentzner_connected.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/brand_theme.dart'; -import 'package:selfprivacy/logic/cubit/forms/setup/initializing/hetzner_form_cubit.dart'; -import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart'; -import 'package:selfprivacy/ui/components/brand_button/filled_button.dart'; -import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; -import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart'; -import 'package:cubit_form/cubit_form.dart'; -import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; -import 'package:selfprivacy/ui/components/brand_md/brand_md.dart'; - -class RecoveryHetznerConnected extends StatelessWidget { - const RecoveryHetznerConnected({final super.key}); - - @override - Widget build(final BuildContext context) { - final ServerInstallationCubit appConfig = - context.watch(); - - return BlocProvider( - create: (final BuildContext context) => HetznerFormCubit(appConfig), - child: Builder( - builder: (final BuildContext context) { - final FormCubitState formCubitState = - context.watch().state; - - return BrandHeroScreen( - heroTitle: 'recovering.hetzner_connected'.tr(), - heroSubtitle: 'recovering.hetzner_connected_description'.tr( - args: [appConfig.state.serverDomain?.domainName ?? 'your domain'], - ), - hasBackButton: true, - hasFlashButton: false, - children: [ - CubitFormTextField( - formFieldCubit: context.read().apiKey, - decoration: InputDecoration( - border: const OutlineInputBorder(), - labelText: 'recovering.hetzner_connected_placeholder'.tr(), - ), - ), - const SizedBox(height: 16), - FilledButton( - title: 'more.continue'.tr(), - onPressed: formCubitState.isSubmitting - ? null - : () => context.read().trySubmit(), - ), - const SizedBox(height: 16), - BrandButton.text( - title: 'initializing.how'.tr(), - onPressed: () => showModalBottomSheet( - context: context, - isScrollControlled: true, - backgroundColor: Colors.transparent, - builder: (final BuildContext context) => BrandBottomSheet( - isExpended: true, - child: Padding( - padding: paddingH15V0, - child: ListView( - padding: const EdgeInsets.symmetric(vertical: 16), - children: const [ - BrandMarkdown( - fileName: 'how_hetzner', - ), - ], - ), - ), - ), - ), - ), - ], - ); - }, - ), - ); - } -} diff --git a/lib/ui/pages/setup/recovering/recovery_method_select.dart b/lib/ui/pages/setup/recovering/recovery_method_select.dart index fe622acb..f8cec44a 100644 --- a/lib/ui/pages/setup/recovering/recovery_method_select.dart +++ b/lib/ui/pages/setup/recovering/recovery_method_select.dart @@ -2,14 +2,14 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; -import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; -import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart'; -import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart'; +import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; +import 'package:selfprivacy/ui/components/cards/outlined_card.dart'; +import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; import 'package:selfprivacy/ui/pages/setup/recovering/recover_by_old_token.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart'; class RecoveryMethodSelect extends StatelessWidget { - const RecoveryMethodSelect({final super.key}); + const RecoveryMethodSelect({super.key}); @override Widget build(final BuildContext context) => BrandHeroScreen( @@ -17,10 +17,11 @@ class RecoveryMethodSelect extends StatelessWidget { heroSubtitle: 'recovering.method_select_description'.tr(), hasBackButton: true, hasFlashButton: false, + ignoreBreakpoints: true, onBackButtonPressed: context.read().revertRecoveryStep, children: [ - BrandCards.outlined( + OutlinedCard( child: ListTile( title: Text( 'recovering.method_select_other_device'.tr(), @@ -33,7 +34,7 @@ class RecoveryMethodSelect extends StatelessWidget { ), ), const SizedBox(height: 16), - BrandCards.outlined( + OutlinedCard( child: ListTile( title: Text( 'recovering.method_select_recovery_key'.tr(), @@ -56,7 +57,7 @@ class RecoveryMethodSelect extends StatelessWidget { } class RecoveryFallbackMethodSelect extends StatelessWidget { - const RecoveryFallbackMethodSelect({final super.key}); + const RecoveryFallbackMethodSelect({super.key}); @override Widget build(final BuildContext context) => @@ -74,8 +75,9 @@ class RecoveryFallbackMethodSelect extends StatelessWidget { heroSubtitle: 'recovering.fallback_select_description'.tr(), hasBackButton: true, hasFlashButton: false, + ignoreBreakpoints: true, children: [ - BrandCards.outlined( + OutlinedCard( child: ListTile( title: Text( 'recovering.fallback_select_token_copy'.tr(), @@ -92,7 +94,7 @@ class RecoveryFallbackMethodSelect extends StatelessWidget { ), ), const SizedBox(height: 16), - BrandCards.outlined( + OutlinedCard( child: ListTile( title: Text( 'recovering.fallback_select_root_ssh'.tr(), @@ -109,7 +111,7 @@ class RecoveryFallbackMethodSelect extends StatelessWidget { ), ), const SizedBox(height: 16), - BrandCards.outlined( + OutlinedCard( child: ListTile( title: Text( 'recovering.fallback_select_provider_console'.tr(), diff --git a/lib/ui/pages/setup/recovering/recovery_routing.dart b/lib/ui/pages/setup/recovering/recovery_routing.dart index 3c375ef0..3ab5109a 100644 --- a/lib/ui/pages/setup/recovering/recovery_routing.dart +++ b/lib/ui/pages/setup/recovering/recovery_routing.dart @@ -1,24 +1,26 @@ +import 'package:auto_route/auto_route.dart'; import 'package:cubit_form/cubit_form.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart'; import 'package:selfprivacy/logic/cubit/forms/setup/recovering/recovery_domain_form_cubit.dart'; -import 'package:selfprivacy/ui/components/brand_button/filled_button.dart'; -import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart'; +import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; +import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; import 'package:selfprivacy/ui/pages/root_route.dart'; import 'package:selfprivacy/ui/pages/setup/recovering/recover_by_old_token.dart'; import 'package:selfprivacy/ui/pages/setup/recovering/recover_by_recovery_key.dart'; import 'package:selfprivacy/ui/pages/setup/recovering/recover_by_new_device_key.dart'; import 'package:selfprivacy/ui/pages/setup/recovering/recovery_confirm_backblaze.dart'; -import 'package:selfprivacy/ui/pages/setup/recovering/recovery_confirm_cloudflare.dart'; +import 'package:selfprivacy/ui/pages/setup/recovering/recovery_confirm_dns.dart'; import 'package:selfprivacy/ui/pages/setup/recovering/recovery_confirm_server.dart'; -import 'package:selfprivacy/ui/pages/setup/recovering/recovery_hentzner_connected.dart'; +import 'package:selfprivacy/ui/pages/setup/recovering/recovery_server_provider_connected.dart'; import 'package:selfprivacy/ui/pages/setup/recovering/recovery_method_select.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart'; +@RoutePage() class RecoveryRouting extends StatelessWidget { - const RecoveryRouting({final super.key}); + const RecoveryRouting({super.key}); @override Widget build(final BuildContext context) { @@ -47,14 +49,14 @@ class RecoveryRouting extends StatelessWidget { case RecoveryStep.oldToken: currentPage = const RecoverByOldToken(); break; - case RecoveryStep.hetznerToken: - currentPage = const RecoveryHetznerConnected(); + case RecoveryStep.serverProviderToken: + currentPage = const RecoveryServerProviderConnected(); break; case RecoveryStep.serverSelection: currentPage = const RecoveryConfirmServer(); break; - case RecoveryStep.cloudflareToken: - currentPage = const RecoveryConfirmCloudflare(); + case RecoveryStep.dnsProviderToken: + currentPage = const RecoveryConfirmDns(); break; case RecoveryStep.backblazeToken: currentPage = const RecoveryConfirmBackblaze(); @@ -77,7 +79,7 @@ class RecoveryRouting extends StatelessWidget { } class SelectDomainToRecover extends StatelessWidget { - const SelectDomainToRecover({final super.key}); + const SelectDomainToRecover({super.key}); @override Widget build(final BuildContext context) { @@ -110,6 +112,7 @@ class SelectDomainToRecover extends StatelessWidget { heroSubtitle: 'recovering.domain_recovery_description'.tr(), hasBackButton: true, hasFlashButton: false, + ignoreBreakpoints: true, onBackButtonPressed: () { Navigator.of(context).pushAndRemoveUntil( materialRoute(const RootPage()), @@ -118,6 +121,7 @@ class SelectDomainToRecover extends StatelessWidget { }, children: [ CubitFormTextField( + autofocus: true, formFieldCubit: context.read().serverDomainField, decoration: InputDecoration( @@ -126,12 +130,12 @@ class SelectDomainToRecover extends StatelessWidget { ), ), const SizedBox(height: 16), - FilledButton( - title: 'more.continue'.tr(), + BrandButton.filled( onPressed: formCubitState.isSubmitting ? null : () => context.read().trySubmit(), + child: Text('basis.continue'.tr()), ) ], ), diff --git a/lib/ui/pages/setup/recovering/recovery_server_provider_connected.dart b/lib/ui/pages/setup/recovering/recovery_server_provider_connected.dart new file mode 100644 index 00000000..40f13eaa --- /dev/null +++ b/lib/ui/pages/setup/recovering/recovery_server_provider_connected.dart @@ -0,0 +1,74 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:selfprivacy/logic/cubit/forms/setup/initializing/server_provider_form_cubit.dart'; +import 'package:selfprivacy/logic/cubit/support_system/support_system_cubit.dart'; +import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; +import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; +import 'package:cubit_form/cubit_form.dart'; +import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; + +class RecoveryServerProviderConnected extends StatelessWidget { + const RecoveryServerProviderConnected({super.key}); + + @override + Widget build(final BuildContext context) { + final ServerInstallationCubit appConfig = + context.watch(); + + return BlocProvider( + create: (final BuildContext context) => + ServerProviderFormCubit(appConfig), + child: Builder( + builder: (final BuildContext context) => BrandHeroScreen( + heroTitle: 'recovering.provider_connected'.tr( + args: [ + appConfig.state.serverDetails?.provider.displayName ?? + 'Server Provider' + ], + ), + heroSubtitle: 'recovering.provider_connected_description'.tr( + args: [appConfig.state.serverDomain?.domainName ?? 'your domain'], + ), + hasBackButton: true, + hasFlashButton: false, + ignoreBreakpoints: true, + hasSupportDrawer: true, + onBackButtonPressed: () { + Navigator.of(context).popUntil((final route) => route.isFirst); + }, + children: [ + CubitFormTextField( + autofocus: true, + formFieldCubit: context.read().apiKey, + decoration: InputDecoration( + border: const OutlineInputBorder(), + labelText: 'recovering.provider_connected_placeholder'.tr( + args: [ + appConfig.state.serverDetails?.provider.displayName ?? + 'Server Provider' + ], + ), + ), + ), + const SizedBox(height: 16), + BrandButton.filled( + onPressed: () => + context.read().trySubmit(), + child: Text('basis.continue'.tr()), + ), + const SizedBox(height: 16), + Builder( + builder: (final context) => BrandButton.text( + title: 'initializing.how'.tr(), + onPressed: () => context.read().showArticle( + article: 'how_hetzner', + context: context, + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/ui/pages/ssh_keys/new_ssh_key.dart b/lib/ui/pages/ssh_keys/new_ssh_key.dart deleted file mode 100644 index 247590b7..00000000 --- a/lib/ui/pages/ssh_keys/new_ssh_key.dart +++ /dev/null @@ -1,76 +0,0 @@ -part of 'ssh_keys.dart'; - -class _NewSshKey extends StatelessWidget { - const _NewSshKey(this.user); - final User user; - - @override - Widget build(final BuildContext context) => BrandBottomSheet( - child: BlocProvider( - create: (final context) { - final jobCubit = context.read(); - final jobState = jobCubit.state; - if (jobState is JobsStateWithJobs) { - final jobs = jobState.jobList; - for (final job in jobs) { - if (job is CreateSSHKeyJob && job.user.login == user.login) { - user.sshKeys.add(job.publicKey); - } - } - } - return SshFormCubit( - jobsCubit: jobCubit, - user: user, - ); - }, - child: Builder( - builder: (final context) { - final formCubitState = context.watch().state; - - return BlocListener( - listener: (final context, final state) { - if (state.isSubmitted) { - Navigator.pop(context); - } - }, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - BrandHeader( - title: user.login, - ), - const SizedBox(width: 14), - Padding( - padding: paddingH15V0, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - IntrinsicHeight( - child: CubitFormTextField( - formFieldCubit: context.read().key, - decoration: InputDecoration( - labelText: 'ssh.input_label'.tr(), - ), - ), - ), - const SizedBox(height: 30), - BrandButton.rised( - onPressed: formCubitState.isSubmitting - ? null - : () => - context.read().trySubmit(), - text: 'ssh.create'.tr(), - ), - const SizedBox(height: 30), - ], - ), - ), - ], - ), - ); - }, - ), - ), - ); -} diff --git a/lib/ui/pages/ssh_keys/ssh_keys.dart b/lib/ui/pages/ssh_keys/ssh_keys.dart deleted file mode 100644 index 4059ba63..00000000 --- a/lib/ui/pages/ssh_keys/ssh_keys.dart +++ /dev/null @@ -1,144 +0,0 @@ -import 'package:cubit_form/cubit_form.dart'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/material.dart'; -import 'package:selfprivacy/logic/cubit/forms/user/ssh_form_cubit.dart'; -import 'package:selfprivacy/logic/models/job.dart'; -import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart'; -import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart'; -import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart'; -import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; - -import 'package:selfprivacy/config/brand_colors.dart'; -import 'package:selfprivacy/config/brand_theme.dart'; -import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart'; -import 'package:selfprivacy/logic/models/hive/user.dart'; -import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; -import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; - -part 'new_ssh_key.dart'; - -// Get user object as a parameter -class SshKeysPage extends StatefulWidget { - const SshKeysPage({required this.user, final super.key}); - final User user; - - @override - State createState() => _SshKeysPageState(); -} - -class _SshKeysPageState extends State { - @override - Widget build(final BuildContext context) => BrandHeroScreen( - heroTitle: 'ssh.title'.tr(), - heroSubtitle: widget.user.login, - heroIcon: BrandIcons.key, - children: [ - if (widget.user.login == 'root') - Column( - children: [ - // Show alert card if user is root - BrandCards.outlined( - child: ListTile( - leading: Icon( - Icons.warning_rounded, - color: Theme.of(context).colorScheme.error, - ), - title: Text('ssh.root.title'.tr()), - subtitle: Text('ssh.root.subtitle'.tr()), - ), - ) - ], - ), - BrandCards.outlined( - child: Column( - children: [ - ListTile( - title: Text( - 'ssh.create'.tr(), - style: Theme.of(context).textTheme.headline6, - ), - leading: const Icon(Icons.add_circle_outline_rounded), - onTap: () { - showModalBottomSheet( - context: context, - isScrollControlled: true, - backgroundColor: Colors.transparent, - builder: (final BuildContext context) => Padding( - padding: MediaQuery.of(context).viewInsets, - child: _NewSshKey(widget.user), - ), - ); - }, - ), - const Divider(height: 0), - // show a list of ListTiles with ssh keys - // Clicking on one should delete it - Column( - children: widget.user.sshKeys.map((final String key) { - final publicKey = - key.split(' ').length > 1 ? key.split(' ')[1] : key; - final keyType = key.split(' ')[0]; - final keyName = key.split(' ').length > 2 - ? key.split(' ')[2] - : 'ssh.no_key_name'.tr(); - return ListTile( - title: Text('$keyName ($keyType)'), - // do not overflow text - subtitle: Text( - publicKey, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - onTap: () { - showDialog( - context: context, - builder: (final BuildContext context) => AlertDialog( - title: Text('ssh.delete'.tr()), - content: SingleChildScrollView( - child: ListBody( - children: [ - Text('ssh.delete_confirm_question'.tr()), - Text('$keyName ($keyType)'), - Text(publicKey), - ], - ), - ), - actions: [ - TextButton( - child: Text('basis.cancel'.tr()), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - TextButton( - child: Text( - 'basis.delete'.tr(), - style: const TextStyle( - color: BrandColors.red1, - ), - ), - onPressed: () { - context.read().addJob( - DeleteSSHKeyJob( - user: widget.user, - publicKey: key, - ), - ); - Navigator.of(context) - ..pop() - ..pop(); - }, - ), - ], - ), - ); - }, - ); - }).toList(), - ) - ], - ), - ), - ], - ); -} diff --git a/lib/ui/pages/users/add_user_fab.dart b/lib/ui/pages/users/add_user_fab.dart deleted file mode 100644 index a78f056d..00000000 --- a/lib/ui/pages/users/add_user_fab.dart +++ /dev/null @@ -1,22 +0,0 @@ -part of 'users.dart'; - -class AddUserFab extends StatelessWidget { - const AddUserFab({final super.key}); - - @override - Widget build(final BuildContext context) => FloatingActionButton.small( - heroTag: 'new_user_fab', - onPressed: () { - showModalBottomSheet( - context: context, - isScrollControlled: true, - backgroundColor: Colors.transparent, - builder: (final BuildContext context) => Padding( - padding: MediaQuery.of(context).viewInsets, - child: const NewUser(), - ), - ); - }, - child: const Icon(Icons.person_add_outlined), - ); -} diff --git a/lib/ui/pages/users/empty.dart b/lib/ui/pages/users/empty.dart deleted file mode 100644 index 847003d3..00000000 --- a/lib/ui/pages/users/empty.dart +++ /dev/null @@ -1,33 +0,0 @@ -part of 'users.dart'; - -class _NoUsers extends StatelessWidget { - const _NoUsers({required this.text}); - - final String text; - - @override - Widget build(final BuildContext context) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Icon(BrandIcons.users, size: 50, color: BrandColors.grey7), - const SizedBox(height: 20), - BrandText.h2( - 'users.nobody_here'.tr(), - style: const TextStyle( - color: BrandColors.grey7, - ), - ), - const SizedBox(height: 10), - BrandText.medium( - text, - textAlign: TextAlign.center, - style: const TextStyle( - color: BrandColors.grey7, - ), - ), - ], - ), - ); -} diff --git a/lib/ui/pages/users/new_user.dart b/lib/ui/pages/users/new_user.dart index 72cb6387..9212307a 100644 --- a/lib/ui/pages/users/new_user.dart +++ b/lib/ui/pages/users/new_user.dart @@ -1,7 +1,8 @@ part of 'users.dart'; -class NewUser extends StatelessWidget { - const NewUser({final super.key}); +@RoutePage() +class NewUserPage extends StatelessWidget { + const NewUserPage({super.key}); @override Widget build(final BuildContext context) { @@ -10,100 +11,90 @@ class NewUser extends StatelessWidget { final String domainName = UiHelpers.getDomainName(config); - return BrandBottomSheet( - child: BlocProvider( - create: (final BuildContext context) { - final jobCubit = context.read(); - final jobState = jobCubit.state; - final users = []; - users.addAll(context.read().state.users); - if (jobState is JobsStateWithJobs) { - final jobs = jobState.jobList; - for (final job in jobs) { - if (job is CreateUserJob) { - users.add(job.user); - } + return BlocProvider( + create: (final BuildContext context) { + final jobCubit = context.read(); + final jobState = jobCubit.state; + final users = []; + users.addAll(context.read().state.users); + if (jobState is JobsStateWithJobs) { + final jobs = jobState.clientJobList; + for (final job in jobs) { + if (job is CreateUserJob) { + users.add(job.user); } } - return UserFormCubit( - jobsCubit: jobCubit, - fieldFactory: FieldCubitFactory(context), - ); - }, - child: Builder( - builder: (final BuildContext context) { - final FormCubitState formCubitState = - context.watch().state; + } + return UserFormCubit( + jobsCubit: jobCubit, + fieldFactory: FieldCubitFactory(context), + ); + }, + child: Builder( + builder: (final BuildContext context) { + final FormCubitState formCubitState = + context.watch().state; - return BlocListener( - listener: - (final BuildContext context, final FormCubitState state) { - if (state.isSubmitted) { - Navigator.pop(context); - } - }, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - BrandHeader( - title: 'users.new_user'.tr(), - ), - const SizedBox(width: 14), - Padding( - padding: paddingH15V0, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - IntrinsicHeight( - child: CubitFormTextField( - formFieldCubit: context.read().login, - decoration: InputDecoration( - labelText: 'users.login'.tr(), - suffixText: '@$domainName', - ), - ), - ), - const SizedBox(height: 20), - CubitFormTextField( - formFieldCubit: - context.read().password, - decoration: InputDecoration( - alignLabelWithHint: false, - labelText: 'basis.password'.tr(), - suffixIcon: Padding( - padding: const EdgeInsets.only(right: 8), - child: IconButton( - icon: Icon( - BrandIcons.refresh, - color: - Theme.of(context).colorScheme.secondary, - ), - onPressed: context - .read() - .genNewPassword, - ), - ), - ), - ), - const SizedBox(height: 30), - BrandButton.rised( - onPressed: formCubitState.isSubmitting - ? null - : () => context.read().trySubmit(), - text: 'basis.create'.tr(), - ), - const SizedBox(height: 40), - Text('users.new_user_info_note'.tr()), - const SizedBox(height: 30), - ], + return BlocListener( + listener: (final BuildContext context, final FormCubitState state) { + if (state.isSubmitted) { + context.router.pop(); + } + }, + child: BrandHeroScreen( + heroTitle: 'users.new_user'.tr(), + heroIcon: Icons.person_add_outlined, + children: [ + if (formCubitState.isErrorShown) + Text( + 'users.username_rule'.tr(), + style: TextStyle( + color: Theme.of(context).colorScheme.error, ), ), - ], - ), - ); - }, - ), + const SizedBox(width: 14), + IntrinsicHeight( + child: CubitFormTextField( + autofocus: true, + formFieldCubit: context.read().login, + decoration: InputDecoration( + labelText: 'users.login'.tr(), + suffixText: '@$domainName', + ), + ), + ), + const SizedBox(height: 20), + CubitFormTextField( + formFieldCubit: context.read().password, + decoration: InputDecoration( + alignLabelWithHint: false, + labelText: 'basis.password'.tr(), + suffixIcon: Padding( + padding: const EdgeInsets.only(right: 8), + child: IconButton( + icon: Icon( + BrandIcons.refresh, + color: Theme.of(context).colorScheme.secondary, + ), + onPressed: context.read().genNewPassword, + ), + ), + ), + ), + const SizedBox(height: 30), + BrandButton.rised( + onPressed: formCubitState.isSubmitting + ? null + : () => context.read().trySubmit(), + text: 'basis.create'.tr(), + ), + const SizedBox(height: 40), + Text('users.new_user_info_note'.tr()), + const SizedBox(height: 30), + ], + ), + ); + }, ), ); } diff --git a/lib/ui/pages/users/reset_password.dart b/lib/ui/pages/users/reset_password.dart new file mode 100644 index 00000000..12bb41ae --- /dev/null +++ b/lib/ui/pages/users/reset_password.dart @@ -0,0 +1,82 @@ +part of 'users.dart'; + +class ResetPassword extends StatelessWidget { + const ResetPassword({ + required this.user, + super.key, + }); + + final User user; + + @override + Widget build(final BuildContext context) => BlocProvider( + create: (final BuildContext context) => UserFormCubit( + jobsCubit: context.read(), + fieldFactory: FieldCubitFactory(context), + initialUser: user, + ), + child: Builder( + builder: (final BuildContext context) { + final FormCubitState formCubitState = + context.watch().state; + + return BlocListener( + listener: + (final BuildContext context, final FormCubitState state) { + if (state.isSubmitted) { + Navigator.pop(context); + } + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + BrandHeader( + title: 'users.reset_password'.tr(), + ), + const SizedBox(width: 14), + Padding( + padding: paddingH15V0, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + CubitFormTextField( + autofocus: true, + formFieldCubit: + context.read().password, + decoration: InputDecoration( + alignLabelWithHint: false, + labelText: 'basis.password'.tr(), + suffixIcon: Padding( + padding: const EdgeInsets.only(right: 8), + child: IconButton( + icon: Icon( + BrandIcons.refresh, + color: + Theme.of(context).colorScheme.secondary, + ), + onPressed: context + .read() + .genNewPassword, + ), + ), + ), + ), + const SizedBox(height: 30), + BrandButton.rised( + onPressed: formCubitState.isSubmitting + ? null + : () => context.read().trySubmit(), + text: 'basis.apply'.tr(), + ), + const SizedBox(height: 30), + ], + ), + ), + ], + ), + ); + }, + ), + ); +} diff --git a/lib/ui/pages/users/user.dart b/lib/ui/pages/users/user.dart index 69a2e5dc..9a90857b 100644 --- a/lib/ui/pages/users/user.dart +++ b/lib/ui/pages/users/user.dart @@ -3,19 +3,15 @@ part of 'users.dart'; class _User extends StatelessWidget { const _User({ required this.user, - required this.isRootUser, + required this.isPrimaryUser, }); final User user; - final bool isRootUser; + final bool isPrimaryUser; @override Widget build(final BuildContext context) => InkWell( onTap: () { - showBrandBottomSheet( - context: context, - builder: (final BuildContext context) => - _UserDetails(user: user, isRootUser: isRootUser), - ); + context.pushRoute(UserDetailsRoute(login: user.login)); }, child: Container( padding: paddingH15V0, @@ -32,17 +28,17 @@ class _User extends StatelessWidget { ), const SizedBox(width: 20), Flexible( - child: isRootUser - ? BrandText.h4Underlined(user.login) - // cross out text if user not found on server - : BrandText.h4( - user.login, - style: user.isFoundOnServer - ? null - : const TextStyle( - decoration: TextDecoration.lineThrough, - ), + child: Text( + user.login, + style: Theme.of(context).textTheme.titleMedium?.copyWith( + color: Theme.of(context).colorScheme.onBackground, + decoration: isPrimaryUser + ? TextDecoration.underline + : user.isFoundOnServer + ? TextDecoration.none + : TextDecoration.lineThrough, ), + ), ), ], ), diff --git a/lib/ui/pages/users/user_details.dart b/lib/ui/pages/users/user_details.dart index d758c1f4..90ab1603 100644 --- a/lib/ui/pages/users/user_details.dart +++ b/lib/ui/pages/users/user_details.dart @@ -1,13 +1,13 @@ part of 'users.dart'; -class _UserDetails extends StatelessWidget { - const _UserDetails({ - required this.user, - required this.isRootUser, +@RoutePage() +class UserDetailsPage extends StatelessWidget { + const UserDetailsPage({ + required this.login, + super.key, }); - final User user; - final bool isRootUser; + final String login; @override Widget build(final BuildContext context) { final ServerInstallationState config = @@ -15,179 +15,319 @@ class _UserDetails extends StatelessWidget { final String domainName = UiHelpers.getDomainName(config); - return BrandBottomSheet( - isExpended: true, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, + final User user = context.watch().state.users.firstWhere( + (final User user) => user.login == login, + orElse: () => const User( + type: UserType.normal, + login: 'error', + ), + ); + + if (user.type == UserType.root) { + return BrandHeroScreen( + hasBackButton: true, + hasFlashButton: true, + heroTitle: 'ssh.root_title'.tr(), + heroSubtitle: 'ssh.root_subtitle'.tr(), children: [ - Container( - height: 200, - decoration: BoxDecoration( - color: user.color, - borderRadius: const BorderRadius.vertical( - top: Radius.circular(20), + _SshKeysCard(user: user), + ], + ); + } + + return BrandHeroScreen( + hasBackButton: true, + hasFlashButton: true, + heroTitle: user.login, + children: [ + _UserLogins(user: user, domainName: domainName), + const SizedBox(height: 8), + _SshKeysCard(user: user), + const SizedBox(height: 8), + ListTile( + iconColor: Theme.of(context).colorScheme.onBackground, + onTap: () => { + showModalBottomSheet( + context: context, + isScrollControlled: true, + useRootNavigator: true, + builder: (final BuildContext context) => Padding( + padding: MediaQuery.of(context).viewInsets, + child: ResetPassword(user: user), ), ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - if (!isRootUser) - Align( - alignment: Alignment.centerRight, - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 4, - horizontal: 2, - ), - child: PopupMenuButton( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10.0), - ), - onSelected: (final PopupMenuItemType result) { - switch (result) { - case PopupMenuItemType.delete: - showDialog( - context: context, - builder: (final BuildContext context) => - AlertDialog( - title: Text('basis.confirmation'.tr()), - content: SingleChildScrollView( - child: ListBody( - children: [ - Text( - 'users.delete_confirm_question'.tr(), - ), - ], - ), - ), - actions: [ - TextButton( - child: Text('basis.cancel'.tr()), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - TextButton( - child: Text( - 'basis.delete'.tr(), - style: const TextStyle( - color: BrandColors.red1, - ), - ), - onPressed: () { - context - .read() - .addJob(DeleteUserJob(user: user)); - Navigator.of(context) - ..pop() - ..pop(); - }, - ), - ], - ), - ); - break; - } - }, - icon: const Icon(Icons.more_vert), - itemBuilder: (final BuildContext context) => [ - PopupMenuItem( - value: PopupMenuItemType.delete, - child: Container( - padding: const EdgeInsets.only(left: 5), - child: Text( - 'basis.delete'.tr(), - style: const TextStyle(color: BrandColors.red1), - ), - ), - ), - ], - ), + }, + leading: const Icon(Icons.lock_reset_outlined), + title: Text( + 'users.reset_password'.tr(), + ), + ), + if (user.type == UserType.normal) _DeleteUserTile(user: user), + const Divider(height: 8), + Padding( + padding: const EdgeInsets.all(16.0), + child: InfoBox( + text: 'users.no_ssh_notice'.tr(), + isWarning: true, + ), + ), + ], + ); + } +} + +class _DeleteUserTile extends StatelessWidget { + const _DeleteUserTile({ + required this.user, + }); + + final User user; + + @override + Widget build(final BuildContext context) => ListTile( + iconColor: Theme.of(context).colorScheme.error, + textColor: Theme.of(context).colorScheme.error, + onTap: () => { + showDialog( + context: context, + // useRootNavigator: false, + builder: (final BuildContext context) => AlertDialog( + title: Text('basis.confirmation'.tr()), + content: SingleChildScrollView( + child: ListBody( + children: [ + Text( + 'users.delete_confirm_question'.tr(), + ), + ], + ), + ), + actions: [ + TextButton( + child: Text('basis.cancel'.tr()), + onPressed: () { + context.router.pop(); + }, + ), + TextButton( + child: Text( + 'basis.delete'.tr(), + style: TextStyle( + color: Theme.of(context).colorScheme.error, ), ), - const Spacer(), - Padding( - padding: const EdgeInsets.symmetric( - vertical: 20, - horizontal: 15, - ), - child: AutoSizeText( - user.login, - style: headline1Style, - softWrap: true, - minFontSize: 9, - maxLines: 3, - overflow: TextOverflow.ellipsis, - ), - ), - ], - ), - ), - const SizedBox(height: 20), - Padding( - padding: paddingH15V0.copyWith(bottom: 20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - BrandText.small('users.account'.tr()), - Container( - height: 40, - alignment: Alignment.centerLeft, - child: BrandText.h4('${user.login}@$domainName'), - ), - if (user.password != null) - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 14), - BrandText.small('basis.password'.tr()), - Container( - height: 40, - alignment: Alignment.centerLeft, - child: BrandText.h4(user.password), - ), - ], - ), - const SizedBox(height: 24), - const BrandDivider(), - const SizedBox(height: 20), - ListTile( - onTap: () { - Navigator.of(context) - .push(materialRoute(SshKeysPage(user: user))); + onPressed: () { + context.read().addJob(DeleteUserJob(user: user)); + context.router.childControllers.first.pop(); + context.router.pop(); }, - title: Text('ssh.title'.tr()), - subtitle: user.sshKeys.isNotEmpty - ? Text( - 'ssh.subtitle_with_keys' - .tr(args: [user.sshKeys.length.toString()]), - ) - : Text('ssh.subtitle_without_keys'.tr()), - trailing: const Icon(BrandIcons.key), - ), - const SizedBox(height: 20), - ListTile( - onTap: () { - Share.share( - 'login: ${user.login}, password: ${user.password}', - ); - }, - title: Text( - 'users.send_registration_data'.tr(), - ), - trailing: const Icon(BrandIcons.share), ), ], ), ) + }, + leading: const Icon(Icons.person_remove_outlined), + title: Text( + 'users.delete_user'.tr(), + ), + ); +} + +class _UserLogins extends StatelessWidget { + const _UserLogins({ + required this.user, + required this.domainName, + }); + + final User user; + final String domainName; + + @override + Widget build(final BuildContext context) { + final email = '${user.login}@$domainName'; + return FilledCard( + child: Column( + children: [ + ListTileOnSurfaceVariant( + onTap: () { + PlatformAdapter.setClipboard(email); + getIt().showSnackBar( + 'basis.copied_to_clipboard'.tr(), + behavior: SnackBarBehavior.floating, + ); + }, + title: email, + subtitle: 'users.email_login'.tr(), + leadingIcon: Icons.alternate_email_outlined, + ), ], ), ); } } -enum PopupMenuItemType { - // reset, - delete, +class _SshKeysCard extends StatelessWidget { + const _SshKeysCard({ + required this.user, + }); + + final User user; + + @override + Widget build(final BuildContext context) => FilledCard( + child: Column( + children: [ + ListTileOnSurfaceVariant( + title: 'ssh.title'.tr(), + ), + const Divider(height: 0), + ListTileOnSurfaceVariant( + title: 'ssh.create'.tr(), + leadingIcon: Icons.add_circle_outline, + onTap: () { + showModalBottomSheet( + context: context, + isScrollControlled: true, + useRootNavigator: true, + builder: (final BuildContext context) => Padding( + padding: MediaQuery.of(context).viewInsets, + child: NewSshKey(user), + ), + ); + }, + ), + Column( + children: user.sshKeys.map((final String key) { + final publicKey = + key.split(' ').length > 1 ? key.split(' ')[1] : key; + final keyType = key.split(' ')[0]; + final keyName = key.split(' ').length > 2 + ? key.split(' ')[2] + : 'ssh.no_key_name'.tr(); + return ListTileOnSurfaceVariant( + title: '$keyName ($keyType)', + disableSubtitleOverflow: true, + // do not overflow text + subtitle: publicKey, + onTap: () { + showDialog( + context: context, + builder: (final BuildContext context) => AlertDialog( + title: Text('ssh.delete'.tr()), + content: SingleChildScrollView( + child: ListBody( + children: [ + Text('ssh.delete_confirm_question'.tr()), + Text('$keyName ($keyType)'), + Text(publicKey), + ], + ), + ), + actions: [ + TextButton( + child: Text('basis.cancel'.tr()), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: Text( + 'basis.delete'.tr(), + style: TextStyle( + color: Theme.of(context).colorScheme.error, + ), + ), + onPressed: () { + context.read().addJob( + DeleteSSHKeyJob( + user: user, + publicKey: key, + ), + ); + context.popRoute(); + }, + ), + ], + ), + ); + }, + ); + }).toList(), + ), + ], + ), + ); +} + +class NewSshKey extends StatelessWidget { + const NewSshKey(this.user, {super.key}); + final User user; + + @override + Widget build(final BuildContext context) => BlocProvider( + create: (final context) { + final jobCubit = context.read(); + final jobState = jobCubit.state; + if (jobState is JobsStateWithJobs) { + final jobs = jobState.clientJobList; + for (final job in jobs) { + if (job is CreateSSHKeyJob && job.user.login == user.login) { + user.sshKeys.add(job.publicKey); + } + } + } + return SshFormCubit( + jobsCubit: jobCubit, + user: user, + ); + }, + child: Builder( + builder: (final context) { + final formCubitState = context.watch().state; + + return BlocListener( + listener: (final context, final state) { + if (state.isSubmitted) { + Navigator.pop(context); + } + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + BrandHeader( + title: user.login, + ), + const SizedBox(width: 14), + Padding( + padding: paddingH15V0, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + IntrinsicHeight( + child: CubitFormTextField( + autofocus: true, + formFieldCubit: context.read().key, + decoration: InputDecoration( + labelText: 'ssh.input_label'.tr(), + ), + ), + ), + const SizedBox(height: 30), + BrandButton.rised( + onPressed: formCubitState.isSubmitting + ? null + : () => context.read().trySubmit(), + text: 'ssh.create'.tr(), + ), + const SizedBox(height: 30), + ], + ), + ), + ], + ), + ); + }, + ), + ); } diff --git a/lib/ui/pages/users/users.dart b/lib/ui/pages/users/users.dart index 659453d1..a3c0d319 100644 --- a/lib/ui/pages/users/users.dart +++ b/lib/ui/pages/users/users.dart @@ -1,39 +1,39 @@ -import 'package:auto_size_text/auto_size_text.dart'; +import 'package:auto_route/auto_route.dart'; import 'package:cubit_form/cubit_form.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:selfprivacy/config/brand_colors.dart'; import 'package:selfprivacy/config/brand_theme.dart'; -import 'package:selfprivacy/config/text_themes.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/cubit/forms/user/ssh_form_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart'; import 'package:selfprivacy/logic/cubit/forms/user/user_form_cubit.dart'; -import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart'; +import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart'; import 'package:selfprivacy/logic/cubit/users/users_cubit.dart'; import 'package:selfprivacy/logic/models/job.dart'; import 'package:selfprivacy/logic/models/hive/user.dart'; -import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart'; -import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; -import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart'; +import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; +import 'package:selfprivacy/ui/components/buttons/outlined_button.dart'; +import 'package:selfprivacy/ui/components/cards/filled_card.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; +import 'package:selfprivacy/ui/helpers/empty_page_placeholder.dart'; +import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; -import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; -import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart'; -import 'package:selfprivacy/ui/helpers/modals.dart'; -import 'package:selfprivacy/ui/pages/ssh_keys/ssh_keys.dart'; +import 'package:selfprivacy/ui/components/info_box/info_box.dart'; +import 'package:selfprivacy/ui/components/list_tiles/list_tile_on_surface_variant.dart'; +import 'package:selfprivacy/ui/router/router.dart'; +import 'package:selfprivacy/utils/breakpoints.dart'; +import 'package:selfprivacy/utils/platform_adapter.dart'; import 'package:selfprivacy/utils/ui_helpers.dart'; -import 'package:share_plus/share_plus.dart'; -import 'package:selfprivacy/utils/route_transitions/basic.dart'; - -part 'empty.dart'; part 'new_user.dart'; part 'user.dart'; part 'user_details.dart'; -part 'add_user_fab.dart'; +part 'reset_password.dart'; +@RoutePage() class UsersPage extends StatelessWidget { - const UsersPage({final super.key}); + const UsersPage({super.key}); @override Widget build(final BuildContext context) { @@ -42,25 +42,84 @@ class UsersPage extends StatelessWidget { Widget child; if (!isReady) { - child = isNotReady(); + child = EmptyPagePlaceholder( + showReadyCard: true, + title: 'users.nobody_here'.tr(), + description: 'basis.please_connect'.tr(), + iconData: BrandIcons.users, + ); } else { child = BlocBuilder( builder: (final BuildContext context, final UsersState state) { - print('Rebuild users page'); - final primaryUser = state.primaryUser; - final users = [primaryUser, ...state.users]; - + final users = state.orderedUsers; + if (users.isEmpty) { + if (state.isLoading) { + return const Center( + child: CircularProgressIndicator(), + ); + } + return RefreshIndicator( + onRefresh: () async { + await context.read().refresh(); + }, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + EmptyPagePlaceholder( + title: 'users.could_not_fetch_users'.tr(), + description: 'users.could_not_fetch_description'.tr(), + iconData: BrandIcons.users, + ), + const SizedBox(height: 18), + BrandOutlinedButton( + onPressed: () { + context.read().refresh(); + }, + title: 'users.refresh_users'.tr(), + ), + ], + ), + ), + ), + ); + } return RefreshIndicator( onRefresh: () async { - context.read().refresh(); + await context.read().refresh(); }, - child: ListView.builder( - itemCount: users.length, - itemBuilder: (final BuildContext context, final int index) => - _User( - user: users[index], - isRootUser: index == 0, - ), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: FilledButton.tonal( + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(Icons.person_add_outlined), + const SizedBox(width: 8), + Text('users.new_user'.tr()), + ], + ), + onPressed: () { + context.pushRoute(const NewUserRoute()); + }, + ), + ), + Expanded( + child: ListView.builder( + itemCount: users.length, + itemBuilder: + (final BuildContext context, final int index) => _User( + user: users[index], + isPrimaryUser: users[index].type == UserType.primary, + ), + ), + ), + ], ), ); }, @@ -68,33 +127,15 @@ class UsersPage extends StatelessWidget { } return Scaffold( - appBar: PreferredSize( - preferredSize: const Size.fromHeight(52), - child: BrandHeader( - title: 'basis.users'.tr(), - ), - ), + appBar: Breakpoints.small.isActive(context) + ? PreferredSize( + preferredSize: const Size.fromHeight(52), + child: BrandHeader( + title: 'basis.users'.tr(), + ), + ) + : null, body: child, ); } - - Widget isNotReady() => Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const Padding( - padding: EdgeInsets.symmetric(horizontal: 15), - child: NotReadyCard(), - ), - Expanded( - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: Center( - child: _NoUsers( - text: 'users.not_ready'.tr(), - ), - ), - ), - ) - ], - ); } diff --git a/lib/ui/router/root_destinations.dart b/lib/ui/router/root_destinations.dart new file mode 100644 index 00000000..0d981894 --- /dev/null +++ b/lib/ui/router/root_destinations.dart @@ -0,0 +1,46 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; +import 'package:selfprivacy/ui/router/router.dart'; + +class RouteDestination { + const RouteDestination({ + required this.route, + required this.icon, + required this.label, + required this.title, + }); + + final PageRouteInfo route; + final IconData icon; + final String label; + final String title; +} + +final rootDestinations = [ + RouteDestination( + route: const ProvidersRoute(), + icon: BrandIcons.server, + label: 'basis.providers'.tr(), + title: 'basis.providers_title'.tr(), + ), + RouteDestination( + route: const ServicesRoute(), + icon: BrandIcons.box, + label: 'basis.services'.tr(), + title: 'basis.services'.tr(), + ), + RouteDestination( + route: const UsersRoute(), + icon: BrandIcons.users, + label: 'basis.users'.tr(), + title: 'basis.users'.tr(), + ), + RouteDestination( + route: const MoreRoute(), + icon: Icons.menu_rounded, + label: 'basis.more'.tr(), + title: 'basis.more'.tr(), + ), +]; diff --git a/lib/ui/router/router.dart b/lib/ui/router/router.dart new file mode 100644 index 00000000..764aacd7 --- /dev/null +++ b/lib/ui/router/router.dart @@ -0,0 +1,155 @@ +import 'package:animations/animations.dart'; +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:selfprivacy/logic/models/disk_status.dart'; +import 'package:selfprivacy/logic/models/service.dart'; +import 'package:selfprivacy/ui/pages/backups/backup_details.dart'; +import 'package:selfprivacy/ui/pages/backups/backups_list.dart'; +import 'package:selfprivacy/ui/pages/devices/devices.dart'; +import 'package:selfprivacy/ui/pages/dns_details/dns_details.dart'; +import 'package:selfprivacy/ui/pages/more/about_application.dart'; +import 'package:selfprivacy/ui/pages/more/app_settings/app_settings.dart'; +import 'package:selfprivacy/ui/pages/more/app_settings/developer_settings.dart'; +import 'package:selfprivacy/ui/pages/more/console.dart'; +import 'package:selfprivacy/ui/pages/more/more.dart'; +import 'package:selfprivacy/ui/pages/onboarding/onboarding.dart'; +import 'package:selfprivacy/ui/pages/providers/providers.dart'; +import 'package:selfprivacy/ui/pages/recovery_key/recovery_key.dart'; +import 'package:selfprivacy/ui/pages/root_route.dart'; +import 'package:selfprivacy/ui/pages/server_details/server_details_screen.dart'; +import 'package:selfprivacy/ui/pages/server_storage/binds_migration/services_migration.dart'; +import 'package:selfprivacy/ui/pages/server_storage/extending_volume.dart'; +import 'package:selfprivacy/ui/pages/server_storage/server_storage.dart'; +import 'package:selfprivacy/ui/pages/services/service_page.dart'; +import 'package:selfprivacy/ui/pages/services/services.dart'; +import 'package:selfprivacy/ui/pages/setup/initializing/initializing.dart'; +import 'package:selfprivacy/ui/pages/setup/recovering/recovery_routing.dart'; +import 'package:selfprivacy/ui/pages/users/users.dart'; + +part 'router.gr.dart'; + +Widget fadeThroughTransition( + final BuildContext context, + final Animation animation, + final Animation secondaryAnimation, + final Widget child, +) => + SharedAxisTransition( + key: UniqueKey(), + animation: animation, + secondaryAnimation: secondaryAnimation, + transitionType: SharedAxisTransitionType.vertical, + child: child, + ); + +@AutoRouterConfig( + // transitionsBuilder: fadeThroughTransition, + replaceInRouteName: 'Page|Screen|Routing,Route', +) +class RootRouter extends _$RootRouter { + RootRouter(final GlobalKey navigatorKey) + : super(navigatorKey: navigatorKey); + + @override + RouteType get defaultRouteType => const RouteType.material(); + @override + final List routes = [ + AutoRoute(page: OnboardingRoute.page), + AutoRoute(page: InitializingRoute.page), + AutoRoute(page: RecoveryRoute.page), + AutoRoute( + page: RootRoute.page, + path: '/', + children: [ + CustomRoute( + page: ProvidersRoute.page, + usesPathAsKey: true, + path: '', + transitionsBuilder: fadeThroughTransition, + durationInMilliseconds: 400, + ), + CustomRoute( + page: ServicesRoute.page, + usesPathAsKey: true, + transitionsBuilder: fadeThroughTransition, + durationInMilliseconds: 400, + ), + CustomRoute( + page: UsersRoute.page, + usesPathAsKey: true, + transitionsBuilder: fadeThroughTransition, + durationInMilliseconds: 400, + ), + CustomRoute( + page: MoreRoute.page, + usesPathAsKey: true, + transitionsBuilder: fadeThroughTransition, + durationInMilliseconds: 400, + ), + AutoRoute(page: AppSettingsRoute.page), + AutoRoute(page: UserDetailsRoute.page), + AutoRoute(page: NewUserRoute.page), + AutoRoute(page: RecoveryKeyRoute.page), + AutoRoute(page: DevicesRoute.page), + AutoRoute(page: AboutApplicationRoute.page), + AutoRoute(page: DeveloperSettingsRoute.page), + AutoRoute(page: ServiceRoute.page), + AutoRoute(page: ServerDetailsRoute.page), + AutoRoute(page: DnsDetailsRoute.page), + AutoRoute(page: BackupDetailsRoute.page), + AutoRoute(page: BackupsListRoute.page), + AutoRoute(page: ServerStorageRoute.page), + AutoRoute(page: ExtendingVolumeRoute.page), + ], + ), + AutoRoute(page: ServicesMigrationRoute.page), + AutoRoute(page: ConsoleRoute.page), + ]; +} + +// Function to map route names to route titles +String getRouteTitle(final String routeName) { + switch (routeName) { + case 'RootRoute': + return 'basis.app_name'; + case 'ProvidersRoute': + return 'basis.providers_title'; + case 'ServicesRoute': + case 'ServiceRoute': + return 'basis.services'; + case 'UsersRoute': + return 'basis.users'; + case 'MoreRoute': + return 'basis.more'; + case 'AppSettingsRoute': + return 'application_settings.title'; + case 'UserDetailsRoute': + return 'users.details_title'; + case 'NewUserRoute': + return 'users.new_user'; + case 'RecoveryKeyRoute': + return 'recovery_key.key_main_header'; + case 'DevicesRoute': + return 'devices.main_screen.header'; + case 'AboutApplicationRoute': + return 'about_us_page.title'; + case 'ConsoleRoute': + return 'console_page.title'; + case 'DeveloperSettingsRoute': + return 'developer_settings.title'; + case 'DnsDetailsRoute': + return 'domain.screen_title'; + case 'ServerDetailsRoute': + return 'server.card_title'; + case 'BackupDetailsRoute': + return 'backup.card_title'; + case 'BackupsListRoute': + return 'backup.snapshots_title'; + case 'ServerStorageRoute': + return 'storage.card_title'; + case 'ExtendingVolumeRoute': + return 'storage.extending_volume_title'; + default: + return routeName; + } +} diff --git a/lib/ui/router/router.gr.dart b/lib/ui/router/router.gr.dart new file mode 100644 index 00000000..a9d401f3 --- /dev/null +++ b/lib/ui/router/router.gr.dart @@ -0,0 +1,685 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ************************************************************************** +// AutoRouterGenerator +// ************************************************************************** + +// ignore_for_file: type=lint +// coverage:ignore-file + +part of 'router.dart'; + +abstract class _$RootRouter extends RootStackRouter { + // ignore: unused_element + _$RootRouter({super.navigatorKey}); + + @override + final Map pagesMap = { + BackupsListRoute.name: (routeData) { + final args = routeData.argsAs(); + return AutoRoutePage( + routeData: routeData, + child: BackupsListPage( + service: args.service, + key: args.key, + ), + ); + }, + BackupDetailsRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const BackupDetailsPage(), + ); + }, + DevicesRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const DevicesScreen(), + ); + }, + DnsDetailsRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const DnsDetailsPage(), + ); + }, + AboutApplicationRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const AboutApplicationPage(), + ); + }, + AppSettingsRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const AppSettingsPage(), + ); + }, + DeveloperSettingsRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const DeveloperSettingsPage(), + ); + }, + ConsoleRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const ConsolePage(), + ); + }, + MoreRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const MorePage(), + ); + }, + OnboardingRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const OnboardingPage(), + ); + }, + ProvidersRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const ProvidersPage(), + ); + }, + RecoveryKeyRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const RecoveryKeyPage(), + ); + }, + RootRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: WrappedRoute(child: const RootPage()), + ); + }, + ServerDetailsRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const ServerDetailsScreen(), + ); + }, + ServicesMigrationRoute.name: (routeData) { + final args = routeData.argsAs(); + return AutoRoutePage( + routeData: routeData, + child: ServicesMigrationPage( + services: args.services, + diskStatus: args.diskStatus, + isMigration: args.isMigration, + key: args.key, + ), + ); + }, + ServerStorageRoute.name: (routeData) { + final args = routeData.argsAs(); + return AutoRoutePage( + routeData: routeData, + child: ServerStoragePage( + diskStatus: args.diskStatus, + key: args.key, + ), + ); + }, + ExtendingVolumeRoute.name: (routeData) { + final args = routeData.argsAs(); + return AutoRoutePage( + routeData: routeData, + child: ExtendingVolumePage( + diskVolumeToResize: args.diskVolumeToResize, + diskStatus: args.diskStatus, + key: args.key, + ), + ); + }, + ServiceRoute.name: (routeData) { + final args = routeData.argsAs(); + return AutoRoutePage( + routeData: routeData, + child: ServicePage( + serviceId: args.serviceId, + key: args.key, + ), + ); + }, + ServicesRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const ServicesPage(), + ); + }, + InitializingRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const InitializingPage(), + ); + }, + RecoveryRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const RecoveryRouting(), + ); + }, + UsersRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const UsersPage(), + ); + }, + NewUserRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const NewUserPage(), + ); + }, + UserDetailsRoute.name: (routeData) { + final args = routeData.argsAs(); + return AutoRoutePage( + routeData: routeData, + child: UserDetailsPage( + login: args.login, + key: args.key, + ), + ); + }, + }; +} + +/// generated route for +/// [BackupsListPage] +class BackupsListRoute extends PageRouteInfo { + BackupsListRoute({ + required Service? service, + Key? key, + List? children, + }) : super( + BackupsListRoute.name, + args: BackupsListRouteArgs( + service: service, + key: key, + ), + initialChildren: children, + ); + + static const String name = 'BackupsListRoute'; + + static const PageInfo page = + PageInfo(name); +} + +class BackupsListRouteArgs { + const BackupsListRouteArgs({ + required this.service, + this.key, + }); + + final Service? service; + + final Key? key; + + @override + String toString() { + return 'BackupsListRouteArgs{service: $service, key: $key}'; + } +} + +/// generated route for +/// [BackupDetailsPage] +class BackupDetailsRoute extends PageRouteInfo { + const BackupDetailsRoute({List? children}) + : super( + BackupDetailsRoute.name, + initialChildren: children, + ); + + static const String name = 'BackupDetailsRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [DevicesScreen] +class DevicesRoute extends PageRouteInfo { + const DevicesRoute({List? children}) + : super( + DevicesRoute.name, + initialChildren: children, + ); + + static const String name = 'DevicesRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [DnsDetailsPage] +class DnsDetailsRoute extends PageRouteInfo { + const DnsDetailsRoute({List? children}) + : super( + DnsDetailsRoute.name, + initialChildren: children, + ); + + static const String name = 'DnsDetailsRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [AboutApplicationPage] +class AboutApplicationRoute extends PageRouteInfo { + const AboutApplicationRoute({List? children}) + : super( + AboutApplicationRoute.name, + initialChildren: children, + ); + + static const String name = 'AboutApplicationRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [AppSettingsPage] +class AppSettingsRoute extends PageRouteInfo { + const AppSettingsRoute({List? children}) + : super( + AppSettingsRoute.name, + initialChildren: children, + ); + + static const String name = 'AppSettingsRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [DeveloperSettingsPage] +class DeveloperSettingsRoute extends PageRouteInfo { + const DeveloperSettingsRoute({List? children}) + : super( + DeveloperSettingsRoute.name, + initialChildren: children, + ); + + static const String name = 'DeveloperSettingsRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [ConsolePage] +class ConsoleRoute extends PageRouteInfo { + const ConsoleRoute({List? children}) + : super( + ConsoleRoute.name, + initialChildren: children, + ); + + static const String name = 'ConsoleRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [MorePage] +class MoreRoute extends PageRouteInfo { + const MoreRoute({List? children}) + : super( + MoreRoute.name, + initialChildren: children, + ); + + static const String name = 'MoreRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [OnboardingPage] +class OnboardingRoute extends PageRouteInfo { + const OnboardingRoute({List? children}) + : super( + OnboardingRoute.name, + initialChildren: children, + ); + + static const String name = 'OnboardingRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [ProvidersPage] +class ProvidersRoute extends PageRouteInfo { + const ProvidersRoute({List? children}) + : super( + ProvidersRoute.name, + initialChildren: children, + ); + + static const String name = 'ProvidersRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [RecoveryKeyPage] +class RecoveryKeyRoute extends PageRouteInfo { + const RecoveryKeyRoute({List? children}) + : super( + RecoveryKeyRoute.name, + initialChildren: children, + ); + + static const String name = 'RecoveryKeyRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [RootPage] +class RootRoute extends PageRouteInfo { + const RootRoute({List? children}) + : super( + RootRoute.name, + initialChildren: children, + ); + + static const String name = 'RootRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [ServerDetailsScreen] +class ServerDetailsRoute extends PageRouteInfo { + const ServerDetailsRoute({List? children}) + : super( + ServerDetailsRoute.name, + initialChildren: children, + ); + + static const String name = 'ServerDetailsRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [ServicesMigrationPage] +class ServicesMigrationRoute extends PageRouteInfo { + ServicesMigrationRoute({ + required List services, + required DiskStatus diskStatus, + required bool isMigration, + Key? key, + List? children, + }) : super( + ServicesMigrationRoute.name, + args: ServicesMigrationRouteArgs( + services: services, + diskStatus: diskStatus, + isMigration: isMigration, + key: key, + ), + initialChildren: children, + ); + + static const String name = 'ServicesMigrationRoute'; + + static const PageInfo page = + PageInfo(name); +} + +class ServicesMigrationRouteArgs { + const ServicesMigrationRouteArgs({ + required this.services, + required this.diskStatus, + required this.isMigration, + this.key, + }); + + final List services; + + final DiskStatus diskStatus; + + final bool isMigration; + + final Key? key; + + @override + String toString() { + return 'ServicesMigrationRouteArgs{services: $services, diskStatus: $diskStatus, isMigration: $isMigration, key: $key}'; + } +} + +/// generated route for +/// [ServerStoragePage] +class ServerStorageRoute extends PageRouteInfo { + ServerStorageRoute({ + required DiskStatus diskStatus, + Key? key, + List? children, + }) : super( + ServerStorageRoute.name, + args: ServerStorageRouteArgs( + diskStatus: diskStatus, + key: key, + ), + initialChildren: children, + ); + + static const String name = 'ServerStorageRoute'; + + static const PageInfo page = + PageInfo(name); +} + +class ServerStorageRouteArgs { + const ServerStorageRouteArgs({ + required this.diskStatus, + this.key, + }); + + final DiskStatus diskStatus; + + final Key? key; + + @override + String toString() { + return 'ServerStorageRouteArgs{diskStatus: $diskStatus, key: $key}'; + } +} + +/// generated route for +/// [ExtendingVolumePage] +class ExtendingVolumeRoute extends PageRouteInfo { + ExtendingVolumeRoute({ + required DiskVolume diskVolumeToResize, + required DiskStatus diskStatus, + Key? key, + List? children, + }) : super( + ExtendingVolumeRoute.name, + args: ExtendingVolumeRouteArgs( + diskVolumeToResize: diskVolumeToResize, + diskStatus: diskStatus, + key: key, + ), + initialChildren: children, + ); + + static const String name = 'ExtendingVolumeRoute'; + + static const PageInfo page = + PageInfo(name); +} + +class ExtendingVolumeRouteArgs { + const ExtendingVolumeRouteArgs({ + required this.diskVolumeToResize, + required this.diskStatus, + this.key, + }); + + final DiskVolume diskVolumeToResize; + + final DiskStatus diskStatus; + + final Key? key; + + @override + String toString() { + return 'ExtendingVolumeRouteArgs{diskVolumeToResize: $diskVolumeToResize, diskStatus: $diskStatus, key: $key}'; + } +} + +/// generated route for +/// [ServicePage] +class ServiceRoute extends PageRouteInfo { + ServiceRoute({ + required String serviceId, + Key? key, + List? children, + }) : super( + ServiceRoute.name, + args: ServiceRouteArgs( + serviceId: serviceId, + key: key, + ), + initialChildren: children, + ); + + static const String name = 'ServiceRoute'; + + static const PageInfo page = + PageInfo(name); +} + +class ServiceRouteArgs { + const ServiceRouteArgs({ + required this.serviceId, + this.key, + }); + + final String serviceId; + + final Key? key; + + @override + String toString() { + return 'ServiceRouteArgs{serviceId: $serviceId, key: $key}'; + } +} + +/// generated route for +/// [ServicesPage] +class ServicesRoute extends PageRouteInfo { + const ServicesRoute({List? children}) + : super( + ServicesRoute.name, + initialChildren: children, + ); + + static const String name = 'ServicesRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [InitializingPage] +class InitializingRoute extends PageRouteInfo { + const InitializingRoute({List? children}) + : super( + InitializingRoute.name, + initialChildren: children, + ); + + static const String name = 'InitializingRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [RecoveryRouting] +class RecoveryRoute extends PageRouteInfo { + const RecoveryRoute({List? children}) + : super( + RecoveryRoute.name, + initialChildren: children, + ); + + static const String name = 'RecoveryRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [UsersPage] +class UsersRoute extends PageRouteInfo { + const UsersRoute({List? children}) + : super( + UsersRoute.name, + initialChildren: children, + ); + + static const String name = 'UsersRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [NewUserPage] +class NewUserRoute extends PageRouteInfo { + const NewUserRoute({List? children}) + : super( + NewUserRoute.name, + initialChildren: children, + ); + + static const String name = 'NewUserRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [UserDetailsPage] +class UserDetailsRoute extends PageRouteInfo { + UserDetailsRoute({ + required String login, + Key? key, + List? children, + }) : super( + UserDetailsRoute.name, + args: UserDetailsRouteArgs( + login: login, + key: key, + ), + initialChildren: children, + ); + + static const String name = 'UserDetailsRoute'; + + static const PageInfo page = + PageInfo(name); +} + +class UserDetailsRouteArgs { + const UserDetailsRouteArgs({ + required this.login, + this.key, + }); + + final String login; + + final Key? key; + + @override + String toString() { + return 'UserDetailsRouteArgs{login: $login, key: $key}'; + } +} diff --git a/lib/utils/breakpoints.dart b/lib/utils/breakpoints.dart new file mode 100644 index 00000000..0e9104e2 --- /dev/null +++ b/lib/utils/breakpoints.dart @@ -0,0 +1,131 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; + +const Set _desktop = { + TargetPlatform.linux, + TargetPlatform.macOS, + TargetPlatform.windows +}; + +const Set _mobile = { + TargetPlatform.android, + TargetPlatform.fuchsia, + TargetPlatform.iOS, +}; + +/// A group of standard breakpoints built according to the material +/// specifications for screen width size. +/// +/// See also: +/// +/// * [AdaptiveScaffold], which uses some of these Breakpoints as defaults. +class Breakpoints { + /// This is a standard breakpoint that can be used as a fallthrough in the + /// case that no other breakpoint is active. + /// + /// It is active from a width of -1 dp to infinity. + static const Breakpoint standard = WidthPlatformBreakpoint(begin: -1); + + /// A window whose width is less than 600 dp and greater than 0 dp. + static const Breakpoint small = WidthPlatformBreakpoint(begin: 0, end: 600); + + /// A window whose width is greater than 0 dp. + static const Breakpoint smallAndUp = WidthPlatformBreakpoint(begin: 0); + + /// A desktop screen whose width is less than 600 dp and greater than 0 dp. + static const Breakpoint smallDesktop = + WidthPlatformBreakpoint(begin: 0, end: 600, platform: _desktop); + + /// A mobile screen whose width is less than 600 dp and greater than 0 dp. + static const Breakpoint smallMobile = + WidthPlatformBreakpoint(begin: 0, end: 600, platform: _mobile); + + /// A window whose width is between 600 dp and 840 dp. + static const Breakpoint medium = + WidthPlatformBreakpoint(begin: 600, end: 840); + + /// A window whose width is greater than 600 dp. + static const Breakpoint mediumAndUp = WidthPlatformBreakpoint(begin: 600); + + /// A desktop window whose width is between 600 dp and 840 dp. + static const Breakpoint mediumDesktop = + WidthPlatformBreakpoint(begin: 600, end: 840, platform: _desktop); + + /// A mobile window whose width is between 600 dp and 840 dp. + static const Breakpoint mediumMobile = + WidthPlatformBreakpoint(begin: 600, end: 840, platform: _mobile); + + /// A window whose width is greater than 840 dp. + static const Breakpoint large = WidthPlatformBreakpoint(begin: 840); + + /// A desktop window whose width is greater than 840 dp. + static const Breakpoint largeDesktop = + WidthPlatformBreakpoint(begin: 840, platform: _desktop); + + /// A mobile window whose width is greater than 840 dp. + static const Breakpoint largeMobile = + WidthPlatformBreakpoint(begin: 840, platform: _mobile); +} + +/// A class that can be used to quickly generate [Breakpoint]s that depend on +/// the screen width and the platform. +class WidthPlatformBreakpoint extends Breakpoint { + /// Returns a const [Breakpoint] with the given constraints. + const WidthPlatformBreakpoint({this.begin, this.end, this.platform}); + + /// The beginning width dp value. If left null then the [Breakpoint] will have + /// no lower bound. + final double? begin; + + /// The end width dp value. If left null then the [Breakpoint] will have no + /// upper bound. + final double? end; + + /// A Set of [TargetPlatform]s that the [Breakpoint] will be active on. If + /// left null then it will be active on all platforms. + final Set? platform; + + @override + bool isActive(final BuildContext context) { + final TargetPlatform host = Theme.of(context).platform; + final bool isRightPlatform = platform?.contains(host) ?? true; + + // Null boundaries are unbounded, assign the max/min of their associated + // direction on a number line. + final double width = MediaQuery.of(context).size.width; + final double lowerBound = begin ?? double.negativeInfinity; + final double upperBound = end ?? double.infinity; + + return width >= lowerBound && width < upperBound && isRightPlatform; + } +} + +/// An interface to define the conditions that distinguish between types of +/// screens. +/// +/// Adaptive apps usually display differently depending on the screen type: a +/// compact layout for smaller screens, or a relaxed layout for larger screens. +/// Override this class by defining `isActive` to fetch the screen property +/// (usually `MediaQuery.of`) and return true if the condition is met. +/// +/// Breakpoints do not need to be exclusive because they are tested in order +/// with the last Breakpoint active taking priority. +/// +/// If the condition is only based on the screen width and/or the device type, +/// use [WidthPlatformBreakpoint] to define the [Breakpoint]. +/// +/// See also: +/// +/// * [SlotLayout.config], which uses breakpoints to dictate the layout of the +/// screen. +abstract class Breakpoint { + /// Returns a const [Breakpoint]. + const Breakpoint(); + + /// A method that returns true based on conditions related to the context of + /// the screen such as MediaQuery.of(context).size.width. + bool isActive(final BuildContext context); +} diff --git a/lib/utils/extensions/duration.dart b/lib/utils/extensions/duration.dart index 2c302fb8..0e20b07a 100644 --- a/lib/utils/extensions/duration.dart +++ b/lib/utils/extensions/duration.dart @@ -1,13 +1,12 @@ // ignore_for_file: unnecessary_this -extension DurationFormatter on Duration { - String toDayHourMinuteSecondFormat() => [ - this.inHours.remainder(24), - this.inMinutes.remainder(60), - this.inSeconds.remainder(60) - ].map((final int seg) => seg.toString().padLeft(2, '0')).join(':'); +import 'dart:ui'; - String toDayHourMinuteFormat() { +import 'package:duration/duration.dart'; +import 'package:duration/locale.dart'; + +extension DurationFormatter on Duration { + String toTimezoneOffsetFormat() { final designator = this >= Duration.zero ? '+' : '-'; final Iterable segments = [ @@ -18,15 +17,10 @@ extension DurationFormatter on Duration { return '$designator${segments.first}:${segments.last}'; } -// WAT: https://flutterigniter.com/how-to-format-duration/ - String toHoursMinutesSecondsFormat() => - this.toString().split('.').first.padLeft(8, '0'); - - String toDayHourMinuteFormat2() { - final Iterable segments = [ - this.inHours.remainder(24), - this.inMinutes.remainder(60), - ].map((final int seg) => seg.toString().padLeft(2, '0')); - return '${segments.first} h ${segments.last} min'; - } + String toPrettyString(final Locale locale) => + prettyDuration(this, locale: getDurationLocale(locale)); } + +DurationLocale getDurationLocale(final Locale locale) => + DurationLocale.fromLanguageCode(locale.languageCode) ?? + const EnglishDurationLocale(); diff --git a/lib/utils/extensions/elevation_extension.dart b/lib/utils/extensions/elevation_extension.dart deleted file mode 100644 index 9c6bbc14..00000000 --- a/lib/utils/extensions/elevation_extension.dart +++ /dev/null @@ -1,32 +0,0 @@ -library elevation_extension; - -import 'package:flutter/material.dart'; -import 'package:flutter/cupertino.dart'; - -extension ElevationExtension on BoxDecoration { - BoxDecoration copyWith({ - final Color? color, - final DecorationImage? image, - final BoxBorder? border, - final BorderRadiusGeometry? borderRadius, - final List? boxShadow, - final Gradient? gradient, - final BlendMode? backgroundBlendMode, - final BoxShape? shape, - }) => - BoxDecoration( - color: color ?? this.color, - image: image ?? this.image, - border: border ?? this.border, - borderRadius: borderRadius ?? this.borderRadius, - boxShadow: this.boxShadow != null || boxShadow != null - ? [ - ...this.boxShadow ?? [], - ...boxShadow ?? [] - ] - : null, - gradient: gradient ?? this.gradient, - backgroundBlendMode: backgroundBlendMode ?? this.backgroundBlendMode, - shape: shape ?? this.shape, - ); -} diff --git a/lib/utils/extensions/string_extensions.dart b/lib/utils/extensions/string_extensions.dart new file mode 100644 index 00000000..91cb543b --- /dev/null +++ b/lib/utils/extensions/string_extensions.dart @@ -0,0 +1,4 @@ +extension StringExtension on String { + String capitalize() => + '${this[0].toUpperCase()}${substring(1).toLowerCase()}'; +} diff --git a/lib/utils/extensions/text_extensions.dart b/lib/utils/extensions/text_extensions.dart index bfacc600..a00f7096 100644 --- a/lib/utils/extensions/text_extensions.dart +++ b/lib/utils/extensions/text_extensions.dart @@ -1,4 +1,4 @@ -import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; extension TextExtension on Text { Text withColor(final Color color) => Text( diff --git a/lib/utils/launch_url.dart b/lib/utils/launch_url.dart new file mode 100644 index 00000000..d7ccd0fa --- /dev/null +++ b/lib/utils/launch_url.dart @@ -0,0 +1,13 @@ +import 'package:url_launcher/url_launcher.dart'; + +void launchURL(final url) async { + try { + final Uri uri = Uri.parse(url); + await launchUrl( + uri, + mode: LaunchMode.externalApplication, + ); + } catch (e) { + print(e); + } +} diff --git a/lib/utils/network_utils.dart b/lib/utils/network_utils.dart new file mode 100644 index 00000000..a94ecb35 --- /dev/null +++ b/lib/utils/network_utils.dart @@ -0,0 +1,43 @@ +import 'package:selfprivacy/logic/models/json/dns_records.dart'; +import 'package:url_launcher/url_launcher.dart'; + +DnsRecord? extractDkimRecord(final List records) { + DnsRecord? dkimRecord; + + for (final DnsRecord record in records) { + if (record.type == 'TXT' && record.name == 'selector._domainkey') { + dkimRecord = record; + } + } + + return dkimRecord; +} + +String getHostnameFromDomain(final String domain) { + // Replace all non-alphanumeric characters with an underscore + String hostname = + domain.split('.')[0].replaceAll(RegExp(r'[^a-zA-Z0-9]'), '-'); + if (hostname.endsWith('-')) { + hostname = hostname.substring(0, hostname.length - 1); + } + if (hostname.startsWith('-')) { + hostname = hostname.substring(1); + } + if (hostname.isEmpty) { + hostname = 'selfprivacy-server'; + } + + return hostname; +} + +void launchURL(final url) async { + try { + final Uri uri = Uri.parse(url); + await launchUrl( + uri, + mode: LaunchMode.externalApplication, + ); + } catch (e) { + print(e); + } +} diff --git a/lib/utils/password_generator.dart b/lib/utils/password_generator.dart index 5acf3888..a940bb19 100644 --- a/lib/utils/password_generator.dart +++ b/lib/utils/password_generator.dart @@ -101,10 +101,10 @@ class StringGenerators { hasSymbols: true, ); - static StringGeneratorFunction dbStorageName = () => getRandomString( + static StringGeneratorFunction storageName = () => getRandomString( 6, hasLowercaseLetters: true, - hasUppercaseLetters: true, + hasUppercaseLetters: false, hasNumbers: true, ); diff --git a/lib/utils/platform_adapter.dart b/lib/utils/platform_adapter.dart new file mode 100644 index 00000000..7e010dd4 --- /dev/null +++ b/lib/utils/platform_adapter.dart @@ -0,0 +1,64 @@ +import 'dart:io'; + +import 'package:device_info_plus/device_info_plus.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +/// SelfPrivacy wrapper for Platform information provider. +class PlatformAdapter { + /// Persistent storage directory for data files. + static String? get storagePath { + String? path; + if (Platform.isLinux) { + // https://wiki.archlinux.org/title/XDG_Base_Directory + path = Platform.environment['XDG_DATA_HOME']; + if (path == null) { + final String home = Platform.environment['HOME'] ?? '.'; + path = '$home/.local/share'; + } + path += '/selfprivacy'; + } + + return path; + } + + /// Running operating environment. + static Future get deviceName async { + final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); + if (kIsWeb) { + return deviceInfo.webBrowserInfo.then( + (final WebBrowserInfo value) => + '${value.browserName} ${value.platform}', + ); + } else { + if (Platform.isAndroid) { + return deviceInfo.androidInfo.then( + (final AndroidDeviceInfo value) => + '${value.model} ${value.version.release}', + ); + } else if (Platform.isIOS) { + return deviceInfo.iosInfo.then( + (final IosDeviceInfo value) => + '${value.utsname.machine} ${value.systemName} ${value.systemVersion}', + ); + } else if (Platform.isLinux) { + return deviceInfo.linuxInfo + .then((final LinuxDeviceInfo value) => value.prettyName); + } else if (Platform.isMacOS) { + return deviceInfo.macOsInfo.then( + (final MacOsDeviceInfo value) => + '${value.hostName} ${value.computerName}', + ); + } else if (Platform.isWindows) { + return deviceInfo.windowsInfo + .then((final WindowsDeviceInfo value) => value.computerName); + } + } + + return 'Unidentified'; + } + + static void setClipboard(final String clipboardData) { + Clipboard.setData(ClipboardData(text: clipboardData)); + } +} diff --git a/lib/utils/route_transitions/basic.dart b/lib/utils/route_transitions/basic.dart index a3148e1d..b72d9a94 100644 --- a/lib/utils/route_transitions/basic.dart +++ b/lib/utils/route_transitions/basic.dart @@ -3,12 +3,3 @@ import 'package:flutter/material.dart'; Route materialRoute(final Widget widget) => MaterialPageRoute( builder: (final BuildContext context) => widget, ); - -Route noAnimationRoute(final Widget widget) => PageRouteBuilder( - pageBuilder: ( - final BuildContext context, - final Animation animation1, - final Animation animation2, - ) => - widget, - ); diff --git a/lib/utils/route_transitions/slide_bottom.dart b/lib/utils/route_transitions/slide_bottom.dart deleted file mode 100644 index e187b4d7..00000000 --- a/lib/utils/route_transitions/slide_bottom.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:flutter/material.dart'; - -Function pageBuilder = (final Widget widget) => ( - final BuildContext context, - final Animation animation, - final Animation secondaryAnimation, - ) => - widget; - -Function transitionsBuilder = ( - final BuildContext context, - final Animation animation, - final Animation secondaryAnimation, - final Widget child, -) => - SlideTransition( - position: Tween( - begin: const Offset(0, 1), - end: Offset.zero, - ).animate(animation), - child: Container( - decoration: animation.isCompleted - ? null - : const BoxDecoration( - border: Border( - bottom: BorderSide( - color: Colors.black, - ), - ), - ), - child: child, - ), - ); - -class SlideBottomRoute extends PageRouteBuilder { - SlideBottomRoute(this.widget) - : super( - transitionDuration: const Duration(milliseconds: 150), - pageBuilder: pageBuilder(widget), - transitionsBuilder: transitionsBuilder as Widget Function( - BuildContext, - Animation, - Animation, - Widget, - ), - ); - - final Widget widget; -} diff --git a/lib/utils/route_transitions/slide_right.dart b/lib/utils/route_transitions/slide_right.dart deleted file mode 100644 index eae4414d..00000000 --- a/lib/utils/route_transitions/slide_right.dart +++ /dev/null @@ -1,48 +0,0 @@ -import 'package:flutter/material.dart'; - -Function pageBuilder = (final Widget widget) => ( - final BuildContext context, - final Animation animation, - final Animation secondaryAnimation, - ) => - widget; - -Function transitionsBuilder = ( - final BuildContext context, - final Animation animation, - final Animation secondaryAnimation, - final Widget child, -) => - SlideTransition( - position: Tween( - begin: const Offset(-1, 0), - end: Offset.zero, - ).animate(animation), - child: Container( - decoration: animation.isCompleted - ? null - : const BoxDecoration( - border: Border( - right: BorderSide( - color: Colors.black, - ), - ), - ), - child: child, - ), - ); - -class SlideRightRoute extends PageRouteBuilder { - SlideRightRoute(this.widget) - : super( - pageBuilder: pageBuilder(widget), - transitionsBuilder: transitionsBuilder as Widget Function( - BuildContext, - Animation, - Animation, - Widget, - ), - ); - - final Widget widget; -} diff --git a/lib/utils/scalars.dart b/lib/utils/scalars.dart new file mode 100644 index 00000000..278fb80a --- /dev/null +++ b/lib/utils/scalars.dart @@ -0,0 +1,9 @@ +// ignore_for_file: prefer_final_parameters, prefer_expression_function_bodies + +String dateTimeToJson(DateTime data) { + return data.toIso8601String(); +} + +DateTime dateTimeFromJson(dynamic data) { + return DateTime.parse(data as String); +} diff --git a/lib/utils/ui_helpers.dart b/lib/utils/ui_helpers.dart index c34721e1..f4d58a05 100644 --- a/lib/utils/ui_helpers.dart +++ b/lib/utils/ui_helpers.dart @@ -4,5 +4,5 @@ import 'package:selfprivacy/logic/cubit/server_installation/server_installation_ class UiHelpers { static String getDomainName(final ServerInstallationState config) => - config.isDomainFilled ? config.serverDomain!.domainName : 'example.com'; + config.isDomainSelected ? config.serverDomain!.domainName : 'example.com'; } diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index cc332a28..94dd8d0b 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -7,7 +7,7 @@ project(runner LANGUAGES CXX) set(BINARY_NAME "selfprivacy") # The unique GTK application identifier for this application. See: # https://wiki.gnome.org/HowDoI/ChooseApplicationID -set(APPLICATION_ID "pro.kherel.selfprivacy") +set(APPLICATION_ID "org.selfprivacy.app") # Explicitly opt in to modern CMake behaviors to avoid warnings with recent # versions of CMake. diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index cf327b12..075ecba3 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,17 +6,17 @@ #include "generated_plugin_registrant.h" +#include #include -#include #include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) dynamic_color_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin"); + dynamic_color_plugin_register_with_registrar(dynamic_color_registrar); g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); - g_autoptr(FlPluginRegistrar) gtk_theme_fl_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "GtkThemeFlPlugin"); - gtk_theme_fl_plugin_register_with_registrar(gtk_theme_fl_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 6c700a87..6fd458b2 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,8 +3,8 @@ # list(APPEND FLUTTER_PLUGIN_LIST + dynamic_color flutter_secure_storage_linux - gtk_theme_fl url_launcher_linux ) diff --git a/macos/.gitignore b/macos/.gitignore new file mode 100644 index 00000000..746adbb6 --- /dev/null +++ b/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/macos/Flutter/Flutter-Debug.xcconfig b/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 00000000..4b81f9b2 --- /dev/null +++ b/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/Flutter-Release.xcconfig b/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 00000000..5caa9d15 --- /dev/null +++ b/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 00000000..13f6db44 --- /dev/null +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,26 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import connectivity_plus +import device_info_plus +import dynamic_color +import flutter_secure_storage_macos +import package_info +import path_provider_foundation +import shared_preferences_foundation +import url_launcher_macos + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) + DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) + DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin")) + FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) + FLTPackageInfoPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlugin")) + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) +} diff --git a/macos/Podfile b/macos/Podfile new file mode 100644 index 00000000..de1123e8 --- /dev/null +++ b/macos/Podfile @@ -0,0 +1,43 @@ +platform :osx, '10.13' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + target.build_configurations.each do |config| + config.build_settings['MACOSX_DEPLOYMENT_TARGET'] = '10.13' + end + end +end diff --git a/macos/Podfile.lock b/macos/Podfile.lock new file mode 100644 index 00000000..7bd65a88 --- /dev/null +++ b/macos/Podfile.lock @@ -0,0 +1,83 @@ +PODS: + - connectivity_plus_macos (0.0.1): + - FlutterMacOS + - ReachabilitySwift + - device_info_plus_macos (0.0.1): + - FlutterMacOS + - dynamic_color (0.0.2): + - FlutterMacOS + - flutter_secure_storage_macos (6.1.1): + - FlutterMacOS + - FlutterMacOS (1.0.0) + - package_info (0.0.1): + - FlutterMacOS + - path_provider_macos (0.0.1): + - FlutterMacOS + - ReachabilitySwift (5.0.0) + - share_plus_macos (0.0.1): + - FlutterMacOS + - shared_preferences_macos (0.0.1): + - FlutterMacOS + - url_launcher_macos (0.0.1): + - FlutterMacOS + - wakelock_macos (0.0.1): + - FlutterMacOS + +DEPENDENCIES: + - connectivity_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus_macos/macos`) + - device_info_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus_macos/macos`) + - dynamic_color (from `Flutter/ephemeral/.symlinks/plugins/dynamic_color/macos`) + - flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`) + - FlutterMacOS (from `Flutter/ephemeral`) + - package_info (from `Flutter/ephemeral/.symlinks/plugins/package_info/macos`) + - path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`) + - share_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos`) + - shared_preferences_macos (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos`) + - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) + - wakelock_macos (from `Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos`) + +SPEC REPOS: + trunk: + - ReachabilitySwift + +EXTERNAL SOURCES: + connectivity_plus_macos: + :path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus_macos/macos + device_info_plus_macos: + :path: Flutter/ephemeral/.symlinks/plugins/device_info_plus_macos/macos + dynamic_color: + :path: Flutter/ephemeral/.symlinks/plugins/dynamic_color/macos + flutter_secure_storage_macos: + :path: Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos + FlutterMacOS: + :path: Flutter/ephemeral + package_info: + :path: Flutter/ephemeral/.symlinks/plugins/package_info/macos + path_provider_macos: + :path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos + share_plus_macos: + :path: Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos + shared_preferences_macos: + :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos + url_launcher_macos: + :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos + wakelock_macos: + :path: Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos + +SPEC CHECKSUMS: + connectivity_plus_macos: f6e86fd000e971d361e54b5afcadc8c8fa773308 + device_info_plus_macos: 1ad388a1ef433505c4038e7dd9605aadd1e2e9c7 + dynamic_color: 394d6a888650f8534e029b27d2f8bc5c64e44008 + flutter_secure_storage_macos: 75c8cadfdba05ca007c0fa4ea0c16e5cf85e521b + FlutterMacOS: ae6af50a8ea7d6103d888583d46bd8328a7e9811 + package_info: 6eba2fd8d3371dda2d85c8db6fe97488f24b74b2 + path_provider_macos: 3c0c3b4b0d4a76d2bf989a913c2de869c5641a19 + ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 + share_plus_macos: 853ee48e7dce06b633998ca0735d482dd671ade4 + shared_preferences_macos: a64dc611287ed6cbe28fd1297898db1336975727 + url_launcher_macos: 597e05b8e514239626bcf4a850fcf9ef5c856ec3 + wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9 + +PODFILE CHECKSUM: 69608711ca93a0af5aac7e2f834b34cf2422afce + +COCOAPODS: 1.11.3 diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000..ce134fe4 --- /dev/null +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,646 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + CE333BC2BFA23F98DF817C09 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0609448D29F0321D616C7454 /* Pods_Runner.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 0609448D29F0321D616C7454 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 0DE40E103CF4B2BD09F1A074 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* selfprivacy.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = selfprivacy.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 481A265FF1547C7E1F902035 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + DCFB3422A901A681B822A004 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + CE333BC2BFA23F98DF817C09 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 32F2223E51A4F9B1A6E798D8 /* Pods */ = { + isa = PBXGroup; + children = ( + 0DE40E103CF4B2BD09F1A074 /* Pods-Runner.debug.xcconfig */, + DCFB3422A901A681B822A004 /* Pods-Runner.release.xcconfig */, + 481A265FF1547C7E1F902035 /* Pods-Runner.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + 32F2223E51A4F9B1A6E798D8 /* Pods */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* selfprivacy.app */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 0609448D29F0321D616C7454 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + C9AFDA92EDCDEAF3FC96A0A3 /* [CP] Check Pods Manifest.lock */, + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + B270A3A9AFF6A3D0E129954A /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* selfprivacy.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; + B270A3A9AFF6A3D0E129954A /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + C9AFDA92EDCDEAF3FC96A0A3 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 46723VZHWZ; + INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = SelfPrivacy; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.13; + MARKETING_VERSION = 0.8.0; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 46723VZHWZ; + INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = SelfPrivacy; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.13; + MARKETING_VERSION = 0.8.0; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 46723VZHWZ; + INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = SelfPrivacy; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.13; + MARKETING_VERSION = 0.8.0; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000..523b108f --- /dev/null +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/Runner.xcworkspace/contents.xcworkspacedata b/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..21a3cc14 --- /dev/null +++ b/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift new file mode 100644 index 00000000..d53ef643 --- /dev/null +++ b/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..a2ec33f1 --- /dev/null +++ b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 00000000..5bde4f92 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 00000000..32177570 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 00000000..182d8350 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 00000000..c7fb361a Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png new file mode 100644 index 00000000..092a3fda Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 00000000..0890b3ad Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 00000000..d60f2697 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/macos/Runner/Base.lproj/MainMenu.xib b/macos/Runner/Base.lproj/MainMenu.xib new file mode 100644 index 00000000..80e867a4 --- /dev/null +++ b/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 00000000..f96ad8ea --- /dev/null +++ b/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = selfprivacy + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = org.selfprivacy.selfprivacy + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2022 org.selfprivacy. All rights reserved. diff --git a/macos/Runner/Configs/Debug.xcconfig b/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 00000000..36b0fd94 --- /dev/null +++ b/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Release.xcconfig b/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 00000000..dff4f495 --- /dev/null +++ b/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Warnings.xcconfig b/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 00000000..42bcbf47 --- /dev/null +++ b/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements new file mode 100644 index 00000000..31be9762 --- /dev/null +++ b/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,18 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.client + + com.apple.security.network.server + + keychain-access-groups + + $(AppIdentifierPrefix)* + + + diff --git a/macos/Runner/Info.plist b/macos/Runner/Info.plist new file mode 100644 index 00000000..4789daa6 --- /dev/null +++ b/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/macos/Runner/MainFlutterWindow.swift b/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 00000000..2722837e --- /dev/null +++ b/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController.init() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements new file mode 100644 index 00000000..041bc9d9 --- /dev/null +++ b/macos/Runner/Release.entitlements @@ -0,0 +1,16 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.network.client + + com.apple.security.network.server + + keychain-access-groups + + $(AppIdentifierPrefix)* + + + diff --git a/org.selfprivacy.app.desktop b/org.selfprivacy.app.desktop new file mode 100644 index 00000000..952582ac --- /dev/null +++ b/org.selfprivacy.app.desktop @@ -0,0 +1,5 @@ +[Desktop Entry] +Type=Application +Name=SelfPrivacy +Icon=org.selfprivacy.app +Exec=selfprivacy %U diff --git a/pubspec.lock b/pubspec.lock index e3faf1b6..05045a0a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,345 +5,394 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - url: "https://pub.dartlang.org" + sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a + url: "https://pub.dev" source: hosted - version: "38.0.0" + version: "61.0.0" analyzer: dependency: transitive description: name: analyzer - url: "https://pub.dartlang.org" + sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 + url: "https://pub.dev" source: hosted - version: "3.4.1" + version: "5.13.0" + animations: + dependency: "direct main" + description: + name: animations + sha256: fe8a6bdca435f718bb1dc8a11661b2c22504c6da40ef934cee8327ed77934164 + url: "https://pub.dev" + source: hosted + version: "2.0.7" archive: dependency: transitive description: name: archive - url: "https://pub.dartlang.org" + sha256: d6347d54a2d8028e0437e3c099f66fdb8ae02c4720c1e7534c9f24c10351f85d + url: "https://pub.dev" source: hosted - version: "3.3.0" + version: "3.3.6" args: dependency: transitive description: name: args - url: "https://pub.dartlang.org" + sha256: "139d809800a412ebb26a3892da228b2d0ba36f0ef5d9a82166e5e52ec8d61611" + url: "https://pub.dev" source: hosted - version: "2.3.1" - asn1lib: - dependency: transitive - description: - name: asn1lib - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" + version: "2.3.2" async: dependency: transitive description: name: async - url: "https://pub.dartlang.org" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" source: hosted - version: "2.8.2" + version: "2.11.0" + auto_route: + dependency: "direct main" + description: + name: auto_route + sha256: cf6cda303fd98608426fa429692a04f643146f981b1c0ac1033e3f0e11a1ed9c + url: "https://pub.dev" + source: hosted + version: "7.3.2" + auto_route_generator: + dependency: "direct dev" + description: + name: auto_route_generator + sha256: d0555913cc54153c38b1dd4f69e0d6a623818ca7195e7c1437901d52b2596eec + url: "https://pub.dev" + source: hosted + version: "7.1.1" auto_size_text: dependency: "direct main" description: name: auto_size_text - url: "https://pub.dartlang.org" + sha256: "3f5261cd3fb5f2a9ab4e2fc3fba84fd9fcaac8821f20a1d4e71f557521b22599" + url: "https://pub.dev" source: hosted version: "3.0.0" - basic_utils: - dependency: "direct main" - description: - name: basic_utils - url: "https://pub.dartlang.org" - source: hosted - version: "4.2.2" bloc: dependency: transitive description: name: bloc - url: "https://pub.dartlang.org" + sha256: "3820f15f502372d979121de1f6b97bfcf1630ebff8fe1d52fb2b0bfa49be5b49" + url: "https://pub.dev" source: hosted - version: "8.0.3" + version: "8.1.2" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.dartlang.org" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" build: dependency: transitive description: name: build - url: "https://pub.dartlang.org" + sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777" + url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.3.1" build_config: dependency: transitive description: name: build_config - url: "https://pub.dartlang.org" + sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.1.1" build_daemon: dependency: transitive description: name: build_daemon - url: "https://pub.dartlang.org" + sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" + url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "4.0.0" build_resolvers: dependency: transitive description: name: build_resolvers - url: "https://pub.dartlang.org" + sha256: "7c35a3a7868626257d8aee47b51c26b9dba11eaddf3431117ed2744951416aab" + url: "https://pub.dev" source: hosted - version: "2.0.8" + version: "2.1.0" build_runner: dependency: "direct dev" description: name: build_runner - url: "https://pub.dartlang.org" + sha256: "220ae4553e50d7c21a17c051afc7b183d28a24a420502e842f303f8e4e6edced" + url: "https://pub.dev" source: hosted - version: "2.1.11" + version: "2.4.4" build_runner_core: dependency: transitive description: name: build_runner_core - url: "https://pub.dartlang.org" + sha256: "14febe0f5bac5ae474117a36099b4de6f1dbc52df6c5e55534b3da9591bf4292" + url: "https://pub.dev" source: hosted - version: "7.2.3" + version: "7.2.7" built_collection: dependency: transitive description: name: built_collection - url: "https://pub.dartlang.org" + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" source: hosted version: "5.1.1" built_value: dependency: transitive description: name: built_value - url: "https://pub.dartlang.org" + sha256: "169565c8ad06adb760c3645bf71f00bff161b00002cace266cad42c5d22a7725" + url: "https://pub.dev" source: hosted - version: "8.3.0" + version: "8.4.3" characters: dependency: transitive description: name: characters - url: "https://pub.dartlang.org" + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" source: hosted - version: "1.2.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.1" + version: "1.3.0" checked_yaml: dependency: transitive description: name: checked_yaml - url: "https://pub.dartlang.org" + sha256: "3d1505d91afa809d177efd4eed5bb0eb65805097a1463abdd2add076effae311" + url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7 + url: "https://pub.dev" + source: hosted + version: "0.4.0" clock: dependency: transitive description: name: clock - url: "https://pub.dartlang.org" + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" code_builder: dependency: transitive description: name: code_builder - url: "https://pub.dartlang.org" + sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe" + url: "https://pub.dev" source: hosted - version: "4.1.0" + version: "4.4.0" collection: dependency: transitive description: name: collection - url: "https://pub.dartlang.org" + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.17.1" + connectivity_plus: + dependency: transitive + description: + name: connectivity_plus + sha256: "745ebcccb1ef73768386154428a55250bc8d44059c19fd27aecda2a6dc013a22" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + connectivity_plus_platform_interface: + dependency: transitive + description: + name: connectivity_plus_platform_interface + sha256: b8795b9238bf83b64375f63492034cb3d8e222af4d9ce59dda085edf038fa06f + url: "https://pub.dev" + source: hosted + version: "1.2.3" convert: dependency: transitive description: name: convert - url: "https://pub.dartlang.org" + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.1.1" coverage: dependency: transitive description: name: coverage - url: "https://pub.dartlang.org" + sha256: "961c4aebd27917269b1896382c7cb1b1ba81629ba669ba09c27a7e5710ec9040" + url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.6.2" crypt: dependency: "direct main" description: name: crypt - url: "https://pub.dartlang.org" + sha256: fef2b24f8fb73b626224b207b2e57ac139d38b63dc7428257f820a2fd9e4688b + url: "https://pub.dev" source: hosted - version: "4.2.1" + version: "4.3.0" crypto: dependency: transitive description: name: crypto - url: "https://pub.dartlang.org" + sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 + url: "https://pub.dev" source: hosted version: "3.0.2" cubit_form: dependency: "direct main" description: name: cubit_form - url: "https://pub.dartlang.org" + sha256: "3e01bb30b0b9fc9c0807536b08865714487d26256566bdbb31e780f51d9a8932" + url: "https://pub.dev" source: hosted version: "2.0.1" - cupertino_icons: - dependency: "direct main" - description: - name: cupertino_icons - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.4" dart_style: dependency: transitive description: name: dart_style - url: "https://pub.dartlang.org" + sha256: "5be16bf1707658e4c03078d4a9b90208ded217fb02c163e207d334082412f2fb" + url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.2.5" + dbus: + dependency: transitive + description: + name: dbus + sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" + url: "https://pub.dev" + source: hosted + version: "0.7.8" device_info_plus: dependency: "direct main" description: name: device_info_plus - url: "https://pub.dartlang.org" + sha256: "2c35b6d1682b028e42d07b3aee4b98fa62996c10bc12cb651ec856a80d6a761b" + url: "https://pub.dev" source: hosted - version: "3.2.3" - device_info_plus_linux: - dependency: transitive - description: - name: device_info_plus_linux - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.1" - device_info_plus_macos: - dependency: transitive - description: - name: device_info_plus_macos - url: "https://pub.dartlang.org" - source: hosted - version: "2.2.3" + version: "9.0.2" device_info_plus_platform_interface: dependency: transitive description: name: device_info_plus_platform_interface - url: "https://pub.dartlang.org" + sha256: d3b01d5868b50ae571cd1dc6e502fc94d956b665756180f7b16ead09e836fd64 + url: "https://pub.dev" source: hosted - version: "2.3.0+1" - device_info_plus_web: - dependency: transitive - description: - name: device_info_plus_web - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - device_info_plus_windows: - dependency: transitive - description: - name: device_info_plus_windows - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.1" + version: "7.0.0" dio: dependency: "direct main" description: name: dio - url: "https://pub.dartlang.org" + sha256: "347d56c26d63519552ef9a569f2a593dda99a81fdbdff13c584b7197cfe05059" + url: "https://pub.dev" source: hosted - version: "4.0.6" + version: "5.1.2" + duration: + dependency: "direct main" + description: + name: duration + sha256: d0b29d0a345429e3986ac56d60e4aef65b37d11e653022b2b9a4b361332b777f + url: "https://pub.dev" + source: hosted + version: "3.0.12" dynamic_color: dependency: "direct main" description: name: dynamic_color - url: "https://pub.dartlang.org" + sha256: "74dff1435a695887ca64899b8990004f8d1232b0e84bfc4faa1fdda7c6f57cc1" + url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.6.5" easy_localization: dependency: "direct main" description: name: easy_localization - url: "https://pub.dartlang.org" + sha256: "30ebf25448ffe169e0bd9bc4b5da94faa8398967a2ad2ca09f438be8b6953645" + url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" easy_logger: dependency: transitive description: name: easy_logger - url: "https://pub.dartlang.org" + sha256: c764a6e024846f33405a2342caf91c62e357c24b02c04dbc712ef232bf30ffb7 + url: "https://pub.dev" source: hosted version: "0.0.2" either_option: dependency: "direct main" description: name: either_option - url: "https://pub.dartlang.org" + sha256: "08f6ddfe95346ad2182dcd11ea6a47a75702229cb4842fdfc75736541d12df3e" + url: "https://pub.dev" source: hosted version: "2.0.1-dev.1" email_validator: dependency: transitive description: name: email_validator - url: "https://pub.dartlang.org" + sha256: e9a90f27ab2b915a27d7f9c2a7ddda5dd752d6942616ee83529b686fc086221b + url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.17" equatable: dependency: "direct main" description: name: equatable - url: "https://pub.dartlang.org" + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "2.0.5" fake_async: dependency: transitive description: name: fake_async - url: "https://pub.dartlang.org" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.3.1" ffi: dependency: transitive description: name: ffi - url: "https://pub.dartlang.org" + sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 + url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "2.0.1" file: dependency: transitive description: name: file - url: "https://pub.dartlang.org" + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" source: hosted - version: "6.1.2" + version: "6.1.4" fixnum: dependency: transitive description: name: fixnum - url: "https://pub.dartlang.org" + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.1.0" fl_chart: dependency: "direct main" description: name: fl_chart - url: "https://pub.dartlang.org" + sha256: "48a1b69be9544e2b03d9a8e843affd89e43f3194c9248776222efcb4206bb1ec" + url: "https://pub.dev" source: hosted - version: "0.50.5" + version: "0.62.0" flutter: dependency: "direct main" description: flutter @@ -353,21 +402,32 @@ packages: dependency: "direct main" description: name: flutter_bloc - url: "https://pub.dartlang.org" + sha256: e74efb89ee6945bcbce74a5b3a5a3376b088e5f21f55c263fc38cbdc6237faae + url: "https://pub.dev" source: hosted - version: "8.0.1" + version: "8.1.3" + flutter_hooks: + dependency: transitive + description: + name: flutter_hooks + sha256: "2b202559a4ed3656bbb7aae9d8b335fb0037b23acc7ae3f377d1ba0b95c21aec" + url: "https://pub.dev" + source: hosted + version: "0.18.5+1" flutter_launcher_icons: dependency: "direct dev" description: name: flutter_launcher_icons - url: "https://pub.dartlang.org" + sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea" + url: "https://pub.dev" source: hosted - version: "0.9.2" + version: "0.13.1" flutter_lints: dependency: "direct dev" description: name: flutter_lints - url: "https://pub.dartlang.org" + sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + url: "https://pub.dev" source: hosted version: "2.0.1" flutter_localizations: @@ -379,58 +439,74 @@ packages: dependency: "direct main" description: name: flutter_markdown - url: "https://pub.dartlang.org" + sha256: "7b25c10de1fea883f3c4f9b8389506b54053cd00807beab69fd65c8653a2711f" + url: "https://pub.dev" source: hosted - version: "0.6.10" + version: "0.6.14" flutter_plugin_android_lifecycle: dependency: transitive description: name: flutter_plugin_android_lifecycle - url: "https://pub.dartlang.org" + sha256: "60fc7b78455b94e6de2333d2f95196d32cf5c22f4b0b0520a628804cb463503b" + url: "https://pub.dev" source: hosted - version: "2.0.6" + version: "2.0.7" flutter_secure_storage: dependency: "direct main" description: name: flutter_secure_storage - url: "https://pub.dartlang.org" + sha256: "98352186ee7ad3639ccc77ad7924b773ff6883076ab952437d20f18a61f0a7c5" + url: "https://pub.dev" source: hosted - version: "5.0.2" + version: "8.0.0" flutter_secure_storage_linux: dependency: transitive description: name: flutter_secure_storage_linux - url: "https://pub.dartlang.org" + sha256: "0912ae29a572230ad52d8a4697e5518d7f0f429052fd51df7e5a7952c7efe2a3" + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.3" flutter_secure_storage_macos: dependency: transitive description: name: flutter_secure_storage_macos - url: "https://pub.dartlang.org" + sha256: "083add01847fc1c80a07a08e1ed6927e9acd9618a35e330239d4422cd2a58c50" + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "3.0.0" flutter_secure_storage_platform_interface: dependency: transitive description: name: flutter_secure_storage_platform_interface - url: "https://pub.dartlang.org" + sha256: b3773190e385a3c8a382007893d678ae95462b3c2279e987b55d140d3b0cb81b + url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.0.1" flutter_secure_storage_web: dependency: transitive description: name: flutter_secure_storage_web - url: "https://pub.dartlang.org" + sha256: "42938e70d4b872e856e678c423cc0e9065d7d294f45bc41fc1981a4eb4beaffe" + url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.1.1" flutter_secure_storage_windows: dependency: transitive description: name: flutter_secure_storage_windows - url: "https://pub.dartlang.org" + sha256: fc2910ec9b28d60598216c29ea763b3a96c401f0ce1d13cdf69ccb0e5c93c3ee + url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "2.0.0" + flutter_svg: + dependency: "direct main" + description: + name: flutter_svg + sha256: "6ff8c902c8056af9736de2689f63f81c42e2d642b9f4c79dbf8790ae48b63012" + url: "https://pub.dev" + source: hosted + version: "2.0.6" flutter_test: dependency: "direct dev" description: flutter @@ -445,513 +521,642 @@ packages: dependency: transitive description: name: frontend_server_client - url: "https://pub.dartlang.org" + sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "3.2.0" get_it: dependency: "direct main" description: name: get_it - url: "https://pub.dartlang.org" + sha256: "529de303c739fca98cd7ece5fca500d8ff89649f1bb4b4e94fb20954abcd7468" + url: "https://pub.dev" source: hosted - version: "7.2.0" + version: "7.6.0" glob: dependency: transitive description: name: glob - url: "https://pub.dartlang.org" + sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c" + url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "2.1.1" + gql: + dependency: "direct main" + description: + name: gql + sha256: "7dd48a2632103154186bf77adb850489efdabe65c05313eab784657df800794a" + url: "https://pub.dev" + source: hosted + version: "1.0.1-alpha+1682715291314" + gql_code_builder: + dependency: transitive + description: + name: gql_code_builder + sha256: "6e386a85f5d91daae82915337f566a43dfeb0a0df38caa372387fbc07d31b8c1" + url: "https://pub.dev" + source: hosted + version: "0.7.2" + gql_dedupe_link: + dependency: transitive + description: + name: gql_dedupe_link + sha256: "2c76b1006cd7445e026d3bc46c6336f28cbcf3711326f128839cfc746f9e2ec9" + url: "https://pub.dev" + source: hosted + version: "2.0.4-alpha+1682715291398" + gql_error_link: + dependency: transitive + description: + name: gql_error_link + sha256: bfdb543137da89448cc5d003fd029c2e8718931d39d4a7dedb16f9169862fbb9 + url: "https://pub.dev" + source: hosted + version: "1.0.0" + gql_exec: + dependency: transitive + description: + name: gql_exec + sha256: "07f73f4edc00698f67c3fb5bda0d95ab7e6ea844572a670ef270154244fae6d4" + url: "https://pub.dev" + source: hosted + version: "1.0.1-alpha+1682715291324" + gql_http_link: + dependency: transitive + description: + name: gql_http_link + sha256: "365c0e72da7e29e007c4a0ed7a470f6c03af1ef00acd2af3c22157c8bb47f314" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + gql_link: + dependency: transitive + description: + name: gql_link + sha256: ee781e59527240adf69e044389a7d0a19b890a6e76b6dfff969ef56ba8d4fea7 + url: "https://pub.dev" + source: hosted + version: "1.0.1-alpha+1682715291332" + gql_transform_link: + dependency: transitive + description: + name: gql_transform_link + sha256: "0645fdd874ca1be695fd327271fdfb24c0cd6fa40774a64b946062f321a59709" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + graphql: + dependency: "direct main" + description: + name: graphql + sha256: f2529e2f606f445bbb38b92c6eb18dd4c11196ca638421fe5aaab62140ac3106 + url: "https://pub.dev" + source: hosted + version: "5.2.0-beta.3" + graphql_codegen: + dependency: "direct main" + description: + name: graphql_codegen + sha256: "773dce58e9bce208a8694badc1af9fe39870bb2cb18078474ba5c2b6a535f87c" + url: "https://pub.dev" + source: hosted + version: "0.13.0" + graphql_flutter: + dependency: "direct main" + description: + name: graphql_flutter + sha256: "9de0365b58c8733130a706e9fddb33f6791d9bbbee45e1cd1bc2ca0920941a19" + url: "https://pub.dev" + source: hosted + version: "5.2.0-beta.3" graphs: dependency: transitive description: name: graphs - url: "https://pub.dartlang.org" + sha256: f9e130f3259f52d26f0cfc0e964513796dafed572fa52e45d2f8d6ca14db39b2 + url: "https://pub.dev" source: hosted - version: "2.1.0" - gtk_theme_fl: - dependency: "direct main" - description: - name: gtk_theme_fl - url: "https://pub.dartlang.org" - source: hosted - version: "0.0.1" + version: "2.2.0" hive: dependency: "direct main" description: name: hive - url: "https://pub.dartlang.org" + sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.2.3" hive_flutter: dependency: "direct main" description: name: hive_flutter - url: "https://pub.dartlang.org" + sha256: dca1da446b1d808a51689fb5d0c6c9510c0a2ba01e22805d492c73b68e33eecc + url: "https://pub.dev" source: hosted version: "1.1.0" hive_generator: dependency: "direct dev" description: name: hive_generator - url: "https://pub.dartlang.org" + sha256: "65998cc4d2cd9680a3d9709d893d2f6bb15e6c1f92626c3f1fa650b4b3281521" + url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "2.0.0" http: - dependency: transitive + dependency: "direct main" description: name: http - url: "https://pub.dartlang.org" + sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482" + url: "https://pub.dev" source: hosted - version: "0.13.4" + version: "0.13.5" http_multi_server: dependency: transitive description: name: http_multi_server - url: "https://pub.dartlang.org" + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "3.2.1" http_parser: dependency: transitive description: name: http_parser - url: "https://pub.dartlang.org" + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "4.0.2" image: dependency: transitive description: name: image - url: "https://pub.dartlang.org" + sha256: a72242c9a0ffb65d03de1b7113bc4e189686fc07c7147b8b41811d0dd0e0d9bf + url: "https://pub.dev" source: hosted - version: "3.1.3" + version: "4.0.17" intl: dependency: "direct main" description: name: intl - url: "https://pub.dartlang.org" + sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 + url: "https://pub.dev" source: hosted - version: "0.17.0" + version: "0.18.0" io: dependency: transitive description: name: io - url: "https://pub.dartlang.org" + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.0.4" ionicons: dependency: "direct main" description: name: ionicons - url: "https://pub.dartlang.org" + sha256: "5496bc65a16115ecf05b15b78f494ee4a8869504357668f0a11d689e970523cf" + url: "https://pub.dev" source: hosted - version: "0.1.2" + version: "0.2.2" js: dependency: transitive description: name: js - url: "https://pub.dartlang.org" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" source: hosted - version: "0.6.4" + version: "0.6.7" json_annotation: dependency: "direct main" description: name: json_annotation - url: "https://pub.dartlang.org" + sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + url: "https://pub.dev" source: hosted - version: "4.5.0" + version: "4.8.1" json_serializable: dependency: "direct dev" description: name: json_serializable - url: "https://pub.dartlang.org" + sha256: "61a60716544392a82726dd0fa1dd6f5f1fd32aec66422b6e229e7b90d52325c4" + url: "https://pub.dev" source: hosted - version: "6.2.0" + version: "6.7.0" lints: dependency: transitive description: name: lints - url: "https://pub.dartlang.org" + sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.0.1" local_auth: dependency: "direct main" description: name: local_auth - url: "https://pub.dartlang.org" + sha256: "0cf238be2bfa51a6c9e7e9cfc11c05ea39f2a3a4d3e5bb255d0ebc917da24401" + url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "2.1.6" local_auth_android: dependency: transitive description: name: local_auth_android - url: "https://pub.dartlang.org" + sha256: ba48fe0e1cae140a0813ce68c2540250d7f573a8ae4d4b6c681b2d2583584953 + url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.17" local_auth_ios: dependency: transitive description: name: local_auth_ios - url: "https://pub.dartlang.org" + sha256: aa32478d7513066564139af57e11e2cad1bbd535c1efd224a88a8764c5665e3b + url: "https://pub.dev" source: hosted - version: "1.0.5" + version: "1.0.12" local_auth_platform_interface: dependency: transitive description: name: local_auth_platform_interface - url: "https://pub.dartlang.org" + sha256: fbb6973f2fd088e2677f39a5ab550aa1cfbc00997859d5e865569872499d6d61 + url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.0.6" + local_auth_windows: + dependency: transitive + description: + name: local_auth_windows + sha256: "888482e4f9ca3560e00bc227ce2badeb4857aad450c42a31c6cfc9dc21e0ccbc" + url: "https://pub.dev" + source: hosted + version: "1.0.5" logging: dependency: transitive description: name: logging - url: "https://pub.dartlang.org" + sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d" + url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.1.1" markdown: dependency: transitive description: name: markdown - url: "https://pub.dartlang.org" + sha256: "8e332924094383133cee218b676871f42db2514f1f6ac617b6cf6152a7faab8e" + url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "7.1.0" mask_text_input_formatter: dependency: transitive description: name: mask_text_input_formatter - url: "https://pub.dartlang.org" + sha256: "19bb7809c3c2559277e95521b3ee421e1409eb2cc85efd2feb191696c92490f4" + url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.4.0" matcher: dependency: transitive description: name: matcher - url: "https://pub.dartlang.org" + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + url: "https://pub.dev" source: hosted - version: "0.12.11" + version: "0.12.15" material_color_utilities: - dependency: transitive + dependency: "direct main" description: name: material_color_utilities - url: "https://pub.dartlang.org" + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + url: "https://pub.dev" source: hosted - version: "0.1.4" + version: "0.2.0" meta: dependency: transitive description: name: meta - url: "https://pub.dartlang.org" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + url: "https://pub.dev" source: hosted - version: "1.7.0" + version: "1.9.1" mime: dependency: transitive description: name: mime - url: "https://pub.dartlang.org" + sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.0.4" modal_bottom_sheet: dependency: "direct main" description: name: modal_bottom_sheet - url: "https://pub.dartlang.org" + sha256: "3bba63c62d35c931bce7f8ae23a47f9a05836d8cb3c11122ada64e0b2f3d718f" + url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.0-pre" nanoid: dependency: "direct main" description: name: nanoid - url: "https://pub.dartlang.org" + sha256: be3f8752d9046c825df2f3914195151eb876f3ad64b9d833dd0b799b77b8759e + url: "https://pub.dev" source: hosted version: "1.0.0" nested: dependency: transitive description: name: nested - url: "https://pub.dartlang.org" + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" source: hosted version: "1.0.0" + nm: + dependency: transitive + description: + name: nm + sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" + url: "https://pub.dev" + source: hosted + version: "0.5.0" node_preamble: dependency: transitive description: name: node_preamble - url: "https://pub.dartlang.org" + sha256: "8ebdbaa3b96d5285d068f80772390d27c21e1fa10fb2df6627b1b9415043608d" + url: "https://pub.dev" source: hosted version: "2.0.1" + normalize: + dependency: transitive + description: + name: normalize + sha256: "8a60e37de5b608eeaf9b839273370c71ebba445e9f73b08eee7725e0d92dbc43" + url: "https://pub.dev" + source: hosted + version: "0.8.2+1" package_config: dependency: transitive description: name: package_config - url: "https://pub.dartlang.org" + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "2.1.0" package_info: dependency: "direct main" description: name: package_info - url: "https://pub.dartlang.org" + sha256: "6c07d9d82c69e16afeeeeb6866fe43985a20b3b50df243091bfc4a4ad2b03b75" + url: "https://pub.dev" source: hosted version: "2.0.2" path: dependency: transitive description: name: path - url: "https://pub.dartlang.org" + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + url: "https://pub.dev" source: hosted - version: "1.8.1" + version: "1.8.3" + path_parsing: + dependency: transitive + description: + name: path_parsing + sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf + url: "https://pub.dev" + source: hosted + version: "1.0.1" path_provider: dependency: transitive description: name: path_provider - url: "https://pub.dartlang.org" + sha256: dcea5feb97d8abf90cab9e9030b497fb7c3cbf26b7a1fe9e3ef7dcb0a1ddec95 + url: "https://pub.dev" source: hosted - version: "2.0.10" + version: "2.0.12" path_provider_android: dependency: transitive description: name: path_provider_android - url: "https://pub.dartlang.org" + sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e + url: "https://pub.dev" source: hosted - version: "2.0.14" - path_provider_ios: + version: "2.0.22" + path_provider_foundation: dependency: transitive description: - name: path_provider_ios - url: "https://pub.dartlang.org" + name: path_provider_foundation + sha256: "62a68e7e1c6c459f9289859e2fae58290c981ce21d1697faf54910fe1faa4c74" + url: "https://pub.dev" source: hosted - version: "2.0.9" + version: "2.1.1" path_provider_linux: dependency: transitive description: name: path_provider_linux - url: "https://pub.dartlang.org" + sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379 + url: "https://pub.dev" source: hosted - version: "2.1.6" - path_provider_macos: - dependency: transitive - description: - name: path_provider_macos - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.6" + version: "2.1.7" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - url: "https://pub.dartlang.org" + sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76 + url: "https://pub.dev" source: hosted - version: "2.0.4" + version: "2.0.5" path_provider_windows: dependency: transitive description: name: path_provider_windows - url: "https://pub.dartlang.org" + sha256: d3f80b32e83ec208ac95253e0cd4d298e104fbc63cb29c5c69edaed43b0c69d6 + url: "https://pub.dev" source: hosted - version: "2.0.6" + version: "2.1.6" petitparser: dependency: transitive description: name: petitparser - url: "https://pub.dartlang.org" + sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 + url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "5.4.0" platform: dependency: transitive description: name: platform - url: "https://pub.dartlang.org" + sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + url: "https://pub.dev" source: hosted version: "3.1.0" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - url: "https://pub.dartlang.org" + sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a + url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.3" pointycastle: dependency: transitive description: name: pointycastle - url: "https://pub.dartlang.org" + sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" + url: "https://pub.dev" source: hosted - version: "3.6.0" + version: "3.7.3" pool: dependency: transitive description: name: pool - url: "https://pub.dartlang.org" + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "1.5.1" pretty_dio_logger: dependency: "direct main" description: name: pretty_dio_logger - url: "https://pub.dartlang.org" + sha256: "00b80053063935cf9a6190da344c5373b9d0e92da4c944c878ff2fbef0ef6dc2" + url: "https://pub.dev" source: hosted - version: "1.2.0-beta-1" + version: "1.3.1" process: dependency: transitive description: name: process - url: "https://pub.dartlang.org" + sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + url: "https://pub.dev" source: hosted version: "4.2.4" provider: dependency: "direct main" description: name: provider - url: "https://pub.dartlang.org" + sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + url: "https://pub.dev" source: hosted - version: "6.0.2" + version: "6.0.5" pub_semver: dependency: "direct main" description: name: pub_semver - url: "https://pub.dartlang.org" + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.4" pubspec_parse: dependency: transitive description: name: pubspec_parse - url: "https://pub.dartlang.org" + sha256: "75f6614d6dde2dc68948dffbaa4fe5dae32cd700eb9fb763fe11dfb45a3c4d0a" + url: "https://pub.dev" source: hosted - version: "1.2.0" - quiver: + version: "1.2.1" + recase: dependency: transitive description: - name: quiver - url: "https://pub.dartlang.org" + name: recase + sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213 + url: "https://pub.dev" source: hosted - version: "3.1.0" - share_plus: - dependency: "direct main" - description: - name: share_plus - url: "https://pub.dartlang.org" - source: hosted - version: "4.0.4" - share_plus_linux: + version: "4.1.0" + rxdart: dependency: transitive description: - name: share_plus_linux - url: "https://pub.dartlang.org" + name: rxdart + sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" + url: "https://pub.dev" source: hosted - version: "3.0.0" - share_plus_macos: - dependency: transitive - description: - name: share_plus_macos - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.0" - share_plus_platform_interface: - dependency: transitive - description: - name: share_plus_platform_interface - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.2" - share_plus_web: - dependency: transitive - description: - name: share_plus_web - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.0" - share_plus_windows: - dependency: transitive - description: - name: share_plus_windows - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.0" + version: "0.27.7" shared_preferences: dependency: transitive description: name: shared_preferences - url: "https://pub.dartlang.org" + sha256: "5949029e70abe87f75cfe59d17bf5c397619c4b74a099b10116baeb34786fad9" + url: "https://pub.dev" source: hosted - version: "2.0.15" + version: "2.0.17" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - url: "https://pub.dartlang.org" + sha256: "955e9736a12ba776bdd261cf030232b30eadfcd9c79b32a3250dd4a494e8c8f7" + url: "https://pub.dev" source: hosted - version: "2.0.12" - shared_preferences_ios: + version: "2.0.15" + shared_preferences_foundation: dependency: transitive description: - name: shared_preferences_ios - url: "https://pub.dartlang.org" + name: shared_preferences_foundation + sha256: "2b55c18636a4edc529fa5cd44c03d3f3100c00513f518c5127c951978efcccd0" + url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.3" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux - url: "https://pub.dartlang.org" + sha256: f8ea038aa6da37090093974ebdcf4397010605fd2ff65c37a66f9d28394cb874 + url: "https://pub.dev" source: hosted - version: "2.1.1" - shared_preferences_macos: - dependency: transitive - description: - name: shared_preferences_macos - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.4" + version: "2.1.3" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface - url: "https://pub.dartlang.org" + sha256: da9431745ede5ece47bc26d5d73a9d3c6936ef6945c101a5aca46f62e52c1cf3 + url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.1.0" shared_preferences_web: dependency: transitive description: name: shared_preferences_web - url: "https://pub.dartlang.org" + sha256: a4b5bc37fe1b368bbc81f953197d55e12f49d0296e7e412dfe2d2d77d6929958 + url: "https://pub.dev" source: hosted version: "2.0.4" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows - url: "https://pub.dartlang.org" + sha256: "5eaf05ae77658d3521d0e993ede1af962d4b326cd2153d312df716dc250f00c9" + url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.3" shelf: dependency: transitive description: name: shelf - url: "https://pub.dartlang.org" + sha256: c24a96135a2ccd62c64b69315a14adc5c3419df63b4d7c05832a346fdb73682c + url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" shelf_packages_handler: dependency: transitive description: name: shelf_packages_handler - url: "https://pub.dartlang.org" + sha256: aef74dc9195746a384843102142ab65b6a4735bb3beea791e63527b88cc83306 + url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "3.0.1" shelf_static: dependency: transitive description: name: shelf_static - url: "https://pub.dartlang.org" + sha256: e792b76b96a36d4a41b819da593aff4bdd413576b3ba6150df5d8d9996d2e74c + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - url: "https://pub.dartlang.org" + sha256: a988c0e8d8ffbdb8a28aa7ec8e449c260f3deb808781fe1284d22c5bba7156e8 + url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.0.3" sky_engine: dependency: transitive description: flutter @@ -961,296 +1166,306 @@ packages: dependency: transitive description: name: source_gen - url: "https://pub.dartlang.org" + sha256: "373f96cf5a8744bc9816c1ff41cf5391bbdbe3d7a96fe98c622b6738a8a7bd33" + url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.3.2" source_helper: dependency: transitive description: name: source_helper - url: "https://pub.dartlang.org" + sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f" + url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.3.3" source_map_stack_trace: dependency: transitive description: name: source_map_stack_trace - url: "https://pub.dartlang.org" + sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" source_maps: dependency: transitive description: name: source_maps - url: "https://pub.dartlang.org" + sha256: "490098075234dcedb83c5d949b4c93dad5e6b7702748de000be2b57b8e6b2427" + url: "https://pub.dev" source: hosted - version: "0.10.10" + version: "0.10.11" source_span: dependency: transitive description: name: source_span - url: "https://pub.dartlang.org" + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + url: "https://pub.dev" source: hosted - version: "1.8.2" - ssh_key: - dependency: "direct main" - description: - name: ssh_key - url: "https://pub.dartlang.org" - source: hosted - version: "0.7.1" + version: "1.9.1" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.dartlang.org" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.dartlang.org" + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" stream_transform: dependency: transitive description: name: stream_transform - url: "https://pub.dartlang.org" + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.1.0" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.dartlang.org" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" source: hosted - version: "1.1.0" - system_theme: - dependency: "direct main" - description: - name: system_theme - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.0" - system_theme_web: - dependency: transitive - description: - name: system_theme_web - url: "https://pub.dartlang.org" - source: hosted - version: "0.0.2" + version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph - url: "https://pub.dartlang.org" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.2.1" test: dependency: transitive description: name: test - url: "https://pub.dartlang.org" + sha256: "3dac9aecf2c3991d09b9cdde4f98ded7b30804a88a0d7e4e7e1678e78d6b97f4" + url: "https://pub.dev" source: hosted - version: "1.21.1" + version: "1.24.1" test_api: dependency: transitive description: name: test_api - url: "https://pub.dartlang.org" + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + url: "https://pub.dev" source: hosted - version: "0.4.9" + version: "0.5.1" test_core: dependency: transitive description: name: test_core - url: "https://pub.dartlang.org" + sha256: "5138dbffb77b2289ecb12b81c11ba46036590b72a64a7a90d6ffb880f1a29e93" + url: "https://pub.dev" source: hosted - version: "0.4.13" + version: "0.5.1" timezone: dependency: "direct main" description: name: timezone - url: "https://pub.dartlang.org" + sha256: "1cfd8ddc2d1cfd836bc93e67b9be88c3adaeca6f40a00ca999104c30693cdca0" + url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.9.2" timing: dependency: transitive description: name: timing - url: "https://pub.dartlang.org" + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" + url: "https://pub.dev" source: hosted - version: "1.0.0" - tuple: - dependency: transitive - description: - name: tuple - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.0" + version: "1.0.1" typed_data: dependency: transitive description: name: typed_data - url: "https://pub.dartlang.org" + sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + url: "https://pub.dev" source: hosted version: "1.3.1" url_launcher: dependency: "direct main" description: name: url_launcher - url: "https://pub.dartlang.org" + sha256: eb1e00ab44303d50dd487aab67ebc575456c146c6af44422f9c13889984c00f3 + url: "https://pub.dev" source: hosted - version: "6.1.2" + version: "6.1.11" url_launcher_android: dependency: transitive description: name: url_launcher_android - url: "https://pub.dartlang.org" + sha256: "3e2f6dfd2c7d9cd123296cab8ef66cfc2c1a13f5845f42c7a0f365690a8a7dd1" + url: "https://pub.dev" source: hosted - version: "6.0.17" + version: "6.0.23" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - url: "https://pub.dartlang.org" + sha256: bb328b24d3bccc20bdf1024a0990ac4f869d57663660de9c936fb8c043edefe3 + url: "https://pub.dev" source: hosted - version: "6.0.16" + version: "6.0.18" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - url: "https://pub.dartlang.org" + sha256: "318c42cba924e18180c029be69caf0a1a710191b9ec49bb42b5998fdcccee3cc" + url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - url: "https://pub.dartlang.org" + sha256: "41988b55570df53b3dd2a7fc90c76756a963de6a8c5f8e113330cb35992e2094" + url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - url: "https://pub.dartlang.org" + sha256: "4eae912628763eb48fc214522e58e942fd16ce195407dbf45638239523c759a6" + url: "https://pub.dev" source: hosted - version: "2.0.5" + version: "2.1.1" url_launcher_web: dependency: transitive description: name: url_launcher_web - url: "https://pub.dartlang.org" + sha256: "44d79408ce9f07052095ef1f9a693c258d6373dc3944249374e30eff7219ccb0" + url: "https://pub.dev" source: hosted - version: "2.0.11" + version: "2.0.14" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - url: "https://pub.dartlang.org" + sha256: b6217370f8eb1fd85c8890c539f5a639a01ab209a36db82c921ebeacefc7a615 + url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.3" + uuid: + dependency: transitive + description: + name: uuid + sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" + url: "https://pub.dev" + source: hosted + version: "3.0.7" + vector_graphics: + dependency: transitive + description: + name: vector_graphics + sha256: b96f10cbdfcbd03a65758633a43e7d04574438f059b1043104b5d61b23d38a4f + url: "https://pub.dev" + source: hosted + version: "1.1.6" + vector_graphics_codec: + dependency: transitive + description: + name: vector_graphics_codec + sha256: "57a8e6e24662a3bdfe3b3d61257db91768700c0b8f844e235877b56480f31c69" + url: "https://pub.dev" + source: hosted + version: "1.1.6" + vector_graphics_compiler: + dependency: transitive + description: + name: vector_graphics_compiler + sha256: "7430f5d834d0db4560d7b19863362cd892f1e52b43838553a3c5cdfc9ab28e5b" + url: "https://pub.dev" + source: hosted + version: "1.1.6" vector_math: dependency: transitive description: name: vector_math - url: "https://pub.dartlang.org" + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" vm_service: dependency: transitive description: name: vm_service - url: "https://pub.dartlang.org" + sha256: e7fb6c2282f7631712b69c19d1bff82f3767eea33a2321c14fa59ad67ea391c7 + url: "https://pub.dev" source: hosted - version: "8.3.0" - wakelock: - dependency: "direct main" - description: - name: wakelock - url: "https://pub.dartlang.org" - source: hosted - version: "0.6.1+2" - wakelock_macos: - dependency: transitive - description: - name: wakelock_macos - url: "https://pub.dartlang.org" - source: hosted - version: "0.4.0" - wakelock_platform_interface: - dependency: transitive - description: - name: wakelock_platform_interface - url: "https://pub.dartlang.org" - source: hosted - version: "0.3.0" - wakelock_web: - dependency: transitive - description: - name: wakelock_web - url: "https://pub.dartlang.org" - source: hosted - version: "0.4.0" - wakelock_windows: - dependency: transitive - description: - name: wakelock_windows - url: "https://pub.dartlang.org" - source: hosted - version: "0.2.0" + version: "9.4.0" watcher: dependency: transitive description: name: watcher - url: "https://pub.dartlang.org" + sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0" + url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.0.2" web_socket_channel: dependency: transitive description: name: web_socket_channel - url: "https://pub.dartlang.org" + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.4.0" webkit_inspection_protocol: dependency: transitive description: name: webkit_inspection_protocol - url: "https://pub.dartlang.org" + sha256: "67d3a8b6c79e1987d19d848b0892e582dbb0c66c57cc1fef58a177dd2aa2823d" + url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.2.0" win32: dependency: transitive description: name: win32 - url: "https://pub.dartlang.org" + sha256: "5a751eddf9db89b3e5f9d50c20ab8612296e4e8db69009788d6c8b060a84191c" + url: "https://pub.dev" source: hosted - version: "2.6.1" + version: "4.1.4" + win32_registry: + dependency: transitive + description: + name: win32_registry + sha256: "1c52f994bdccb77103a6231ad4ea331a244dbcef5d1f37d8462f713143b0bfae" + url: "https://pub.dev" + source: hosted + version: "1.1.0" xdg_directories: dependency: transitive description: name: xdg_directories - url: "https://pub.dartlang.org" + sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86 + url: "https://pub.dev" source: hosted - version: "0.2.0+1" + version: "0.2.0+3" xml: dependency: transitive description: name: xml - url: "https://pub.dartlang.org" + sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" + url: "https://pub.dev" source: hosted - version: "5.4.1" + version: "6.3.0" yaml: dependency: transitive description: name: yaml - url: "https://pub.dartlang.org" + sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370" + url: "https://pub.dev" source: hosted version: "3.1.1" sdks: - dart: ">=2.17.0 <3.0.0" - flutter: ">=3.0.0" + dart: ">=3.0.2 <4.0.0" + flutter: ">=3.10.2" diff --git a/pubspec.yaml b/pubspec.yaml index 71a5f2f9..1d58f581 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,58 +1,64 @@ name: selfprivacy description: selfprivacy.org publish_to: 'none' -version: 0.6.0+15 +version: 0.9.1+19 environment: - sdk: '>=2.17.0 <3.0.0' - flutter: ">=3.0.0" + sdk: '>=3.0.2 <4.0.0' + flutter: ">=3.10.2" dependencies: + animations: ^2.0.7 + auto_route: ^7.3.2 auto_size_text: ^3.0.0 - basic_utils: ^4.2.0 - crypt: ^4.2.1 + crypt: ^4.3.0 cubit_form: ^2.0.1 - cupertino_icons: ^1.0.4 - device_info_plus: ^3.2.3 - dio: ^4.0.4 - dynamic_color: ^1.2.2 - easy_localization: ^3.0.0 + device_info_plus: ^9.0.2 + dio: ^5.1.2 + duration: 3.0.12 + dynamic_color: ^1.6.5 + easy_localization: ^3.0.2 either_option: ^2.0.1-dev.1 - equatable: ^2.0.3 - fl_chart: ^0.50.1 + equatable: ^2.0.5 + fl_chart: ^0.62.0 flutter: sdk: flutter - flutter_bloc: ^8.0.1 - flutter_markdown: ^0.6.9 - flutter_secure_storage: ^5.0.2 - get_it: ^7.2.0 - gtk_theme_fl: ^0.0.1 - hive: ^2.0.5 + flutter_bloc: ^8.1.3 + flutter_markdown: ^0.6.14 + flutter_secure_storage: ^8.0.0 + flutter_svg: ^2.0.6 + get_it: ^7.6.0 + gql: ^1.0.0 + graphql: ^5.1.2 + graphql_codegen: ^0.13.0 + graphql_flutter: ^5.1.2 + hive: ^2.2.3 hive_flutter: ^1.1.0 - intl: ^0.17.0 - ionicons: ^0.1.2 - json_annotation: ^4.4.0 - local_auth: ^2.0.2 - modal_bottom_sheet: ^2.0.1 + http: ^0.13.5 + intl: ^0.18.0 + ionicons: ^0.2.2 + json_annotation: ^4.8.1 + local_auth: ^2.1.6 + material_color_utilities: ^0.2.0 + modal_bottom_sheet: ^3.0.0-pre nanoid: ^1.0.0 package_info: ^2.0.2 - pretty_dio_logger: ^1.2.0-beta-1 - provider: ^6.0.2 - pub_semver: ^2.1.1 - share_plus: ^4.0.4 - ssh_key: ^0.7.1 - system_theme: ^2.0.0 - timezone: ^0.8.0 - url_launcher: ^6.0.20 - wakelock: ^0.6.1+1 + pretty_dio_logger: ^1.3.1 + provider: ^6.0.5 + pub_semver: ^2.1.4 + timezone: ^0.9.2 + url_launcher: ^6.1.11 + # TODO: Developer is not available, update later. +# wakelock: ^0.6.2 dev_dependencies: + auto_route_generator: ^7.1.1 flutter_test: sdk: flutter - build_runner: ^2.1.1 - flutter_launcher_icons: ^0.9.2 - hive_generator: ^1.0.0 - json_serializable: ^6.1.4 + build_runner: ^2.4.4 + flutter_launcher_icons: ^0.13.1 + hive_generator: ^2.0.0 + json_serializable: ^6.7.0 flutter_lints: ^2.0.1 flutter_icons: diff --git a/shell.nix b/shell.nix new file mode 100644 index 00000000..f7cbc91a --- /dev/null +++ b/shell.nix @@ -0,0 +1,35 @@ +with (import { }); + +mkShell { + buildInputs = [ + at-spi2-core.dev + clang + cmake + dart + dbus.dev + flutter + gtk3 + libdatrie + libepoxy.dev + libselinux + libsepol + libthai + libxkbcommon + libsecret + ninja + pcre + pkg-config + util-linux.dev + xorg.libXdmcp + xorg.libXtst + xorg.libX11 + + glib + jsoncpp + libgcrypt + libgpg-error + ]; + shellHook = '' + export LD_LIBRARY_PATH=${libepoxy}/lib + ''; +} diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 37b7695e..4ff4b899 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,15 +6,21 @@ #include "generated_plugin_registrant.h" +#include +#include #include -#include +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { + ConnectivityPlusWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); + DynamicColorPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("DynamicColorPluginCApi")); FlutterSecureStorageWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); - SystemThemePluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("SystemThemePlugin")); + LocalAuthPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("LocalAuthPlugin")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index a1aaa278..ae5ef358 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,8 +3,10 @@ # list(APPEND FLUTTER_PLUGIN_LIST + connectivity_plus + dynamic_color flutter_secure_storage_windows - system_theme + local_auth_windows url_launcher_windows ) diff --git a/windows/runner/CMakeLists.txt b/windows/runner/CMakeLists.txt index b9e550fb..17411a8a 100644 --- a/windows/runner/CMakeLists.txt +++ b/windows/runner/CMakeLists.txt @@ -20,6 +20,13 @@ add_executable(${BINARY_NAME} WIN32 # that need different build settings. apply_standard_settings(${BINARY_NAME}) +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + # Disable Windows macros that collide with C++ standard library functions. target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") diff --git a/windows/runner/Runner.rc b/windows/runner/Runner.rc index e2d13c33..ed5ecffb 100644 --- a/windows/runner/Runner.rc +++ b/windows/runner/Runner.rc @@ -60,14 +60,14 @@ IDI_APP_ICON ICON "resources\\app_icon.ico" // Version // -#ifdef FLUTTER_BUILD_NUMBER -#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD #else -#define VERSION_AS_NUMBER 1,0,0 +#define VERSION_AS_NUMBER 1,0,0,0 #endif -#ifdef FLUTTER_BUILD_NAME -#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION #else #define VERSION_AS_STRING "1.0.0" #endif @@ -89,11 +89,11 @@ BEGIN BEGIN BLOCK "040904e4" BEGIN - VALUE "CompanyName", "pro.kherel" "\0" + VALUE "CompanyName", "org.selfprivacy" "\0" VALUE "FileDescription", "selfprivacy" "\0" VALUE "FileVersion", VERSION_AS_STRING "\0" VALUE "InternalName", "selfprivacy" "\0" - VALUE "LegalCopyright", "Copyright (C) 2022 pro.kherel. All rights reserved." "\0" + VALUE "LegalCopyright", "Copyright (C) 2022 org.selfprivacy. All rights reserved." "\0" VALUE "OriginalFilename", "selfprivacy.exe" "\0" VALUE "ProductName", "selfprivacy" "\0" VALUE "ProductVersion", VERSION_AS_STRING "\0" diff --git a/windows/runner/resources/app_icon.ico b/windows/runner/resources/app_icon.ico index c04e20ca..1919b8cd 100644 Binary files a/windows/runner/resources/app_icon.ico and b/windows/runner/resources/app_icon.ico differ diff --git a/windows/runner/runner.exe.manifest b/windows/runner/runner.exe.manifest index c977c4a4..a42ea768 100644 --- a/windows/runner/runner.exe.manifest +++ b/windows/runner/runner.exe.manifest @@ -7,7 +7,7 @@ - +