Compare commits
1 Commits
auth-v2.0.
...
update_doc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6146350aae |
0
server/.gitattributes → .gitattributes
vendored
0
server/.gitattributes → .gitattributes
vendored
8
.github/workflows/auth-crowdin.yml
vendored
8
.github/workflows/auth-crowdin.yml
vendored
@@ -2,15 +2,15 @@ name: "Sync Crowdin translations (auth)"
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
|
||||||
paths:
|
paths:
|
||||||
# Run workflow when auth's intl_en.arb is changed
|
# Run workflow when auth's intl_en.arb is changed
|
||||||
- "mobile/lib/l10n/arb/app_en.arb"
|
- "mobile/lib/l10n/arb/app_en.arb"
|
||||||
# Or the workflow itself is changed
|
# Or the workflow itself is changed
|
||||||
- ".github/workflows/auth-crowdin.yml"
|
- ".github/workflows/auth-crowdin.yml"
|
||||||
|
branches: [main]
|
||||||
schedule:
|
schedule:
|
||||||
# See: [Note: Run workflow on specific days of the week]
|
# See: [Note: Run every 24 hours]
|
||||||
- cron: "50 1 * * 2,5"
|
- cron: "50 1 * * *"
|
||||||
# Also allow manually running the workflow
|
# Also allow manually running the workflow
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ jobs:
|
|||||||
base_path: "auth/"
|
base_path: "auth/"
|
||||||
config: "auth/crowdin.yml"
|
config: "auth/crowdin.yml"
|
||||||
upload_sources: true
|
upload_sources: true
|
||||||
upload_translations: false
|
upload_translations: true
|
||||||
download_translations: true
|
download_translations: true
|
||||||
localization_branch_name: crowdin-translations-auth
|
localization_branch_name: crowdin-translations-auth
|
||||||
create_pull_request: true
|
create_pull_request: true
|
||||||
|
|||||||
4
.github/workflows/auth-lint.yml
vendored
4
.github/workflows/auth-lint.yml
vendored
@@ -3,13 +3,13 @@ name: "Lint (auth)"
|
|||||||
on:
|
on:
|
||||||
# Run on every push to a branch other than main that changes auth/
|
# Run on every push to a branch other than main that changes auth/
|
||||||
push:
|
push:
|
||||||
branches-ignore: [main, "deploy/**"]
|
branches-ignore: [main]
|
||||||
paths:
|
paths:
|
||||||
- "auth/**"
|
- "auth/**"
|
||||||
- ".github/workflows/auth-lint.yml"
|
- ".github/workflows/auth-lint.yml"
|
||||||
|
|
||||||
env:
|
env:
|
||||||
FLUTTER_VERSION: "3.19.3"
|
FLUTTER_VERSION: "3.16.9"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint:
|
lint:
|
||||||
|
|||||||
28
.github/workflows/auth-release.yml
vendored
28
.github/workflows/auth-release.yml
vendored
@@ -29,11 +29,11 @@ on:
|
|||||||
- "auth-v*"
|
- "auth-v*"
|
||||||
|
|
||||||
env:
|
env:
|
||||||
FLUTTER_VERSION: "3.19.3"
|
FLUTTER_VERSION: "3.13.4"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-ubuntu:
|
build-ubuntu:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
@@ -72,8 +72,6 @@ jobs:
|
|||||||
SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }}
|
SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }}
|
||||||
|
|
||||||
- name: Build PlayStore AAB
|
- name: Build PlayStore AAB
|
||||||
# disable this step if release tag contains nightly or beta
|
|
||||||
if: startsWith(github.ref, 'refs/tags/auth-v') && !contains(github.ref, 'nightly') && !contains(github.ref, 'beta')
|
|
||||||
run: |
|
run: |
|
||||||
flutter build appbundle --release --flavor playstore --dart-define=app.flavor=playstore
|
flutter build appbundle --release --flavor playstore --dart-define=app.flavor=playstore
|
||||||
env:
|
env:
|
||||||
@@ -85,7 +83,7 @@ jobs:
|
|||||||
- name: Install dependencies for desktop build
|
- name: Install dependencies for desktop build
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -y
|
sudo apt-get update -y
|
||||||
sudo apt-get install -y libsecret-1-dev libsodium-dev libwebkit2gtk-4.0-dev libfuse2 ninja-build libgtk-3-dev dpkg-dev pkg-config rpm libsqlite3-dev locate appindicator3-0.1 libappindicator3-dev
|
sudo apt-get install -y libsecret-1-dev libsodium-dev libwebkit2gtk-4.0-dev libfuse2 ninja-build libgtk-3-dev dpkg-dev pkg-config rpm libsqlite3-dev locate
|
||||||
|
|
||||||
- name: Install appimagetool
|
- name: Install appimagetool
|
||||||
run: |
|
run: |
|
||||||
@@ -94,6 +92,8 @@ jobs:
|
|||||||
mv appimagetool /usr/local/bin/
|
mv appimagetool /usr/local/bin/
|
||||||
|
|
||||||
- name: Build desktop app
|
- name: Build desktop app
|
||||||
|
# Temporarily disable desktop builds
|
||||||
|
if: false
|
||||||
run: |
|
run: |
|
||||||
flutter config --enable-linux-desktop
|
flutter config --enable-linux-desktop
|
||||||
dart pub global activate flutter_distributor
|
dart pub global activate flutter_distributor
|
||||||
@@ -118,8 +118,6 @@ jobs:
|
|||||||
updateOnlyUnreleased: true
|
updateOnlyUnreleased: true
|
||||||
|
|
||||||
- name: Upload AAB to PlayStore
|
- name: Upload AAB to PlayStore
|
||||||
# disable this step if release tag contains nightly or beta
|
|
||||||
if: startsWith(github.ref, 'refs/tags/auth-v') && !contains(github.ref, 'nightly') && !contains(github.ref, 'beta')
|
|
||||||
uses: r0adkll/upload-google-play@v1
|
uses: r0adkll/upload-google-play@v1
|
||||||
with:
|
with:
|
||||||
serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
|
serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
|
||||||
@@ -151,6 +149,8 @@ jobs:
|
|||||||
run: mkdir artifacts
|
run: mkdir artifacts
|
||||||
|
|
||||||
- name: Build Windows installer
|
- name: Build Windows installer
|
||||||
|
# Temporarily disable desktop builds
|
||||||
|
if: false
|
||||||
run: |
|
run: |
|
||||||
flutter config --enable-windows-desktop
|
flutter config --enable-windows-desktop
|
||||||
dart pub global activate flutter_distributor
|
dart pub global activate flutter_distributor
|
||||||
@@ -159,9 +159,13 @@ jobs:
|
|||||||
mv dist/**/ente_auth-*-windows-setup.exe artifacts/ente-${{ github.ref_name }}-installer.exe
|
mv dist/**/ente_auth-*-windows-setup.exe artifacts/ente-${{ github.ref_name }}-installer.exe
|
||||||
|
|
||||||
- name: Retain Windows EXE and DLLs
|
- name: Retain Windows EXE and DLLs
|
||||||
|
# Temporarily disable desktop builds
|
||||||
|
if: false
|
||||||
run: cp -r build/windows/x64/runner/Release ente-${{ github.ref_name }}-windows
|
run: cp -r build/windows/x64/runner/Release ente-${{ github.ref_name }}-windows
|
||||||
|
|
||||||
- name: Code sign Windows installer and EXE
|
- name: Code sign Windows installer and EXE
|
||||||
|
# Temporarily disable desktop builds
|
||||||
|
if: false
|
||||||
uses: dlemstra/code-sign-action@v1
|
uses: dlemstra/code-sign-action@v1
|
||||||
with:
|
with:
|
||||||
certificate: "${{ secrets.WINDOWS_CERTIFICATE }}"
|
certificate: "${{ secrets.WINDOWS_CERTIFICATE }}"
|
||||||
@@ -171,7 +175,9 @@ jobs:
|
|||||||
auth/ente-${{ github.ref_name }}-windows/auth.exe
|
auth/ente-${{ github.ref_name }}-windows/auth.exe
|
||||||
|
|
||||||
- name: Zip Windows EXE and DLLs
|
- name: Zip Windows EXE and DLLs
|
||||||
run: tar.exe -a -c -f artifacts/ente-${{ github.ref_name }}-windows.zip ente-${{ github.ref_name }}-windows
|
# Temporarily disable desktop builds
|
||||||
|
if: false
|
||||||
|
run: tar.exe -a -c -f auth/artifacts/ente-${{ github.ref_name }}-windows.zip auth/ente-${{ github.ref_name }}-windows
|
||||||
|
|
||||||
- name: Create a draft GitHub release
|
- name: Create a draft GitHub release
|
||||||
uses: ncipollo/release-action@v1
|
uses: ncipollo/release-action@v1
|
||||||
@@ -242,6 +248,8 @@ jobs:
|
|||||||
run: mkdir artifacts
|
run: mkdir artifacts
|
||||||
|
|
||||||
- name: Build macOS DMG
|
- name: Build macOS DMG
|
||||||
|
# Temporarily disable desktop builds
|
||||||
|
if: false
|
||||||
run: |
|
run: |
|
||||||
flutter config --enable-macos-desktop
|
flutter config --enable-macos-desktop
|
||||||
dart pub global activate flutter_distributor
|
dart pub global activate flutter_distributor
|
||||||
@@ -249,12 +257,16 @@ jobs:
|
|||||||
mv dist/**/ente_auth-*-macos.dmg artifacts/ente-${{ github.ref_name }}.dmg
|
mv dist/**/ente_auth-*-macos.dmg artifacts/ente-${{ github.ref_name }}.dmg
|
||||||
|
|
||||||
- name: Code sign DMG
|
- name: Code sign DMG
|
||||||
|
# Temporarily disable desktop builds
|
||||||
|
if: false
|
||||||
run: |
|
run: |
|
||||||
CERT_NAME=$(security find-identity -v -p codesigning | grep "Developer ID Application" | awk -F'"' '{print $2}' | grep -m1 "")
|
CERT_NAME=$(security find-identity -v -p codesigning | grep "Developer ID Application" | awk -F'"' '{print $2}' | grep -m1 "")
|
||||||
codesign --force --timestamp --sign "$CERT_NAME" --options runtime artifacts/ente-${{ github.ref_name }}.dmg
|
codesign --force --timestamp --sign "$CERT_NAME" --options runtime artifacts/ente-${{ github.ref_name }}.dmg
|
||||||
codesign --verify --verbose=4 artifacts/ente-${{ github.ref_name }}.dmg
|
codesign --verify --verbose=4 artifacts/ente-${{ github.ref_name }}.dmg
|
||||||
|
|
||||||
- name: Notarize and staple DMG
|
- name: Notarize and staple DMG
|
||||||
|
# Temporarily disable desktop builds
|
||||||
|
if: false
|
||||||
run: |
|
run: |
|
||||||
xcrun notarytool submit artifacts/ente-${{ github.ref_name }}.dmg \
|
xcrun notarytool submit artifacts/ente-${{ github.ref_name }}.dmg \
|
||||||
--wait \
|
--wait \
|
||||||
|
|||||||
24
.github/workflows/copycat-db-release.yaml
vendored
24
.github/workflows/copycat-db-release.yaml
vendored
@@ -1,24 +0,0 @@
|
|||||||
name: "Release (copycat-db)"
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch: # Run manually
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
name: Check out code
|
|
||||||
|
|
||||||
- uses: mr-smithers-excellent/docker-build-push@v6
|
|
||||||
name: Build & Push
|
|
||||||
with:
|
|
||||||
dockerfile: infra/copycat-db/Dockerfile
|
|
||||||
directory: infra/copycat-db
|
|
||||||
image: ente/copycat-db
|
|
||||||
registry: rg.fr-par.scw.cloud
|
|
||||||
enableBuildKit: true
|
|
||||||
buildArgs: GIT_COMMIT=${GITHUB_SHA}
|
|
||||||
tags: ${GITHUB_SHA}, latest
|
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
|
||||||
2
.github/workflows/docs-verify-build.yml
vendored
2
.github/workflows/docs-verify-build.yml
vendored
@@ -6,7 +6,7 @@ name: "Verify build (docs)"
|
|||||||
on:
|
on:
|
||||||
# Run on every push to a branch other than main that changes docs/
|
# Run on every push to a branch other than main that changes docs/
|
||||||
push:
|
push:
|
||||||
branches-ignore: [main, "deploy/**"]
|
branches-ignore: [main]
|
||||||
paths:
|
paths:
|
||||||
- "docs/**"
|
- "docs/**"
|
||||||
- ".github/workflows/docs-verify-build.yml"
|
- ".github/workflows/docs-verify-build.yml"
|
||||||
|
|||||||
8
.github/workflows/mobile-crowdin.yml
vendored
8
.github/workflows/mobile-crowdin.yml
vendored
@@ -2,15 +2,15 @@ name: "Sync Crowdin translations (mobile)"
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
|
||||||
paths:
|
paths:
|
||||||
# Run workflow when mobiles's intl_en.arb is changed
|
# Run workflow when mobiles's intl_en.arb is changed
|
||||||
- "mobile/lib/l10n/intl_en.arb"
|
- "mobile/lib/l10n/intl_en.arb"
|
||||||
# Or the workflow itself is changed
|
# Or the workflow itself is changed
|
||||||
- ".github/workflows/mobile-crowdin.yml"
|
- ".github/workflows/mobile-crowdin.yml"
|
||||||
|
branches: [main]
|
||||||
schedule:
|
schedule:
|
||||||
# See: [Note: Run workflow on specific days of the week]
|
# See: [Note: Run every 24 hours]
|
||||||
- cron: "40 1 * * 2,5"
|
- cron: "40 1 * * *"
|
||||||
# Also allow manually running the workflow
|
# Also allow manually running the workflow
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ jobs:
|
|||||||
base_path: "mobile/"
|
base_path: "mobile/"
|
||||||
config: "mobile/crowdin.yml"
|
config: "mobile/crowdin.yml"
|
||||||
upload_sources: true
|
upload_sources: true
|
||||||
upload_translations: false
|
upload_translations: true
|
||||||
download_translations: true
|
download_translations: true
|
||||||
localization_branch_name: crowdin-translations-mobile
|
localization_branch_name: crowdin-translations-mobile
|
||||||
create_pull_request: true
|
create_pull_request: true
|
||||||
|
|||||||
2
.github/workflows/mobile-lint.yml
vendored
2
.github/workflows/mobile-lint.yml
vendored
@@ -3,7 +3,7 @@ name: "Lint (mobile)"
|
|||||||
on:
|
on:
|
||||||
# Run on every push to a branch other than main that changes mobile/
|
# Run on every push to a branch other than main that changes mobile/
|
||||||
push:
|
push:
|
||||||
branches-ignore: [main, f-droid, "deploy/**"]
|
branches-ignore: [main]
|
||||||
paths:
|
paths:
|
||||||
- "mobile/**"
|
- "mobile/**"
|
||||||
- ".github/workflows/mobile-lint.yml"
|
- ".github/workflows/mobile-lint.yml"
|
||||||
|
|||||||
2
.github/workflows/mobile-release.yml
vendored
2
.github/workflows/mobile-release.yml
vendored
@@ -49,7 +49,7 @@ jobs:
|
|||||||
SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD_PHOTOS }}
|
SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD_PHOTOS }}
|
||||||
|
|
||||||
- name: Checksum
|
- name: Checksum
|
||||||
run: sha256sum build/app/outputs/flutter-apk/ente-${{ github.ref_name }}.apk > build/app/outputs/flutter-apk/sha256sum
|
run: sha256sum build/app/outputs/flutter-apk/ente.apk > build/app/outputs/flutter-apk/sha256sum
|
||||||
|
|
||||||
- name: Create a draft GitHub release
|
- name: Create a draft GitHub release
|
||||||
uses: ncipollo/release-action@v1
|
uses: ncipollo/release-action@v1
|
||||||
|
|||||||
2
.github/workflows/server-lint.yml
vendored
2
.github/workflows/server-lint.yml
vendored
@@ -3,7 +3,7 @@ name: "Lint (server)"
|
|||||||
on:
|
on:
|
||||||
# Run on every push to a branch other than main that changes server/
|
# Run on every push to a branch other than main that changes server/
|
||||||
push:
|
push:
|
||||||
branches-ignore: [main, "deploy/**"]
|
branches-ignore: [main]
|
||||||
paths:
|
paths:
|
||||||
- "server/**"
|
- "server/**"
|
||||||
- ".github/workflows/server-lint.yml"
|
- ".github/workflows/server-lint.yml"
|
||||||
|
|||||||
14
.github/workflows/web-crowdin.yml
vendored
14
.github/workflows/web-crowdin.yml
vendored
@@ -2,21 +2,15 @@ name: "Sync Crowdin translations (web)"
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
|
||||||
paths:
|
paths:
|
||||||
# Run workflow when web's en-US/translation.json is changed
|
# Run workflow when web's en-US/translation.json is changed
|
||||||
- "web/apps/photos/public/locales/en-US/translation.json"
|
- "web/apps/photos/public/locales/en-US/translation.json"
|
||||||
# Or the workflow itself is changed
|
# Or the workflow itself is changed
|
||||||
- ".github/workflows/web-crowdin.yml"
|
- ".github/workflows/web-crowdin.yml"
|
||||||
|
branches: [main]
|
||||||
schedule:
|
schedule:
|
||||||
# [Note: Run workflow on specific days of the week]
|
# See: [Note: Run every 24 hours]
|
||||||
#
|
- cron: "20 1 * * *"
|
||||||
# The last (5th) component of the cron syntax denotes the day of the
|
|
||||||
# week, with 0 == SUN and 6 == SAT. So, for example, to run on every TUE
|
|
||||||
# and FRI, this can be set to `2,5`.
|
|
||||||
#
|
|
||||||
# See also: [Note: Run workflow every 24 hours]
|
|
||||||
- cron: "20 1 * * 2,5"
|
|
||||||
# Also allow manually running the workflow
|
# Also allow manually running the workflow
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
@@ -34,7 +28,7 @@ jobs:
|
|||||||
base_path: "web/"
|
base_path: "web/"
|
||||||
config: "web/crowdin.yml"
|
config: "web/crowdin.yml"
|
||||||
upload_sources: true
|
upload_sources: true
|
||||||
upload_translations: false
|
upload_translations: true
|
||||||
download_translations: true
|
download_translations: true
|
||||||
localization_branch_name: crowdin-translations-web
|
localization_branch_name: crowdin-translations-web
|
||||||
create_pull_request: true
|
create_pull_request: true
|
||||||
|
|||||||
2
.github/workflows/web-lint.yml
vendored
2
.github/workflows/web-lint.yml
vendored
@@ -3,7 +3,7 @@ name: "Lint (web)"
|
|||||||
on:
|
on:
|
||||||
# Run on every push to a branch other than main that changes web/
|
# Run on every push to a branch other than main that changes web/
|
||||||
push:
|
push:
|
||||||
branches-ignore: [main, "deploy/**"]
|
branches-ignore: [main]
|
||||||
paths:
|
paths:
|
||||||
- "web/**"
|
- "web/**"
|
||||||
- ".github/workflows/web-lint.yml"
|
- ".github/workflows/web-lint.yml"
|
||||||
|
|||||||
2
.github/workflows/web-nightly.yml
vendored
2
.github/workflows/web-nightly.yml
vendored
@@ -2,7 +2,7 @@ name: "Nightly (web)"
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
# [Note: Run workflow every 24 hours]
|
# [Note: Run every 24 hours]
|
||||||
#
|
#
|
||||||
# Run every 24 hours - First field is minute, second is hour of the day
|
# Run every 24 hours - First field is minute, second is hour of the day
|
||||||
# This runs 23:15 UTC everyday - 1 and 15 are just arbitrary offset to
|
# This runs 23:15 UTC everyday - 1 and 15 are just arbitrary offset to
|
||||||
|
|||||||
7
.gitmodules
vendored
7
.gitmodules
vendored
@@ -9,9 +9,16 @@
|
|||||||
[submodule "auth/assets/simple-icons"]
|
[submodule "auth/assets/simple-icons"]
|
||||||
path = auth/assets/simple-icons
|
path = auth/assets/simple-icons
|
||||||
url = https://github.com/simple-icons/simple-icons.git
|
url = https://github.com/simple-icons/simple-icons.git
|
||||||
|
[submodule "mobile/thirdparty/flutter"]
|
||||||
|
path = mobile/thirdparty/flutter
|
||||||
|
url = https://github.com/flutter/flutter.git
|
||||||
|
branch = stable
|
||||||
[submodule "mobile/plugins/clip_ggml"]
|
[submodule "mobile/plugins/clip_ggml"]
|
||||||
path = mobile/plugins/clip_ggml
|
path = mobile/plugins/clip_ggml
|
||||||
url = https://github.com/ente-io/clip-ggml.git
|
url = https://github.com/ente-io/clip-ggml.git
|
||||||
|
[submodule "mobile/thirdparty/isar"]
|
||||||
|
path = mobile/thirdparty/isar
|
||||||
|
url = https://github.com/isar/isar
|
||||||
[submodule "web/apps/photos/thirdparty/ffmpeg-wasm"]
|
[submodule "web/apps/photos/thirdparty/ffmpeg-wasm"]
|
||||||
path = web/apps/photos/thirdparty/ffmpeg-wasm
|
path = web/apps/photos/thirdparty/ffmpeg-wasm
|
||||||
url = https://github.com/abhinavkgrd/ffmpeg.wasm.git
|
url = https://github.com/abhinavkgrd/ffmpeg.wasm.git
|
||||||
|
|||||||
9
auth/.gitignore
vendored
9
auth/.gitignore
vendored
@@ -9,20 +9,12 @@
|
|||||||
.history
|
.history
|
||||||
.svn/
|
.svn/
|
||||||
|
|
||||||
# Editors
|
|
||||||
.vscode/
|
|
||||||
|
|
||||||
# IntelliJ related
|
# IntelliJ related
|
||||||
*.iml
|
*.iml
|
||||||
*.ipr
|
*.ipr
|
||||||
*.iws
|
*.iws
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
# The .vscode folder contains launch configuration and tasks you configure in
|
|
||||||
# VS Code which you may wish to be included in version control, so this line
|
|
||||||
# is commented out by default.
|
|
||||||
#.vscode/
|
|
||||||
|
|
||||||
# Flutter/Dart/Pub related
|
# Flutter/Dart/Pub related
|
||||||
**/doc/api/
|
**/doc/api/
|
||||||
.dart_tool/
|
.dart_tool/
|
||||||
@@ -40,4 +32,3 @@ lib/generated_plugin_registrant.dart
|
|||||||
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
|
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
|
||||||
|
|
||||||
android/key.properties
|
android/key.properties
|
||||||
dist/
|
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
# This file tracks properties of this Flutter project.
|
# This file tracks properties of this Flutter project.
|
||||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||||
#
|
#
|
||||||
# This file should be version controlled and should not be manually edited.
|
# This file should be version controlled.
|
||||||
|
|
||||||
version:
|
version:
|
||||||
revision: "ba393198430278b6595976de84fe170f553cc728"
|
revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851
|
||||||
channel: "[user-branch]"
|
channel: unknown
|
||||||
|
|
||||||
project_type: app
|
project_type: app
|
||||||
|
|
||||||
@@ -13,26 +13,17 @@ project_type: app
|
|||||||
migration:
|
migration:
|
||||||
platforms:
|
platforms:
|
||||||
- platform: root
|
- platform: root
|
||||||
create_revision: ba393198430278b6595976de84fe170f553cc728
|
create_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851
|
||||||
base_revision: ba393198430278b6595976de84fe170f553cc728
|
base_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851
|
||||||
- platform: android
|
|
||||||
create_revision: ba393198430278b6595976de84fe170f553cc728
|
|
||||||
base_revision: ba393198430278b6595976de84fe170f553cc728
|
|
||||||
- platform: ios
|
|
||||||
create_revision: ba393198430278b6595976de84fe170f553cc728
|
|
||||||
base_revision: ba393198430278b6595976de84fe170f553cc728
|
|
||||||
- platform: linux
|
- platform: linux
|
||||||
create_revision: ba393198430278b6595976de84fe170f553cc728
|
create_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851
|
||||||
base_revision: ba393198430278b6595976de84fe170f553cc728
|
base_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851
|
||||||
- platform: macos
|
- platform: macos
|
||||||
create_revision: ba393198430278b6595976de84fe170f553cc728
|
create_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851
|
||||||
base_revision: ba393198430278b6595976de84fe170f553cc728
|
base_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851
|
||||||
- platform: web
|
|
||||||
create_revision: ba393198430278b6595976de84fe170f553cc728
|
|
||||||
base_revision: ba393198430278b6595976de84fe170f553cc728
|
|
||||||
- platform: windows
|
- platform: windows
|
||||||
create_revision: ba393198430278b6595976de84fe170f553cc728
|
create_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851
|
||||||
base_revision: ba393198430278b6595976de84fe170f553cc728
|
base_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851
|
||||||
|
|
||||||
# User provided section
|
# User provided section
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ if (keystorePropertiesFile.exists()) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 34
|
compileSdkVersion 33
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
main.java.srcDirs += 'src/main/kotlin'
|
main.java.srcDirs += 'src/main/kotlin'
|
||||||
@@ -46,7 +46,7 @@ android {
|
|||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "io.ente.auth"
|
applicationId "io.ente.auth"
|
||||||
minSdkVersion 21
|
minSdkVersion 20
|
||||||
targetSdkVersion 33
|
targetSdkVersion 33
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
@@ -56,11 +56,11 @@ android {
|
|||||||
|
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
release {
|
release {
|
||||||
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : System.getenv("SIGNING_KEY_PATH") ? file(System.getenv("SIGNING_KEY_PATH")) : null
|
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : System.getenv("SIGNING_KEY_PATH") ? file(System.getenv("SIGNING_KEY_PATH")) : null
|
||||||
keyAlias keystoreProperties['keyAlias'] ? keystoreProperties['keyAlias'] : System.getenv("SIGNING_KEY_ALIAS")
|
keyAlias keystoreProperties['keyAlias'] ? keystoreProperties['keyAlias'] : System.getenv("SIGNING_KEY_ALIAS")
|
||||||
keyPassword keystoreProperties['keyPassword'] ? keystoreProperties['keyPassword'] : System.getenv("SIGNING_KEY_PASSWORD")
|
keyPassword keystoreProperties['keyPassword'] ? keystoreProperties['keyPassword'] : System.getenv("SIGNING_KEY_PASSWORD")
|
||||||
storePassword keystoreProperties['storePassword'] ? keystoreProperties['storePassword'] : System.getenv("SIGNING_STORE_PASSWORD")
|
storePassword keystoreProperties['storePassword'] ? keystoreProperties['storePassword'] : System.getenv("SIGNING_STORE_PASSWORD")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flavorDimensions "default"
|
flavorDimensions "default"
|
||||||
@@ -109,7 +109,6 @@ dependencies {
|
|||||||
implementation 'io.sentry:sentry-android:2.0.0'
|
implementation 'io.sentry:sentry-android:2.0.0'
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
implementation 'com.android.support:multidex:1.0.3'
|
implementation 'com.android.support:multidex:1.0.3'
|
||||||
implementation 'com.google.guava:guava:28.2-android'
|
|
||||||
implementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava'
|
implementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava'
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
androidTestImplementation 'androidx.test:runner:1.1.1'
|
androidTestImplementation 'androidx.test:runner:1.1.1'
|
||||||
|
|||||||
@@ -62,11 +62,6 @@
|
|||||||
{
|
{
|
||||||
"title": "Crowdpear"
|
"title": "Crowdpear"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"title": "DCS",
|
|
||||||
"altNames": ["Digital Combat Simulator"],
|
|
||||||
"slug": "dcs"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"title": "DEGIRO"
|
"title": "DEGIRO"
|
||||||
},
|
},
|
||||||
@@ -272,10 +267,6 @@
|
|||||||
"title": "Revolt",
|
"title": "Revolt",
|
||||||
"hex": "858585"
|
"hex": "858585"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"title": "Rockstar Games",
|
|
||||||
"slug": "rockstar_games"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"title": "Rust Language Forum",
|
"title": "Rust Language Forum",
|
||||||
"slug": "rust_language_forum",
|
"slug": "rust_language_forum",
|
||||||
@@ -365,10 +356,6 @@
|
|||||||
{
|
{
|
||||||
"title": "Wise"
|
"title": "Wise"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"title": "WYZE",
|
|
||||||
"slug": "wyze"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"title": "X",
|
"title": "X",
|
||||||
"altNames": ["twitter"],
|
"altNames": ["twitter"],
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg width="100%" height="100%" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;">
|
|
||||||
<g transform="matrix(1.59257,0,0,1.59257,0,9.06171)">
|
|
||||||
<path d="M0.87,0.08L3.17,0.08C4.2,0.08 5.13,0.51 5.13,1.67C5.13,2.92 3.91,3.6 2.78,3.6L0.06,3.6C0.06,3.6 0.87,0.09 0.87,0.08ZM2.85,2.82C3.44,2.82 4.14,2.39 4.14,1.74C4.14,1.19 3.7,0.85 3.17,0.85L1.71,0.85L1.26,2.81L2.85,2.81L2.85,2.82Z" style="fill:rgb(245,158,15);fill-rule:nonzero;stroke:rgb(245,158,15);stroke-width:0.09px;"/>
|
|
||||||
<path d="M11.95,1.33C11.87,1.31 11.65,1.26 11.65,1.14C11.65,0.81 12.52,0.81 12.74,0.81C13.25,0.81 13.96,0.91 14.4,1.17L15,0.59C14.39,0.18 13.59,0.04 12.86,0.04C12.07,0.04 10.65,0.18 10.65,1.24C10.65,2.45 13.65,1.96 13.65,2.52C13.65,2.86 12.89,2.88 12.66,2.88C11.9,2.88 11.39,2.74 10.77,2.29C10.57,2.48 10.36,2.67 10.16,2.86C10.95,3.44 11.7,3.64 12.67,3.64C13.4,3.64 14.68,3.36 14.68,2.42C14.68,1.34 12.67,1.5 11.97,1.32L11.95,1.32L11.95,1.33Z" style="fill:rgb(245,158,15);fill-rule:nonzero;stroke:rgb(245,158,15);stroke-width:0.09px;"/>
|
|
||||||
<path d="M9.18,2.35L9.16,2.35C9.07,2.43 8.41,2.95 7.67,2.87C7.14,2.81 6.41,2.44 6.41,1.95C6.41,1.15 7.26,0.83 7.94,0.83C8.39,0.83 8.82,0.93 9.17,1.18C9.48,1.07 9.8,0.97 10.11,0.86C9.59,0.3 8.82,0.07 8.06,0.07C6.92,0.07 5.43,0.73 5.43,2.06C5.43,3.22 6.65,3.63 7.61,3.63C8.41,3.63 9.18,3.4 9.78,2.88C9.78,2.88 9.18,2.38 9.17,2.37L9.16,2.37L9.18,2.35Z" style="fill:rgb(245,158,15);fill-rule:nonzero;stroke:rgb(245,158,15);stroke-width:0.09px;"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="6.525 7.459 339.266 319.582"><path fill="orange" d="M71.598 11.25H280.72c33.844 0 61.282 25.782 61.282 57.586v196.512c0 31.804-27.437 57.586-61.282 57.586H71.598c-33.845 0-61.28-25.782-61.28-57.586V68.836c0-31.804 27.435-57.586 61.28-57.586z"/><path d="M280.719 326.725H71.598c-35.881 0-65.072-27.533-65.072-61.377V68.836c0-33.844 29.19-61.377 65.072-61.377H280.72c35.88 0 65.072 27.533 65.072 61.377v196.512c0 33.844-29.192 61.377-65.073 61.377zM71.598 15.042c-31.7 0-57.49 24.131-57.49 53.794v196.512c0 29.662 25.79 53.794 57.49 53.794H280.72c31.7 0 57.49-24.132 57.49-53.794V68.836c0-29.662-25.79-53.794-57.49-53.794H71.598z"/><path d="M127.423 64.013l62.975.149c13.161-.099 22.989 2.002 29.48 6.303 7.928 5.272 11.89 14.343 11.89 27.213 0 21.19-9.828 33.195-29.482 36.012v.297c8.667 2.159 13.048 9.335 13.146 21.528 0 6.245-.233 14.245-.7 24 0 6.542 1.384 12.202 4.156 16.98H184.38c-1.611-1.747-2.416-5.305-2.416-10.677.66-8.98.99-16.347.99-22.098 0-11.617-5.582-17.425-16.746-17.425h-22.329l-9.738 46.987h-33.613l26.895-129.269zm53.236 26.941h-24.744l-6.453 31.192h26.775c14.967 0 22.497-5.906 22.595-17.721.001-8.98-6.058-13.47-18.173-13.47z"/><path d="M223.456 196.346l24.915-43.18 6.717 43.478h42.506l-38.349 27.534 6.14 43.18-33.204-26.05-44.633 27.089 20.878-45.973-24.333-26.2 39.363.122zm113.568 113.887c1.38 0 2.726.362 4.04 1.086 1.315.723 2.339 1.76 3.07 3.108.735 1.348 1.101 2.753 1.101 4.216 0 1.449-.36 2.841-1.084 4.177a7.735 7.735 0 01-3.039 3.114c-1.302.74-2.665 1.108-4.09 1.108-1.421 0-2.784-.37-4.089-1.108a7.762 7.762 0 01-3.044-3.114c-.725-1.336-1.089-2.73-1.089-4.177 0-1.463.369-2.868 1.106-4.216.738-1.348 1.763-2.384 3.077-3.108 1.316-.725 2.662-1.086 4.04-1.086zm0 1.391c-1.154 0-2.278.303-3.37.909a6.451 6.451 0 00-2.566 2.594c-.617 1.126-.926 2.297-.926 3.514 0 1.211.304 2.372.91 3.482a6.542 6.542 0 002.542 2.596c1.089.619 2.224.93 3.409.93 1.183 0 2.32-.311 3.41-.93a6.498 6.498 0 002.536-2.596c.603-1.11.904-2.27.904-3.482 0-1.218-.308-2.388-.92-3.514a6.39 6.39 0 00-2.565-2.594c-1.094-.606-2.216-.909-3.364-.909zm-3.604 11.664v-9.045h3.038c1.039 0 1.79.083 2.254.25.466.167.835.459 1.11.875.277.416.415.857.415 1.325 0 .661-.23 1.237-.692 1.726-.46.49-1.072.764-1.835.824.312.135.563.294.75.48.358.356.792.954 1.308 1.792l1.078 1.772h-1.742l-.783-1.426c-.617-1.12-1.115-1.823-1.492-2.105-.262-.209-.643-.313-1.145-.313h-.838v3.844h-1.426zm1.426-5.092h1.732c.829 0 1.393-.126 1.694-.38.3-.25.451-.586.451-1.002a1.24 1.24 0 00-.218-.718 1.302 1.302 0 00-.604-.473c-.258-.103-.734-.157-1.431-.157h-1.624v2.73z"/><path fill="#FFF" d="M252.503 221.088l25.47-18.142h-28.177l-4.881-31.464-17.882 31.168h-28.467l17.302 18.587-14.305 31.193 31.052-18.735 24.48 19.18-4.592-31.787z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 2.7 KiB |
@@ -1 +0,0 @@
|
|||||||
<svg fill="#1DF0BB" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Wyze</title><path d="M5.475 13.171 7.3 9.469h.974L5.779 14.53h-.608l-1.034-2.082-1.034 2.082h-.609L0 9.469h.973l1.826 3.673.851-1.706-.973-1.967h.973l1.825 3.702Zm8.457-3.702-2.251 3.442v1.591h-.882v-1.591L8.517 9.469h1.034l1.673 2.545 1.673-2.545h1.035Zm5.444 4.194H24v.867h-4.624v-.867Zm0-4.194H24v.868h-4.624v-.868Zm0 2.083H24v.867h-4.624v-.867Zm-.273-2.083-3.438 4.223h3.133v.838H13.84l3.407-4.222h-3.042v-.839h4.898Z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 523 B |
Binary file not shown.
|
Before Width: | Height: | Size: 2.1 KiB |
@@ -1,25 +0,0 @@
|
|||||||
output: dist/
|
|
||||||
|
|
||||||
releases:
|
|
||||||
- name: dev
|
|
||||||
jobs:
|
|
||||||
- name: release-dev-linux-zip
|
|
||||||
package:
|
|
||||||
platform: linux
|
|
||||||
target: zip
|
|
||||||
- name: release-dev-linux-deb
|
|
||||||
package:
|
|
||||||
platform: linux
|
|
||||||
target: deb
|
|
||||||
- name: release-dev-linux-appimage
|
|
||||||
package:
|
|
||||||
platform: linux
|
|
||||||
target: appimage
|
|
||||||
- name: release-dev-windows-exe
|
|
||||||
package:
|
|
||||||
platform: windows
|
|
||||||
target: exe
|
|
||||||
- name: release-dev-macos-dmg
|
|
||||||
package:
|
|
||||||
platform: macos
|
|
||||||
target: dmg
|
|
||||||
@@ -1,14 +1,7 @@
|
|||||||
# Releases
|
# Releases
|
||||||
|
|
||||||
Create a PR to bump up the version in `pubspec.yaml`.
|
Create a PR to bump up the version in `pubspec.yaml`. Once that is merged, tag
|
||||||
|
main, and push the tag.
|
||||||
> [!NOTE]
|
|
||||||
>
|
|
||||||
> Use [semver](https://semver.org/) for the tags, with `auth-` as a prefix.
|
|
||||||
> Multiple beta releases for the same upcoming version can be done by adding
|
|
||||||
> build metadata at the end, e.g. `auth-v1.2.3-beta+3`.
|
|
||||||
|
|
||||||
Once that is merged, tag main, and push the tag.
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git tag auth-v1.2.3
|
git tag auth-v1.2.3
|
||||||
@@ -23,11 +16,6 @@ This'll trigger a GitHub workflow that:
|
|||||||
* Creates a new release in the internal track on Play Store.
|
* Creates a new release in the internal track on Play Store.
|
||||||
|
|
||||||
Once the workflow completes, go to the draft GitHub release that was created.
|
Once the workflow completes, go to the draft GitHub release that was created.
|
||||||
|
|
||||||
> [!NOTE]
|
|
||||||
>
|
|
||||||
> Keep the title of the release same as the tag.
|
|
||||||
|
|
||||||
Set "Previous tag" to the last release of auth and press "Generate release
|
Set "Previous tag" to the last release of auth and press "Generate release
|
||||||
notes". The generated release note will contain all PRs and new contributors
|
notes". The generated release note will contain all PRs and new contributors
|
||||||
from all the releases in the monorepo, so you'll need to filter them to keep
|
from all the releases in the monorepo, so you'll need to filter them to keep
|
||||||
|
|||||||
@@ -37,4 +37,4 @@ file, that adheres to the above format.
|
|||||||
SUPPORT
|
SUPPORT
|
||||||
|
|
||||||
If you need help, please reach out to support@ente.io, and a human will get in touch with you.
|
If you need help, please reach out to support@ente.io, and a human will get in touch with you.
|
||||||
If you have feature requests, please create an issue @ https://github.com/ente-io/ente
|
If you have feature requests, please create an issue @ https://github.com/ente-io/auth
|
||||||
|
|||||||
Submodule auth/flutter updated: ba39319843...41456452f2
@@ -1,9 +1,7 @@
|
|||||||
PODS:
|
PODS:
|
||||||
- app_links (0.0.1):
|
- connectivity (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- connectivity_plus (0.0.1):
|
- Reachability
|
||||||
- Flutter
|
|
||||||
- ReachabilitySwift
|
|
||||||
- device_info_plus (0.0.1):
|
- device_info_plus (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- DKImagePickerController/Core (4.3.4):
|
- DKImagePickerController/Core (4.3.4):
|
||||||
@@ -47,26 +45,27 @@ PODS:
|
|||||||
- Flutter (1.0.0)
|
- Flutter (1.0.0)
|
||||||
- flutter_email_sender (0.0.1):
|
- flutter_email_sender (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- flutter_inappwebview_ios (0.0.1):
|
- flutter_inappwebview (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- flutter_inappwebview_ios/Core (= 0.0.1)
|
- flutter_inappwebview/Core (= 0.0.1)
|
||||||
- OrderedSet (~> 5.0)
|
- OrderedSet (~> 5.0)
|
||||||
- flutter_inappwebview_ios/Core (0.0.1):
|
- flutter_inappwebview/Core (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- OrderedSet (~> 5.0)
|
- OrderedSet (~> 5.0)
|
||||||
- flutter_local_authentication (1.2.0):
|
|
||||||
- Flutter
|
|
||||||
- flutter_local_notifications (0.0.1):
|
- flutter_local_notifications (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- flutter_native_splash (0.0.1):
|
- flutter_native_splash (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- flutter_secure_storage (6.0.0):
|
- flutter_secure_storage (6.0.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- flutter_sodium (0.0.1):
|
||||||
|
- Flutter
|
||||||
- fluttertoast (0.0.2):
|
- fluttertoast (0.0.2):
|
||||||
- Flutter
|
- Flutter
|
||||||
- Toast
|
- Toast
|
||||||
- local_auth_darwin (0.0.1):
|
- FMDB (2.7.5):
|
||||||
- Flutter
|
- FMDB/standard (= 2.7.5)
|
||||||
|
- FMDB/standard (2.7.5)
|
||||||
- local_auth_ios (0.0.1):
|
- local_auth_ios (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- move_to_background (0.0.1):
|
- move_to_background (0.0.1):
|
||||||
@@ -83,65 +82,46 @@ PODS:
|
|||||||
- qr_code_scanner (0.2.0):
|
- qr_code_scanner (0.2.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
- MTBBarcodeScanner
|
- MTBBarcodeScanner
|
||||||
- ReachabilitySwift (5.2.1)
|
- Reachability (3.2)
|
||||||
- SDWebImage (5.19.0):
|
- SDWebImage (5.17.0):
|
||||||
- SDWebImage/Core (= 5.19.0)
|
- SDWebImage/Core (= 5.17.0)
|
||||||
- SDWebImage/Core (5.19.0)
|
- SDWebImage/Core (5.17.0)
|
||||||
- Sentry/HybridSDK (8.21.0):
|
- Sentry/HybridSDK (8.9.1):
|
||||||
- SentryPrivate (= 8.21.0)
|
- SentryPrivate (= 8.9.1)
|
||||||
- sentry_flutter (0.0.1):
|
- sentry_flutter (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- Sentry/HybridSDK (= 8.21.0)
|
- Sentry/HybridSDK (= 8.9.1)
|
||||||
- SentryPrivate (8.21.0)
|
- SentryPrivate (8.9.1)
|
||||||
- share_plus (0.0.1):
|
- share_plus (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- shared_preferences_foundation (0.0.1):
|
- shared_preferences_foundation (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- smart_auth (0.0.1):
|
|
||||||
- Flutter
|
|
||||||
- sodium_libs (2.2.1):
|
|
||||||
- Flutter
|
|
||||||
- sqflite (0.0.3):
|
- sqflite (0.0.3):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FMDB (>= 2.7.5)
|
||||||
- sqlite3 (3.45.1):
|
|
||||||
- sqlite3/common (= 3.45.1)
|
|
||||||
- sqlite3/common (3.45.1)
|
|
||||||
- sqlite3/fts5 (3.45.1):
|
|
||||||
- sqlite3/common
|
|
||||||
- sqlite3/perf-threadsafe (3.45.1):
|
|
||||||
- sqlite3/common
|
|
||||||
- sqlite3/rtree (3.45.1):
|
|
||||||
- sqlite3/common
|
|
||||||
- sqlite3_flutter_libs (0.0.1):
|
|
||||||
- Flutter
|
|
||||||
- sqlite3 (~> 3.45.1)
|
|
||||||
- sqlite3/fts5
|
|
||||||
- sqlite3/perf-threadsafe
|
|
||||||
- sqlite3/rtree
|
|
||||||
- SwiftyGif (5.4.4)
|
- SwiftyGif (5.4.4)
|
||||||
- Toast (4.1.0)
|
- Toast (4.0.0)
|
||||||
|
- uni_links (0.0.1):
|
||||||
|
- Flutter
|
||||||
- url_launcher_ios (0.0.1):
|
- url_launcher_ios (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- app_links (from `.symlinks/plugins/app_links/ios`)
|
- connectivity (from `.symlinks/plugins/connectivity/ios`)
|
||||||
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
|
||||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||||
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
||||||
- file_saver (from `.symlinks/plugins/file_saver/ios`)
|
- file_saver (from `.symlinks/plugins/file_saver/ios`)
|
||||||
- fk_user_agent (from `.symlinks/plugins/fk_user_agent/ios`)
|
- fk_user_agent (from `.symlinks/plugins/fk_user_agent/ios`)
|
||||||
- Flutter (from `Flutter`)
|
- Flutter (from `Flutter`)
|
||||||
- flutter_email_sender (from `.symlinks/plugins/flutter_email_sender/ios`)
|
- flutter_email_sender (from `.symlinks/plugins/flutter_email_sender/ios`)
|
||||||
- flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
|
- flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`)
|
||||||
- flutter_local_authentication (from `.symlinks/plugins/flutter_local_authentication/ios`)
|
|
||||||
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
|
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
|
||||||
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
||||||
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
||||||
|
- flutter_sodium (from `.symlinks/plugins/flutter_sodium/ios`)
|
||||||
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
|
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
|
||||||
- local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`)
|
|
||||||
- local_auth_ios (from `.symlinks/plugins/local_auth_ios/ios`)
|
- local_auth_ios (from `.symlinks/plugins/local_auth_ios/ios`)
|
||||||
- move_to_background (from `.symlinks/plugins/move_to_background/ios`)
|
- move_to_background (from `.symlinks/plugins/move_to_background/ios`)
|
||||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||||
@@ -151,31 +131,27 @@ DEPENDENCIES:
|
|||||||
- sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`)
|
- sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`)
|
||||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||||
- smart_auth (from `.symlinks/plugins/smart_auth/ios`)
|
- sqflite (from `.symlinks/plugins/sqflite/ios`)
|
||||||
- sodium_libs (from `.symlinks/plugins/sodium_libs/ios`)
|
- uni_links (from `.symlinks/plugins/uni_links/ios`)
|
||||||
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
|
|
||||||
- sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`)
|
|
||||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||||
|
|
||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
trunk:
|
trunk:
|
||||||
- DKImagePickerController
|
- DKImagePickerController
|
||||||
- DKPhotoGallery
|
- DKPhotoGallery
|
||||||
|
- FMDB
|
||||||
- MTBBarcodeScanner
|
- MTBBarcodeScanner
|
||||||
- OrderedSet
|
- OrderedSet
|
||||||
- ReachabilitySwift
|
- Reachability
|
||||||
- SDWebImage
|
- SDWebImage
|
||||||
- Sentry
|
- Sentry
|
||||||
- SentryPrivate
|
- SentryPrivate
|
||||||
- sqlite3
|
|
||||||
- SwiftyGif
|
- SwiftyGif
|
||||||
- Toast
|
- Toast
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
app_links:
|
connectivity:
|
||||||
:path: ".symlinks/plugins/app_links/ios"
|
:path: ".symlinks/plugins/connectivity/ios"
|
||||||
connectivity_plus:
|
|
||||||
:path: ".symlinks/plugins/connectivity_plus/ios"
|
|
||||||
device_info_plus:
|
device_info_plus:
|
||||||
:path: ".symlinks/plugins/device_info_plus/ios"
|
:path: ".symlinks/plugins/device_info_plus/ios"
|
||||||
file_picker:
|
file_picker:
|
||||||
@@ -188,20 +164,18 @@ EXTERNAL SOURCES:
|
|||||||
:path: Flutter
|
:path: Flutter
|
||||||
flutter_email_sender:
|
flutter_email_sender:
|
||||||
:path: ".symlinks/plugins/flutter_email_sender/ios"
|
:path: ".symlinks/plugins/flutter_email_sender/ios"
|
||||||
flutter_inappwebview_ios:
|
flutter_inappwebview:
|
||||||
:path: ".symlinks/plugins/flutter_inappwebview_ios/ios"
|
:path: ".symlinks/plugins/flutter_inappwebview/ios"
|
||||||
flutter_local_authentication:
|
|
||||||
:path: ".symlinks/plugins/flutter_local_authentication/ios"
|
|
||||||
flutter_local_notifications:
|
flutter_local_notifications:
|
||||||
:path: ".symlinks/plugins/flutter_local_notifications/ios"
|
:path: ".symlinks/plugins/flutter_local_notifications/ios"
|
||||||
flutter_native_splash:
|
flutter_native_splash:
|
||||||
:path: ".symlinks/plugins/flutter_native_splash/ios"
|
:path: ".symlinks/plugins/flutter_native_splash/ios"
|
||||||
flutter_secure_storage:
|
flutter_secure_storage:
|
||||||
:path: ".symlinks/plugins/flutter_secure_storage/ios"
|
:path: ".symlinks/plugins/flutter_secure_storage/ios"
|
||||||
|
flutter_sodium:
|
||||||
|
:path: ".symlinks/plugins/flutter_sodium/ios"
|
||||||
fluttertoast:
|
fluttertoast:
|
||||||
:path: ".symlinks/plugins/fluttertoast/ios"
|
:path: ".symlinks/plugins/fluttertoast/ios"
|
||||||
local_auth_darwin:
|
|
||||||
:path: ".symlinks/plugins/local_auth_darwin/darwin"
|
|
||||||
local_auth_ios:
|
local_auth_ios:
|
||||||
:path: ".symlinks/plugins/local_auth_ios/ios"
|
:path: ".symlinks/plugins/local_auth_ios/ios"
|
||||||
move_to_background:
|
move_to_background:
|
||||||
@@ -220,58 +194,50 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/share_plus/ios"
|
:path: ".symlinks/plugins/share_plus/ios"
|
||||||
shared_preferences_foundation:
|
shared_preferences_foundation:
|
||||||
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
||||||
smart_auth:
|
|
||||||
:path: ".symlinks/plugins/smart_auth/ios"
|
|
||||||
sodium_libs:
|
|
||||||
:path: ".symlinks/plugins/sodium_libs/ios"
|
|
||||||
sqflite:
|
sqflite:
|
||||||
:path: ".symlinks/plugins/sqflite/darwin"
|
:path: ".symlinks/plugins/sqflite/ios"
|
||||||
sqlite3_flutter_libs:
|
uni_links:
|
||||||
:path: ".symlinks/plugins/sqlite3_flutter_libs/ios"
|
:path: ".symlinks/plugins/uni_links/ios"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
app_links: e70ca16b4b0f88253b3b3660200d4a10b4ea9795
|
connectivity: c4130b2985d4ef6fd26f9702e886bd5260681467
|
||||||
connectivity_plus: bf0076dd84a130856aa636df1c71ccaff908fa1d
|
device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed
|
||||||
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
|
|
||||||
DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
|
DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
|
||||||
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
|
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
|
||||||
file_picker: 15fd9539e4eb735dc54bae8c0534a7a9511a03de
|
file_picker: ce3938a0df3cc1ef404671531facef740d03f920
|
||||||
file_saver: 503e386464dbe118f630e17b4c2e1190fa0cf808
|
file_saver: 503e386464dbe118f630e17b4c2e1190fa0cf808
|
||||||
fk_user_agent: 1f47ec39291e8372b1d692b50084b0d54103c545
|
fk_user_agent: 1f47ec39291e8372b1d692b50084b0d54103c545
|
||||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
||||||
flutter_email_sender: 02d7443217d8c41483223627972bfdc09f74276b
|
flutter_email_sender: 02d7443217d8c41483223627972bfdc09f74276b
|
||||||
flutter_inappwebview_ios: 97215cf7d4677db55df76782dbd2930c5e1c1ea0
|
flutter_inappwebview: acd4fc0f012cefd09015000c241137d82f01ba62
|
||||||
flutter_local_authentication: 1172a4dd88f6306dadce067454e2c4caf07977bb
|
flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743
|
||||||
flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086
|
|
||||||
flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef
|
flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef
|
||||||
flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be
|
flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be
|
||||||
|
flutter_sodium: c84426b4de738514b5b66cfdeb8a06634e72fe0b
|
||||||
fluttertoast: 31b00dabfa7fb7bacd9e7dbee580d7a2ff4bf265
|
fluttertoast: 31b00dabfa7fb7bacd9e7dbee580d7a2ff4bf265
|
||||||
local_auth_darwin: c7e464000a6a89e952235699e32b329457608d98
|
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
||||||
local_auth_ios: 5046a18c018dd973247a0564496c8898dbb5adf9
|
local_auth_ios: c6cf091ded637a88f24f86a8875d8b0f526e2605
|
||||||
move_to_background: 39a5b79b26d577b0372cbe8a8c55e7aa9fcd3a2d
|
move_to_background: 39a5b79b26d577b0372cbe8a8c55e7aa9fcd3a2d
|
||||||
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
|
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
|
||||||
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
|
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
|
||||||
package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85
|
package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7
|
||||||
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
|
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
|
||||||
privacy_screen: 1a131c052ceb3c3659934b003b0d397c2381a24e
|
privacy_screen: 1a131c052ceb3c3659934b003b0d397c2381a24e
|
||||||
qr_code_scanner: bb67d64904c3b9658ada8c402e8b4d406d5d796e
|
qr_code_scanner: bb67d64904c3b9658ada8c402e8b4d406d5d796e
|
||||||
ReachabilitySwift: 5ae15e16814b5f9ef568963fb2c87aeb49158c66
|
Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96
|
||||||
SDWebImage: 981fd7e860af070920f249fd092420006014c3eb
|
SDWebImage: 750adf017a315a280c60fde706ab1e552a3ae4e9
|
||||||
Sentry: ebc12276bd17613a114ab359074096b6b3725203
|
Sentry: e3203780941722a1fcfee99e351de14244c7f806
|
||||||
sentry_flutter: dff1df05dc39c83d04f9330b36360fc374574c5e
|
sentry_flutter: 8f0ffd53088e6a4d50c095852c5cad9e4405025c
|
||||||
SentryPrivate: d651efb234cf385ec9a1cdd3eff94b5e78a0e0fe
|
SentryPrivate: 5e3683390f66611fc7c6215e27645873adb55d13
|
||||||
share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5
|
share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5
|
||||||
shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695
|
shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
|
||||||
smart_auth: 4bedbc118723912d0e45a07e8ab34039c19e04f2
|
sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a
|
||||||
sodium_libs: 1faae17af662384acbd13e41867a0008cd2e2318
|
|
||||||
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
|
|
||||||
sqlite3: 73b7fc691fdc43277614250e04d183740cb15078
|
|
||||||
sqlite3_flutter_libs: af0e8fe9bce48abddd1ffdbbf839db0302d72d80
|
|
||||||
SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f
|
SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f
|
||||||
Toast: ec33c32b8688982cecc6348adeae667c1b9938da
|
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
|
||||||
url_launcher_ios: 6116280ddcfe98ab8820085d8d76ae7449447586
|
uni_links: d97da20c7701486ba192624d99bffaaffcfc298a
|
||||||
|
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
|
||||||
|
|
||||||
PODFILE CHECKSUM: b4e3a7eabb03395b66e81fc061789f61526ee6bb
|
PODFILE CHECKSUM: b4e3a7eabb03395b66e81fc061789f61526ee6bb
|
||||||
|
|
||||||
|
|||||||
@@ -159,7 +159,7 @@
|
|||||||
97C146E61CF9000F007C117D /* Project object */ = {
|
97C146E61CF9000F007C117D /* Project object */ = {
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastUpgradeCheck = 1510;
|
LastUpgradeCheck = 1430;
|
||||||
ORGANIZATIONNAME = "";
|
ORGANIZATIONNAME = "";
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
97C146ED1CF9000F007C117D = {
|
97C146ED1CF9000F007C117D = {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1510"
|
LastUpgradeVersion = "1430"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import Flutter
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import app_links
|
import Flutter
|
||||||
|
|
||||||
@UIApplicationMain
|
@UIApplicationMain
|
||||||
@objc class AppDelegate: FlutterAppDelegate {
|
@objc class AppDelegate: FlutterAppDelegate {
|
||||||
@@ -9,15 +8,6 @@ import app_links
|
|||||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||||
) -> Bool {
|
) -> Bool {
|
||||||
GeneratedPluginRegistrant.register(with: self)
|
GeneratedPluginRegistrant.register(with: self)
|
||||||
|
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||||
super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
|
||||||
|
|
||||||
if let url = AppLinks.shared.getLink(launchOptions: launchOptions) {
|
|
||||||
AppLinks.shared.handleLink(url: url)
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
|
|
||||||
// return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,9 +78,5 @@
|
|||||||
</array>
|
</array>
|
||||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
|
||||||
<true/>
|
|
||||||
<key>UIFileSharingEnabled</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -12,18 +12,15 @@ import 'package:ente_auth/locale.dart';
|
|||||||
import "package:ente_auth/onboarding/view/onboarding_page.dart";
|
import "package:ente_auth/onboarding/view/onboarding_page.dart";
|
||||||
import 'package:ente_auth/services/update_service.dart';
|
import 'package:ente_auth/services/update_service.dart';
|
||||||
import 'package:ente_auth/services/user_service.dart';
|
import 'package:ente_auth/services/user_service.dart';
|
||||||
import 'package:ente_auth/services/window_listener_service.dart';
|
|
||||||
import 'package:ente_auth/ui/home_page.dart';
|
import 'package:ente_auth/ui/home_page.dart';
|
||||||
import 'package:ente_auth/ui/settings/app_update_dialog.dart';
|
import 'package:ente_auth/ui/settings/app_update_dialog.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
import 'package:system_tray/system_tray.dart';
|
|
||||||
import 'package:window_manager/window_manager.dart';
|
|
||||||
|
|
||||||
class App extends StatefulWidget {
|
class App extends StatefulWidget {
|
||||||
final Locale locale;
|
final Locale locale;
|
||||||
const App({super.key, this.locale = const Locale("en")});
|
const App({Key? key, this.locale = const Locale("en")}) : super(key: key);
|
||||||
|
|
||||||
static void setLocale(BuildContext context, Locale newLocale) {
|
static void setLocale(BuildContext context, Locale newLocale) {
|
||||||
_AppState state = context.findAncestorStateOfType<_AppState>()!;
|
_AppState state = context.findAncestorStateOfType<_AppState>()!;
|
||||||
@@ -34,7 +31,7 @@ class App extends StatefulWidget {
|
|||||||
State<App> createState() => _AppState();
|
State<App> createState() => _AppState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AppState extends State<App> with WindowListener {
|
class _AppState extends State<App> {
|
||||||
late StreamSubscription<SignedOutEvent> _signedOutEvent;
|
late StreamSubscription<SignedOutEvent> _signedOutEvent;
|
||||||
late StreamSubscription<SignedInEvent> _signedInEvent;
|
late StreamSubscription<SignedInEvent> _signedInEvent;
|
||||||
Locale? locale;
|
Locale? locale;
|
||||||
@@ -44,14 +41,8 @@ class _AppState extends State<App> with WindowListener {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> initWindowManager() async {
|
|
||||||
windowManager.addListener(this);
|
|
||||||
await windowManager.setPreventClose(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
initWindowManager();
|
|
||||||
_signedOutEvent = Bus.instance.on<SignedOutEvent>().listen((event) {
|
_signedOutEvent = Bus.instance.on<SignedOutEvent>().listen((event) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
@@ -85,7 +76,6 @@ class _AppState extends State<App> with WindowListener {
|
|||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
windowManager.removeListener(this);
|
|
||||||
_signedOutEvent.cancel();
|
_signedOutEvent.cancel();
|
||||||
_signedInEvent.cancel();
|
_signedInEvent.cancel();
|
||||||
}
|
}
|
||||||
@@ -144,15 +134,4 @@ class _AppState extends State<App> with WindowListener {
|
|||||||
: const OnboardingPage(),
|
: const OnboardingPage(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void onWindowClose() async {
|
|
||||||
final AppWindow appWindow = AppWindow();
|
|
||||||
await appWindow.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void onWindowResize() {
|
|
||||||
WindowListenerService.instance.onWindowResize().ignore();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
4
auth/lib/app/view/app_route.dart
Normal file
4
auth/lib/app/view/app_route.dart
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
class AppRoute {
|
||||||
|
static const String enterSecretKeyPage = "enterSecretKeyPage";
|
||||||
|
static const String settings = "settings";
|
||||||
|
}
|
||||||
163
auth/lib/app/view/app_theme_extension.dart
Normal file
163
auth/lib/app/view/app_theme_extension.dart
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
import "package:flutter/material.dart";
|
||||||
|
|
||||||
|
final lightTheme = ThemeData(
|
||||||
|
fontFamily: "Inter",
|
||||||
|
brightness: Brightness.light,
|
||||||
|
scaffoldBackgroundColor: Colors.white,
|
||||||
|
appBarTheme: const AppBarTheme().copyWith(
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
foregroundColor: Colors.black,
|
||||||
|
iconTheme: const IconThemeData(color: Colors.black),
|
||||||
|
elevation: 0,
|
||||||
|
),
|
||||||
|
colorScheme: const ColorScheme.light(
|
||||||
|
primary: Colors.black,
|
||||||
|
),
|
||||||
|
textTheme: _buildTextTheme(Colors.black),
|
||||||
|
outlinedButtonTheme: buildOutlinedButtonThemeData(
|
||||||
|
bgDisabled: Colors.grey.shade500,
|
||||||
|
bgEnabled: Colors.black,
|
||||||
|
fgDisabled: Colors.white,
|
||||||
|
fgEnabled: Colors.white,
|
||||||
|
),
|
||||||
|
inputDecorationTheme: InputDecorationTheme(
|
||||||
|
fillColor: null,
|
||||||
|
filled: true,
|
||||||
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16,
|
||||||
|
vertical: 14,
|
||||||
|
),
|
||||||
|
border: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide.none,
|
||||||
|
borderRadius: BorderRadius.circular(6),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final darkTheme = ThemeData(
|
||||||
|
fontFamily: "Inter",
|
||||||
|
brightness: Brightness.dark,
|
||||||
|
scaffoldBackgroundColor: Colors.black,
|
||||||
|
appBarTheme: const AppBarTheme(color: Colors.orange),
|
||||||
|
textTheme: _buildTextTheme(Colors.white),
|
||||||
|
outlinedButtonTheme: buildOutlinedButtonThemeData(
|
||||||
|
bgDisabled: Colors.grey.shade500,
|
||||||
|
bgEnabled: Colors.white,
|
||||||
|
fgDisabled: Colors.white,
|
||||||
|
fgEnabled: Colors.black,
|
||||||
|
),
|
||||||
|
inputDecorationTheme: InputDecorationTheme(
|
||||||
|
fillColor: null,
|
||||||
|
filled: true,
|
||||||
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16,
|
||||||
|
vertical: 14,
|
||||||
|
),
|
||||||
|
border: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide.none,
|
||||||
|
borderRadius: BorderRadius.circular(6),
|
||||||
|
),
|
||||||
|
), colorScheme: const ColorScheme.dark(primary: Colors.white).copyWith(background: Colors.black),
|
||||||
|
);
|
||||||
|
|
||||||
|
TextTheme _buildTextTheme(Color textColor) {
|
||||||
|
return const TextTheme().copyWith(
|
||||||
|
headlineMedium: TextStyle(
|
||||||
|
color: textColor,
|
||||||
|
fontSize: 32,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
),
|
||||||
|
headlineSmall: TextStyle(
|
||||||
|
color: textColor,
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
),
|
||||||
|
// AG: Body
|
||||||
|
titleLarge: TextStyle(
|
||||||
|
color: textColor,
|
||||||
|
fontSize: 18,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
// Use labels for buttons or notifications
|
||||||
|
labelMedium: TextStyle(
|
||||||
|
color: textColor,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
height: 28,
|
||||||
|
),
|
||||||
|
|
||||||
|
titleMedium: TextStyle(
|
||||||
|
color: textColor,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
titleSmall: TextStyle(
|
||||||
|
color: textColor,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
bodyLarge: TextStyle(
|
||||||
|
fontFamily: "Inter",
|
||||||
|
color: textColor,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
bodyMedium: TextStyle(
|
||||||
|
fontFamily: "Inter",
|
||||||
|
color: textColor,
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
bodySmall: TextStyle(
|
||||||
|
color: textColor.withOpacity(0.6),
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
OutlinedButtonThemeData buildOutlinedButtonThemeData({
|
||||||
|
required Color bgDisabled,
|
||||||
|
required Color bgEnabled,
|
||||||
|
required Color fgDisabled,
|
||||||
|
required Color fgEnabled,
|
||||||
|
}) {
|
||||||
|
return OutlinedButtonThemeData(
|
||||||
|
style: OutlinedButton.styleFrom(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 50),
|
||||||
|
textStyle: const TextStyle(
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
fontFamily: "Inter",
|
||||||
|
fontSize: 18,
|
||||||
|
),
|
||||||
|
).copyWith(
|
||||||
|
backgroundColor: MaterialStateProperty.resolveWith<Color>(
|
||||||
|
(Set<MaterialState> states) {
|
||||||
|
if (states.contains(MaterialState.disabled)) {
|
||||||
|
return bgDisabled;
|
||||||
|
}
|
||||||
|
return bgEnabled;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
foregroundColor: MaterialStateProperty.resolveWith<Color>(
|
||||||
|
(Set<MaterialState> states) {
|
||||||
|
if (states.contains(MaterialState.disabled)) {
|
||||||
|
return fgDisabled;
|
||||||
|
}
|
||||||
|
return fgEnabled;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
// ignore_for_file: deprecated_member_use
|
|
||||||
|
|
||||||
import "dart:async";
|
import "dart:async";
|
||||||
import "dart:developer";
|
import "dart:developer";
|
||||||
|
|
||||||
|
|||||||
@@ -13,12 +13,12 @@ import 'package:ente_auth/models/key_attributes.dart';
|
|||||||
import 'package:ente_auth/models/key_gen_result.dart';
|
import 'package:ente_auth/models/key_gen_result.dart';
|
||||||
import 'package:ente_auth/models/private_key_attributes.dart';
|
import 'package:ente_auth/models/private_key_attributes.dart';
|
||||||
import 'package:ente_auth/store/authenticator_db.dart';
|
import 'package:ente_auth/store/authenticator_db.dart';
|
||||||
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
|
import 'package:ente_auth/utils/crypto_util.dart';
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
|
import 'package:flutter_sodium/flutter_sodium.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
class Configuration {
|
class Configuration {
|
||||||
@@ -72,10 +72,9 @@ class Configuration {
|
|||||||
|
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
_preferences = await SharedPreferences.getInstance();
|
_preferences = await SharedPreferences.getInstance();
|
||||||
sqfliteFfiInit();
|
|
||||||
_secureStorage = const FlutterSecureStorage();
|
_secureStorage = const FlutterSecureStorage();
|
||||||
_documentsDirectory = (await getApplicationDocumentsDirectory()).path;
|
_documentsDirectory = (await getApplicationDocumentsDirectory()).path;
|
||||||
_tempDirectory = "$_documentsDirectory/temp/";
|
_tempDirectory = _documentsDirectory + "/temp/";
|
||||||
final tempDirectory = io.Directory(_tempDirectory);
|
final tempDirectory = io.Directory(_tempDirectory);
|
||||||
try {
|
try {
|
||||||
final currentTime = DateTime.now().microsecondsSinceEpoch;
|
final currentTime = DateTime.now().microsecondsSinceEpoch;
|
||||||
@@ -163,7 +162,7 @@ class Configuration {
|
|||||||
// decrypt the master key
|
// decrypt the master key
|
||||||
final kekSalt = CryptoUtil.getSaltToDeriveKey();
|
final kekSalt = CryptoUtil.getSaltToDeriveKey();
|
||||||
final derivedKeyResult = await CryptoUtil.deriveSensitiveKey(
|
final derivedKeyResult = await CryptoUtil.deriveSensitiveKey(
|
||||||
utf8.encode(password),
|
utf8.encode(password) as Uint8List,
|
||||||
kekSalt,
|
kekSalt,
|
||||||
);
|
);
|
||||||
final loginKey = await CryptoUtil.deriveLoginKey(derivedKeyResult.key);
|
final loginKey = await CryptoUtil.deriveLoginKey(derivedKeyResult.key);
|
||||||
@@ -173,28 +172,28 @@ class Configuration {
|
|||||||
CryptoUtil.encryptSync(masterKey, derivedKeyResult.key);
|
CryptoUtil.encryptSync(masterKey, derivedKeyResult.key);
|
||||||
|
|
||||||
// Generate a public-private keypair and encrypt the latter
|
// Generate a public-private keypair and encrypt the latter
|
||||||
final keyPair = CryptoUtil.generateKeyPair();
|
final keyPair = await CryptoUtil.generateKeyPair();
|
||||||
final encryptedSecretKeyData =
|
final encryptedSecretKeyData =
|
||||||
CryptoUtil.encryptSync(keyPair.secretKey.extractBytes(), masterKey);
|
CryptoUtil.encryptSync(keyPair.sk, masterKey);
|
||||||
|
|
||||||
final attributes = KeyAttributes(
|
final attributes = KeyAttributes(
|
||||||
CryptoUtil.bin2base64(kekSalt),
|
Sodium.bin2base64(kekSalt),
|
||||||
CryptoUtil.bin2base64(encryptedKeyData.encryptedData!),
|
Sodium.bin2base64(encryptedKeyData.encryptedData!),
|
||||||
CryptoUtil.bin2base64(encryptedKeyData.nonce!),
|
Sodium.bin2base64(encryptedKeyData.nonce!),
|
||||||
CryptoUtil.bin2base64(keyPair.publicKey),
|
Sodium.bin2base64(keyPair.pk),
|
||||||
CryptoUtil.bin2base64(encryptedSecretKeyData.encryptedData!),
|
Sodium.bin2base64(encryptedSecretKeyData.encryptedData!),
|
||||||
CryptoUtil.bin2base64(encryptedSecretKeyData.nonce!),
|
Sodium.bin2base64(encryptedSecretKeyData.nonce!),
|
||||||
derivedKeyResult.memLimit,
|
derivedKeyResult.memLimit,
|
||||||
derivedKeyResult.opsLimit,
|
derivedKeyResult.opsLimit,
|
||||||
CryptoUtil.bin2base64(encryptedMasterKey.encryptedData!),
|
Sodium.bin2base64(encryptedMasterKey.encryptedData!),
|
||||||
CryptoUtil.bin2base64(encryptedMasterKey.nonce!),
|
Sodium.bin2base64(encryptedMasterKey.nonce!),
|
||||||
CryptoUtil.bin2base64(encryptedRecoveryKey.encryptedData!),
|
Sodium.bin2base64(encryptedRecoveryKey.encryptedData!),
|
||||||
CryptoUtil.bin2base64(encryptedRecoveryKey.nonce!),
|
Sodium.bin2base64(encryptedRecoveryKey.nonce!),
|
||||||
);
|
);
|
||||||
final privateAttributes = PrivateKeyAttributes(
|
final privateAttributes = PrivateKeyAttributes(
|
||||||
CryptoUtil.bin2base64(masterKey),
|
Sodium.bin2base64(masterKey),
|
||||||
CryptoUtil.bin2hex(recoveryKey),
|
Sodium.bin2hex(recoveryKey),
|
||||||
CryptoUtil.bin2base64(keyPair.secretKey.extractBytes()),
|
Sodium.bin2base64(keyPair.sk),
|
||||||
);
|
);
|
||||||
return KeyGenResult(attributes, privateAttributes, loginKey);
|
return KeyGenResult(attributes, privateAttributes, loginKey);
|
||||||
}
|
}
|
||||||
@@ -209,7 +208,7 @@ class Configuration {
|
|||||||
// decrypt the master key
|
// decrypt the master key
|
||||||
final kekSalt = CryptoUtil.getSaltToDeriveKey();
|
final kekSalt = CryptoUtil.getSaltToDeriveKey();
|
||||||
final derivedKeyResult = await CryptoUtil.deriveSensitiveKey(
|
final derivedKeyResult = await CryptoUtil.deriveSensitiveKey(
|
||||||
utf8.encode(password),
|
utf8.encode(password) as Uint8List,
|
||||||
kekSalt,
|
kekSalt,
|
||||||
);
|
);
|
||||||
final loginKey = await CryptoUtil.deriveLoginKey(derivedKeyResult.key);
|
final loginKey = await CryptoUtil.deriveLoginKey(derivedKeyResult.key);
|
||||||
@@ -221,9 +220,9 @@ class Configuration {
|
|||||||
final existingAttributes = getKeyAttributes();
|
final existingAttributes = getKeyAttributes();
|
||||||
|
|
||||||
final updatedAttributes = existingAttributes!.copyWith(
|
final updatedAttributes = existingAttributes!.copyWith(
|
||||||
kekSalt: CryptoUtil.bin2base64(kekSalt),
|
kekSalt: Sodium.bin2base64(kekSalt),
|
||||||
encryptedKey: CryptoUtil.bin2base64(encryptedKeyData.encryptedData!),
|
encryptedKey: Sodium.bin2base64(encryptedKeyData.encryptedData!),
|
||||||
keyDecryptionNonce: CryptoUtil.bin2base64(encryptedKeyData.nonce!),
|
keyDecryptionNonce: Sodium.bin2base64(encryptedKeyData.nonce!),
|
||||||
memLimit: derivedKeyResult.memLimit,
|
memLimit: derivedKeyResult.memLimit,
|
||||||
opsLimit: derivedKeyResult.opsLimit,
|
opsLimit: derivedKeyResult.opsLimit,
|
||||||
);
|
);
|
||||||
@@ -241,8 +240,8 @@ class Configuration {
|
|||||||
}) async {
|
}) async {
|
||||||
_logger.info('Start decryptAndSaveSecrets');
|
_logger.info('Start decryptAndSaveSecrets');
|
||||||
keyEncryptionKey ??= await CryptoUtil.deriveKey(
|
keyEncryptionKey ??= await CryptoUtil.deriveKey(
|
||||||
utf8.encode(password),
|
utf8.encode(password) as Uint8List,
|
||||||
CryptoUtil.base642bin(attributes.kekSalt),
|
Sodium.base642bin(attributes.kekSalt),
|
||||||
attributes.memLimit,
|
attributes.memLimit,
|
||||||
attributes.opsLimit,
|
attributes.opsLimit,
|
||||||
);
|
);
|
||||||
@@ -251,31 +250,31 @@ class Configuration {
|
|||||||
Uint8List key;
|
Uint8List key;
|
||||||
try {
|
try {
|
||||||
key = CryptoUtil.decryptSync(
|
key = CryptoUtil.decryptSync(
|
||||||
CryptoUtil.base642bin(attributes.encryptedKey),
|
Sodium.base642bin(attributes.encryptedKey),
|
||||||
keyEncryptionKey,
|
keyEncryptionKey,
|
||||||
CryptoUtil.base642bin(attributes.keyDecryptionNonce),
|
Sodium.base642bin(attributes.keyDecryptionNonce),
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_logger.severe('master-key failed, incorrect password?', e);
|
_logger.severe('master-key failed, incorrect password?', e);
|
||||||
throw Exception("Incorrect password");
|
throw Exception("Incorrect password");
|
||||||
}
|
}
|
||||||
_logger.info("master-key done");
|
_logger.info("master-key done");
|
||||||
await setKey(CryptoUtil.bin2base64(key));
|
await setKey(Sodium.bin2base64(key));
|
||||||
final secretKey = CryptoUtil.decryptSync(
|
final secretKey = CryptoUtil.decryptSync(
|
||||||
CryptoUtil.base642bin(attributes.encryptedSecretKey),
|
Sodium.base642bin(attributes.encryptedSecretKey),
|
||||||
key,
|
key,
|
||||||
CryptoUtil.base642bin(attributes.secretKeyDecryptionNonce),
|
Sodium.base642bin(attributes.secretKeyDecryptionNonce),
|
||||||
);
|
);
|
||||||
_logger.info("secret-key done");
|
_logger.info("secret-key done");
|
||||||
await setSecretKey(CryptoUtil.bin2base64(secretKey));
|
await setSecretKey(Sodium.bin2base64(secretKey));
|
||||||
final token = CryptoUtil.openSealSync(
|
final token = CryptoUtil.openSealSync(
|
||||||
CryptoUtil.base642bin(getEncryptedToken()!),
|
Sodium.base642bin(getEncryptedToken()!),
|
||||||
CryptoUtil.base642bin(attributes.publicKey),
|
Sodium.base642bin(attributes.publicKey),
|
||||||
secretKey,
|
secretKey,
|
||||||
);
|
);
|
||||||
_logger.info('appToken done');
|
_logger.info('appToken done');
|
||||||
await setToken(
|
await setToken(
|
||||||
CryptoUtil.bin2base64(token, urlSafe: true),
|
Sodium.bin2base64(token, variant: Sodium.base64VariantUrlsafe),
|
||||||
);
|
);
|
||||||
return keyEncryptionKey;
|
return keyEncryptionKey;
|
||||||
}
|
}
|
||||||
@@ -294,28 +293,28 @@ class Configuration {
|
|||||||
Uint8List masterKey;
|
Uint8List masterKey;
|
||||||
try {
|
try {
|
||||||
masterKey = await CryptoUtil.decrypt(
|
masterKey = await CryptoUtil.decrypt(
|
||||||
CryptoUtil.base642bin(attributes!.masterKeyEncryptedWithRecoveryKey),
|
Sodium.base642bin(attributes!.masterKeyEncryptedWithRecoveryKey),
|
||||||
CryptoUtil.hex2bin(recoveryKey),
|
Sodium.hex2bin(recoveryKey),
|
||||||
CryptoUtil.base642bin(attributes.masterKeyDecryptionNonce),
|
Sodium.base642bin(attributes.masterKeyDecryptionNonce),
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_logger.severe(e);
|
_logger.severe(e);
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
await setKey(CryptoUtil.bin2base64(masterKey));
|
await setKey(Sodium.bin2base64(masterKey));
|
||||||
final secretKey = CryptoUtil.decryptSync(
|
final secretKey = CryptoUtil.decryptSync(
|
||||||
CryptoUtil.base642bin(attributes.encryptedSecretKey),
|
Sodium.base642bin(attributes.encryptedSecretKey),
|
||||||
masterKey,
|
masterKey,
|
||||||
CryptoUtil.base642bin(attributes.secretKeyDecryptionNonce),
|
Sodium.base642bin(attributes.secretKeyDecryptionNonce),
|
||||||
);
|
);
|
||||||
await setSecretKey(CryptoUtil.bin2base64(secretKey));
|
await setSecretKey(Sodium.bin2base64(secretKey));
|
||||||
final token = CryptoUtil.openSealSync(
|
final token = CryptoUtil.openSealSync(
|
||||||
CryptoUtil.base642bin(getEncryptedToken()!),
|
Sodium.base642bin(getEncryptedToken()!),
|
||||||
CryptoUtil.base642bin(attributes.publicKey),
|
Sodium.base642bin(attributes.publicKey),
|
||||||
secretKey,
|
secretKey,
|
||||||
);
|
);
|
||||||
await setToken(
|
await setToken(
|
||||||
CryptoUtil.bin2base64(token, urlSafe: true),
|
Sodium.bin2base64(token, variant: Sodium.base64VariantUrlsafe),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -408,31 +407,27 @@ class Configuration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Uint8List? getKey() {
|
Uint8List? getKey() {
|
||||||
return _key == null ? null : CryptoUtil.base642bin(_key!);
|
return _key == null ? null : Sodium.base642bin(_key!);
|
||||||
}
|
}
|
||||||
|
|
||||||
Uint8List? getSecretKey() {
|
Uint8List? getSecretKey() {
|
||||||
return _secretKey == null ? null : CryptoUtil.base642bin(_secretKey!);
|
return _secretKey == null ? null : Sodium.base642bin(_secretKey!);
|
||||||
}
|
}
|
||||||
|
|
||||||
Uint8List? getAuthSecretKey() {
|
Uint8List? getAuthSecretKey() {
|
||||||
return _authSecretKey == null
|
return _authSecretKey == null ? null : Sodium.base642bin(_authSecretKey!);
|
||||||
? null
|
|
||||||
: CryptoUtil.base642bin(_authSecretKey!);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Uint8List? getOfflineSecretKey() {
|
Uint8List? getOfflineSecretKey() {
|
||||||
return _offlineAuthKey == null
|
return _offlineAuthKey == null ? null : Sodium.base642bin(_offlineAuthKey!);
|
||||||
? null
|
|
||||||
: CryptoUtil.base642bin(_offlineAuthKey!);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Uint8List getRecoveryKey() {
|
Uint8List getRecoveryKey() {
|
||||||
final keyAttributes = getKeyAttributes()!;
|
final keyAttributes = getKeyAttributes()!;
|
||||||
return CryptoUtil.decryptSync(
|
return CryptoUtil.decryptSync(
|
||||||
CryptoUtil.base642bin(keyAttributes.recoveryKeyEncryptedWithMasterKey),
|
Sodium.base642bin(keyAttributes.recoveryKeyEncryptedWithMasterKey),
|
||||||
getKey()!,
|
getKey()!,
|
||||||
CryptoUtil.base642bin(keyAttributes.recoveryKeyDecryptionNonce),
|
Sodium.base642bin(keyAttributes.recoveryKeyDecryptionNonce),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -459,7 +454,7 @@ class Configuration {
|
|||||||
iOptions: _secureStorageOptionsIOS,
|
iOptions: _secureStorageOptionsIOS,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
_offlineAuthKey = CryptoUtil.bin2base64(CryptoUtil.generateKey());
|
_offlineAuthKey = Sodium.bin2base64(CryptoUtil.generateKey());
|
||||||
await _secureStorage.write(
|
await _secureStorage.write(
|
||||||
key: offlineAuthSecretKey,
|
key: offlineAuthSecretKey,
|
||||||
value: _offlineAuthKey,
|
value: _offlineAuthKey,
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ const String sentryDSN =
|
|||||||
"https://ed4ddd6309b847ba8849935e26e9b648@sentry.ente.io/9";
|
"https://ed4ddd6309b847ba8849935e26e9b648@sentry.ente.io/9";
|
||||||
const String sentryTunnel = "https://sentry-reporter.ente.io";
|
const String sentryTunnel = "https://sentry-reporter.ente.io";
|
||||||
const String roadmapURL = "https://roadmap.ente.io";
|
const String roadmapURL = "https://roadmap.ente.io";
|
||||||
const String githubIssuesUrl =
|
const String githubDiscussionsUrl = "https://github.com/ente-io/ente/discussions";
|
||||||
"https://github.com/ente-io/ente/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc";
|
|
||||||
const int microSecondsInDay = 86400000000;
|
const int microSecondsInDay = 86400000000;
|
||||||
const int android11SDKINT = 30;
|
const int android11SDKINT = 30;
|
||||||
const int galleryLoadStartTime = -8000000000000000; // Wednesday, March 6, 1748
|
const int galleryLoadStartTime = -8000000000000000; // Wednesday, March 6, 1748
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
class InvalidFileError extends ArgumentError {
|
class InvalidFileError extends ArgumentError {
|
||||||
InvalidFileError(String super.message);
|
InvalidFileError(String message) : super(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
class InvalidFileUploadState extends AssertionError {
|
class InvalidFileUploadState extends AssertionError {
|
||||||
InvalidFileUploadState(String super.message);
|
InvalidFileUploadState(String message) : super(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
class SubscriptionAlreadyClaimedError extends Error {}
|
class SubscriptionAlreadyClaimedError extends Error {}
|
||||||
@@ -30,15 +30,19 @@ class UnauthorizedError extends Error {}
|
|||||||
class RequestCancelledError extends Error {}
|
class RequestCancelledError extends Error {}
|
||||||
|
|
||||||
class InvalidSyncStatusError extends AssertionError {
|
class InvalidSyncStatusError extends AssertionError {
|
||||||
InvalidSyncStatusError(String super.message);
|
InvalidSyncStatusError(String message) : super(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
class UnauthorizedEditError extends AssertionError {}
|
class UnauthorizedEditError extends AssertionError {}
|
||||||
|
|
||||||
class InvalidStateError extends AssertionError {
|
class InvalidStateError extends AssertionError {
|
||||||
InvalidStateError(String super.message);
|
InvalidStateError(String message) : super(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class KeyDerivationError extends Error {}
|
||||||
|
|
||||||
|
class LoginKeyDerivationError extends Error {}
|
||||||
|
|
||||||
class SrpSetupNotCompleteError extends Error {}
|
class SrpSetupNotCompleteError extends Error {}
|
||||||
|
|
||||||
class AuthenticatorKeyNotFound extends Error {}
|
class AuthenticatorKeyNotFound extends Error {}
|
||||||
|
|||||||
@@ -235,14 +235,14 @@ class SuperLogging {
|
|||||||
extraLines = null;
|
extraLines = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final str = "${config.prefix} ${rec.toPrettyString(extraLines)}";
|
final str = (config.prefix) + " " + rec.toPrettyString(extraLines);
|
||||||
|
|
||||||
// write to stdout
|
// write to stdout
|
||||||
printLog(str);
|
printLog(str);
|
||||||
|
|
||||||
// push to log queue
|
// push to log queue
|
||||||
if (fileIsEnabled) {
|
if (fileIsEnabled) {
|
||||||
fileQueueEntries.add('$str\n');
|
fileQueueEntries.add(str + '\n');
|
||||||
if (fileQueueEntries.length == 1) {
|
if (fileQueueEntries.length == 1) {
|
||||||
flushQueue();
|
flushQueue();
|
||||||
}
|
}
|
||||||
@@ -275,7 +275,7 @@ class SuperLogging {
|
|||||||
static var logChunkSize = 800;
|
static var logChunkSize = 800;
|
||||||
|
|
||||||
static void printLog(String text) {
|
static void printLog(String text) {
|
||||||
text.chunked(logChunkSize).forEach(debugPrint);
|
text.chunked(logChunkSize).forEach(print);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A queue to be consumed by [setupSentry].
|
/// A queue to be consumed by [setupSentry].
|
||||||
@@ -354,7 +354,7 @@ class SuperLogging {
|
|||||||
final date = config.dateFmt!.parse(basename(file.path));
|
final date = config.dateFmt!.parse(basename(file.path));
|
||||||
dates[file as File] = date;
|
dates[file as File] = date;
|
||||||
files.add(file);
|
files.add(file);
|
||||||
} on Exception catch (_) {}
|
} on FormatException {}
|
||||||
}
|
}
|
||||||
final nowTime = DateTime.now();
|
final nowTime = DateTime.now();
|
||||||
|
|
||||||
@@ -374,7 +374,7 @@ class SuperLogging {
|
|||||||
"deleting log file ${file.path}",
|
"deleting log file ${file.path}",
|
||||||
);
|
);
|
||||||
await file.delete();
|
await file.delete();
|
||||||
} on Exception catch (_) {}
|
} catch (ignore) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class TunneledTransport implements Transport {
|
|||||||
_options.logger(
|
_options.logger(
|
||||||
SentryLevel.error,
|
SentryLevel.error,
|
||||||
'API returned an error, statusCode = ${response.statusCode}, '
|
'API returned an error, statusCode = ${response.statusCode}, '
|
||||||
'body = ${response.body}',
|
'body = ${response.body}',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return const SentryId.empty();
|
return const SentryId.empty();
|
||||||
@@ -65,8 +65,8 @@ class TunneledTransport implements Transport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<StreamedRequest> _createStreamedRequest(
|
Future<StreamedRequest> _createStreamedRequest(
|
||||||
SentryEnvelope envelope,
|
SentryEnvelope envelope,
|
||||||
) async {
|
) async {
|
||||||
final streamedRequest = StreamedRequest('POST', _tunnel);
|
final streamedRequest = StreamedRequest('POST', _tunnel);
|
||||||
envelope
|
envelope
|
||||||
.envelopeStream(_options)
|
.envelopeStream(_options)
|
||||||
@@ -91,10 +91,10 @@ class _CredentialBuilder {
|
|||||||
_clock = clock;
|
_clock = clock;
|
||||||
|
|
||||||
factory _CredentialBuilder(
|
factory _CredentialBuilder(
|
||||||
Dsn? dsn,
|
Dsn? dsn,
|
||||||
String sdkIdentifier,
|
String sdkIdentifier,
|
||||||
ClockProvider clock,
|
ClockProvider clock,
|
||||||
) {
|
) {
|
||||||
final authHeader = _buildAuthHeader(
|
final authHeader = _buildAuthHeader(
|
||||||
publicKey: dsn?.publicKey,
|
publicKey: dsn?.publicKey,
|
||||||
secretKey: dsn?.secretKey,
|
secretKey: dsn?.secretKey,
|
||||||
|
|||||||
@@ -4,10 +4,9 @@ import 'package:dio/dio.dart';
|
|||||||
import 'package:ente_auth/core/configuration.dart';
|
import 'package:ente_auth/core/configuration.dart';
|
||||||
import 'package:ente_auth/core/event_bus.dart';
|
import 'package:ente_auth/core/event_bus.dart';
|
||||||
import 'package:ente_auth/events/endpoint_updated_event.dart';
|
import 'package:ente_auth/events/endpoint_updated_event.dart';
|
||||||
import 'package:ente_auth/utils/package_info_util.dart';
|
|
||||||
import 'package:ente_auth/utils/platform_util.dart';
|
|
||||||
import 'package:fk_user_agent/fk_user_agent.dart';
|
import 'package:fk_user_agent/fk_user_agent.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
int kConnectTimeout = 15000;
|
int kConnectTimeout = 15000;
|
||||||
@@ -17,41 +16,34 @@ class Network {
|
|||||||
late Dio _enteDio;
|
late Dio _enteDio;
|
||||||
|
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
if (PlatformUtil.isMobile()) await FkUserAgent.init();
|
await FkUserAgent.init();
|
||||||
final packageInfo = await PackageInfoUtil().getPackageInfo();
|
final packageInfo = await PackageInfo.fromPlatform();
|
||||||
final version = PackageInfoUtil().getVersion(packageInfo);
|
|
||||||
final packageName = PackageInfoUtil().getPackageName(packageInfo);
|
|
||||||
final endpoint = Configuration.instance.getHttpEndpoint();
|
final endpoint = Configuration.instance.getHttpEndpoint();
|
||||||
|
|
||||||
_dio = Dio(
|
_dio = Dio(
|
||||||
BaseOptions(
|
BaseOptions(
|
||||||
connectTimeout: Duration(milliseconds: kConnectTimeout),
|
connectTimeout: kConnectTimeout,
|
||||||
headers: {
|
headers: {
|
||||||
HttpHeaders.userAgentHeader: PlatformUtil.isMobile()
|
HttpHeaders.userAgentHeader: FkUserAgent.userAgent,
|
||||||
? FkUserAgent.userAgent
|
'X-Client-Version': packageInfo.version,
|
||||||
: Platform.operatingSystem,
|
'X-Client-Package': packageInfo.packageName,
|
||||||
'X-Client-Version': version,
|
|
||||||
'X-Client-Package': packageName,
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
_enteDio = Dio(
|
_enteDio = Dio(
|
||||||
BaseOptions(
|
BaseOptions(
|
||||||
baseUrl: endpoint,
|
baseUrl: endpoint,
|
||||||
connectTimeout: Duration(milliseconds: kConnectTimeout),
|
connectTimeout: kConnectTimeout,
|
||||||
headers: {
|
headers: {
|
||||||
if (PlatformUtil.isMobile())
|
HttpHeaders.userAgentHeader: FkUserAgent.userAgent,
|
||||||
HttpHeaders.userAgentHeader: FkUserAgent.userAgent
|
'X-Client-Version': packageInfo.version,
|
||||||
else
|
'X-Client-Package': packageInfo.packageName,
|
||||||
HttpHeaders.userAgentHeader: Platform.operatingSystem,
|
|
||||||
'X-Client-Version': version,
|
|
||||||
'X-Client-Package': packageName,
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
_setupInterceptors(endpoint);
|
_setupInterceptors(endpoint);
|
||||||
|
|
||||||
Bus.instance.on<EndpointUpdatedEvent>().listen((event) {
|
Bus.instance.on<EndpointUpdatedEvent>().listen((event) {
|
||||||
final endpoint = Configuration.instance.getHttpEndpoint();
|
final endpoint = Configuration.instance.getHttpEndpoint();
|
||||||
_enteDio.options.baseUrl = endpoint;
|
_enteDio.options.baseUrl = endpoint;
|
||||||
|
|||||||
@@ -5,16 +5,12 @@ import 'package:flutter/material.dart';
|
|||||||
final lightThemeData = ThemeData(
|
final lightThemeData = ThemeData(
|
||||||
fontFamily: 'Inter',
|
fontFamily: 'Inter',
|
||||||
brightness: Brightness.light,
|
brightness: Brightness.light,
|
||||||
dividerTheme: const DividerThemeData(
|
|
||||||
color: Colors.black12,
|
|
||||||
),
|
|
||||||
hintColor: const Color.fromRGBO(158, 158, 158, 1),
|
hintColor: const Color.fromRGBO(158, 158, 158, 1),
|
||||||
primaryColor: const Color.fromRGBO(255, 110, 64, 1),
|
primaryColor: const Color.fromRGBO(255, 110, 64, 1),
|
||||||
primaryColorLight: const Color.fromRGBO(0, 0, 0, 0.541),
|
primaryColorLight: const Color.fromRGBO(0, 0, 0, 0.541),
|
||||||
iconTheme: const IconThemeData(color: Colors.black),
|
iconTheme: const IconThemeData(color: Colors.black),
|
||||||
primaryIconTheme:
|
primaryIconTheme:
|
||||||
const IconThemeData(color: Colors.red, opacity: 1.0, size: 50.0),
|
const IconThemeData(color: Colors.red, opacity: 1.0, size: 50.0),
|
||||||
buttonTheme: const ButtonThemeData(),
|
|
||||||
outlinedButtonTheme: buildOutlinedButtonThemeData(
|
outlinedButtonTheme: buildOutlinedButtonThemeData(
|
||||||
bgDisabled: const Color.fromRGBO(158, 158, 158, 1),
|
bgDisabled: const Color.fromRGBO(158, 158, 158, 1),
|
||||||
bgEnabled: const Color.fromRGBO(0, 0, 0, 1),
|
bgEnabled: const Color.fromRGBO(0, 0, 0, 1),
|
||||||
@@ -76,42 +72,24 @@ final lightThemeData = ThemeData(
|
|||||||
? const Color.fromRGBO(255, 255, 255, 1)
|
? const Color.fromRGBO(255, 255, 255, 1)
|
||||||
: const Color.fromRGBO(0, 0, 0, 1);
|
: const Color.fromRGBO(0, 0, 0, 1);
|
||||||
}),
|
}),
|
||||||
),
|
), radioTheme: RadioThemeData(
|
||||||
radioTheme: RadioThemeData(
|
fillColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
|
||||||
fillColor:
|
if (states.contains(MaterialState.disabled)) { return null; }
|
||||||
MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
|
if (states.contains(MaterialState.selected)) { return const Color.fromRGBO(102, 187, 106, 1); }
|
||||||
if (states.contains(MaterialState.disabled)) {
|
return null;
|
||||||
return null;
|
}),
|
||||||
}
|
), switchTheme: SwitchThemeData(
|
||||||
if (states.contains(MaterialState.selected)) {
|
thumbColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
|
||||||
return const Color.fromRGBO(102, 187, 106, 1);
|
if (states.contains(MaterialState.disabled)) { return null; }
|
||||||
}
|
if (states.contains(MaterialState.selected)) { return const Color.fromRGBO(102, 187, 106, 1); }
|
||||||
return null;
|
return null;
|
||||||
}),
|
}),
|
||||||
),
|
trackColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
|
||||||
switchTheme: SwitchThemeData(
|
if (states.contains(MaterialState.disabled)) { return null; }
|
||||||
thumbColor:
|
if (states.contains(MaterialState.selected)) { return const Color.fromRGBO(102, 187, 106, 1); }
|
||||||
MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
|
return null;
|
||||||
if (states.contains(MaterialState.disabled)) {
|
}),
|
||||||
return null;
|
), colorScheme: const ColorScheme.light(
|
||||||
}
|
|
||||||
if (states.contains(MaterialState.selected)) {
|
|
||||||
return const Color.fromRGBO(102, 187, 106, 1);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}),
|
|
||||||
trackColor:
|
|
||||||
MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
|
|
||||||
if (states.contains(MaterialState.disabled)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (states.contains(MaterialState.selected)) {
|
|
||||||
return const Color.fromRGBO(102, 187, 106, 1);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
colorScheme: const ColorScheme.light(
|
|
||||||
primary: Colors.black,
|
primary: Colors.black,
|
||||||
secondary: Color.fromARGB(255, 163, 163, 163),
|
secondary: Color.fromARGB(255, 163, 163, 163),
|
||||||
).copyWith(background: const Color.fromRGBO(255, 255, 255, 1)),
|
).copyWith(background: const Color.fromRGBO(255, 255, 255, 1)),
|
||||||
@@ -120,9 +98,6 @@ final lightThemeData = ThemeData(
|
|||||||
final darkThemeData = ThemeData(
|
final darkThemeData = ThemeData(
|
||||||
fontFamily: 'Inter',
|
fontFamily: 'Inter',
|
||||||
brightness: Brightness.dark,
|
brightness: Brightness.dark,
|
||||||
dividerTheme: const DividerThemeData(
|
|
||||||
color: Colors.white12,
|
|
||||||
),
|
|
||||||
primaryColorLight: const Color.fromRGBO(255, 255, 255, 0.702),
|
primaryColorLight: const Color.fromRGBO(255, 255, 255, 0.702),
|
||||||
iconTheme: const IconThemeData(color: Colors.white),
|
iconTheme: const IconThemeData(color: Colors.white),
|
||||||
primaryIconTheme:
|
primaryIconTheme:
|
||||||
@@ -130,7 +105,6 @@ final darkThemeData = ThemeData(
|
|||||||
hintColor: const Color.fromRGBO(158, 158, 158, 1),
|
hintColor: const Color.fromRGBO(158, 158, 158, 1),
|
||||||
buttonTheme: const ButtonThemeData().copyWith(
|
buttonTheme: const ButtonThemeData().copyWith(
|
||||||
buttonColor: const Color.fromRGBO(45, 194, 98, 1.0),
|
buttonColor: const Color.fromRGBO(45, 194, 98, 1.0),
|
||||||
height: 56,
|
|
||||||
),
|
),
|
||||||
textTheme: _buildTextTheme(const Color.fromRGBO(255, 255, 255, 1)),
|
textTheme: _buildTextTheme(const Color.fromRGBO(255, 255, 255, 1)),
|
||||||
outlinedButtonTheme: buildOutlinedButtonThemeData(
|
outlinedButtonTheme: buildOutlinedButtonThemeData(
|
||||||
@@ -190,43 +164,24 @@ final darkThemeData = ThemeData(
|
|||||||
return const Color.fromRGBO(158, 158, 158, 1);
|
return const Color.fromRGBO(158, 158, 158, 1);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
),
|
), radioTheme: RadioThemeData(
|
||||||
radioTheme: RadioThemeData(
|
fillColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
|
||||||
fillColor:
|
if (states.contains(MaterialState.disabled)) { return null; }
|
||||||
MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
|
if (states.contains(MaterialState.selected)) { return const Color.fromRGBO(102, 187, 106, 1); }
|
||||||
if (states.contains(MaterialState.disabled)) {
|
return null;
|
||||||
return null;
|
}),
|
||||||
}
|
), switchTheme: SwitchThemeData(
|
||||||
if (states.contains(MaterialState.selected)) {
|
thumbColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
|
||||||
return const Color.fromRGBO(102, 187, 106, 1);
|
if (states.contains(MaterialState.disabled)) { return null; }
|
||||||
}
|
if (states.contains(MaterialState.selected)) { return const Color.fromRGBO(102, 187, 106, 1); }
|
||||||
return null;
|
return null;
|
||||||
}),
|
}),
|
||||||
),
|
trackColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
|
||||||
switchTheme: SwitchThemeData(
|
if (states.contains(MaterialState.disabled)) { return null; }
|
||||||
thumbColor:
|
if (states.contains(MaterialState.selected)) { return const Color.fromRGBO(102, 187, 106, 1); }
|
||||||
MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
|
return null;
|
||||||
if (states.contains(MaterialState.disabled)) {
|
}),
|
||||||
return null;
|
), colorScheme: const ColorScheme.dark(primary: Colors.white).copyWith(background: const Color.fromRGBO(0, 0, 0, 1)),
|
||||||
}
|
|
||||||
if (states.contains(MaterialState.selected)) {
|
|
||||||
return const Color.fromRGBO(102, 187, 106, 1);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}),
|
|
||||||
trackColor:
|
|
||||||
MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
|
|
||||||
if (states.contains(MaterialState.disabled)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (states.contains(MaterialState.selected)) {
|
|
||||||
return const Color.fromRGBO(102, 187, 106, 1);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
colorScheme: const ColorScheme.dark(primary: Colors.white)
|
|
||||||
.copyWith(background: const Color.fromRGBO(0, 0, 0, 1)),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
TextTheme _buildTextTheme(Color textColor) {
|
TextTheme _buildTextTheme(Color textColor) {
|
||||||
@@ -445,7 +400,6 @@ OutlinedButtonThemeData buildOutlinedButtonThemeData({
|
|||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
),
|
),
|
||||||
fixedSize: const Size.fromHeight(56),
|
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
padding: const EdgeInsets.fromLTRB(50, 16, 50, 16),
|
padding: const EdgeInsets.fromLTRB(50, 16, 50, 16),
|
||||||
textStyle: const TextStyle(
|
textStyle: const TextStyle(
|
||||||
@@ -482,9 +436,7 @@ ElevatedButtonThemeData buildElevatedButtonThemeData({
|
|||||||
}) {
|
}) {
|
||||||
return ElevatedButtonThemeData(
|
return ElevatedButtonThemeData(
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
foregroundColor: onPrimary,
|
foregroundColor: onPrimary, backgroundColor: primary, elevation: elevation,
|
||||||
backgroundColor: primary,
|
|
||||||
elevation: elevation,
|
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
textStyle: const TextStyle(
|
textStyle: const TextStyle(
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class AuthenticatorGateway {
|
|||||||
try {
|
try {
|
||||||
final response = await _enteDio.get("/authenticator/key");
|
final response = await _enteDio.get("/authenticator/key");
|
||||||
return AuthKey.fromMap(response.data);
|
return AuthKey.fromMap(response.data);
|
||||||
} on DioException catch (e) {
|
} on DioError catch (e) {
|
||||||
if (e.response != null && (e.response!.statusCode ?? 0) == 404) {
|
if (e.response != null && (e.response!.statusCode ?? 0) == 404) {
|
||||||
throw AuthenticatorKeyNotFound();
|
throw AuthenticatorKeyNotFound();
|
||||||
} else {
|
} else {
|
||||||
@@ -90,7 +90,7 @@ class AuthenticatorGateway {
|
|||||||
}
|
}
|
||||||
return authEntities;
|
return authEntities;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e is DioException && e.response?.statusCode == 401) {
|
if (e is DioError && e.response?.statusCode == 401) {
|
||||||
throw UnauthorizedError();
|
throw UnauthorizedError();
|
||||||
} else {
|
} else {
|
||||||
rethrow;
|
rethrow;
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
{}
|
|
||||||
@@ -144,8 +144,6 @@
|
|||||||
"enterCodeHint": "Geben Sie den 6-stelligen Code \naus Ihrer Authentifikator-App ein.",
|
"enterCodeHint": "Geben Sie den 6-stelligen Code \naus Ihrer Authentifikator-App ein.",
|
||||||
"lostDeviceTitle": "Gerät verloren?",
|
"lostDeviceTitle": "Gerät verloren?",
|
||||||
"twoFactorAuthTitle": "Zwei-Faktor-Authentifizierung",
|
"twoFactorAuthTitle": "Zwei-Faktor-Authentifizierung",
|
||||||
"passkeyAuthTitle": "Passkey Authentifizierung",
|
|
||||||
"verifyPasskey": "Passkey verifizieren",
|
|
||||||
"recoverAccount": "Konto wiederherstellen",
|
"recoverAccount": "Konto wiederherstellen",
|
||||||
"enterRecoveryKeyHint": "Geben Sie Ihren Wiederherstellungsschlüssel ein",
|
"enterRecoveryKeyHint": "Geben Sie Ihren Wiederherstellungsschlüssel ein",
|
||||||
"recover": "Wiederherstellen",
|
"recover": "Wiederherstellen",
|
||||||
@@ -406,15 +404,5 @@
|
|||||||
"signOutOtherDevices": "Andere Geräte abmelden",
|
"signOutOtherDevices": "Andere Geräte abmelden",
|
||||||
"doNotSignOut": "Nicht abmelden",
|
"doNotSignOut": "Nicht abmelden",
|
||||||
"hearUsWhereTitle": "Wie hast du von Ente erfahren? (optional)",
|
"hearUsWhereTitle": "Wie hast du von Ente erfahren? (optional)",
|
||||||
"hearUsExplanation": "Wir tracken keine App-Installationen. Es würde uns jedoch helfen, wenn du uns mitteilst, wie du von uns erfahren hast!",
|
"hearUsExplanation": "Wir tracken keine App-Installationen. Es würde uns jedoch helfen, wenn du uns mitteilst, wie du von uns erfahren hast!"
|
||||||
"waitingForBrowserRequest": "Warten auf Browseranfrage...",
|
|
||||||
"waitingForVerification": "Warte auf Bestätigung...",
|
|
||||||
"passkey": "Passkey",
|
|
||||||
"developerSettingsWarning": "Sind Sie sicher, dass Sie die Entwicklereinstellungen ändern möchten?",
|
|
||||||
"developerSettings": "Entwicklereinstellungen",
|
|
||||||
"serverEndpoint": "Server Endpunkt",
|
|
||||||
"invalidEndpoint": "Ungültiger Endpunkt",
|
|
||||||
"invalidEndpointMessage": "Der eingegebene Endpunkt ist ungültig. Bitte geben Sie einen gültigen Endpunkt ein und versuchen Sie es erneut.",
|
|
||||||
"endpointUpdatedMessage": "Endpunkt erfolgreich aktualisiert",
|
|
||||||
"customEndpoint": "Mit {endpoint} verbunden"
|
|
||||||
}
|
}
|
||||||
@@ -199,10 +199,6 @@
|
|||||||
"recoveryKeySaveDescription": "We don't store this key, please save this 24 word key in a safe place.",
|
"recoveryKeySaveDescription": "We don't store this key, please save this 24 word key in a safe place.",
|
||||||
"doThisLater": "Do this later",
|
"doThisLater": "Do this later",
|
||||||
"saveKey": "Save key",
|
"saveKey": "Save key",
|
||||||
"save": "Save",
|
|
||||||
"send": "Send",
|
|
||||||
"saveOrSendDescription": "Do you want to save this to your storage (Downloads folder by default) or send it to other apps?",
|
|
||||||
"saveOnlyDescription": "Do you want to save this to your storage (Downloads folder by default)?",
|
|
||||||
"back": "Back",
|
"back": "Back",
|
||||||
"createAccount": "Create account",
|
"createAccount": "Create account",
|
||||||
"passwordStrength": "Password strength: {passwordStrengthValue}",
|
"passwordStrength": "Password strength: {passwordStrengthValue}",
|
||||||
@@ -411,7 +407,6 @@
|
|||||||
"doNotSignOut": "Do not sign out",
|
"doNotSignOut": "Do not sign out",
|
||||||
"hearUsWhereTitle": "How did you hear about Ente? (optional)",
|
"hearUsWhereTitle": "How did you hear about Ente? (optional)",
|
||||||
"hearUsExplanation": "We don't track app installs. It'd help if you told us where you found us!",
|
"hearUsExplanation": "We don't track app installs. It'd help if you told us where you found us!",
|
||||||
"recoveryKeySaved": "Recovery key saved in Downloads folder!",
|
|
||||||
"waitingForBrowserRequest": "Waiting for browser request...",
|
"waitingForBrowserRequest": "Waiting for browser request...",
|
||||||
"waitingForVerification": "Waiting for verification...",
|
"waitingForVerification": "Waiting for verification...",
|
||||||
"passkey": "Passkey",
|
"passkey": "Passkey",
|
||||||
|
|||||||
@@ -144,8 +144,6 @@
|
|||||||
"enterCodeHint": "認証アプリに表示された 6 桁のコードを入力してください",
|
"enterCodeHint": "認証アプリに表示された 6 桁のコードを入力してください",
|
||||||
"lostDeviceTitle": "デバイスを紛失しましたか?",
|
"lostDeviceTitle": "デバイスを紛失しましたか?",
|
||||||
"twoFactorAuthTitle": "2 要素認証",
|
"twoFactorAuthTitle": "2 要素認証",
|
||||||
"passkeyAuthTitle": "パスキー認証",
|
|
||||||
"verifyPasskey": "パスキーの認証",
|
|
||||||
"recoverAccount": "アカウントを回復",
|
"recoverAccount": "アカウントを回復",
|
||||||
"enterRecoveryKeyHint": "回復キーを入力",
|
"enterRecoveryKeyHint": "回復キーを入力",
|
||||||
"recover": "回復",
|
"recover": "回復",
|
||||||
@@ -406,15 +404,5 @@
|
|||||||
"signOutOtherDevices": "他のデバイスからサインアウトする",
|
"signOutOtherDevices": "他のデバイスからサインアウトする",
|
||||||
"doNotSignOut": "サインアウトしない",
|
"doNotSignOut": "サインアウトしない",
|
||||||
"hearUsWhereTitle": "Ente についてどのようにお聞きになりましたか?(任意)",
|
"hearUsWhereTitle": "Ente についてどのようにお聞きになりましたか?(任意)",
|
||||||
"hearUsExplanation": "私たちはアプリのインストールを追跡していません。私たちをお知りになった場所を教えてください!",
|
"hearUsExplanation": "私たちはアプリのインストールを追跡していません。私たちをお知りになった場所を教えてください!"
|
||||||
"waitingForBrowserRequest": "ブラウザのリクエストを待っています...",
|
|
||||||
"waitingForVerification": "認証を待っています...",
|
|
||||||
"passkey": "パスキー",
|
|
||||||
"developerSettingsWarning": "開発者向け設定を変更してもよろしいですか?",
|
|
||||||
"developerSettings": "開発者向け設定",
|
|
||||||
"serverEndpoint": "サーバーエンドポイント",
|
|
||||||
"invalidEndpoint": "無効なエンドポイントです",
|
|
||||||
"invalidEndpointMessage": "入力されたエンドポイントは無効です。有効なエンドポイントを入力して再試行してください。",
|
|
||||||
"endpointUpdatedMessage": "エンドポイントの更新に成功しました",
|
|
||||||
"customEndpoint": "{endpoint} に接続しました"
|
|
||||||
}
|
}
|
||||||
@@ -144,8 +144,6 @@
|
|||||||
"enterCodeHint": "Digite o código de 6 dígitos de\nseu aplicativo autenticador",
|
"enterCodeHint": "Digite o código de 6 dígitos de\nseu aplicativo autenticador",
|
||||||
"lostDeviceTitle": "Perdeu seu dispositivo?",
|
"lostDeviceTitle": "Perdeu seu dispositivo?",
|
||||||
"twoFactorAuthTitle": "Autenticação de dois fatores",
|
"twoFactorAuthTitle": "Autenticação de dois fatores",
|
||||||
"passkeyAuthTitle": "Autenticação via Chave de acesso",
|
|
||||||
"verifyPasskey": "Verificar chave de acesso",
|
|
||||||
"recoverAccount": "Recuperar conta",
|
"recoverAccount": "Recuperar conta",
|
||||||
"enterRecoveryKeyHint": "Digite sua chave de recuperação",
|
"enterRecoveryKeyHint": "Digite sua chave de recuperação",
|
||||||
"recover": "Recuperar",
|
"recover": "Recuperar",
|
||||||
@@ -406,15 +404,5 @@
|
|||||||
"signOutOtherDevices": "Terminar sessão em outros dispositivos",
|
"signOutOtherDevices": "Terminar sessão em outros dispositivos",
|
||||||
"doNotSignOut": "Não encerrar sessão",
|
"doNotSignOut": "Não encerrar sessão",
|
||||||
"hearUsWhereTitle": "Como você ouviu sobre o Ente? (opcional)",
|
"hearUsWhereTitle": "Como você ouviu sobre o Ente? (opcional)",
|
||||||
"hearUsExplanation": "Não rastreamos instalações do aplicativo. Seria útil se você nos contasse onde nos encontrou!",
|
"hearUsExplanation": "Não rastreamos instalações do aplicativo. Seria útil se você nos contasse onde nos encontrou!"
|
||||||
"waitingForBrowserRequest": "Aguardando solicitação do navegador...",
|
|
||||||
"waitingForVerification": "Esperando por verificação...",
|
|
||||||
"passkey": "Chave de acesso",
|
|
||||||
"developerSettingsWarning": "Tem certeza de que deseja modificar as configurações de Desenvolvedor?",
|
|
||||||
"developerSettings": "Configurações de desenvolvedor",
|
|
||||||
"serverEndpoint": "Endpoint do servidor",
|
|
||||||
"invalidEndpoint": "Endpoint inválido",
|
|
||||||
"invalidEndpointMessage": "Desculpe, o endpoint que você inseriu é inválido. Por favor, insira um endpoint válido e tente novamente.",
|
|
||||||
"endpointUpdatedMessage": "Endpoint atualizado com sucesso",
|
|
||||||
"customEndpoint": "Conectado a {endpoint}"
|
|
||||||
}
|
}
|
||||||
@@ -131,16 +131,6 @@
|
|||||||
"about": "Om",
|
"about": "Om",
|
||||||
"terms": "Villkor",
|
"terms": "Villkor",
|
||||||
"warning": "Varning",
|
"warning": "Varning",
|
||||||
"importSuccessDesc": "Du har importerat {count} koder!",
|
|
||||||
"@importSuccessDesc": {
|
|
||||||
"placeholders": {
|
|
||||||
"count": {
|
|
||||||
"description": "The number of codes imported",
|
|
||||||
"type": "int",
|
|
||||||
"example": "1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pendingSyncs": "Varning",
|
"pendingSyncs": "Varning",
|
||||||
"activeSessions": "Aktiva sessioner",
|
"activeSessions": "Aktiva sessioner",
|
||||||
"enterPassword": "Ange lösenord",
|
"enterPassword": "Ange lösenord",
|
||||||
@@ -153,7 +143,5 @@
|
|||||||
"iOSOkButton": "OK",
|
"iOSOkButton": "OK",
|
||||||
"@iOSOkButton": {
|
"@iOSOkButton": {
|
||||||
"description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters."
|
"description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters."
|
||||||
},
|
}
|
||||||
"noInternetConnection": "Ingen internetanslutning",
|
|
||||||
"pleaseCheckYourInternetConnectionAndTryAgain": "Kontrollera din internetanslutning och försök igen."
|
|
||||||
}
|
}
|
||||||
@@ -145,7 +145,6 @@
|
|||||||
"lostDeviceTitle": "丢失了设备吗?",
|
"lostDeviceTitle": "丢失了设备吗?",
|
||||||
"twoFactorAuthTitle": "双因素认证",
|
"twoFactorAuthTitle": "双因素认证",
|
||||||
"passkeyAuthTitle": "通行密钥认证",
|
"passkeyAuthTitle": "通行密钥认证",
|
||||||
"verifyPasskey": "验证通行密钥",
|
|
||||||
"recoverAccount": "恢复账户",
|
"recoverAccount": "恢复账户",
|
||||||
"enterRecoveryKeyHint": "输入您的恢复密钥",
|
"enterRecoveryKeyHint": "输入您的恢复密钥",
|
||||||
"recover": "恢复",
|
"recover": "恢复",
|
||||||
@@ -408,13 +407,6 @@
|
|||||||
"hearUsWhereTitle": "您是如何知道Ente的? (可选的)",
|
"hearUsWhereTitle": "您是如何知道Ente的? (可选的)",
|
||||||
"hearUsExplanation": "我们不跟踪应用程序安装情况。如果您告诉我们您是在哪里找到我们的,将会有所帮助!",
|
"hearUsExplanation": "我们不跟踪应用程序安装情况。如果您告诉我们您是在哪里找到我们的,将会有所帮助!",
|
||||||
"waitingForBrowserRequest": "正在等待浏览器请求...",
|
"waitingForBrowserRequest": "正在等待浏览器请求...",
|
||||||
"waitingForVerification": "等待验证...",
|
"launchPasskeyUrlAgain": "再次启动 通行密钥 URL",
|
||||||
"passkey": "通行密钥",
|
"passkey": "通行密钥"
|
||||||
"developerSettingsWarning": "您确定要修改开发者设置吗?",
|
|
||||||
"developerSettings": "开发者设置",
|
|
||||||
"serverEndpoint": "服务器端点",
|
|
||||||
"invalidEndpoint": "端点无效",
|
|
||||||
"invalidEndpointMessage": "抱歉,您输入的端点无效。请输入有效的端点,然后重试。",
|
|
||||||
"endpointUpdatedMessage": "端点更新成功",
|
|
||||||
"customEndpoint": "已连接至 {endpoint}"
|
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ import 'dart:async';
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:adaptive_theme/adaptive_theme.dart';
|
import 'package:adaptive_theme/adaptive_theme.dart';
|
||||||
|
import 'package:computer/computer.dart';
|
||||||
import "package:ente_auth/app/view/app.dart";
|
import "package:ente_auth/app/view/app.dart";
|
||||||
import 'package:ente_auth/core/configuration.dart';
|
import 'package:ente_auth/core/configuration.dart';
|
||||||
import 'package:ente_auth/core/constants.dart';
|
import 'package:ente_auth/core/constants.dart';
|
||||||
@@ -16,14 +17,11 @@ import 'package:ente_auth/services/preference_service.dart';
|
|||||||
import 'package:ente_auth/services/update_service.dart';
|
import 'package:ente_auth/services/update_service.dart';
|
||||||
import 'package:ente_auth/services/user_remote_flag_service.dart';
|
import 'package:ente_auth/services/user_remote_flag_service.dart';
|
||||||
import 'package:ente_auth/services/user_service.dart';
|
import 'package:ente_auth/services/user_service.dart';
|
||||||
import 'package:ente_auth/services/window_listener_service.dart';
|
|
||||||
import 'package:ente_auth/store/code_store.dart';
|
import 'package:ente_auth/store/code_store.dart';
|
||||||
import 'package:ente_auth/ui/tools/app_lock.dart';
|
import 'package:ente_auth/ui/tools/app_lock.dart';
|
||||||
import 'package:ente_auth/ui/tools/lock_screen.dart';
|
import 'package:ente_auth/ui/tools/lock_screen.dart';
|
||||||
import 'package:ente_auth/ui/utils/icon_utils.dart';
|
import 'package:ente_auth/ui/utils/icon_utils.dart';
|
||||||
import 'package:ente_auth/utils/platform_util.dart';
|
import 'package:ente_auth/utils/crypto_util.dart';
|
||||||
import 'package:ente_auth/utils/window_protocol_handler.dart';
|
|
||||||
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
@@ -31,60 +29,11 @@ import 'package:flutter_displaymode/flutter_displaymode.dart';
|
|||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:privacy_screen/privacy_screen.dart';
|
import 'package:privacy_screen/privacy_screen.dart';
|
||||||
import 'package:system_tray/system_tray.dart';
|
|
||||||
import 'package:window_manager/window_manager.dart';
|
|
||||||
|
|
||||||
final _logger = Logger("main");
|
final _logger = Logger("main");
|
||||||
|
|
||||||
Future<void> initSystemTray() async {
|
|
||||||
String path =
|
|
||||||
Platform.isWindows ? 'assets/icon-light.ico' : 'assets/icon-light.png';
|
|
||||||
|
|
||||||
final AppWindow appWindow = AppWindow();
|
|
||||||
final SystemTray systemTray = SystemTray();
|
|
||||||
|
|
||||||
// We first init the systray menu
|
|
||||||
await systemTray.initSystemTray(
|
|
||||||
title: "",
|
|
||||||
iconPath: path,
|
|
||||||
);
|
|
||||||
|
|
||||||
// create context menu
|
|
||||||
final show = MenuItem(label: 'Show', onClicked: () => appWindow.show());
|
|
||||||
final hide = MenuItem(label: 'Hide', onClicked: () => appWindow.hide());
|
|
||||||
final exit = MenuItem(label: 'Exit', onClicked: () => appWindow.close());
|
|
||||||
|
|
||||||
// set context menu
|
|
||||||
await systemTray.setContextMenu([show, hide, exit]);
|
|
||||||
|
|
||||||
const kSystemTrayEventClick = 'leftMouseDown';
|
|
||||||
const kSystemTrayEventRightClick = 'rightMouseDown';
|
|
||||||
// // handle system tray event
|
|
||||||
systemTray.registerSystemTrayEventHandler((eventName) {
|
|
||||||
if (eventName == kSystemTrayEventClick) {
|
|
||||||
Platform.isWindows ? appWindow.show() : systemTray.popUpContextMenu();
|
|
||||||
} else if (eventName == kSystemTrayEventRightClick) {
|
|
||||||
Platform.isWindows ? systemTray.popUpContextMenu() : appWindow.show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
initSystemTray().ignore();
|
|
||||||
|
|
||||||
if (PlatformUtil.isDesktop()) {
|
|
||||||
await windowManager.ensureInitialized();
|
|
||||||
await WindowListenerService.instance.init();
|
|
||||||
WindowOptions windowOptions = WindowOptions(
|
|
||||||
size: WindowListenerService.instance.getWindowSize(),
|
|
||||||
);
|
|
||||||
await windowManager.waitUntilReadyToShow(windowOptions, () async {
|
|
||||||
await windowManager.show();
|
|
||||||
await windowManager.focus();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
await _runInForeground();
|
await _runInForeground();
|
||||||
await _setupPrivacyScreen();
|
await _setupPrivacyScreen();
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
@@ -121,14 +70,10 @@ ThemeMode _themeMode(AdaptiveThemeMode? savedThemeMode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future _runWithLogs(Function() function, {String prefix = ""}) async {
|
Future _runWithLogs(Function() function, {String prefix = ""}) async {
|
||||||
String dir = "";
|
|
||||||
try {
|
|
||||||
dir = "${(await getApplicationSupportDirectory()).path}/logs";
|
|
||||||
} catch (_) {}
|
|
||||||
await SuperLogging.main(
|
await SuperLogging.main(
|
||||||
LogConfig(
|
LogConfig(
|
||||||
body: function,
|
body: function,
|
||||||
logDirPath: dir,
|
logDirPath: (await getApplicationSupportDirectory()).path + "/logs",
|
||||||
maxLogFiles: 5,
|
maxLogFiles: 5,
|
||||||
sentryDsn: sentryDSN,
|
sentryDsn: sentryDSN,
|
||||||
enableInDebugMode: true,
|
enableInDebugMode: true,
|
||||||
@@ -137,19 +82,10 @@ Future _runWithLogs(Function() function, {String prefix = ""}) async {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _registerWindowsProtocol() {
|
|
||||||
const kWindowsScheme = 'ente';
|
|
||||||
// Register our protocol only on Windows platform
|
|
||||||
if (!kIsWeb && Platform.isWindows) {
|
|
||||||
WindowsProtocolHandler()
|
|
||||||
.register(kWindowsScheme, executable: null, arguments: null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _init(bool bool, {String? via}) async {
|
Future<void> _init(bool bool, {String? via}) async {
|
||||||
_registerWindowsProtocol();
|
// Start workers asynchronously. No need to wait for them to start
|
||||||
await initCryptoUtil();
|
Computer.shared().turnOn(workersCount: 4, verbose: kDebugMode).ignore();
|
||||||
|
CryptoUtil.init();
|
||||||
await PreferenceService.instance.init();
|
await PreferenceService.instance.init();
|
||||||
await CodeStore.instance.init();
|
await CodeStore.instance.init();
|
||||||
await Configuration.instance.init();
|
await Configuration.instance.init();
|
||||||
@@ -164,7 +100,6 @@ Future<void> _init(bool bool, {String? via}) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _setupPrivacyScreen() async {
|
Future<void> _setupPrivacyScreen() async {
|
||||||
if (!PlatformUtil.isMobile()) return;
|
|
||||||
final brightness =
|
final brightness =
|
||||||
SchedulerBinding.instance.platformDispatcher.platformBrightness;
|
SchedulerBinding.instance.platformDispatcher.platformBrightness;
|
||||||
bool isInDarkMode = brightness == Brightness.dark;
|
bool isInDarkMode = brightness == Brightness.dark;
|
||||||
|
|||||||
@@ -57,7 +57,14 @@ class Code {
|
|||||||
updatedAlgo,
|
updatedAlgo,
|
||||||
updatedType,
|
updatedType,
|
||||||
updatedCounter,
|
updatedCounter,
|
||||||
"otpauth://${updatedType.name}/$updateIssuer:$updateAccount?algorithm=${updatedAlgo.name}&digits=$updatedDigits&issuer=$updateIssuer&period=$updatePeriod&secret=$updatedSecret${updatedType == Type.hotp ? "&counter=$updatedCounter" : ""}",
|
"otpauth://${updatedType.name}/" +
|
||||||
|
updateIssuer +
|
||||||
|
":" +
|
||||||
|
updateAccount +
|
||||||
|
"?algorithm=${updatedAlgo.name}&digits=$updatedDigits&issuer=" +
|
||||||
|
updateIssuer +
|
||||||
|
"&period=$updatePeriod&secret=" +
|
||||||
|
updatedSecret + (updatedType == Type.hotp ? "&counter=$updatedCounter" : ""),
|
||||||
generatedID: generatedID,
|
generatedID: generatedID,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -76,28 +83,35 @@ class Code {
|
|||||||
Algorithm.sha1,
|
Algorithm.sha1,
|
||||||
Type.totp,
|
Type.totp,
|
||||||
0,
|
0,
|
||||||
"otpauth://totp/$issuer:$account?algorithm=SHA1&digits=6&issuer=$issuer&period=30&secret=$secret",
|
"otpauth://totp/" +
|
||||||
|
issuer +
|
||||||
|
":" +
|
||||||
|
account +
|
||||||
|
"?algorithm=SHA1&digits=6&issuer=" +
|
||||||
|
issuer +
|
||||||
|
"&period=30&secret=" +
|
||||||
|
secret,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Code fromRawData(String rawData) {
|
static Code fromRawData(String rawData) {
|
||||||
Uri uri = Uri.parse(rawData);
|
Uri uri = Uri.parse(rawData);
|
||||||
try {
|
try {
|
||||||
return Code(
|
return Code(
|
||||||
_getAccount(uri),
|
_getAccount(uri),
|
||||||
_getIssuer(uri),
|
_getIssuer(uri),
|
||||||
_getDigits(uri),
|
_getDigits(uri),
|
||||||
_getPeriod(uri),
|
_getPeriod(uri),
|
||||||
getSanitizedSecret(uri.queryParameters['secret']!),
|
getSanitizedSecret(uri.queryParameters['secret']!),
|
||||||
_getAlgorithm(uri),
|
_getAlgorithm(uri),
|
||||||
_getType(uri),
|
_getType(uri),
|
||||||
_getCounter(uri),
|
_getCounter(uri),
|
||||||
rawData,
|
rawData,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch(e) {
|
||||||
// if account name contains # without encoding,
|
// if account name contains # without encoding,
|
||||||
// rest of the url are treated as url fragment
|
// rest of the url are treated as url fragment
|
||||||
if (rawData.contains("#")) {
|
if(rawData.contains("#")) {
|
||||||
return Code.fromRawData(rawData.replaceAll("#", '%23'));
|
return Code.fromRawData(rawData.replaceAll("#", '%23'));
|
||||||
} else {
|
} else {
|
||||||
rethrow;
|
rethrow;
|
||||||
@@ -127,7 +141,7 @@ class Code {
|
|||||||
if (uri.queryParameters.containsKey("issuer")) {
|
if (uri.queryParameters.containsKey("issuer")) {
|
||||||
String issuerName = uri.queryParameters['issuer']!;
|
String issuerName = uri.queryParameters['issuer']!;
|
||||||
// Handle issuer name with period
|
// Handle issuer name with period
|
||||||
// See https://github.com/ente-io/ente/pull/77
|
// See https://github.com/ente-io/auth/pull/77
|
||||||
if (issuerName.contains("period=")) {
|
if (issuerName.contains("period=")) {
|
||||||
return issuerName.substring(0, issuerName.indexOf("period="));
|
return issuerName.substring(0, issuerName.indexOf("period="));
|
||||||
}
|
}
|
||||||
|
|||||||
9
auth/lib/models/derived_key_result.dart
Normal file
9
auth/lib/models/derived_key_result.dart
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
class DerivedKeyResult {
|
||||||
|
final Uint8List key;
|
||||||
|
final int memLimit;
|
||||||
|
final int opsLimit;
|
||||||
|
|
||||||
|
DerivedKeyResult(this.key, this.memLimit, this.opsLimit);
|
||||||
|
}
|
||||||
15
auth/lib/models/encryption_result.dart
Normal file
15
auth/lib/models/encryption_result.dart
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
class EncryptionResult {
|
||||||
|
final Uint8List? encryptedData;
|
||||||
|
final Uint8List? key;
|
||||||
|
final Uint8List? header;
|
||||||
|
final Uint8List? nonce;
|
||||||
|
|
||||||
|
EncryptionResult({
|
||||||
|
this.encryptedData,
|
||||||
|
this.key,
|
||||||
|
this.header,
|
||||||
|
this.nonce,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:ente_auth/app/view/app.dart';
|
import 'package:ente_auth/app/view/app.dart';
|
||||||
import 'package:ente_auth/core/configuration.dart';
|
import 'package:ente_auth/core/configuration.dart';
|
||||||
@@ -29,7 +28,7 @@ import "package:flutter/material.dart";
|
|||||||
import 'package:local_auth/local_auth.dart';
|
import 'package:local_auth/local_auth.dart';
|
||||||
|
|
||||||
class OnboardingPage extends StatefulWidget {
|
class OnboardingPage extends StatefulWidget {
|
||||||
const OnboardingPage({super.key});
|
const OnboardingPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<OnboardingPage> createState() => _OnboardingPageState();
|
State<OnboardingPage> createState() => _OnboardingPageState();
|
||||||
@@ -87,128 +86,118 @@ class _OnboardingPageState extends State<OnboardingPage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: SingleChildScrollView(
|
child: Center(
|
||||||
child: Center(
|
child: SingleChildScrollView(
|
||||||
child: ConstrainedBox(
|
child: Padding(
|
||||||
constraints:
|
padding:
|
||||||
const BoxConstraints.tightFor(height: 800, width: 450),
|
const EdgeInsets.symmetric(vertical: 40.0, horizontal: 40),
|
||||||
child: Padding(
|
child: Column(
|
||||||
padding: const EdgeInsets.symmetric(
|
children: [
|
||||||
vertical: 40.0,
|
Column(
|
||||||
horizontal: 40,
|
children: [
|
||||||
),
|
kDebugMode
|
||||||
child: Column(
|
? GestureDetector(
|
||||||
children: [
|
child: const Align(
|
||||||
Column(
|
alignment: Alignment.topRight,
|
||||||
children: [
|
child: Text("Lang"),
|
||||||
kDebugMode
|
|
||||||
? GestureDetector(
|
|
||||||
child: const Align(
|
|
||||||
alignment: Alignment.topRight,
|
|
||||||
child: Text("Lang"),
|
|
||||||
),
|
|
||||||
onTap: () async {
|
|
||||||
final locale = await getLocale();
|
|
||||||
// ignore: unawaited_futures
|
|
||||||
routeToPage(
|
|
||||||
context,
|
|
||||||
LanguageSelectorPage(
|
|
||||||
appSupportedLocales,
|
|
||||||
(locale) async {
|
|
||||||
await setLocale(locale);
|
|
||||||
App.setLocale(context, locale);
|
|
||||||
},
|
|
||||||
locale,
|
|
||||||
),
|
|
||||||
).then((value) {
|
|
||||||
setState(() {});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
)
|
|
||||||
: const SizedBox(),
|
|
||||||
Image.asset(
|
|
||||||
"assets/sheild-front-gradient.png",
|
|
||||||
width: 200,
|
|
||||||
height: 200,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
const Text(
|
|
||||||
"ente",
|
|
||||||
style: TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontFamily: 'Montserrat',
|
|
||||||
fontSize: 42,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text(
|
|
||||||
"Authenticator",
|
|
||||||
style: Theme.of(context).textTheme.headlineMedium,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
Text(
|
|
||||||
l10n.onBoardingBody,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.titleLarge!
|
|
||||||
.copyWith(
|
|
||||||
color: Colors.white38,
|
|
||||||
),
|
),
|
||||||
),
|
onTap: () async {
|
||||||
],
|
final locale = await getLocale();
|
||||||
),
|
// ignore: unawaited_futures
|
||||||
const SizedBox(height: 100),
|
routeToPage(
|
||||||
Container(
|
context,
|
||||||
width: double.infinity,
|
LanguageSelectorPage(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
appSupportedLocales,
|
||||||
child: GradientButton(
|
(locale) async {
|
||||||
onTap: _navigateToSignUpPage,
|
await setLocale(locale);
|
||||||
text: l10n.newUser,
|
App.setLocale(context, locale);
|
||||||
|
},
|
||||||
|
locale,
|
||||||
|
),
|
||||||
|
).then((value) {
|
||||||
|
setState(() {});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: const SizedBox(),
|
||||||
|
Image.asset(
|
||||||
|
"assets/sheild-front-gradient.png",
|
||||||
|
width: 200,
|
||||||
|
height: 200,
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
const Text(
|
||||||
|
"ente",
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontFamily: 'Montserrat',
|
||||||
|
fontSize: 42,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
"Authenticator",
|
||||||
|
style: Theme.of(context).textTheme.headlineMedium,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 32),
|
||||||
|
Text(
|
||||||
|
l10n.onBoardingBody,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style:
|
||||||
|
Theme.of(context).textTheme.titleLarge!.copyWith(
|
||||||
|
color: Colors.white38,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 100),
|
||||||
|
Container(
|
||||||
|
width: double.infinity,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
|
child: GradientButton(
|
||||||
|
onTap: _navigateToSignUpPage,
|
||||||
|
text: l10n.newUser,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
),
|
||||||
Container(
|
const SizedBox(height: 4),
|
||||||
height: 56,
|
Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
|
padding: const EdgeInsets.fromLTRB(20, 12, 20, 0),
|
||||||
child: Hero(
|
child: Hero(
|
||||||
tag: "log_in",
|
tag: "log_in",
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
.colorScheme
|
.colorScheme
|
||||||
.optionalActionButtonStyle,
|
.optionalActionButtonStyle,
|
||||||
onPressed: _navigateToSignInPage,
|
onPressed: _navigateToSignInPage,
|
||||||
child: Text(
|
child: Text(
|
||||||
l10n.existingUser,
|
l10n.existingUser,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.black, // same for both themes
|
color: Colors.black, // same for both themes
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
),
|
||||||
Container(
|
const SizedBox(height: 4),
|
||||||
width: double.infinity,
|
Container(
|
||||||
padding: const EdgeInsets.only(top: 20, bottom: 20),
|
width: double.infinity,
|
||||||
child: GestureDetector(
|
padding: const EdgeInsets.only(top: 20, bottom: 20),
|
||||||
onTap: _optForOfflineMode,
|
child: GestureDetector(
|
||||||
child: Center(
|
onTap: _optForOfflineMode,
|
||||||
child: Text(
|
child: Center(
|
||||||
l10n.useOffline,
|
child: Text(
|
||||||
style: body.copyWith(
|
l10n.useOffline,
|
||||||
color: Theme.of(context)
|
style: body.copyWith(
|
||||||
.colorScheme
|
color:
|
||||||
.mutedTextColor,
|
Theme.of(context).colorScheme.mutedTextColor,
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const DeveloperSettingsWidget(),
|
),
|
||||||
],
|
const DeveloperSettingsWidget(),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -219,9 +208,7 @@ class _OnboardingPageState extends State<OnboardingPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _optForOfflineMode() async {
|
Future<void> _optForOfflineMode() async {
|
||||||
bool canCheckBio = Platform.isMacOS || Platform.isLinux
|
bool canCheckBio = await LocalAuthentication().canCheckBiometrics;
|
||||||
? true
|
|
||||||
: await LocalAuthentication().canCheckBiometrics;
|
|
||||||
if (!canCheckBio) {
|
if (!canCheckBio) {
|
||||||
showToast(
|
showToast(
|
||||||
context,
|
context,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import "package:flutter/material.dart";
|
|||||||
class SetupEnterSecretKeyPage extends StatefulWidget {
|
class SetupEnterSecretKeyPage extends StatefulWidget {
|
||||||
final Code? code;
|
final Code? code;
|
||||||
|
|
||||||
SetupEnterSecretKeyPage({this.code, super.key});
|
SetupEnterSecretKeyPage({this.code, Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<SetupEnterSecretKeyPage> createState() =>
|
State<SetupEnterSecretKeyPage> createState() =>
|
||||||
@@ -32,7 +32,7 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
|
|||||||
widget.code != null ? safeDecode(widget.code!.account).trim() : null,
|
widget.code != null ? safeDecode(widget.code!.account).trim() : null,
|
||||||
);
|
);
|
||||||
_secretController = TextEditingController(
|
_secretController = TextEditingController(
|
||||||
text: widget.code?.secret,
|
text: widget.code != null ? widget.code!.secret : null,
|
||||||
);
|
);
|
||||||
_secretKeyObscured = widget.code != null;
|
_secretKeyObscured = widget.code != null;
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -45,8 +45,8 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
|
|||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(l10n.importAccountPageTitle),
|
title: Text(l10n.importAccountPageTitle),
|
||||||
),
|
),
|
||||||
body: Center(
|
body: SafeArea(
|
||||||
child: SingleChildScrollView(
|
child: Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 40.0, horizontal: 40),
|
padding: const EdgeInsets.symmetric(vertical: 40.0, horizontal: 40),
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import "package:ente_auth/l10n/l10n.dart";
|
import "package:ente_auth/l10n/l10n.dart";
|
||||||
@@ -9,7 +10,7 @@ import 'package:qr_flutter/qr_flutter.dart';
|
|||||||
class ViewQrPage extends StatelessWidget {
|
class ViewQrPage extends StatelessWidget {
|
||||||
final Code? code;
|
final Code? code;
|
||||||
|
|
||||||
ViewQrPage({this.code, super.key});
|
ViewQrPage({this.code, Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -21,22 +22,15 @@ class ViewQrPage extends StatelessWidget {
|
|||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(l10n.qrCode),
|
title: Text(l10n.qrCode),
|
||||||
),
|
),
|
||||||
body: Center(
|
body: SafeArea(
|
||||||
child: SingleChildScrollView(
|
child: Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 40.0, horizontal: 40),
|
padding: const EdgeInsets.symmetric(vertical: 40.0, horizontal: 40),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
QrImageView(
|
QrImage(
|
||||||
data: code!.rawData,
|
data: code!.rawData,
|
||||||
eyeStyle: QrEyeStyle(
|
foregroundColor: Theme.of(context).colorScheme.onBackground,
|
||||||
eyeShape: QrEyeShape.square,
|
|
||||||
color: Theme.of(context).colorScheme.onBackground,
|
|
||||||
),
|
|
||||||
dataModuleStyle: QrDataModuleStyle(
|
|
||||||
dataModuleShape: QrDataModuleShape.square,
|
|
||||||
color: Theme.of(context).colorScheme.onBackground,
|
|
||||||
),
|
|
||||||
version: QrVersions.auto,
|
version: QrVersions.auto,
|
||||||
size: qrSize,
|
size: qrSize,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -15,8 +15,9 @@ import 'package:ente_auth/models/authenticator/entity_result.dart';
|
|||||||
import 'package:ente_auth/models/authenticator/local_auth_entity.dart';
|
import 'package:ente_auth/models/authenticator/local_auth_entity.dart';
|
||||||
import 'package:ente_auth/store/authenticator_db.dart';
|
import 'package:ente_auth/store/authenticator_db.dart';
|
||||||
import 'package:ente_auth/store/offline_authenticator_db.dart';
|
import 'package:ente_auth/store/offline_authenticator_db.dart';
|
||||||
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
|
import 'package:ente_auth/utils/crypto_util.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter_sodium/flutter_sodium.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
@@ -74,10 +75,10 @@ class AuthenticatorService {
|
|||||||
final key = await getOrCreateAuthDataKey(mode);
|
final key = await getOrCreateAuthDataKey(mode);
|
||||||
for (LocalAuthEntity e in result) {
|
for (LocalAuthEntity e in result) {
|
||||||
try {
|
try {
|
||||||
final decryptedValue = await CryptoUtil.decryptData(
|
final decryptedValue = await CryptoUtil.decryptChaCha(
|
||||||
CryptoUtil.base642bin(e.encryptedData),
|
Sodium.base642bin(e.encryptedData),
|
||||||
key,
|
key,
|
||||||
CryptoUtil.base642bin(e.header),
|
Sodium.base642bin(e.header),
|
||||||
);
|
);
|
||||||
final hasSynced = !(e.id == null || e.shouldSync);
|
final hasSynced = !(e.id == null || e.shouldSync);
|
||||||
entities.add(
|
entities.add(
|
||||||
@@ -100,13 +101,12 @@ class AuthenticatorService {
|
|||||||
AccountMode accountMode,
|
AccountMode accountMode,
|
||||||
) async {
|
) async {
|
||||||
var key = await getOrCreateAuthDataKey(accountMode);
|
var key = await getOrCreateAuthDataKey(accountMode);
|
||||||
final encryptedKeyData = await CryptoUtil.encryptData(
|
final encryptedKeyData = await CryptoUtil.encryptChaCha(
|
||||||
utf8.encode(plainText),
|
utf8.encode(plainText) as Uint8List,
|
||||||
key,
|
key,
|
||||||
);
|
);
|
||||||
String encryptedData =
|
String encryptedData = Sodium.bin2base64(encryptedKeyData.encryptedData!);
|
||||||
CryptoUtil.bin2base64(encryptedKeyData.encryptedData!);
|
String header = Sodium.bin2base64(encryptedKeyData.header!);
|
||||||
String header = CryptoUtil.bin2base64(encryptedKeyData.header!);
|
|
||||||
final insertedID = accountMode.isOnline
|
final insertedID = accountMode.isOnline
|
||||||
? await _db.insert(encryptedData, header)
|
? await _db.insert(encryptedData, header)
|
||||||
: await _offlineDb.insert(encryptedData, header);
|
: await _offlineDb.insert(encryptedData, header);
|
||||||
@@ -123,13 +123,12 @@ class AuthenticatorService {
|
|||||||
AccountMode accountMode,
|
AccountMode accountMode,
|
||||||
) async {
|
) async {
|
||||||
var key = await getOrCreateAuthDataKey(accountMode);
|
var key = await getOrCreateAuthDataKey(accountMode);
|
||||||
final encryptedKeyData = await CryptoUtil.encryptData(
|
final encryptedKeyData = await CryptoUtil.encryptChaCha(
|
||||||
utf8.encode(plainText),
|
utf8.encode(plainText) as Uint8List,
|
||||||
key,
|
key,
|
||||||
);
|
);
|
||||||
String encryptedData =
|
String encryptedData = Sodium.bin2base64(encryptedKeyData.encryptedData!);
|
||||||
CryptoUtil.bin2base64(encryptedKeyData.encryptedData!);
|
String header = Sodium.bin2base64(encryptedKeyData.header!);
|
||||||
String header = CryptoUtil.bin2base64(encryptedKeyData.header!);
|
|
||||||
final int affectedRows = accountMode.isOnline
|
final int affectedRows = accountMode.isOnline
|
||||||
? await _db.updateEntry(generatedID, encryptedData, header)
|
? await _db.updateEntry(generatedID, encryptedData, header)
|
||||||
: await _offlineDb.updateEntry(generatedID, encryptedData, header);
|
: await _offlineDb.updateEntry(generatedID, encryptedData, header);
|
||||||
@@ -192,25 +191,25 @@ class AuthenticatorService {
|
|||||||
Future<void> _remoteToLocalSync() async {
|
Future<void> _remoteToLocalSync() async {
|
||||||
_logger.info('Initiating remote to local sync');
|
_logger.info('Initiating remote to local sync');
|
||||||
final int lastSyncTime = _prefs.getInt(_lastEntitySyncTime) ?? 0;
|
final int lastSyncTime = _prefs.getInt(_lastEntitySyncTime) ?? 0;
|
||||||
_logger.info("Current sync is $lastSyncTime");
|
_logger.info("Current sync is " + lastSyncTime.toString());
|
||||||
const int fetchLimit = 500;
|
const int fetchLimit = 500;
|
||||||
final List<AuthEntity> result =
|
final List<AuthEntity> result =
|
||||||
await _gateway.getDiff(lastSyncTime, limit: fetchLimit);
|
await _gateway.getDiff(lastSyncTime, limit: fetchLimit);
|
||||||
_logger.info("${result.length} entries fetched from remote");
|
_logger.info(result.length.toString() + " entries fetched from remote");
|
||||||
if (result.isEmpty) {
|
if (result.isEmpty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final maxSyncTime = result.map((e) => e.updatedAt).reduce(max);
|
final maxSyncTime = result.map((e) => e.updatedAt).reduce(max);
|
||||||
List<String> deletedIDs =
|
List<String> deletedIDs =
|
||||||
result.where((element) => element.isDeleted).map((e) => e.id).toList();
|
result.where((element) => element.isDeleted).map((e) => e.id).toList();
|
||||||
_logger.info("${deletedIDs.length} entries deleted");
|
_logger.info(deletedIDs.length.toString() + " entries deleted");
|
||||||
result.removeWhere((element) => element.isDeleted);
|
result.removeWhere((element) => element.isDeleted);
|
||||||
await _db.insertOrReplace(result);
|
await _db.insertOrReplace(result);
|
||||||
if (deletedIDs.isNotEmpty) {
|
if (deletedIDs.isNotEmpty) {
|
||||||
await _db.deleteByIDs(ids: deletedIDs);
|
await _db.deleteByIDs(ids: deletedIDs);
|
||||||
}
|
}
|
||||||
await _prefs.setInt(_lastEntitySyncTime, maxSyncTime);
|
await _prefs.setInt(_lastEntitySyncTime, maxSyncTime);
|
||||||
_logger.info("Setting synctime to $maxSyncTime");
|
_logger.info("Setting synctime to " + maxSyncTime.toString());
|
||||||
if (result.length == fetchLimit) {
|
if (result.length == fetchLimit) {
|
||||||
_logger.info("Diff limit reached, pulling again");
|
_logger.info("Diff limit reached, pulling again");
|
||||||
await _remoteToLocalSync();
|
await _remoteToLocalSync();
|
||||||
@@ -224,7 +223,7 @@ class AuthenticatorService {
|
|||||||
.where((element) => element.shouldSync || element.id == null)
|
.where((element) => element.shouldSync || element.id == null)
|
||||||
.toList();
|
.toList();
|
||||||
_logger.info(
|
_logger.info(
|
||||||
"${pendingUpdate.length} entries to be updated at remote",
|
pendingUpdate.length.toString() + " entries to be updated at remote",
|
||||||
);
|
);
|
||||||
for (LocalAuthEntity entity in pendingUpdate) {
|
for (LocalAuthEntity entity in pendingUpdate) {
|
||||||
if (entity.id == null) {
|
if (entity.id == null) {
|
||||||
@@ -263,21 +262,21 @@ class AuthenticatorService {
|
|||||||
try {
|
try {
|
||||||
final AuthKey response = await _gateway.getKey();
|
final AuthKey response = await _gateway.getKey();
|
||||||
final authKey = CryptoUtil.decryptSync(
|
final authKey = CryptoUtil.decryptSync(
|
||||||
CryptoUtil.base642bin(response.encryptedKey),
|
Sodium.base642bin(response.encryptedKey),
|
||||||
_config.getKey()!,
|
_config.getKey()!,
|
||||||
CryptoUtil.base642bin(response.header),
|
Sodium.base642bin(response.header),
|
||||||
);
|
);
|
||||||
await _config.setAuthSecretKey(CryptoUtil.bin2base64(authKey));
|
await _config.setAuthSecretKey(Sodium.bin2base64(authKey));
|
||||||
return authKey;
|
return authKey;
|
||||||
} on AuthenticatorKeyNotFound catch (e) {
|
} on AuthenticatorKeyNotFound catch (e) {
|
||||||
_logger.info("AuthenticatorKeyNotFound generating key ${e.stackTrace}");
|
_logger.info("AuthenticatorKeyNotFound generating key ${e.stackTrace}");
|
||||||
final key = CryptoUtil.generateKey();
|
final key = CryptoUtil.generateKey();
|
||||||
final encryptedKeyData = CryptoUtil.encryptSync(key, _config.getKey()!);
|
final encryptedKeyData = CryptoUtil.encryptSync(key, _config.getKey()!);
|
||||||
await _gateway.createKey(
|
await _gateway.createKey(
|
||||||
CryptoUtil.bin2base64(encryptedKeyData.encryptedData!),
|
Sodium.bin2base64(encryptedKeyData.encryptedData!),
|
||||||
CryptoUtil.bin2base64(encryptedKeyData.nonce!),
|
Sodium.bin2base64(encryptedKeyData.nonce!),
|
||||||
);
|
);
|
||||||
await _config.setAuthSecretKey(CryptoUtil.bin2base64(key));
|
await _config.setAuthSecretKey(Sodium.bin2base64(key));
|
||||||
return key;
|
return key;
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
_logger.severe("Failed to getOrCreateAuthDataKey", e, s);
|
_logger.severe("Failed to getOrCreateAuthDataKey", e, s);
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ class BillingService {
|
|||||||
|
|
||||||
Future<Response<dynamic>> _fetchPrivateBillingPlans() {
|
Future<Response<dynamic>> _fetchPrivateBillingPlans() {
|
||||||
return _dio.get(
|
return _dio.get(
|
||||||
"${_config.getHttpEndpoint()}/billing/user-plans/",
|
_config.getHttpEndpoint() + "/billing/user-plans/",
|
||||||
options: Options(
|
options: Options(
|
||||||
headers: {
|
headers: {
|
||||||
"X-Auth-Token": _config.getToken(),
|
"X-Auth-Token": _config.getToken(),
|
||||||
@@ -60,7 +60,7 @@ class BillingService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<Response<dynamic>> _fetchPublicBillingPlans() {
|
Future<Response<dynamic>> _fetchPublicBillingPlans() {
|
||||||
return _dio.get("${_config.getHttpEndpoint()}/billing/plans/v2");
|
return _dio.get(_config.getHttpEndpoint() + "/billing/plans/v2");
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Subscription> verifySubscription(
|
Future<Subscription> verifySubscription(
|
||||||
@@ -70,7 +70,7 @@ class BillingService {
|
|||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final response = await _dio.post(
|
final response = await _dio.post(
|
||||||
"${_config.getHttpEndpoint()}/billing/verify-subscription",
|
_config.getHttpEndpoint() + "/billing/verify-subscription",
|
||||||
data: {
|
data: {
|
||||||
"paymentProvider": paymentProvider ??
|
"paymentProvider": paymentProvider ??
|
||||||
(Platform.isAndroid ? "playstore" : "appstore"),
|
(Platform.isAndroid ? "playstore" : "appstore"),
|
||||||
@@ -84,7 +84,7 @@ class BillingService {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
return Subscription.fromMap(response.data["subscription"]);
|
return Subscription.fromMap(response.data["subscription"]);
|
||||||
} on DioException catch (e) {
|
} on DioError catch (e) {
|
||||||
if (e.response != null && e.response!.statusCode == 409) {
|
if (e.response != null && e.response!.statusCode == 409) {
|
||||||
throw SubscriptionAlreadyClaimedError();
|
throw SubscriptionAlreadyClaimedError();
|
||||||
} else {
|
} else {
|
||||||
@@ -100,7 +100,7 @@ class BillingService {
|
|||||||
if (_cachedSubscription == null) {
|
if (_cachedSubscription == null) {
|
||||||
try {
|
try {
|
||||||
final response = await _dio.get(
|
final response = await _dio.get(
|
||||||
"${_config.getHttpEndpoint()}/billing/subscription",
|
_config.getHttpEndpoint() + "/billing/subscription",
|
||||||
options: Options(
|
options: Options(
|
||||||
headers: {
|
headers: {
|
||||||
"X-Auth-Token": _config.getToken(),
|
"X-Auth-Token": _config.getToken(),
|
||||||
@@ -109,7 +109,7 @@ class BillingService {
|
|||||||
);
|
);
|
||||||
_cachedSubscription =
|
_cachedSubscription =
|
||||||
Subscription.fromMap(response.data["subscription"]);
|
Subscription.fromMap(response.data["subscription"]);
|
||||||
} on DioException catch (e, s) {
|
} on DioError catch (e, s) {
|
||||||
_logger.severe(e, s);
|
_logger.severe(e, s);
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
@@ -120,7 +120,7 @@ class BillingService {
|
|||||||
Future<Subscription> cancelStripeSubscription() async {
|
Future<Subscription> cancelStripeSubscription() async {
|
||||||
try {
|
try {
|
||||||
final response = await _dio.post(
|
final response = await _dio.post(
|
||||||
"${_config.getHttpEndpoint()}/billing/stripe/cancel-subscription",
|
_config.getHttpEndpoint() + "/billing/stripe/cancel-subscription",
|
||||||
options: Options(
|
options: Options(
|
||||||
headers: {
|
headers: {
|
||||||
"X-Auth-Token": _config.getToken(),
|
"X-Auth-Token": _config.getToken(),
|
||||||
@@ -129,7 +129,7 @@ class BillingService {
|
|||||||
);
|
);
|
||||||
final subscription = Subscription.fromMap(response.data["subscription"]);
|
final subscription = Subscription.fromMap(response.data["subscription"]);
|
||||||
return subscription;
|
return subscription;
|
||||||
} on DioException catch (e, s) {
|
} on DioError catch (e, s) {
|
||||||
_logger.severe(e, s);
|
_logger.severe(e, s);
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
@@ -138,7 +138,7 @@ class BillingService {
|
|||||||
Future<Subscription> activateStripeSubscription() async {
|
Future<Subscription> activateStripeSubscription() async {
|
||||||
try {
|
try {
|
||||||
final response = await _dio.post(
|
final response = await _dio.post(
|
||||||
"${_config.getHttpEndpoint()}/billing/stripe/activate-subscription",
|
_config.getHttpEndpoint() + "/billing/stripe/activate-subscription",
|
||||||
options: Options(
|
options: Options(
|
||||||
headers: {
|
headers: {
|
||||||
"X-Auth-Token": _config.getToken(),
|
"X-Auth-Token": _config.getToken(),
|
||||||
@@ -147,7 +147,7 @@ class BillingService {
|
|||||||
);
|
);
|
||||||
final subscription = Subscription.fromMap(response.data["subscription"]);
|
final subscription = Subscription.fromMap(response.data["subscription"]);
|
||||||
return subscription;
|
return subscription;
|
||||||
} on DioException catch (e, s) {
|
} on DioError catch (e, s) {
|
||||||
_logger.severe(e, s);
|
_logger.severe(e, s);
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
@@ -158,7 +158,7 @@ class BillingService {
|
|||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final response = await _dio.get(
|
final response = await _dio.get(
|
||||||
"${_config.getHttpEndpoint()}/billing/stripe/customer-portal",
|
_config.getHttpEndpoint() + "/billing/stripe/customer-portal",
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
"redirectURL": kWebPaymentRedirectUrl,
|
"redirectURL": kWebPaymentRedirectUrl,
|
||||||
},
|
},
|
||||||
@@ -169,7 +169,7 @@ class BillingService {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
return response.data["url"];
|
return response.data["url"];
|
||||||
} on DioException catch (e, s) {
|
} on DioError catch (e, s) {
|
||||||
_logger.severe(e, s);
|
_logger.severe(e, s);
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,15 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:ente_auth/core/configuration.dart';
|
import 'package:ente_auth/core/configuration.dart';
|
||||||
import 'package:ente_auth/ui/tools/app_lock.dart';
|
import 'package:ente_auth/ui/tools/app_lock.dart';
|
||||||
import 'package:ente_auth/utils/auth_util.dart';
|
import 'package:ente_auth/utils/auth_util.dart';
|
||||||
import 'package:ente_auth/utils/dialog_util.dart';
|
import 'package:ente_auth/utils/dialog_util.dart';
|
||||||
import 'package:ente_auth/utils/toast_util.dart';
|
import 'package:ente_auth/utils/toast_util.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_local_authentication/flutter_local_authentication.dart';
|
|
||||||
import 'package:local_auth/local_auth.dart';
|
import 'package:local_auth/local_auth.dart';
|
||||||
import 'package:logging/logging.dart';
|
|
||||||
|
|
||||||
class LocalAuthenticationService {
|
class LocalAuthenticationService {
|
||||||
LocalAuthenticationService._privateConstructor();
|
LocalAuthenticationService._privateConstructor();
|
||||||
static final LocalAuthenticationService instance =
|
static final LocalAuthenticationService instance =
|
||||||
LocalAuthenticationService._privateConstructor();
|
LocalAuthenticationService._privateConstructor();
|
||||||
final logger = Logger((LocalAuthenticationService).toString());
|
|
||||||
|
|
||||||
Future<bool> requestLocalAuthentication(
|
Future<bool> requestLocalAuthentication(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
@@ -44,7 +38,7 @@ class LocalAuthenticationService {
|
|||||||
String errorDialogContent, [
|
String errorDialogContent, [
|
||||||
String errorDialogTitle = "",
|
String errorDialogTitle = "",
|
||||||
]) async {
|
]) async {
|
||||||
if (await _isLocalAuthSupportedOnDevice()) {
|
if (await LocalAuthentication().isDeviceSupported()) {
|
||||||
AppLock.of(context)!.disable();
|
AppLock.of(context)!.disable();
|
||||||
final result = await requestAuthentication(
|
final result = await requestAuthentication(
|
||||||
context,
|
context,
|
||||||
@@ -71,12 +65,6 @@ class LocalAuthenticationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> _isLocalAuthSupportedOnDevice() async {
|
Future<bool> _isLocalAuthSupportedOnDevice() async {
|
||||||
try {
|
return await LocalAuthentication().isDeviceSupported();
|
||||||
return Platform.isMacOS || Platform.isLinux
|
|
||||||
? await FlutterLocalAuthentication().canAuthenticate()
|
|
||||||
: await LocalAuthentication().isDeviceSupported();
|
|
||||||
} on MissingPluginException {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ class NotificationService {
|
|||||||
_flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<
|
_flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<
|
||||||
AndroidFlutterLocalNotificationsPlugin>();
|
AndroidFlutterLocalNotificationsPlugin>();
|
||||||
if (implementation != null) {
|
if (implementation != null) {
|
||||||
await implementation.requestNotificationsPermission();
|
// ignore: unawaited_futures
|
||||||
|
implementation.requestPermission();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import 'dart:io';
|
|||||||
import 'package:ente_auth/core/constants.dart';
|
import 'package:ente_auth/core/constants.dart';
|
||||||
import 'package:ente_auth/core/network.dart';
|
import 'package:ente_auth/core/network.dart';
|
||||||
import 'package:ente_auth/services/notification_service.dart';
|
import 'package:ente_auth/services/notification_service.dart';
|
||||||
import 'package:ente_auth/utils/platform_util.dart';
|
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
@@ -131,8 +130,7 @@ class UpdateService {
|
|||||||
|
|
||||||
bool isIndependent() {
|
bool isIndependent() {
|
||||||
return flavor == "independent" ||
|
return flavor == "independent" ||
|
||||||
_packageInfo.packageName.endsWith("independent") ||
|
_packageInfo.packageName.endsWith("independent");
|
||||||
PlatformUtil.isDesktop();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,7 +141,6 @@ class LatestVersionInfo {
|
|||||||
final bool? shouldForceUpdate;
|
final bool? shouldForceUpdate;
|
||||||
final int lastSupportedVersionCode;
|
final int lastSupportedVersionCode;
|
||||||
final String? url;
|
final String? url;
|
||||||
final String? release;
|
|
||||||
final int? size;
|
final int? size;
|
||||||
final bool? shouldNotify;
|
final bool? shouldNotify;
|
||||||
|
|
||||||
@@ -154,7 +151,6 @@ class LatestVersionInfo {
|
|||||||
this.shouldForceUpdate,
|
this.shouldForceUpdate,
|
||||||
this.lastSupportedVersionCode,
|
this.lastSupportedVersionCode,
|
||||||
this.url,
|
this.url,
|
||||||
this.release,
|
|
||||||
this.size,
|
this.size,
|
||||||
this.shouldNotify,
|
this.shouldNotify,
|
||||||
);
|
);
|
||||||
@@ -167,7 +163,6 @@ class LatestVersionInfo {
|
|||||||
map['shouldForceUpdate'],
|
map['shouldForceUpdate'],
|
||||||
map['lastSupportedVersionCode'] ?? 1,
|
map['lastSupportedVersionCode'] ?? 1,
|
||||||
map['url'],
|
map['url'],
|
||||||
map['release'],
|
|
||||||
map['size'],
|
map['size'],
|
||||||
map['shouldNotify'],
|
map['shouldNotify'],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ class UserRemoteFlagService {
|
|||||||
queryParams["defaultValue"] = defaultValue;
|
queryParams["defaultValue"] = defaultValue;
|
||||||
}
|
}
|
||||||
final response = await _dio.get(
|
final response = await _dio.get(
|
||||||
"${_config.getHttpEndpoint()}/remote-store",
|
_config.getHttpEndpoint() + "/remote-store",
|
||||||
queryParameters: queryParams,
|
queryParameters: queryParams,
|
||||||
options: Options(
|
options: Options(
|
||||||
headers: {
|
headers: {
|
||||||
@@ -119,7 +119,7 @@ class UserRemoteFlagService {
|
|||||||
Future<void> _updateKeyValue(String key, String value) async {
|
Future<void> _updateKeyValue(String key, String value) async {
|
||||||
try {
|
try {
|
||||||
final response = await _dio.post(
|
final response = await _dio.post(
|
||||||
"${_config.getHttpEndpoint()}/remote-store/update",
|
_config.getHttpEndpoint() + "/remote-store/update",
|
||||||
data: {
|
data: {
|
||||||
"key": key,
|
"key": key,
|
||||||
"value": value,
|
"value": value,
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ import 'package:ente_auth/ui/home_page.dart';
|
|||||||
import 'package:ente_auth/ui/passkey_page.dart';
|
import 'package:ente_auth/ui/passkey_page.dart';
|
||||||
import 'package:ente_auth/ui/two_factor_authentication_page.dart';
|
import 'package:ente_auth/ui/two_factor_authentication_page.dart';
|
||||||
import 'package:ente_auth/ui/two_factor_recovery_page.dart';
|
import 'package:ente_auth/ui/two_factor_recovery_page.dart';
|
||||||
|
import 'package:ente_auth/utils/crypto_util.dart';
|
||||||
import 'package:ente_auth/utils/dialog_util.dart';
|
import 'package:ente_auth/utils/dialog_util.dart';
|
||||||
import 'package:ente_auth/utils/toast_util.dart';
|
import 'package:ente_auth/utils/toast_util.dart';
|
||||||
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
|
|
||||||
import "package:flutter/foundation.dart";
|
import "package:flutter/foundation.dart";
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
@@ -80,7 +80,7 @@ class UserService {
|
|||||||
await dialog.show();
|
await dialog.show();
|
||||||
try {
|
try {
|
||||||
final response = await _dio.post(
|
final response = await _dio.post(
|
||||||
"${_config.getHttpEndpoint()}/users/ott",
|
_config.getHttpEndpoint() + "/users/ott",
|
||||||
data: {"email": email, "purpose": isChangeEmail ? "change" : ""},
|
data: {"email": email, "purpose": isChangeEmail ? "change" : ""},
|
||||||
);
|
);
|
||||||
await dialog.hide();
|
await dialog.hide();
|
||||||
@@ -102,7 +102,7 @@ class UserService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
unawaited(showGenericErrorDialog(context: context));
|
unawaited(showGenericErrorDialog(context: context));
|
||||||
} on DioException catch (e) {
|
} on DioError catch (e) {
|
||||||
await dialog.hide();
|
await dialog.hide();
|
||||||
_logger.info(e);
|
_logger.info(e);
|
||||||
if (e.response != null && e.response!.statusCode == 403) {
|
if (e.response != null && e.response!.statusCode == 403) {
|
||||||
@@ -129,7 +129,7 @@ class UserService {
|
|||||||
String type = "SubCancellation",
|
String type = "SubCancellation",
|
||||||
}) async {
|
}) async {
|
||||||
await _dio.post(
|
await _dio.post(
|
||||||
"${_config.getHttpEndpoint()}/anonymous/feedback",
|
_config.getHttpEndpoint() + "/anonymous/feedback",
|
||||||
data: {"feedback": feedback, "type": "type"},
|
data: {"feedback": feedback, "type": "type"},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -173,7 +173,7 @@ class UserService {
|
|||||||
try {
|
try {
|
||||||
final response = await _enteDio.get("/users/sessions");
|
final response = await _enteDio.get("/users/sessions");
|
||||||
return Sessions.fromMap(response.data);
|
return Sessions.fromMap(response.data);
|
||||||
} on DioException catch (e) {
|
} on DioError catch (e) {
|
||||||
_logger.info(e);
|
_logger.info(e);
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
@@ -187,7 +187,7 @@ class UserService {
|
|||||||
"token": token,
|
"token": token,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} on DioException catch (e) {
|
} on DioError catch (e) {
|
||||||
_logger.info(e);
|
_logger.info(e);
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
@@ -196,7 +196,7 @@ class UserService {
|
|||||||
Future<void> leaveFamilyPlan() async {
|
Future<void> leaveFamilyPlan() async {
|
||||||
try {
|
try {
|
||||||
await _enteDio.delete("/family/leave");
|
await _enteDio.delete("/family/leave");
|
||||||
} on DioException catch (e) {
|
} on DioError catch (e) {
|
||||||
_logger.warning('failed to leave family plan', e);
|
_logger.warning('failed to leave family plan', e);
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
@@ -306,11 +306,11 @@ class UserService {
|
|||||||
"ott": ott,
|
"ott": ott,
|
||||||
};
|
};
|
||||||
if (!_config.isLoggedIn()) {
|
if (!_config.isLoggedIn()) {
|
||||||
verifyData["source"] = 'auth:${_getRefSource()}';
|
verifyData["source"] = 'auth:' + _getRefSource();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
final response = await _dio.post(
|
final response = await _dio.post(
|
||||||
"${_config.getHttpEndpoint()}/users/verify-email",
|
_config.getHttpEndpoint() + "/users/verify-email",
|
||||||
data: verifyData,
|
data: verifyData,
|
||||||
);
|
);
|
||||||
await dialog.hide();
|
await dialog.hide();
|
||||||
@@ -346,7 +346,7 @@ class UserService {
|
|||||||
// should never reach here
|
// should never reach here
|
||||||
throw Exception("unexpected response during email verification");
|
throw Exception("unexpected response during email verification");
|
||||||
}
|
}
|
||||||
} on DioException catch (e) {
|
} on DioError catch (e) {
|
||||||
_logger.info(e);
|
_logger.info(e);
|
||||||
await dialog.hide();
|
await dialog.hide();
|
||||||
if (e.response != null && e.response!.statusCode == 410) {
|
if (e.response != null && e.response!.statusCode == 410) {
|
||||||
@@ -410,7 +410,7 @@ class UserService {
|
|||||||
context.l10n.oops,
|
context.l10n.oops,
|
||||||
context.l10n.verificationFailedPleaseTryAgain,
|
context.l10n.verificationFailedPleaseTryAgain,
|
||||||
);
|
);
|
||||||
} on DioException catch (e) {
|
} on DioError catch (e) {
|
||||||
await dialog.hide();
|
await dialog.hide();
|
||||||
if (e.response != null && e.response!.statusCode == 403) {
|
if (e.response != null && e.response!.statusCode == 403) {
|
||||||
// ignore: unawaited_futures
|
// ignore: unawaited_futures
|
||||||
@@ -460,7 +460,7 @@ class UserService {
|
|||||||
Future<SrpAttributes> getSrpAttributes(String email) async {
|
Future<SrpAttributes> getSrpAttributes(String email) async {
|
||||||
try {
|
try {
|
||||||
final response = await _dio.get(
|
final response = await _dio.get(
|
||||||
"${_config.getHttpEndpoint()}/users/srp/attributes",
|
_config.getHttpEndpoint() + "/users/srp/attributes",
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
"email": email,
|
"email": email,
|
||||||
},
|
},
|
||||||
@@ -470,7 +470,7 @@ class UserService {
|
|||||||
} else {
|
} else {
|
||||||
throw Exception("get-srp-attributes action failed");
|
throw Exception("get-srp-attributes action failed");
|
||||||
}
|
}
|
||||||
} on DioException catch (e) {
|
} on DioError catch (e) {
|
||||||
if (e.response != null && e.response!.statusCode == 404) {
|
if (e.response != null && e.response!.statusCode == 404) {
|
||||||
throw SrpSetupNotCompleteError();
|
throw SrpSetupNotCompleteError();
|
||||||
}
|
}
|
||||||
@@ -523,7 +523,7 @@ class UserService {
|
|||||||
// ignore: need to calculate secret to get M1, unused_local_variable
|
// ignore: need to calculate secret to get M1, unused_local_variable
|
||||||
final clientS = client.calculateSecret(serverB);
|
final clientS = client.calculateSecret(serverB);
|
||||||
final clientM = client.calculateClientEvidenceMessage();
|
final clientM = client.calculateClientEvidenceMessage();
|
||||||
|
// ignore: unused_local_variable
|
||||||
late Response _;
|
late Response _;
|
||||||
if (setKeysRequest == null) {
|
if (setKeysRequest == null) {
|
||||||
_ = await _enteDio.post(
|
_ = await _enteDio.post(
|
||||||
@@ -573,7 +573,7 @@ class UserService {
|
|||||||
late Uint8List keyEncryptionKey;
|
late Uint8List keyEncryptionKey;
|
||||||
_logger.finest('Start deriving key');
|
_logger.finest('Start deriving key');
|
||||||
keyEncryptionKey = await CryptoUtil.deriveKey(
|
keyEncryptionKey = await CryptoUtil.deriveKey(
|
||||||
utf8.encode(userPassword),
|
utf8.encode(userPassword) as Uint8List,
|
||||||
CryptoUtil.base642bin(srpAttributes.kekSalt),
|
CryptoUtil.base642bin(srpAttributes.kekSalt),
|
||||||
srpAttributes.memLimit,
|
srpAttributes.memLimit,
|
||||||
srpAttributes.opsLimit,
|
srpAttributes.opsLimit,
|
||||||
@@ -596,7 +596,7 @@ class UserService {
|
|||||||
|
|
||||||
final A = client.generateClientCredentials(salt, identity, password);
|
final A = client.generateClientCredentials(salt, identity, password);
|
||||||
final createSessionResponse = await _dio.post(
|
final createSessionResponse = await _dio.post(
|
||||||
"${_config.getHttpEndpoint()}/users/srp/create-session",
|
_config.getHttpEndpoint() + "/users/srp/create-session",
|
||||||
data: {
|
data: {
|
||||||
"srpUserID": srpAttributes.srpUserID,
|
"srpUserID": srpAttributes.srpUserID,
|
||||||
"srpA": base64Encode(SRP6Util.encodeBigInt(A!)),
|
"srpA": base64Encode(SRP6Util.encodeBigInt(A!)),
|
||||||
@@ -610,7 +610,7 @@ class UserService {
|
|||||||
final clientS = client.calculateSecret(serverB);
|
final clientS = client.calculateSecret(serverB);
|
||||||
final clientM = client.calculateClientEvidenceMessage();
|
final clientM = client.calculateClientEvidenceMessage();
|
||||||
final response = await _dio.post(
|
final response = await _dio.post(
|
||||||
"${_config.getHttpEndpoint()}/users/srp/verify-session",
|
_config.getHttpEndpoint() + "/users/srp/verify-session",
|
||||||
data: {
|
data: {
|
||||||
"sessionID": sessionID,
|
"sessionID": sessionID,
|
||||||
"srpUserID": srpAttributes.srpUserID,
|
"srpUserID": srpAttributes.srpUserID,
|
||||||
@@ -709,7 +709,7 @@ class UserService {
|
|||||||
await dialog.show();
|
await dialog.show();
|
||||||
try {
|
try {
|
||||||
final response = await _dio.post(
|
final response = await _dio.post(
|
||||||
"${_config.getHttpEndpoint()}/users/two-factor/verify",
|
_config.getHttpEndpoint() + "/users/two-factor/verify",
|
||||||
data: {
|
data: {
|
||||||
"sessionID": sessionID,
|
"sessionID": sessionID,
|
||||||
"code": code,
|
"code": code,
|
||||||
@@ -729,7 +729,7 @@ class UserService {
|
|||||||
(route) => route.isFirst,
|
(route) => route.isFirst,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} on DioException catch (e) {
|
} on DioError catch (e) {
|
||||||
await dialog.hide();
|
await dialog.hide();
|
||||||
_logger.severe(e);
|
_logger.severe(e);
|
||||||
if (e.response != null && e.response!.statusCode == 404) {
|
if (e.response != null && e.response!.statusCode == 404) {
|
||||||
@@ -772,7 +772,7 @@ class UserService {
|
|||||||
await dialog.show();
|
await dialog.show();
|
||||||
try {
|
try {
|
||||||
final response = await _dio.get(
|
final response = await _dio.get(
|
||||||
"${_config.getHttpEndpoint()}/users/two-factor/recover",
|
_config.getHttpEndpoint() + "/users/two-factor/recover",
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
"sessionID": sessionID,
|
"sessionID": sessionID,
|
||||||
"twoFactorType": twoFactorTypeToString(type),
|
"twoFactorType": twoFactorTypeToString(type),
|
||||||
@@ -794,7 +794,7 @@ class UserService {
|
|||||||
(route) => route.isFirst,
|
(route) => route.isFirst,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} on DioException catch (e) {
|
} on DioError catch (e) {
|
||||||
await dialog.hide();
|
await dialog.hide();
|
||||||
_logger.severe(e);
|
_logger.severe(e);
|
||||||
if (e.response != null && e.response!.statusCode == 404) {
|
if (e.response != null && e.response!.statusCode == 404) {
|
||||||
@@ -868,7 +868,7 @@ class UserService {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
final response = await _dio.post(
|
final response = await _dio.post(
|
||||||
"${_config.getHttpEndpoint()}/users/two-factor/remove",
|
_config.getHttpEndpoint() + "/users/two-factor/remove",
|
||||||
data: {
|
data: {
|
||||||
"sessionID": sessionID,
|
"sessionID": sessionID,
|
||||||
"secret": secret,
|
"secret": secret,
|
||||||
@@ -891,7 +891,7 @@ class UserService {
|
|||||||
(route) => route.isFirst,
|
(route) => route.isFirst,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} on DioException catch (e) {
|
} on DioError catch (e) {
|
||||||
await dialog.hide();
|
await dialog.hide();
|
||||||
_logger.severe(e);
|
_logger.severe(e);
|
||||||
if (e.response != null && e.response!.statusCode == 404) {
|
if (e.response != null && e.response!.statusCode == 404) {
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'dart:ui';
|
|
||||||
|
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
|
||||||
import 'package:window_manager/window_manager.dart';
|
|
||||||
|
|
||||||
class WindowListenerService {
|
|
||||||
late SharedPreferences _preferences;
|
|
||||||
|
|
||||||
WindowListenerService._privateConstructor();
|
|
||||||
|
|
||||||
static final WindowListenerService instance =
|
|
||||||
WindowListenerService._privateConstructor();
|
|
||||||
|
|
||||||
Future<void> init() async {
|
|
||||||
_preferences = await SharedPreferences.getInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
Size getWindowSize() {
|
|
||||||
final double windowWidth = _preferences.getDouble('windowWidth') ?? 450.0;
|
|
||||||
final double windowHeight = _preferences.getDouble('windowHeight') ?? 800.0;
|
|
||||||
return Size(windowWidth, windowHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> onWindowResize() async {
|
|
||||||
// Save the window size to shared preferences
|
|
||||||
await _preferences.setDouble(
|
|
||||||
'windowWidth',
|
|
||||||
(await windowManager.getSize()).width,
|
|
||||||
);
|
|
||||||
await _preferences.setDouble(
|
|
||||||
'windowHeight',
|
|
||||||
(await windowManager.getSize()).height,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,12 +3,10 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:ente_auth/models/authenticator/auth_entity.dart';
|
import 'package:ente_auth/models/authenticator/auth_entity.dart';
|
||||||
import 'package:ente_auth/models/authenticator/local_auth_entity.dart';
|
import 'package:ente_auth/models/authenticator/local_auth_entity.dart';
|
||||||
import 'package:ente_auth/utils/directory_utils.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:path/path.dart';
|
import 'package:path/path.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:sqflite/sqflite.dart';
|
import 'package:sqflite/sqflite.dart';
|
||||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
|
||||||
|
|
||||||
class AuthenticatorDB {
|
class AuthenticatorDB {
|
||||||
static const _databaseName = "ente.authenticator.db";
|
static const _databaseName = "ente.authenticator.db";
|
||||||
@@ -27,16 +25,6 @@ class AuthenticatorDB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<Database> _initDatabase() async {
|
Future<Database> _initDatabase() async {
|
||||||
if (Platform.isWindows || Platform.isLinux) {
|
|
||||||
var databaseFactory = databaseFactoryFfi;
|
|
||||||
return await databaseFactory.openDatabase(
|
|
||||||
await DirectoryUtils.getDatabasePath(_databaseName),
|
|
||||||
options: OpenDatabaseOptions(
|
|
||||||
version: _databaseVersion,
|
|
||||||
onCreate: _onCreate,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
final Directory documentsDirectory =
|
final Directory documentsDirectory =
|
||||||
await getApplicationDocumentsDirectory();
|
await getApplicationDocumentsDirectory();
|
||||||
final String path = join(documentsDirectory.path, _databaseName);
|
final String path = join(documentsDirectory.path, _databaseName);
|
||||||
@@ -178,7 +166,7 @@ class AuthenticatorDB {
|
|||||||
batch.delete(entityTable, where: whereID, whereArgs: [id]);
|
batch.delete(entityTable, where: whereID, whereArgs: [id]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final _ = await batch.commit();
|
await batch.commit();
|
||||||
debugPrint("Done");
|
debugPrint("Done");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,10 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:ente_auth/models/authenticator/auth_entity.dart';
|
import 'package:ente_auth/models/authenticator/auth_entity.dart';
|
||||||
import 'package:ente_auth/models/authenticator/local_auth_entity.dart';
|
import 'package:ente_auth/models/authenticator/local_auth_entity.dart';
|
||||||
import 'package:ente_auth/utils/directory_utils.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:path/path.dart';
|
import 'package:path/path.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
import 'package:sqflite/sqflite.dart';
|
||||||
|
|
||||||
class OfflineAuthenticatorDB {
|
class OfflineAuthenticatorDB {
|
||||||
static const _databaseName = "ente.offline_authenticator.db";
|
static const _databaseName = "ente.offline_authenticator.db";
|
||||||
@@ -27,16 +26,6 @@ class OfflineAuthenticatorDB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<Database> _initDatabase() async {
|
Future<Database> _initDatabase() async {
|
||||||
if (Platform.isWindows || Platform.isLinux) {
|
|
||||||
var databaseFactory = databaseFactoryFfi;
|
|
||||||
return await databaseFactory.openDatabase(
|
|
||||||
await DirectoryUtils.getDatabasePath(_databaseName),
|
|
||||||
options: OpenDatabaseOptions(
|
|
||||||
version: _databaseVersion,
|
|
||||||
onCreate: _onCreate,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
final Directory documentsDirectory =
|
final Directory documentsDirectory =
|
||||||
await getApplicationDocumentsDirectory();
|
await getApplicationDocumentsDirectory();
|
||||||
final String path = join(documentsDirectory.path, _databaseName);
|
final String path = join(documentsDirectory.path, _databaseName);
|
||||||
@@ -163,7 +152,7 @@ class OfflineAuthenticatorDB {
|
|||||||
batch.delete(entityTable, where: whereID, whereArgs: [id]);
|
batch.delete(entityTable, where: whereID, whereArgs: [id]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final _ = await batch.commit();
|
await batch.commit();
|
||||||
debugPrint("Done");
|
debugPrint("Done");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -204,7 +204,6 @@ const Color _warning700 = Color.fromRGBO(234, 63, 63, 1);
|
|||||||
const Color _warning500 = Color.fromRGBO(255, 101, 101, 1);
|
const Color _warning500 = Color.fromRGBO(255, 101, 101, 1);
|
||||||
const Color _warning800 = Color(0xFFF53434);
|
const Color _warning800 = Color(0xFFF53434);
|
||||||
const Color warning500 = Color.fromRGBO(255, 101, 101, 1);
|
const Color warning500 = Color.fromRGBO(255, 101, 101, 1);
|
||||||
// ignore: unused_element
|
|
||||||
const Color _warning400 = Color.fromRGBO(255, 111, 111, 1);
|
const Color _warning400 = Color.fromRGBO(255, 111, 111, 1);
|
||||||
|
|
||||||
const Color _caution500 = Color.fromRGBO(255, 194, 71, 1);
|
const Color _caution500 = Color.fromRGBO(255, 194, 71, 1);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import 'package:ente_auth/utils/email_util.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class ChangeEmailDialog extends StatefulWidget {
|
class ChangeEmailDialog extends StatefulWidget {
|
||||||
const ChangeEmailDialog({super.key});
|
const ChangeEmailDialog({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ChangeEmailDialog> createState() => _ChangeEmailDialogState();
|
State<ChangeEmailDialog> createState() => _ChangeEmailDialogState();
|
||||||
|
|||||||
@@ -7,15 +7,15 @@ import 'package:ente_auth/services/local_authentication_service.dart';
|
|||||||
import 'package:ente_auth/services/user_service.dart';
|
import 'package:ente_auth/services/user_service.dart';
|
||||||
import 'package:ente_auth/ui/common/dialogs.dart';
|
import 'package:ente_auth/ui/common/dialogs.dart';
|
||||||
import 'package:ente_auth/ui/common/gradient_button.dart';
|
import 'package:ente_auth/ui/common/gradient_button.dart';
|
||||||
|
import 'package:ente_auth/utils/crypto_util.dart';
|
||||||
import 'package:ente_auth/utils/email_util.dart';
|
import 'package:ente_auth/utils/email_util.dart';
|
||||||
import 'package:ente_auth/utils/platform_util.dart';
|
|
||||||
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_sodium/flutter_sodium.dart';
|
||||||
|
|
||||||
class DeleteAccountPage extends StatelessWidget {
|
class DeleteAccountPage extends StatelessWidget {
|
||||||
const DeleteAccountPage({
|
const DeleteAccountPage({
|
||||||
super.key,
|
Key? key,
|
||||||
});
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -150,8 +150,6 @@ class DeleteAccountPage extends StatelessWidget {
|
|||||||
l10n.initiateAccountDeleteTitle,
|
l10n.initiateAccountDeleteTitle,
|
||||||
);
|
);
|
||||||
|
|
||||||
await PlatformUtil.refocusWindows();
|
|
||||||
|
|
||||||
if (hasAuthenticated) {
|
if (hasAuthenticated) {
|
||||||
final choice = await showChoiceDialogOld(
|
final choice = await showChoiceDialogOld(
|
||||||
context,
|
context,
|
||||||
@@ -166,10 +164,8 @@ class DeleteAccountPage extends StatelessWidget {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final decryptChallenge = CryptoUtil.openSealSync(
|
final decryptChallenge = CryptoUtil.openSealSync(
|
||||||
CryptoUtil.base642bin(response.encryptedChallenge),
|
Sodium.base642bin(response.encryptedChallenge),
|
||||||
CryptoUtil.base642bin(
|
Sodium.base642bin(Configuration.instance.getKeyAttributes()!.publicKey),
|
||||||
Configuration.instance.getKeyAttributes()!.publicKey,
|
|
||||||
),
|
|
||||||
Configuration.instance.getSecretKey()!,
|
Configuration.instance.getSecretKey()!,
|
||||||
);
|
);
|
||||||
final challengeResponseStr = utf8.decode(decryptChallenge);
|
final challengeResponseStr = utf8.decode(decryptChallenge);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import 'package:ente_auth/l10n/l10n.dart';
|
|||||||
import 'package:ente_auth/services/user_service.dart';
|
import 'package:ente_auth/services/user_service.dart';
|
||||||
import 'package:ente_auth/theme/ente_theme.dart';
|
import 'package:ente_auth/theme/ente_theme.dart';
|
||||||
import 'package:ente_auth/ui/common/dynamic_fab.dart';
|
import 'package:ente_auth/ui/common/dynamic_fab.dart';
|
||||||
import 'package:ente_auth/utils/platform_util.dart';
|
import 'package:ente_auth/ui/common/web_page.dart';
|
||||||
import 'package:ente_auth/utils/toast_util.dart';
|
import 'package:ente_auth/utils/toast_util.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
@@ -14,7 +14,7 @@ import 'package:step_progress_indicator/step_progress_indicator.dart';
|
|||||||
import "package:styled_text/styled_text.dart";
|
import "package:styled_text/styled_text.dart";
|
||||||
|
|
||||||
class EmailEntryPage extends StatefulWidget {
|
class EmailEntryPage extends StatefulWidget {
|
||||||
const EmailEntryPage({super.key});
|
const EmailEntryPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<EmailEntryPage> createState() => _EmailEntryPageState();
|
State<EmailEntryPage> createState() => _EmailEntryPageState();
|
||||||
@@ -190,7 +190,6 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
|
|||||||
padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
|
padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
keyboardType: TextInputType.text,
|
keyboardType: TextInputType.text,
|
||||||
textInputAction: TextInputAction.next,
|
|
||||||
controller: _passwordController1,
|
controller: _passwordController1,
|
||||||
obscureText: !_password1Visible,
|
obscureText: !_password1Visible,
|
||||||
enableSuggestions: true,
|
enableSuggestions: true,
|
||||||
@@ -428,10 +427,15 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
|
|||||||
tags: {
|
tags: {
|
||||||
'u-terms': StyledTextActionTag(
|
'u-terms': StyledTextActionTag(
|
||||||
(String? text, Map<String?, String?> attrs) =>
|
(String? text, Map<String?, String?> attrs) =>
|
||||||
PlatformUtil.openWebView(
|
Navigator.of(context).push(
|
||||||
context,
|
MaterialPageRoute(
|
||||||
context.l10n.termsOfServicesTitle,
|
builder: (BuildContext context) {
|
||||||
"https://ente.io/terms",
|
return WebPage(
|
||||||
|
context.l10n.termsOfServicesTitle,
|
||||||
|
"https://ente.io/terms",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
decoration: TextDecoration.underline,
|
decoration: TextDecoration.underline,
|
||||||
@@ -439,10 +443,15 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
|
|||||||
),
|
),
|
||||||
'u-policy': StyledTextActionTag(
|
'u-policy': StyledTextActionTag(
|
||||||
(String? text, Map<String?, String?> attrs) =>
|
(String? text, Map<String?, String?> attrs) =>
|
||||||
PlatformUtil.openWebView(
|
Navigator.of(context).push(
|
||||||
context,
|
MaterialPageRoute(
|
||||||
context.l10n.privacyPolicyTitle,
|
builder: (BuildContext context) {
|
||||||
"https://ente.io/privacy",
|
return WebPage(
|
||||||
|
context.l10n.privacyPolicyTitle,
|
||||||
|
"https://ente.io/privacy",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
decoration: TextDecoration.underline,
|
decoration: TextDecoration.underline,
|
||||||
@@ -485,10 +494,15 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
|
|||||||
tags: {
|
tags: {
|
||||||
'underline': StyledTextActionTag(
|
'underline': StyledTextActionTag(
|
||||||
(String? text, Map<String?, String?> attrs) =>
|
(String? text, Map<String?, String?> attrs) =>
|
||||||
PlatformUtil.openWebView(
|
Navigator.of(context).push(
|
||||||
context,
|
MaterialPageRoute(
|
||||||
context.l10n.encryption,
|
builder: (BuildContext context) {
|
||||||
"https://ente.io/architecture",
|
return WebPage(
|
||||||
|
context.l10n.encryption,
|
||||||
|
"https://ente.io/architecture",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
decoration: TextDecoration.underline,
|
decoration: TextDecoration.underline,
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ import 'package:ente_auth/models/api/user/srp.dart';
|
|||||||
import 'package:ente_auth/services/user_service.dart';
|
import 'package:ente_auth/services/user_service.dart';
|
||||||
import 'package:ente_auth/ui/account/login_pwd_verification_page.dart';
|
import 'package:ente_auth/ui/account/login_pwd_verification_page.dart';
|
||||||
import 'package:ente_auth/ui/common/dynamic_fab.dart';
|
import 'package:ente_auth/ui/common/dynamic_fab.dart';
|
||||||
import 'package:ente_auth/utils/platform_util.dart';
|
import 'package:ente_auth/ui/common/web_page.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import "package:styled_text/styled_text.dart";
|
import "package:styled_text/styled_text.dart";
|
||||||
|
|
||||||
class LoginPage extends StatefulWidget {
|
class LoginPage extends StatefulWidget {
|
||||||
const LoginPage({super.key});
|
const LoginPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<LoginPage> createState() => _LoginPageState();
|
State<LoginPage> createState() => _LoginPageState();
|
||||||
@@ -25,36 +25,6 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
Color? _emailInputFieldColor;
|
Color? _emailInputFieldColor;
|
||||||
final Logger _logger = Logger('_LoginPageState');
|
final Logger _logger = Logger('_LoginPageState');
|
||||||
|
|
||||||
Future<void> onPressed() async {
|
|
||||||
await UserService.instance.setEmail(_email!);
|
|
||||||
Configuration.instance.resetVolatilePassword();
|
|
||||||
SrpAttributes? attr;
|
|
||||||
bool isEmailVerificationEnabled = true;
|
|
||||||
try {
|
|
||||||
attr = await UserService.instance.getSrpAttributes(_email!);
|
|
||||||
isEmailVerificationEnabled = attr.isEmailMFAEnabled;
|
|
||||||
} catch (e) {
|
|
||||||
if (e is! SrpSetupNotCompleteError) {
|
|
||||||
_logger.severe('Error getting SRP attributes', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (attr != null && !isEmailVerificationEnabled) {
|
|
||||||
await Navigator.of(context).push(
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return LoginPasswordVerificationPage(
|
|
||||||
srpAttributes: attr!,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
await UserService.instance
|
|
||||||
.sendOtt(context, _email!, isCreateAccountScreen: false);
|
|
||||||
}
|
|
||||||
FocusScope.of(context).unfocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
_email = _config.getEmail();
|
_email = _config.getEmail();
|
||||||
@@ -90,7 +60,36 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
isKeypadOpen: isKeypadOpen,
|
isKeypadOpen: isKeypadOpen,
|
||||||
isFormValid: _emailIsValid,
|
isFormValid: _emailIsValid,
|
||||||
buttonText: context.l10n.logInLabel,
|
buttonText: context.l10n.logInLabel,
|
||||||
onPressedFunction: onPressed,
|
onPressedFunction: () async {
|
||||||
|
await UserService.instance.setEmail(_email!);
|
||||||
|
Configuration.instance.resetVolatilePassword();
|
||||||
|
SrpAttributes? attr;
|
||||||
|
bool isEmailVerificationEnabled = true;
|
||||||
|
try {
|
||||||
|
attr = await UserService.instance.getSrpAttributes(_email!);
|
||||||
|
isEmailVerificationEnabled = attr.isEmailMFAEnabled;
|
||||||
|
} catch (e) {
|
||||||
|
if (e is! SrpSetupNotCompleteError) {
|
||||||
|
_logger.severe('Error getting SRP attributes', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (attr != null && !isEmailVerificationEnabled) {
|
||||||
|
// ignore: unawaited_futures
|
||||||
|
Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return LoginPasswordVerificationPage(
|
||||||
|
srpAttributes: attr!,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await UserService.instance
|
||||||
|
.sendOtt(context, _email!, isCreateAccountScreen: false);
|
||||||
|
}
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
},
|
||||||
),
|
),
|
||||||
floatingActionButtonLocation: fabLocation(),
|
floatingActionButtonLocation: fabLocation(),
|
||||||
floatingActionButtonAnimator: NoScalingAnimation(),
|
floatingActionButtonAnimator: NoScalingAnimation(),
|
||||||
@@ -117,8 +116,6 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
padding: const EdgeInsets.fromLTRB(20, 24, 20, 0),
|
padding: const EdgeInsets.fromLTRB(20, 24, 20, 0),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
autofillHints: const [AutofillHints.email],
|
autofillHints: const [AutofillHints.email],
|
||||||
onFieldSubmitted:
|
|
||||||
_emailIsValid ? (value) => onPressed() : null,
|
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
fillColor: _emailInputFieldColor,
|
fillColor: _emailInputFieldColor,
|
||||||
filled: true,
|
filled: true,
|
||||||
@@ -182,10 +179,15 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
tags: {
|
tags: {
|
||||||
'u-terms': StyledTextActionTag(
|
'u-terms': StyledTextActionTag(
|
||||||
(String? text, Map<String?, String?> attrs) =>
|
(String? text, Map<String?, String?> attrs) =>
|
||||||
PlatformUtil.openWebView(
|
Navigator.of(context).push(
|
||||||
context,
|
MaterialPageRoute(
|
||||||
context.l10n.termsOfServicesTitle,
|
builder: (BuildContext context) {
|
||||||
"https://ente.io/terms",
|
return WebPage(
|
||||||
|
context.l10n.termsOfServicesTitle,
|
||||||
|
"https://ente.io/terms",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
decoration: TextDecoration.underline,
|
decoration: TextDecoration.underline,
|
||||||
@@ -193,10 +195,15 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
),
|
),
|
||||||
'u-policy': StyledTextActionTag(
|
'u-policy': StyledTextActionTag(
|
||||||
(String? text, Map<String?, String?> attrs) =>
|
(String? text, Map<String?, String?> attrs) =>
|
||||||
PlatformUtil.openWebView(
|
Navigator.of(context).push(
|
||||||
context,
|
MaterialPageRoute(
|
||||||
context.l10n.privacyPolicyTitle,
|
builder: (BuildContext context) {
|
||||||
"https://ente.io/privacy",
|
return WebPage(
|
||||||
|
context.l10n.privacyPolicyTitle,
|
||||||
|
"https://ente.io/privacy",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
decoration: TextDecoration.underline,
|
decoration: TextDecoration.underline,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import "package:dio/dio.dart";
|
import "package:dio/dio.dart";
|
||||||
import 'package:ente_auth/core/configuration.dart';
|
import 'package:ente_auth/core/configuration.dart';
|
||||||
|
import "package:ente_auth/core/errors.dart";
|
||||||
import "package:ente_auth/l10n/l10n.dart";
|
import "package:ente_auth/l10n/l10n.dart";
|
||||||
import "package:ente_auth/models/api/user/srp.dart";
|
import "package:ente_auth/models/api/user/srp.dart";
|
||||||
import "package:ente_auth/services/user_service.dart";
|
import "package:ente_auth/services/user_service.dart";
|
||||||
@@ -8,7 +9,6 @@ import 'package:ente_auth/ui/common/dynamic_fab.dart';
|
|||||||
import "package:ente_auth/ui/components/buttons/button_widget.dart";
|
import "package:ente_auth/ui/components/buttons/button_widget.dart";
|
||||||
import "package:ente_auth/utils/dialog_util.dart";
|
import "package:ente_auth/utils/dialog_util.dart";
|
||||||
import "package:ente_auth/utils/email_util.dart";
|
import "package:ente_auth/utils/email_util.dart";
|
||||||
import "package:ente_crypto_dart/ente_crypto_dart.dart";
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import "package:logging/logging.dart";
|
import "package:logging/logging.dart";
|
||||||
|
|
||||||
@@ -19,7 +19,8 @@ import "package:logging/logging.dart";
|
|||||||
// volatile password.
|
// volatile password.
|
||||||
class LoginPasswordVerificationPage extends StatefulWidget {
|
class LoginPasswordVerificationPage extends StatefulWidget {
|
||||||
final SrpAttributes srpAttributes;
|
final SrpAttributes srpAttributes;
|
||||||
const LoginPasswordVerificationPage({super.key, required this.srpAttributes});
|
const LoginPasswordVerificationPage({Key? key, required this.srpAttributes})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<LoginPasswordVerificationPage> createState() =>
|
State<LoginPasswordVerificationPage> createState() =>
|
||||||
@@ -35,11 +36,6 @@ class _LoginPasswordVerificationPageState
|
|||||||
bool _passwordInFocus = false;
|
bool _passwordInFocus = false;
|
||||||
bool _passwordVisible = false;
|
bool _passwordVisible = false;
|
||||||
|
|
||||||
Future<void> onPressed() async {
|
|
||||||
FocusScope.of(context).unfocus();
|
|
||||||
await verifyPassword(context, _passwordController.text);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -81,7 +77,10 @@ class _LoginPasswordVerificationPageState
|
|||||||
isKeypadOpen: isKeypadOpen,
|
isKeypadOpen: isKeypadOpen,
|
||||||
isFormValid: _passwordController.text.isNotEmpty,
|
isFormValid: _passwordController.text.isNotEmpty,
|
||||||
buttonText: context.l10n.logInLabel,
|
buttonText: context.l10n.logInLabel,
|
||||||
onPressedFunction: onPressed,
|
onPressedFunction: () async {
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
await verifyPassword(context, _passwordController.text);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
floatingActionButtonLocation: fabLocation(),
|
floatingActionButtonLocation: fabLocation(),
|
||||||
floatingActionButtonAnimator: NoScalingAnimation(),
|
floatingActionButtonAnimator: NoScalingAnimation(),
|
||||||
@@ -102,7 +101,7 @@ class _LoginPasswordVerificationPageState
|
|||||||
password,
|
password,
|
||||||
dialog,
|
dialog,
|
||||||
);
|
);
|
||||||
} on DioException catch (e, s) {
|
} on DioError catch (e, s) {
|
||||||
await dialog.hide();
|
await dialog.hide();
|
||||||
if (e.response != null && e.response!.statusCode == 401) {
|
if (e.response != null && e.response!.statusCode == 401) {
|
||||||
_logger.severe('server reject, failed verify SRP login', e, s);
|
_logger.severe('server reject, failed verify SRP login', e, s);
|
||||||
@@ -113,7 +112,7 @@ class _LoginPasswordVerificationPageState
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
_logger.severe('API failure during SRP login', e, s);
|
_logger.severe('API failure during SRP login', e, s);
|
||||||
if (e.type == DioExceptionType.unknown) {
|
if (e.type == DioErrorType.other) {
|
||||||
await _showContactSupportDialog(
|
await _showContactSupportDialog(
|
||||||
context,
|
context,
|
||||||
context.l10n.noInternetConnection,
|
context.l10n.noInternetConnection,
|
||||||
@@ -230,9 +229,6 @@ class _LoginPasswordVerificationPageState
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(20, 24, 20, 0),
|
padding: const EdgeInsets.fromLTRB(20, 24, 20, 0),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
onFieldSubmitted: _passwordController.text.isNotEmpty
|
|
||||||
? (_) => onPressed()
|
|
||||||
: null,
|
|
||||||
key: const ValueKey("passwordInputField"),
|
key: const ValueKey("passwordInputField"),
|
||||||
autofillHints: const [AutofillHints.password],
|
autofillHints: const [AutofillHints.password],
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ class OTTVerificationPage extends StatefulWidget {
|
|||||||
this.isChangeEmail = false,
|
this.isChangeEmail = false,
|
||||||
this.isCreateAccountScreen = false,
|
this.isCreateAccountScreen = false,
|
||||||
this.isResetPasswordScreen = false,
|
this.isResetPasswordScreen = false,
|
||||||
super.key,
|
Key? key,
|
||||||
});
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<OTTVerificationPage> createState() => _OTTVerificationPageState();
|
State<OTTVerificationPage> createState() => _OTTVerificationPageState();
|
||||||
@@ -27,23 +27,6 @@ class OTTVerificationPage extends StatefulWidget {
|
|||||||
class _OTTVerificationPageState extends State<OTTVerificationPage> {
|
class _OTTVerificationPageState extends State<OTTVerificationPage> {
|
||||||
final _verificationCodeController = TextEditingController();
|
final _verificationCodeController = TextEditingController();
|
||||||
|
|
||||||
Future<void> onPressed() async {
|
|
||||||
if (widget.isChangeEmail) {
|
|
||||||
await UserService.instance.changeEmail(
|
|
||||||
context,
|
|
||||||
widget.email,
|
|
||||||
_verificationCodeController.text,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
await UserService.instance.verifyEmail(
|
|
||||||
context,
|
|
||||||
_verificationCodeController.text,
|
|
||||||
isResettingPasswordScreen: widget.isResetPasswordScreen,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
FocusScope.of(context).unfocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
@@ -85,9 +68,22 @@ class _OTTVerificationPageState extends State<OTTVerificationPage> {
|
|||||||
body: _getBody(),
|
body: _getBody(),
|
||||||
floatingActionButton: DynamicFAB(
|
floatingActionButton: DynamicFAB(
|
||||||
isKeypadOpen: isKeypadOpen,
|
isKeypadOpen: isKeypadOpen,
|
||||||
isFormValid: _verificationCodeController.text.isNotEmpty,
|
isFormValid: !(_verificationCodeController.text.isEmpty),
|
||||||
buttonText: l10n.verify,
|
buttonText: l10n.verify,
|
||||||
onPressedFunction: onPressed,
|
onPressedFunction: () {
|
||||||
|
if (widget.isChangeEmail) {
|
||||||
|
UserService.instance.changeEmail(
|
||||||
|
context,
|
||||||
|
widget.email,
|
||||||
|
_verificationCodeController.text,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
UserService.instance
|
||||||
|
.verifyEmail(context, _verificationCodeController.text,
|
||||||
|
isResettingPasswordScreen: widget.isResetPasswordScreen,);
|
||||||
|
}
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
},
|
||||||
),
|
),
|
||||||
floatingActionButtonLocation: fabLocation(),
|
floatingActionButtonLocation: fabLocation(),
|
||||||
floatingActionButtonAnimator: NoScalingAnimation(),
|
floatingActionButtonAnimator: NoScalingAnimation(),
|
||||||
@@ -164,9 +160,6 @@ class _OTTVerificationPageState extends State<OTTVerificationPage> {
|
|||||||
padding: const EdgeInsets.fromLTRB(20, 16, 20, 16),
|
padding: const EdgeInsets.fromLTRB(20, 16, 20, 16),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
onFieldSubmitted: _verificationCodeController.text.isNotEmpty
|
|
||||||
? (_) => onPressed()
|
|
||||||
: null,
|
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
filled: true,
|
filled: true,
|
||||||
hintText: l10n.tapToEnterCode,
|
hintText: l10n.tapToEnterCode,
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import 'package:ente_auth/models/key_gen_result.dart';
|
|||||||
import 'package:ente_auth/services/user_service.dart';
|
import 'package:ente_auth/services/user_service.dart';
|
||||||
import 'package:ente_auth/ui/account/recovery_key_page.dart';
|
import 'package:ente_auth/ui/account/recovery_key_page.dart';
|
||||||
import 'package:ente_auth/ui/common/dynamic_fab.dart';
|
import 'package:ente_auth/ui/common/dynamic_fab.dart';
|
||||||
|
import 'package:ente_auth/ui/common/web_page.dart';
|
||||||
import 'package:ente_auth/ui/components/models/button_type.dart';
|
import 'package:ente_auth/ui/components/models/button_type.dart';
|
||||||
import 'package:ente_auth/ui/home_page.dart';
|
import 'package:ente_auth/ui/home_page.dart';
|
||||||
import 'package:ente_auth/utils/dialog_util.dart';
|
import 'package:ente_auth/utils/dialog_util.dart';
|
||||||
import 'package:ente_auth/utils/navigation_util.dart';
|
import 'package:ente_auth/utils/navigation_util.dart';
|
||||||
import 'package:ente_auth/utils/platform_util.dart';
|
|
||||||
import 'package:ente_auth/utils/toast_util.dart';
|
import 'package:ente_auth/utils/toast_util.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
@@ -25,7 +25,7 @@ enum PasswordEntryMode {
|
|||||||
class PasswordEntryPage extends StatefulWidget {
|
class PasswordEntryPage extends StatefulWidget {
|
||||||
final PasswordEntryMode mode;
|
final PasswordEntryMode mode;
|
||||||
|
|
||||||
const PasswordEntryPage({required this.mode, super.key});
|
const PasswordEntryPage({required this.mode, Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<PasswordEntryPage> createState() => _PasswordEntryPageState();
|
State<PasswordEntryPage> createState() => _PasswordEntryPageState();
|
||||||
@@ -149,239 +149,227 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: AutofillGroup(
|
child: AutofillGroup(
|
||||||
child: FocusTraversalGroup(
|
child: ListView(
|
||||||
policy: OrderedTraversalPolicy(),
|
children: [
|
||||||
child: ListView(
|
Padding(
|
||||||
children: [
|
padding:
|
||||||
Padding(
|
const EdgeInsets.symmetric(vertical: 30, horizontal: 20),
|
||||||
padding: const EdgeInsets.symmetric(
|
child: Text(
|
||||||
vertical: 30,
|
buttonTextAndHeading,
|
||||||
horizontal: 20,
|
style: Theme.of(context).textTheme.headlineMedium,
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
buttonTextAndHeading,
|
|
||||||
style: Theme.of(context).textTheme.headlineMedium,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Padding(
|
),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
Padding(
|
||||||
child: Text(
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
widget.mode == PasswordEntryMode.set
|
child: Text(
|
||||||
? context.l10n.enterPasswordToEncrypt
|
widget.mode == PasswordEntryMode.set
|
||||||
: context.l10n.enterNewPasswordToEncrypt,
|
? context.l10n.enterPasswordToEncrypt
|
||||||
textAlign: TextAlign.start,
|
: context.l10n.enterNewPasswordToEncrypt,
|
||||||
style: Theme.of(context)
|
textAlign: TextAlign.start,
|
||||||
.textTheme
|
style: Theme.of(context)
|
||||||
.titleMedium!
|
.textTheme
|
||||||
.copyWith(fontSize: 14),
|
.titleMedium!
|
||||||
),
|
.copyWith(fontSize: 14),
|
||||||
),
|
),
|
||||||
const Padding(padding: EdgeInsets.all(8)),
|
),
|
||||||
Padding(
|
const Padding(padding: EdgeInsets.all(8)),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
Padding(
|
||||||
child: StyledText(
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
text: context.l10n.passwordWarning,
|
child: StyledText(
|
||||||
style: Theme.of(context)
|
text: context.l10n.passwordWarning,
|
||||||
.textTheme
|
style: Theme.of(context)
|
||||||
.titleMedium!
|
.textTheme
|
||||||
.copyWith(fontSize: 14),
|
.titleMedium!
|
||||||
tags: {
|
.copyWith(fontSize: 14),
|
||||||
'underline': StyledTextTag(
|
tags: {
|
||||||
style:
|
'underline': StyledTextTag(
|
||||||
Theme.of(context).textTheme.titleMedium!.copyWith(
|
style:
|
||||||
fontSize: 14,
|
Theme.of(context).textTheme.titleMedium!.copyWith(
|
||||||
decoration: TextDecoration.underline,
|
fontSize: 14,
|
||||||
),
|
decoration: TextDecoration.underline,
|
||||||
),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Padding(padding: EdgeInsets.all(12)),
|
|
||||||
Visibility(
|
|
||||||
// hidden textForm for suggesting auto-fill service for saving
|
|
||||||
// password
|
|
||||||
visible: false,
|
|
||||||
child: TextFormField(
|
|
||||||
autofillHints: const [
|
|
||||||
AutofillHints.email,
|
|
||||||
],
|
|
||||||
autocorrect: false,
|
|
||||||
keyboardType: TextInputType.emailAddress,
|
|
||||||
initialValue: email,
|
|
||||||
textInputAction: TextInputAction.next,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
|
|
||||||
child: TextFormField(
|
|
||||||
autofillHints: const [AutofillHints.newPassword],
|
|
||||||
onFieldSubmitted: (_) {
|
|
||||||
do {
|
|
||||||
FocusScope.of(context).nextFocus();
|
|
||||||
} while (FocusScope.of(context).focusedChild!.context ==
|
|
||||||
null);
|
|
||||||
},
|
|
||||||
decoration: InputDecoration(
|
|
||||||
fillColor:
|
|
||||||
_isPasswordValid ? _validFieldValueColor : null,
|
|
||||||
filled: true,
|
|
||||||
hintText: context.l10n.password,
|
|
||||||
contentPadding: const EdgeInsets.all(20),
|
|
||||||
border: UnderlineInputBorder(
|
|
||||||
borderSide: BorderSide.none,
|
|
||||||
borderRadius: BorderRadius.circular(6),
|
|
||||||
),
|
|
||||||
suffixIcon: _password1InFocus
|
|
||||||
? IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
_password1Visible
|
|
||||||
? Icons.visibility
|
|
||||||
: Icons.visibility_off,
|
|
||||||
color: Theme.of(context).iconTheme.color,
|
|
||||||
size: 20,
|
|
||||||
),
|
),
|
||||||
onPressed: () {
|
|
||||||
setState(() {
|
|
||||||
_password1Visible = !_password1Visible;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
)
|
|
||||||
: _isPasswordValid
|
|
||||||
? Icon(
|
|
||||||
Icons.check,
|
|
||||||
color: Theme.of(context)
|
|
||||||
.inputDecorationTheme
|
|
||||||
.focusedBorder!
|
|
||||||
.borderSide
|
|
||||||
.color,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
),
|
),
|
||||||
obscureText: !_password1Visible,
|
},
|
||||||
controller: _passwordController1,
|
),
|
||||||
autofocus: false,
|
),
|
||||||
autocorrect: false,
|
const Padding(padding: EdgeInsets.all(12)),
|
||||||
keyboardType: TextInputType.visiblePassword,
|
Visibility(
|
||||||
onChanged: (password) {
|
// hidden textForm for suggesting auto-fill service for saving
|
||||||
setState(() {
|
// password
|
||||||
_passwordInInputBox = password;
|
visible: false,
|
||||||
_passwordStrength =
|
child: TextFormField(
|
||||||
estimatePasswordStrength(password);
|
autofillHints: const [
|
||||||
_isPasswordValid = _passwordStrength >=
|
AutofillHints.email,
|
||||||
kMildPasswordStrengthThreshold;
|
],
|
||||||
|
autocorrect: false,
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
initialValue: email,
|
||||||
|
textInputAction: TextInputAction.next,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
|
||||||
|
child: TextFormField(
|
||||||
|
autofillHints: const [AutofillHints.newPassword],
|
||||||
|
decoration: InputDecoration(
|
||||||
|
fillColor:
|
||||||
|
_isPasswordValid ? _validFieldValueColor : null,
|
||||||
|
filled: true,
|
||||||
|
hintText: context.l10n.password,
|
||||||
|
contentPadding: const EdgeInsets.all(20),
|
||||||
|
border: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide.none,
|
||||||
|
borderRadius: BorderRadius.circular(6),
|
||||||
|
),
|
||||||
|
suffixIcon: _password1InFocus
|
||||||
|
? IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
_password1Visible
|
||||||
|
? Icons.visibility
|
||||||
|
: Icons.visibility_off,
|
||||||
|
color: Theme.of(context).iconTheme.color,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
_password1Visible = !_password1Visible;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: _isPasswordValid
|
||||||
|
? Icon(
|
||||||
|
Icons.check,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.inputDecorationTheme
|
||||||
|
.focusedBorder!
|
||||||
|
.borderSide
|
||||||
|
.color,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
obscureText: !_password1Visible,
|
||||||
|
controller: _passwordController1,
|
||||||
|
autofocus: false,
|
||||||
|
autocorrect: false,
|
||||||
|
keyboardType: TextInputType.visiblePassword,
|
||||||
|
onChanged: (password) {
|
||||||
|
setState(() {
|
||||||
|
_passwordInInputBox = password;
|
||||||
|
_passwordStrength = estimatePasswordStrength(password);
|
||||||
|
_isPasswordValid =
|
||||||
|
_passwordStrength >= kMildPasswordStrengthThreshold;
|
||||||
|
_passwordsMatch = _passwordInInputBox ==
|
||||||
|
_passwordInInputConfirmationBox;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
textInputAction: TextInputAction.next,
|
||||||
|
focusNode: _password1FocusNode,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
|
||||||
|
child: TextFormField(
|
||||||
|
keyboardType: TextInputType.visiblePassword,
|
||||||
|
controller: _passwordController2,
|
||||||
|
obscureText: !_password2Visible,
|
||||||
|
autofillHints: const [AutofillHints.newPassword],
|
||||||
|
onEditingComplete: () => TextInput.finishAutofillContext(),
|
||||||
|
decoration: InputDecoration(
|
||||||
|
fillColor: _passwordsMatch ? _validFieldValueColor : null,
|
||||||
|
filled: true,
|
||||||
|
hintText: context.l10n.confirmPassword,
|
||||||
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 20,
|
||||||
|
vertical: 20,
|
||||||
|
),
|
||||||
|
suffixIcon: _password2InFocus
|
||||||
|
? IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
_password2Visible
|
||||||
|
? Icons.visibility
|
||||||
|
: Icons.visibility_off,
|
||||||
|
color: Theme.of(context).iconTheme.color,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
_password2Visible = !_password2Visible;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: _passwordsMatch
|
||||||
|
? Icon(
|
||||||
|
Icons.check,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.inputDecorationTheme
|
||||||
|
.focusedBorder!
|
||||||
|
.borderSide
|
||||||
|
.color,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
border: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide.none,
|
||||||
|
borderRadius: BorderRadius.circular(6),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
focusNode: _password2FocusNode,
|
||||||
|
onChanged: (cnfPassword) {
|
||||||
|
setState(() {
|
||||||
|
_passwordInInputConfirmationBox = cnfPassword;
|
||||||
|
if (_passwordInInputBox != '') {
|
||||||
_passwordsMatch = _passwordInInputBox ==
|
_passwordsMatch = _passwordInInputBox ==
|
||||||
_passwordInInputConfirmationBox;
|
_passwordInInputConfirmationBox;
|
||||||
});
|
}
|
||||||
},
|
});
|
||||||
textInputAction: TextInputAction.next,
|
|
||||||
focusNode: _password1FocusNode,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
|
|
||||||
child: TextFormField(
|
|
||||||
keyboardType: TextInputType.visiblePassword,
|
|
||||||
controller: _passwordController2,
|
|
||||||
obscureText: !_password2Visible,
|
|
||||||
autofillHints: const [AutofillHints.newPassword],
|
|
||||||
onEditingComplete: () =>
|
|
||||||
TextInput.finishAutofillContext(),
|
|
||||||
decoration: InputDecoration(
|
|
||||||
fillColor:
|
|
||||||
_passwordsMatch ? _validFieldValueColor : null,
|
|
||||||
filled: true,
|
|
||||||
hintText: context.l10n.confirmPassword,
|
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 20,
|
|
||||||
vertical: 20,
|
|
||||||
),
|
|
||||||
suffixIcon: _password2InFocus
|
|
||||||
? IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
_password2Visible
|
|
||||||
? Icons.visibility
|
|
||||||
: Icons.visibility_off,
|
|
||||||
color: Theme.of(context).iconTheme.color,
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
setState(() {
|
|
||||||
_password2Visible = !_password2Visible;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
)
|
|
||||||
: _passwordsMatch
|
|
||||||
? Icon(
|
|
||||||
Icons.check,
|
|
||||||
color: Theme.of(context)
|
|
||||||
.inputDecorationTheme
|
|
||||||
.focusedBorder!
|
|
||||||
.borderSide
|
|
||||||
.color,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
border: UnderlineInputBorder(
|
|
||||||
borderSide: BorderSide.none,
|
|
||||||
borderRadius: BorderRadius.circular(6),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
focusNode: _password2FocusNode,
|
|
||||||
onChanged: (cnfPassword) {
|
|
||||||
setState(() {
|
|
||||||
_passwordInInputConfirmationBox = cnfPassword;
|
|
||||||
if (_passwordInInputBox != '') {
|
|
||||||
_passwordsMatch = _passwordInInputBox ==
|
|
||||||
_passwordInInputConfirmationBox;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Opacity(
|
|
||||||
opacity: (_passwordInInputBox != '') && _password1InFocus
|
|
||||||
? 1
|
|
||||||
: 0,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 20,
|
|
||||||
vertical: 8,
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
context.l10n.passwordStrength(passwordStrengthText),
|
|
||||||
style: TextStyle(
|
|
||||||
color: passwordStrengthColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
GestureDetector(
|
|
||||||
behavior: HitTestBehavior.translucent,
|
|
||||||
onTap: () {
|
|
||||||
PlatformUtil.openWebView(
|
|
||||||
context,
|
|
||||||
context.l10n.howItWorks,
|
|
||||||
"https://ente.io/architecture",
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
child: Container(
|
),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
),
|
||||||
child: RichText(
|
Opacity(
|
||||||
text: TextSpan(
|
opacity:
|
||||||
text: context.l10n.howItWorks,
|
(_passwordInInputBox != '') && _password1InFocus ? 1 : 0,
|
||||||
style:
|
child: Padding(
|
||||||
Theme.of(context).textTheme.titleMedium!.copyWith(
|
padding:
|
||||||
fontSize: 14,
|
const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
|
||||||
decoration: TextDecoration.underline,
|
child: Text(
|
||||||
),
|
context.l10n.passwordStrength(passwordStrengthText),
|
||||||
),
|
style: TextStyle(
|
||||||
|
color: passwordStrengthColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Padding(padding: EdgeInsets.all(20)),
|
),
|
||||||
],
|
const SizedBox(height: 8),
|
||||||
),
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return WebPage(
|
||||||
|
context.l10n.howItWorks,
|
||||||
|
"https://ente.io/architecture",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
|
child: RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
text: context.l10n.howItWorks,
|
||||||
|
style:
|
||||||
|
Theme.of(context).textTheme.titleMedium!.copyWith(
|
||||||
|
fontSize: 14,
|
||||||
|
decoration: TextDecoration.underline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Padding(padding: EdgeInsets.all(20)),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -475,7 +463,6 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
|
|||||||
showGenericErrorDialog(context: context);
|
showGenericErrorDialog(context: context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore: unawaited_futures
|
// ignore: unawaited_futures
|
||||||
routeToPage(
|
routeToPage(
|
||||||
context,
|
context,
|
||||||
|
|||||||
@@ -9,14 +9,14 @@ import 'package:ente_auth/ui/account/recovery_page.dart';
|
|||||||
import 'package:ente_auth/ui/common/dynamic_fab.dart';
|
import 'package:ente_auth/ui/common/dynamic_fab.dart';
|
||||||
import 'package:ente_auth/ui/components/buttons/button_widget.dart';
|
import 'package:ente_auth/ui/components/buttons/button_widget.dart';
|
||||||
import 'package:ente_auth/ui/home_page.dart';
|
import 'package:ente_auth/ui/home_page.dart';
|
||||||
|
import 'package:ente_auth/utils/crypto_util.dart';
|
||||||
import 'package:ente_auth/utils/dialog_util.dart';
|
import 'package:ente_auth/utils/dialog_util.dart';
|
||||||
import 'package:ente_auth/utils/email_util.dart';
|
import 'package:ente_auth/utils/email_util.dart';
|
||||||
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
class PasswordReentryPage extends StatefulWidget {
|
class PasswordReentryPage extends StatefulWidget {
|
||||||
const PasswordReentryPage({super.key});
|
const PasswordReentryPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<PasswordReentryPage> createState() => _PasswordReentryPageState();
|
State<PasswordReentryPage> createState() => _PasswordReentryPageState();
|
||||||
@@ -261,8 +261,8 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
|
|||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
child: Row(
|
child: Wrap(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
alignment: WrapAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
behavior: HitTestBehavior.opaque,
|
behavior: HitTestBehavior.opaque,
|
||||||
@@ -275,17 +275,13 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: Center(
|
child: Text(
|
||||||
child: Text(
|
context.l10n.forgotPassword,
|
||||||
context.l10n.forgotPassword,
|
style:
|
||||||
style: Theme.of(context)
|
Theme.of(context).textTheme.titleMedium!.copyWith(
|
||||||
.textTheme
|
fontSize: 14,
|
||||||
.titleMedium!
|
decoration: TextDecoration.underline,
|
||||||
.copyWith(
|
),
|
||||||
fontSize: 14,
|
|
||||||
decoration: TextDecoration.underline,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
@@ -301,17 +297,13 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
|
|||||||
Navigator.of(context)
|
Navigator.of(context)
|
||||||
.popUntil((route) => route.isFirst);
|
.popUntil((route) => route.isFirst);
|
||||||
},
|
},
|
||||||
child: Center(
|
child: Text(
|
||||||
child: Text(
|
context.l10n.changeEmail,
|
||||||
context.l10n.changeEmail,
|
style:
|
||||||
style: Theme.of(context)
|
Theme.of(context).textTheme.titleMedium!.copyWith(
|
||||||
.textTheme
|
fontSize: 14,
|
||||||
.titleMedium!
|
decoration: TextDecoration.underline,
|
||||||
.copyWith(
|
),
|
||||||
fontSize: 14,
|
|
||||||
decoration: TextDecoration.underline,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'dart:convert';
|
|
||||||
import 'dart:io' as io;
|
import 'dart:io' as io;
|
||||||
|
|
||||||
import 'package:bip39/bip39.dart' as bip39;
|
import 'package:bip39/bip39.dart' as bip39;
|
||||||
@@ -8,10 +7,7 @@ import 'package:ente_auth/core/constants.dart';
|
|||||||
import 'package:ente_auth/ente_theme_data.dart';
|
import 'package:ente_auth/ente_theme_data.dart';
|
||||||
import 'package:ente_auth/l10n/l10n.dart';
|
import 'package:ente_auth/l10n/l10n.dart';
|
||||||
import 'package:ente_auth/ui/common/gradient_button.dart';
|
import 'package:ente_auth/ui/common/gradient_button.dart';
|
||||||
import 'package:ente_auth/utils/platform_util.dart';
|
|
||||||
import 'package:ente_auth/utils/share_utils.dart';
|
|
||||||
import 'package:ente_auth/utils/toast_util.dart';
|
import 'package:ente_auth/utils/toast_util.dart';
|
||||||
import 'package:file_saver/file_saver.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
@@ -31,7 +27,7 @@ class RecoveryKeyPage extends StatefulWidget {
|
|||||||
const RecoveryKeyPage(
|
const RecoveryKeyPage(
|
||||||
this.recoveryKey,
|
this.recoveryKey,
|
||||||
this.doneText, {
|
this.doneText, {
|
||||||
super.key,
|
Key? key,
|
||||||
this.showAppBar,
|
this.showAppBar,
|
||||||
this.onDone,
|
this.onDone,
|
||||||
this.isDismissible,
|
this.isDismissible,
|
||||||
@@ -39,7 +35,7 @@ class RecoveryKeyPage extends StatefulWidget {
|
|||||||
this.text,
|
this.text,
|
||||||
this.subText,
|
this.subText,
|
||||||
this.showProgressBar = false,
|
this.showProgressBar = false,
|
||||||
});
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<RecoveryKeyPage> createState() => _RecoveryKeyPageState();
|
State<RecoveryKeyPage> createState() => _RecoveryKeyPageState();
|
||||||
@@ -48,7 +44,7 @@ class RecoveryKeyPage extends StatefulWidget {
|
|||||||
class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
|
class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
|
||||||
bool _hasTriedToSave = false;
|
bool _hasTriedToSave = false;
|
||||||
final _recoveryKeyFile = io.File(
|
final _recoveryKeyFile = io.File(
|
||||||
"${Configuration.instance.getTempDirectory()}ente-recovery-key.txt",
|
Configuration.instance.getTempDirectory() + "ente-recovery-key.txt",
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -65,21 +61,6 @@ class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
|
|||||||
? 32
|
? 32
|
||||||
: 120;
|
: 120;
|
||||||
|
|
||||||
Future<void> copy() async {
|
|
||||||
await Clipboard.setData(
|
|
||||||
ClipboardData(
|
|
||||||
text: recoveryKey,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
showShortToast(
|
|
||||||
context,
|
|
||||||
context.l10n.recoveryKeyCopiedToClipboard,
|
|
||||||
);
|
|
||||||
setState(() {
|
|
||||||
_hasTriedToSave = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: widget.showProgressBar
|
appBar: widget.showProgressBar
|
||||||
? AppBar(
|
? AppBar(
|
||||||
@@ -132,73 +113,62 @@ class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
|
|||||||
style: Theme.of(context).textTheme.titleMedium,
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
const Padding(padding: EdgeInsets.only(top: 24)),
|
const Padding(padding: EdgeInsets.only(top: 24)),
|
||||||
Container(
|
DottedBorder(
|
||||||
padding: const EdgeInsets.all(1),
|
color: const Color.fromARGB(255, 105, 17, 127),
|
||||||
decoration: BoxDecoration(
|
//color of dotted/dash line
|
||||||
borderRadius: BorderRadius.circular(8),
|
strokeWidth: 1,
|
||||||
gradient: const LinearGradient(
|
//thickness of dash/dots
|
||||||
begin: Alignment.topCenter,
|
dashPattern: const [6, 6],
|
||||||
end: Alignment.bottomCenter,
|
radius: const Radius.circular(8),
|
||||||
colors: [
|
//dash patterns, 10 is dash width, 6 is space width
|
||||||
Color(0x8E9610D6),
|
child: SizedBox(
|
||||||
Color(0x8E9F4FC6),
|
//inner container
|
||||||
],
|
// height: 120, //height of inner container
|
||||||
stops: [0.0, 0.9753],
|
width: double
|
||||||
),
|
.infinity, //width to 100% match to parent container.
|
||||||
),
|
// ignore: prefer_const_literals_to_create_immutables
|
||||||
child: DottedBorder(
|
child: Column(
|
||||||
padding: EdgeInsets.zero,
|
children: [
|
||||||
borderType: BorderType.RRect,
|
GestureDetector(
|
||||||
strokeWidth: 1,
|
onTap: () async {
|
||||||
color: const Color(0xFF6B6B6B),
|
await Clipboard.setData(
|
||||||
dashPattern: const [6, 6],
|
ClipboardData(text: recoveryKey),
|
||||||
radius: const Radius.circular(8),
|
);
|
||||||
child: SizedBox(
|
showShortToast(
|
||||||
width: double.infinity,
|
context,
|
||||||
child: Stack(
|
context.l10n.recoveryKeyCopiedToClipboard,
|
||||||
children: [
|
);
|
||||||
Column(
|
setState(() {
|
||||||
children: [
|
_hasTriedToSave = true;
|
||||||
Builder(
|
});
|
||||||
builder: (context) {
|
},
|
||||||
final content = Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(20),
|
decoration: BoxDecoration(
|
||||||
width: double.infinity,
|
border: Border.all(
|
||||||
child: Text(
|
color: const Color.fromRGBO(
|
||||||
recoveryKey,
|
49,
|
||||||
textAlign: TextAlign.justify,
|
155,
|
||||||
style: Theme.of(context)
|
86,
|
||||||
.textTheme
|
.2,
|
||||||
.bodyLarge,
|
),
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (PlatformUtil.isMobile()) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: () async => await copy(),
|
|
||||||
child: content,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return SelectableRegion(
|
|
||||||
focusNode: FocusNode(),
|
|
||||||
selectionControls:
|
|
||||||
PlatformUtil.selectionControls,
|
|
||||||
child: content,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
borderRadius: const BorderRadius.all(
|
||||||
),
|
Radius.circular(2),
|
||||||
Positioned(
|
),
|
||||||
right: 0,
|
color: Theme.of(context)
|
||||||
top: 0,
|
.colorScheme
|
||||||
child: PlatformCopy(
|
.recoveryKeyBoxColor,
|
||||||
onPressed: copy,
|
),
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
width: double.infinity,
|
||||||
|
child: Text(
|
||||||
|
recoveryKey,
|
||||||
|
style:
|
||||||
|
Theme.of(context).textTheme.bodyLarge,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -223,7 +193,7 @@ class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
), // columnEnds
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -237,15 +207,12 @@ class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
|
|||||||
final List<Widget> childrens = [];
|
final List<Widget> childrens = [];
|
||||||
if (!_hasTriedToSave) {
|
if (!_hasTriedToSave) {
|
||||||
childrens.add(
|
childrens.add(
|
||||||
SizedBox(
|
ElevatedButton(
|
||||||
height: 56,
|
style: Theme.of(context).colorScheme.optionalActionButtonStyle,
|
||||||
child: ElevatedButton(
|
onPressed: () async {
|
||||||
style: Theme.of(context).colorScheme.optionalActionButtonStyle,
|
await _saveKeys();
|
||||||
onPressed: () async {
|
},
|
||||||
await _saveKeys();
|
child: Text(context.l10n.doThisLater),
|
||||||
},
|
|
||||||
child: Text(context.l10n.doThisLater),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
childrens.add(const SizedBox(height: 10));
|
childrens.add(const SizedBox(height: 10));
|
||||||
@@ -254,32 +221,19 @@ class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
|
|||||||
childrens.add(
|
childrens.add(
|
||||||
GradientButton(
|
GradientButton(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await shareDialog(
|
await _shareRecoveryKey(recoveryKey);
|
||||||
context,
|
|
||||||
context.l10n.recoveryKey,
|
|
||||||
saveAction: () async {
|
|
||||||
await _saveRecoveryKey(recoveryKey);
|
|
||||||
},
|
|
||||||
sendAction: () async {
|
|
||||||
await _shareRecoveryKey(recoveryKey);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
text: context.l10n.saveKey,
|
text: context.l10n.saveKey,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (_hasTriedToSave) {
|
if (_hasTriedToSave) {
|
||||||
childrens.add(const SizedBox(height: 10));
|
childrens.add(const SizedBox(height: 10));
|
||||||
childrens.add(
|
childrens.add(
|
||||||
SizedBox(
|
ElevatedButton(
|
||||||
height: 56,
|
child: Text(widget.doneText),
|
||||||
child: ElevatedButton(
|
onPressed: () async {
|
||||||
child: Text(widget.doneText),
|
await _saveKeys();
|
||||||
onPressed: () async {
|
},
|
||||||
await _saveKeys();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -287,34 +241,11 @@ class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
|
|||||||
return childrens;
|
return childrens;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future _saveRecoveryKey(String recoveryKey) async {
|
|
||||||
final bytes = utf8.encode(recoveryKey);
|
|
||||||
final time = DateTime.now().millisecondsSinceEpoch;
|
|
||||||
|
|
||||||
await PlatformUtil.shareFile(
|
|
||||||
"ente_recovery_key_$time",
|
|
||||||
"txt",
|
|
||||||
bytes,
|
|
||||||
MimeType.text,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (mounted) {
|
|
||||||
showToast(
|
|
||||||
context,
|
|
||||||
context.l10n.recoveryKeySaved,
|
|
||||||
);
|
|
||||||
setState(() {
|
|
||||||
_hasTriedToSave = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _shareRecoveryKey(String recoveryKey) async {
|
Future _shareRecoveryKey(String recoveryKey) async {
|
||||||
if (_recoveryKeyFile.existsSync()) {
|
if (_recoveryKeyFile.existsSync()) {
|
||||||
await _recoveryKeyFile.delete();
|
await _recoveryKeyFile.delete();
|
||||||
}
|
}
|
||||||
_recoveryKeyFile.writeAsStringSync(recoveryKey);
|
_recoveryKeyFile.writeAsStringSync(recoveryKey);
|
||||||
// ignore: deprecated_member_use
|
|
||||||
await Share.shareFiles([_recoveryKeyFile.path]);
|
await Share.shareFiles([_recoveryKeyFile.path]);
|
||||||
Future.delayed(const Duration(milliseconds: 500), () {
|
Future.delayed(const Duration(milliseconds: 500), () {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
@@ -333,24 +264,3 @@ class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
|
|||||||
widget.onDone!();
|
widget.onDone!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PlatformCopy extends StatelessWidget {
|
|
||||||
const PlatformCopy({
|
|
||||||
super.key,
|
|
||||||
required this.onPressed,
|
|
||||||
});
|
|
||||||
|
|
||||||
final void Function() onPressed;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return IconButton(
|
|
||||||
onPressed: () => onPressed(),
|
|
||||||
visualDensity: VisualDensity.compact,
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.copy,
|
|
||||||
size: 16,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:ente_auth/core/configuration.dart';
|
import 'package:ente_auth/core/configuration.dart';
|
||||||
import 'package:ente_auth/l10n/l10n.dart';
|
|
||||||
import 'package:ente_auth/ui/account/password_entry_page.dart';
|
import 'package:ente_auth/ui/account/password_entry_page.dart';
|
||||||
import 'package:ente_auth/ui/common/dynamic_fab.dart';
|
import 'package:ente_auth/ui/common/dynamic_fab.dart';
|
||||||
import 'package:ente_auth/utils/dialog_util.dart';
|
import 'package:ente_auth/utils/dialog_util.dart';
|
||||||
@@ -7,7 +10,7 @@ import 'package:ente_auth/utils/toast_util.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class RecoveryPage extends StatefulWidget {
|
class RecoveryPage extends StatefulWidget {
|
||||||
const RecoveryPage({super.key});
|
const RecoveryPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<RecoveryPage> createState() => _RecoveryPageState();
|
State<RecoveryPage> createState() => _RecoveryPageState();
|
||||||
@@ -16,36 +19,6 @@ class RecoveryPage extends StatefulWidget {
|
|||||||
class _RecoveryPageState extends State<RecoveryPage> {
|
class _RecoveryPageState extends State<RecoveryPage> {
|
||||||
final _recoveryKey = TextEditingController();
|
final _recoveryKey = TextEditingController();
|
||||||
|
|
||||||
Future<void> onPressed() async {
|
|
||||||
FocusScope.of(context).unfocus();
|
|
||||||
final dialog = createProgressDialog(context, "Decrypting...");
|
|
||||||
await dialog.show();
|
|
||||||
try {
|
|
||||||
await Configuration.instance.recover(_recoveryKey.text.trim());
|
|
||||||
await dialog.hide();
|
|
||||||
showToast(context, "Recovery successful!");
|
|
||||||
await Navigator.of(context).pushReplacement(
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return const PopScope(
|
|
||||||
canPop: false,
|
|
||||||
child: PasswordEntryPage(
|
|
||||||
mode: PasswordEntryMode.reset,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
await dialog.hide();
|
|
||||||
String errMessage = 'The recovery key you entered is incorrect';
|
|
||||||
if (e is AssertionError) {
|
|
||||||
errMessage = '$errMessage : ${e.message}';
|
|
||||||
}
|
|
||||||
await showErrorDialog(context, "Incorrect recovery key", errMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100;
|
final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100;
|
||||||
@@ -73,7 +46,37 @@ class _RecoveryPageState extends State<RecoveryPage> {
|
|||||||
isKeypadOpen: isKeypadOpen,
|
isKeypadOpen: isKeypadOpen,
|
||||||
isFormValid: _recoveryKey.text.isNotEmpty,
|
isFormValid: _recoveryKey.text.isNotEmpty,
|
||||||
buttonText: 'Recover',
|
buttonText: 'Recover',
|
||||||
onPressedFunction: onPressed,
|
onPressedFunction: () async {
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
final dialog = createProgressDialog(context, "Decrypting...");
|
||||||
|
await dialog.show();
|
||||||
|
try {
|
||||||
|
await Configuration.instance.recover(_recoveryKey.text.trim());
|
||||||
|
await dialog.hide();
|
||||||
|
showToast(context, "Recovery successful!");
|
||||||
|
// ignore: unawaited_futures
|
||||||
|
Navigator.of(context).pushReplacement(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return WillPopScope(
|
||||||
|
onWillPop: () async => false,
|
||||||
|
child: const PasswordEntryPage(
|
||||||
|
mode: PasswordEntryMode.reset,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
await dialog.hide();
|
||||||
|
String errMessage = 'The recovery key you entered is incorrect';
|
||||||
|
if (e is AssertionError) {
|
||||||
|
errMessage = '$errMessage : ${e.message}';
|
||||||
|
}
|
||||||
|
// ignore: unawaited_futures
|
||||||
|
showErrorDialog(context, "Incorrect recovery key", errMessage);
|
||||||
|
}
|
||||||
|
},
|
||||||
),
|
),
|
||||||
floatingActionButtonLocation: fabLocation(),
|
floatingActionButtonLocation: fabLocation(),
|
||||||
floatingActionButtonAnimator: NoScalingAnimation(),
|
floatingActionButtonAnimator: NoScalingAnimation(),
|
||||||
@@ -86,7 +89,7 @@ class _RecoveryPageState extends State<RecoveryPage> {
|
|||||||
padding:
|
padding:
|
||||||
const EdgeInsets.symmetric(vertical: 30, horizontal: 20),
|
const EdgeInsets.symmetric(vertical: 30, horizontal: 20),
|
||||||
child: Text(
|
child: Text(
|
||||||
context.l10n.forgotPassword,
|
'Forgot password',
|
||||||
style: Theme.of(context).textTheme.headlineMedium,
|
style: Theme.of(context).textTheme.headlineMedium,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -137,14 +140,12 @@ class _RecoveryPageState extends State<RecoveryPage> {
|
|||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
context.l10n.noRecoveryKeyTitle,
|
"No recovery key?",
|
||||||
style: Theme.of(context)
|
style:
|
||||||
.textTheme
|
Theme.of(context).textTheme.titleMedium!.copyWith(
|
||||||
.titleMedium!
|
fontSize: 14,
|
||||||
.copyWith(
|
decoration: TextDecoration.underline,
|
||||||
fontSize: 14,
|
),
|
||||||
decoration: TextDecoration.underline,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -5,9 +5,10 @@ import 'package:ente_auth/core/configuration.dart';
|
|||||||
import "package:ente_auth/l10n/l10n.dart";
|
import "package:ente_auth/l10n/l10n.dart";
|
||||||
import "package:ente_auth/theme/ente_theme.dart";
|
import "package:ente_auth/theme/ente_theme.dart";
|
||||||
import 'package:ente_auth/ui/common/dynamic_fab.dart';
|
import 'package:ente_auth/ui/common/dynamic_fab.dart';
|
||||||
|
import "package:ente_auth/utils/crypto_util.dart";
|
||||||
import "package:ente_auth/utils/dialog_util.dart";
|
import "package:ente_auth/utils/dialog_util.dart";
|
||||||
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import "package:flutter_sodium/flutter_sodium.dart";
|
||||||
import "package:logging/logging.dart";
|
import "package:logging/logging.dart";
|
||||||
|
|
||||||
typedef OnPasswordVerifiedFn = Future<void> Function(Uint8List bytes);
|
typedef OnPasswordVerifiedFn = Future<void> Function(Uint8List bytes);
|
||||||
@@ -16,11 +17,8 @@ class RequestPasswordVerificationPage extends StatefulWidget {
|
|||||||
final OnPasswordVerifiedFn onPasswordVerified;
|
final OnPasswordVerifiedFn onPasswordVerified;
|
||||||
final Function? onPasswordError;
|
final Function? onPasswordError;
|
||||||
|
|
||||||
const RequestPasswordVerificationPage({
|
const RequestPasswordVerificationPage(
|
||||||
super.key,
|
{super.key, required this.onPasswordVerified, this.onPasswordError,});
|
||||||
required this.onPasswordVerified,
|
|
||||||
this.onPasswordError,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<RequestPasswordVerificationPage> createState() =>
|
State<RequestPasswordVerificationPage> createState() =>
|
||||||
@@ -84,15 +82,15 @@ class _RequestPasswordVerificationPageState
|
|||||||
try {
|
try {
|
||||||
final attributes = Configuration.instance.getKeyAttributes()!;
|
final attributes = Configuration.instance.getKeyAttributes()!;
|
||||||
final Uint8List keyEncryptionKey = await CryptoUtil.deriveKey(
|
final Uint8List keyEncryptionKey = await CryptoUtil.deriveKey(
|
||||||
utf8.encode(_passwordController.text),
|
utf8.encode(_passwordController.text) as Uint8List,
|
||||||
CryptoUtil.base642bin(attributes.kekSalt),
|
Sodium.base642bin(attributes.kekSalt),
|
||||||
attributes.memLimit,
|
attributes.memLimit,
|
||||||
attributes.opsLimit,
|
attributes.opsLimit,
|
||||||
);
|
);
|
||||||
CryptoUtil.decryptSync(
|
CryptoUtil.decryptSync(
|
||||||
CryptoUtil.base642bin(attributes.encryptedKey),
|
Sodium.base642bin(attributes.encryptedKey),
|
||||||
keyEncryptionKey,
|
keyEncryptionKey,
|
||||||
CryptoUtil.base642bin(attributes.keyDecryptionNonce),
|
Sodium.base642bin(attributes.keyDecryptionNonce),
|
||||||
);
|
);
|
||||||
await dialog.show();
|
await dialog.show();
|
||||||
// pop
|
// pop
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
class SessionsPage extends StatefulWidget {
|
class SessionsPage extends StatefulWidget {
|
||||||
const SessionsPage({super.key});
|
const SessionsPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<SessionsPage> createState() => _SessionsPageState();
|
State<SessionsPage> createState() => _SessionsPageState();
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:bip39/bip39.dart' as bip39;
|
import 'package:bip39/bip39.dart' as bip39;
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:ente_auth/core/configuration.dart';
|
import 'package:ente_auth/core/configuration.dart';
|
||||||
@@ -10,13 +12,12 @@ import 'package:ente_auth/ui/common/gradient_button.dart';
|
|||||||
import 'package:ente_auth/ui/components/buttons/button_widget.dart';
|
import 'package:ente_auth/ui/components/buttons/button_widget.dart';
|
||||||
import 'package:ente_auth/utils/dialog_util.dart';
|
import 'package:ente_auth/utils/dialog_util.dart';
|
||||||
import 'package:ente_auth/utils/navigation_util.dart';
|
import 'package:ente_auth/utils/navigation_util.dart';
|
||||||
import 'package:ente_auth/utils/platform_util.dart';
|
|
||||||
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_sodium/flutter_sodium.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
class VerifyRecoveryPage extends StatefulWidget {
|
class VerifyRecoveryPage extends StatefulWidget {
|
||||||
const VerifyRecoveryPage({super.key});
|
const VerifyRecoveryPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<VerifyRecoveryPage> createState() => _VerifyRecoveryPageState();
|
State<VerifyRecoveryPage> createState() => _VerifyRecoveryPageState();
|
||||||
@@ -33,14 +34,14 @@ class _VerifyRecoveryPageState extends State<VerifyRecoveryPage> {
|
|||||||
try {
|
try {
|
||||||
final String inputKey = _recoveryKey.text.trim();
|
final String inputKey = _recoveryKey.text.trim();
|
||||||
final String recoveryKey =
|
final String recoveryKey =
|
||||||
CryptoUtil.bin2hex(Configuration.instance.getRecoveryKey());
|
Sodium.bin2hex(Configuration.instance.getRecoveryKey());
|
||||||
final String recoveryKeyWords = bip39.entropyToMnemonic(recoveryKey);
|
final String recoveryKeyWords = bip39.entropyToMnemonic(recoveryKey);
|
||||||
if (inputKey == recoveryKey || inputKey == recoveryKeyWords) {
|
if (inputKey == recoveryKey || inputKey == recoveryKeyWords) {
|
||||||
try {
|
try {
|
||||||
await UserRemoteFlagService.instance.markRecoveryVerificationAsDone();
|
await UserRemoteFlagService.instance.markRecoveryVerificationAsDone();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await dialog.hide();
|
await dialog.hide();
|
||||||
if (e is DioException && e.type == DioExceptionType.unknown) {
|
if (e is DioError && e.type == DioErrorType.other) {
|
||||||
await showErrorDialog(
|
await showErrorDialog(
|
||||||
context,
|
context,
|
||||||
"No internet connection",
|
"No internet connection",
|
||||||
@@ -87,14 +88,12 @@ class _VerifyRecoveryPageState extends State<VerifyRecoveryPage> {
|
|||||||
context,
|
context,
|
||||||
"Please authenticate to view your recovery key",
|
"Please authenticate to view your recovery key",
|
||||||
);
|
);
|
||||||
await PlatformUtil.refocusWindows();
|
|
||||||
|
|
||||||
if (hasAuthenticated) {
|
if (hasAuthenticated) {
|
||||||
String recoveryKey;
|
String recoveryKey;
|
||||||
try {
|
try {
|
||||||
recoveryKey =
|
recoveryKey = Sodium.bin2hex(Configuration.instance.getRecoveryKey());
|
||||||
CryptoUtil.bin2hex(Configuration.instance.getRecoveryKey());
|
// ignore: unawaited_futures
|
||||||
await routeToPage(
|
routeToPage(
|
||||||
context,
|
context,
|
||||||
RecoveryKeyPage(
|
RecoveryKeyPage(
|
||||||
recoveryKey,
|
recoveryKey,
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ class CodeTimerProgress extends StatefulWidget {
|
|||||||
final int period;
|
final int period;
|
||||||
|
|
||||||
CodeTimerProgress({
|
CodeTimerProgress({
|
||||||
super.key,
|
Key? key,
|
||||||
required this.period,
|
required this.period,
|
||||||
});
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State createState() => _CodeTimerProgressState();
|
_CodeTimerProgressState createState() => _CodeTimerProgressState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CodeTimerProgressState extends State<CodeTimerProgress>
|
class _CodeTimerProgressState extends State<CodeTimerProgress>
|
||||||
|
|||||||
@@ -14,10 +14,8 @@ import 'package:ente_auth/store/code_store.dart';
|
|||||||
import 'package:ente_auth/ui/code_timer_progress.dart';
|
import 'package:ente_auth/ui/code_timer_progress.dart';
|
||||||
import 'package:ente_auth/ui/utils/icon_utils.dart';
|
import 'package:ente_auth/ui/utils/icon_utils.dart';
|
||||||
import 'package:ente_auth/utils/dialog_util.dart';
|
import 'package:ente_auth/utils/dialog_util.dart';
|
||||||
import 'package:ente_auth/utils/platform_util.dart';
|
|
||||||
import 'package:ente_auth/utils/toast_util.dart';
|
import 'package:ente_auth/utils/toast_util.dart';
|
||||||
import 'package:ente_auth/utils/totp_util.dart';
|
import 'package:ente_auth/utils/totp_util.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
@@ -26,7 +24,7 @@ import 'package:move_to_background/move_to_background.dart';
|
|||||||
class CodeWidget extends StatefulWidget {
|
class CodeWidget extends StatefulWidget {
|
||||||
final Code code;
|
final Code code;
|
||||||
|
|
||||||
const CodeWidget(this.code, {super.key});
|
const CodeWidget(this.code, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<CodeWidget> createState() => _CodeWidgetState();
|
State<CodeWidget> createState() => _CodeWidgetState();
|
||||||
@@ -135,59 +133,35 @@ class _CodeWidgetState extends State<CodeWidget> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
child: Builder(
|
child: ClipRRect(
|
||||||
builder: (context) {
|
borderRadius: BorderRadius.circular(8),
|
||||||
return RawGestureDetector(
|
child: Container(
|
||||||
gestures: {
|
color: Theme.of(context).colorScheme.codeCardBackgroundColor,
|
||||||
PanGestureRecognizer:
|
child: Material(
|
||||||
GestureRecognizerFactoryWithHandlers<PanGestureRecognizer>(
|
color: Colors.transparent,
|
||||||
() => PanGestureRecognizer(
|
child: InkWell(
|
||||||
debugOwner: this,
|
customBorder: RoundedRectangleBorder(
|
||||||
// This recognizer accepts any button press made with a secondary button.
|
borderRadius: BorderRadius.circular(10),
|
||||||
allowedButtonsFilter: (int buttons) =>
|
|
||||||
buttons & kSecondaryButton != 0,
|
|
||||||
),
|
|
||||||
(PanGestureRecognizer instance) {
|
|
||||||
instance
|
|
||||||
..dragStartBehavior = DragStartBehavior.down
|
|
||||||
..onEnd = (DragEndDetails details) {
|
|
||||||
Slidable.of(context)?.openEndActionPane();
|
|
||||||
};
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
child: Container(
|
|
||||||
color: Theme.of(context).colorScheme.codeCardBackgroundColor,
|
|
||||||
child: Material(
|
|
||||||
color: Colors.transparent,
|
|
||||||
child: InkWell(
|
|
||||||
customBorder: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
onTap: () {
|
|
||||||
_copyCurrentOTPToClipboard();
|
|
||||||
},
|
|
||||||
onDoubleTap: isMaskingEnabled
|
|
||||||
? () {
|
|
||||||
setState(
|
|
||||||
() {
|
|
||||||
_hideCode = !_hideCode;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
onLongPress: () {
|
|
||||||
_copyCurrentOTPToClipboard();
|
|
||||||
},
|
|
||||||
child: _getCardContents(l10n),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
onTap: () {
|
||||||
|
_copyCurrentOTPToClipboard();
|
||||||
|
},
|
||||||
|
onDoubleTap: isMaskingEnabled
|
||||||
|
? () {
|
||||||
|
setState(
|
||||||
|
() {
|
||||||
|
_hideCode = !_hideCode;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
onLongPress: () {
|
||||||
|
_copyCurrentOTPToClipboard();
|
||||||
|
},
|
||||||
|
child: _getCardContents(l10n),
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -399,10 +373,9 @@ class _CodeWidgetState extends State<CodeWidget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onEditPressed(_) async {
|
Future<void> _onEditPressed(_) async {
|
||||||
bool isAuthSuccessful = await LocalAuthenticationService.instance
|
bool _isAuthSuccessful = await LocalAuthenticationService.instance
|
||||||
.requestLocalAuthentication(context, context.l10n.editCodeAuthMessage);
|
.requestLocalAuthentication(context, context.l10n.editCodeAuthMessage);
|
||||||
await PlatformUtil.refocusWindows();
|
if (!_isAuthSuccessful) {
|
||||||
if (!isAuthSuccessful) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final Code? code = await Navigator.of(context).push(
|
final Code? code = await Navigator.of(context).push(
|
||||||
@@ -418,10 +391,9 @@ class _CodeWidgetState extends State<CodeWidget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onShowQrPressed(_) async {
|
Future<void> _onShowQrPressed(_) async {
|
||||||
bool isAuthSuccessful = await LocalAuthenticationService.instance
|
bool _isAuthSuccessful = await LocalAuthenticationService.instance
|
||||||
.requestLocalAuthentication(context, context.l10n.showQRAuthMessage);
|
.requestLocalAuthentication(context, context.l10n.showQRAuthMessage);
|
||||||
await PlatformUtil.refocusWindows();
|
if (!_isAuthSuccessful) {
|
||||||
if (!isAuthSuccessful) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// ignore: unused_local_variable
|
// ignore: unused_local_variable
|
||||||
@@ -435,15 +407,14 @@ class _CodeWidgetState extends State<CodeWidget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _onDeletePressed(_) async {
|
void _onDeletePressed(_) async {
|
||||||
bool isAuthSuccessful =
|
bool _isAuthSuccessful =
|
||||||
await LocalAuthenticationService.instance.requestLocalAuthentication(
|
await LocalAuthenticationService.instance.requestLocalAuthentication(
|
||||||
context,
|
context,
|
||||||
context.l10n.deleteCodeAuthMessage,
|
context.l10n.deleteCodeAuthMessage,
|
||||||
);
|
);
|
||||||
if (!isAuthSuccessful) {
|
if (!_isAuthSuccessful) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
FocusScope.of(context).requestFocus();
|
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
await showChoiceActionSheet(
|
await showChoiceActionSheet(
|
||||||
context,
|
context,
|
||||||
@@ -480,7 +451,7 @@ class _CodeWidgetState extends State<CodeWidget> {
|
|||||||
code = code.replaceAll(RegExp(r'\d'), '•');
|
code = code.replaceAll(RegExp(r'\d'), '•');
|
||||||
}
|
}
|
||||||
if (code.length == 6) {
|
if (code.length == 6) {
|
||||||
return "${code.substring(0, 3)} ${code.substring(3, 6)}";
|
return code.substring(0, 3) + " " + code.substring(3, 6);
|
||||||
}
|
}
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class DividerWithPadding extends StatelessWidget {
|
class DividerWithPadding extends StatelessWidget {
|
||||||
final double left, top, right, bottom, thinckness;
|
final double left, top, right, bottom, thinckness;
|
||||||
const DividerWithPadding({
|
const DividerWithPadding({
|
||||||
super.key,
|
Key? key,
|
||||||
this.left = 0,
|
this.left = 0,
|
||||||
this.top = 0,
|
this.top = 0,
|
||||||
this.right = 0,
|
this.right = 0,
|
||||||
this.bottom = 0,
|
this.bottom = 0,
|
||||||
this.thinckness = 0.5,
|
this.thinckness = 0.5,
|
||||||
});
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -5,7 +5,8 @@ import 'package:flutter/material.dart';
|
|||||||
class BottomShadowWidget extends StatelessWidget {
|
class BottomShadowWidget extends StatelessWidget {
|
||||||
final double offsetDy;
|
final double offsetDy;
|
||||||
final Color? shadowColor;
|
final Color? shadowColor;
|
||||||
const BottomShadowWidget({this.offsetDy = 28, this.shadowColor, super.key});
|
const BottomShadowWidget({this.offsetDy = 28, this.shadowColor, Key? key})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ class DynamicFAB extends StatelessWidget {
|
|||||||
final Function? onPressedFunction;
|
final Function? onPressedFunction;
|
||||||
|
|
||||||
const DynamicFAB({
|
const DynamicFAB({
|
||||||
super.key,
|
Key? key,
|
||||||
this.isKeypadOpen,
|
this.isKeypadOpen,
|
||||||
this.buttonText,
|
this.buttonText,
|
||||||
this.isFormValid,
|
this.isFormValid,
|
||||||
this.onPressedFunction,
|
this.onPressedFunction,
|
||||||
});
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -60,7 +60,6 @@ class DynamicFAB extends StatelessWidget {
|
|||||||
} else {
|
} else {
|
||||||
return Container(
|
return Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: 56,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
child: OutlinedButton(
|
child: OutlinedButton(
|
||||||
onPressed:
|
onPressed:
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class GradientButton extends StatelessWidget {
|
class GradientButton extends StatelessWidget {
|
||||||
@@ -13,21 +15,17 @@ class GradientButton extends StatelessWidget {
|
|||||||
// padding between the text and icon
|
// padding between the text and icon
|
||||||
final double paddingValue;
|
final double paddingValue;
|
||||||
|
|
||||||
// used when two icons are in row
|
|
||||||
final bool reversedGradient;
|
|
||||||
|
|
||||||
const GradientButton({
|
const GradientButton({
|
||||||
super.key,
|
Key? key,
|
||||||
this.linearGradientColors = const [
|
this.linearGradientColors = const [
|
||||||
Color.fromARGB(255, 133, 44, 210),
|
Color.fromARGB(255, 133, 44, 210),
|
||||||
Color.fromARGB(255, 187, 26, 93),
|
Color.fromARGB(255, 187, 26, 93),
|
||||||
],
|
],
|
||||||
this.reversedGradient = false,
|
|
||||||
this.onTap,
|
this.onTap,
|
||||||
this.text = '',
|
this.text = '',
|
||||||
this.iconData,
|
this.iconData,
|
||||||
this.paddingValue = 0.0,
|
this.paddingValue = 0.0,
|
||||||
});
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -73,9 +71,7 @@ class GradientButton extends StatelessWidget {
|
|||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
begin: const Alignment(0.1, -0.9),
|
begin: const Alignment(0.1, -0.9),
|
||||||
end: const Alignment(-0.6, 0.9),
|
end: const Alignment(-0.6, 0.9),
|
||||||
colors: reversedGradient
|
colors: linearGradientColors,
|
||||||
? linearGradientColors.reversed.toList()
|
|
||||||
: linearGradientColors,
|
|
||||||
),
|
),
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
|
|
||||||
|
|
||||||
import 'package:ente_auth/ente_theme_data.dart';
|
import 'package:ente_auth/ente_theme_data.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class LinearProgressDialog extends StatefulWidget {
|
class LinearProgressDialog extends StatefulWidget {
|
||||||
final String message;
|
final String message;
|
||||||
|
|
||||||
const LinearProgressDialog(this.message, {super.key});
|
const LinearProgressDialog(this.message, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
LinearProgressDialogState createState() => LinearProgressDialogState();
|
LinearProgressDialogState createState() => LinearProgressDialogState();
|
||||||
@@ -27,8 +29,8 @@ class LinearProgressDialogState extends State<LinearProgressDialog> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return PopScope(
|
return WillPopScope(
|
||||||
canPop: false,
|
onWillPop: () async => false,
|
||||||
child: AlertDialog(
|
child: AlertDialog(
|
||||||
title: Text(
|
title: Text(
|
||||||
widget.message,
|
widget.message,
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ class EnteLoadingWidget extends StatelessWidget {
|
|||||||
this.size = 14,
|
this.size = 14,
|
||||||
this.padding = 5,
|
this.padding = 5,
|
||||||
this.alignment = Alignment.center,
|
this.alignment = Alignment.center,
|
||||||
super.key,
|
Key? key,
|
||||||
});
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|||||||
@@ -153,8 +153,8 @@ class ProgressDialog {
|
|||||||
barrierColor: _barrierColor,
|
barrierColor: _barrierColor,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
_dismissingContext = context;
|
_dismissingContext = context;
|
||||||
return PopScope(
|
return WillPopScope(
|
||||||
canPop: _barrierDismissible,
|
onWillPop: () async => _barrierDismissible,
|
||||||
child: Dialog(
|
child: Dialog(
|
||||||
backgroundColor: _backgroundColor,
|
backgroundColor: _backgroundColor,
|
||||||
insetAnimationCurve: _insetAnimCurve,
|
insetAnimationCurve: _insetAnimCurve,
|
||||||
@@ -198,7 +198,6 @@ class _Body extends StatefulWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
State<StatefulWidget> createState() {
|
State<StatefulWidget> createState() {
|
||||||
// ignore: no_logic_in_create_state
|
|
||||||
return _dialog;
|
return _dialog;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ class RenameDialog extends StatefulWidget {
|
|||||||
final String type;
|
final String type;
|
||||||
final int maxLength;
|
final int maxLength;
|
||||||
|
|
||||||
const RenameDialog(this.name, this.type, {super.key, this.maxLength = 100});
|
const RenameDialog(this.name, this.type, {Key? key, this.maxLength = 100})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<RenameDialog> createState() => _RenameDialogState();
|
State<RenameDialog> createState() => _RenameDialogState();
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ class WebPage extends StatefulWidget {
|
|||||||
final String title;
|
final String title;
|
||||||
final String url;
|
final String url;
|
||||||
|
|
||||||
const WebPage(this.title, this.url, {super.key});
|
const WebPage(this.title, this.url, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<WebPage> createState() => _WebPageState();
|
State<WebPage> createState() => _WebPageState();
|
||||||
@@ -28,9 +28,9 @@ class _WebPageState extends State<WebPage> {
|
|||||||
),
|
),
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: Colors.black,
|
||||||
body: InAppWebView(
|
body: InAppWebView(
|
||||||
initialUrlRequest: URLRequest(url: WebUri(widget.url)),
|
initialUrlRequest: URLRequest(url: Uri.parse(widget.url)),
|
||||||
initialSettings: InAppWebViewSettings(
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
transparentBackground: true,
|
crossPlatform: InAppWebViewOptions(transparentBackground: true),
|
||||||
),
|
),
|
||||||
onLoadStop: (c, url) {
|
onLoadStop: (c, url) {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ class ButtonWidget extends StatelessWidget {
|
|||||||
final ValueNotifier<String>? progressStatus;
|
final ValueNotifier<String>? progressStatus;
|
||||||
|
|
||||||
const ButtonWidget({
|
const ButtonWidget({
|
||||||
super.key,
|
Key? key,
|
||||||
required this.buttonType,
|
required this.buttonType,
|
||||||
this.buttonSize = ButtonSize.large,
|
this.buttonSize = ButtonSize.large,
|
||||||
this.icon,
|
this.icon,
|
||||||
@@ -71,7 +71,7 @@ class ButtonWidget extends StatelessWidget {
|
|||||||
this.shouldSurfaceExecutionStates = true,
|
this.shouldSurfaceExecutionStates = true,
|
||||||
this.progressStatus,
|
this.progressStatus,
|
||||||
this.shouldShowSuccessConfirmation = false,
|
this.shouldShowSuccessConfirmation = false,
|
||||||
});
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -155,7 +155,7 @@ class ButtonChildWidget extends StatefulWidget {
|
|||||||
final bool shouldShowSuccessConfirmation;
|
final bool shouldShowSuccessConfirmation;
|
||||||
|
|
||||||
const ButtonChildWidget({
|
const ButtonChildWidget({
|
||||||
super.key,
|
Key? key,
|
||||||
required this.buttonStyle,
|
required this.buttonStyle,
|
||||||
required this.buttonType,
|
required this.buttonType,
|
||||||
required this.isDisabled,
|
required this.isDisabled,
|
||||||
@@ -168,7 +168,7 @@ class ButtonChildWidget extends StatefulWidget {
|
|||||||
this.labelText,
|
this.labelText,
|
||||||
this.icon,
|
this.icon,
|
||||||
this.buttonAction,
|
this.buttonAction,
|
||||||
});
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ButtonChildWidget> createState() => _ButtonChildWidgetState();
|
State<ButtonChildWidget> createState() => _ButtonChildWidgetState();
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class IconButtonWidget extends StatefulWidget {
|
|||||||
final Color? pressedColor;
|
final Color? pressedColor;
|
||||||
final Color? iconColor;
|
final Color? iconColor;
|
||||||
const IconButtonWidget({
|
const IconButtonWidget({
|
||||||
super.key,
|
Key? key,
|
||||||
required this.icon,
|
required this.icon,
|
||||||
required this.iconButtonType,
|
required this.iconButtonType,
|
||||||
this.disableGestureDetector = false,
|
this.disableGestureDetector = false,
|
||||||
@@ -25,7 +25,7 @@ class IconButtonWidget extends StatefulWidget {
|
|||||||
this.defaultColor,
|
this.defaultColor,
|
||||||
this.pressedColor,
|
this.pressedColor,
|
||||||
this.iconColor,
|
this.iconColor,
|
||||||
});
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<IconButtonWidget> createState() => _IconButtonWidgetState();
|
State<IconButtonWidget> createState() => _IconButtonWidgetState();
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ class CaptionedTextWidget extends StatelessWidget {
|
|||||||
this.textStyle,
|
this.textStyle,
|
||||||
this.makeTextBold = false,
|
this.makeTextBold = false,
|
||||||
this.textColor,
|
this.textColor,
|
||||||
super.key,
|
Key? key,
|
||||||
});
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ class DividerWidget extends StatelessWidget {
|
|||||||
final bool divColorHasBlur;
|
final bool divColorHasBlur;
|
||||||
final EdgeInsets? padding;
|
final EdgeInsets? padding;
|
||||||
const DividerWidget({
|
const DividerWidget({
|
||||||
super.key,
|
Key? key,
|
||||||
required this.dividerType,
|
required this.dividerType,
|
||||||
this.bgColor = Colors.transparent,
|
this.bgColor = Colors.transparent,
|
||||||
this.divColorHasBlur = true,
|
this.divColorHasBlur = true,
|
||||||
this.padding,
|
this.padding,
|
||||||
});
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ class ExpandableMenuItemWidget extends StatefulWidget {
|
|||||||
required this.title,
|
required this.title,
|
||||||
required this.selectionOptionsWidget,
|
required this.selectionOptionsWidget,
|
||||||
required this.leadingIcon,
|
required this.leadingIcon,
|
||||||
super.key,
|
Key? key,
|
||||||
});
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ExpandableMenuItemWidget> createState() =>
|
State<ExpandableMenuItemWidget> createState() =>
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:ente_auth/core/event_bus.dart';
|
import 'package:ente_auth/core/event_bus.dart';
|
||||||
import 'package:ente_auth/events/opened_settings_event.dart';
|
import 'package:ente_auth/events/opened_settings_event.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class HomeHeaderWidget extends StatefulWidget {
|
class HomeHeaderWidget extends StatefulWidget {
|
||||||
final Widget centerWidget;
|
final Widget centerWidget;
|
||||||
const HomeHeaderWidget({required this.centerWidget, super.key});
|
const HomeHeaderWidget({required this.centerWidget, Key? key})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<HomeHeaderWidget> createState() => _HomeHeaderWidgetState();
|
State<HomeHeaderWidget> createState() => _HomeHeaderWidgetState();
|
||||||
@@ -13,7 +16,7 @@ class HomeHeaderWidget extends StatefulWidget {
|
|||||||
class _HomeHeaderWidgetState extends State<HomeHeaderWidget> {
|
class _HomeHeaderWidgetState extends State<HomeHeaderWidget> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final hasNotch = View.of(context).viewPadding.top > 65;
|
final hasNotch = window.viewPadding.top > 65;
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.fromLTRB(4, hasNotch ? 4 : 8, 4, 4),
|
padding: EdgeInsets.fromLTRB(4, hasNotch ? 4 : 8, 4, 4),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class TrailingWidget extends StatefulWidget {
|
|||||||
final double trailingExtraMargin;
|
final double trailingExtraMargin;
|
||||||
final bool showExecutionStates;
|
final bool showExecutionStates;
|
||||||
const TrailingWidget({
|
const TrailingWidget({
|
||||||
super.key,
|
Key? key,
|
||||||
required this.executionStateNotifier,
|
required this.executionStateNotifier,
|
||||||
this.trailingIcon,
|
this.trailingIcon,
|
||||||
this.trailingIconColor,
|
this.trailingIconColor,
|
||||||
@@ -20,7 +20,7 @@ class TrailingWidget extends StatefulWidget {
|
|||||||
required this.trailingIconIsMuted,
|
required this.trailingIconIsMuted,
|
||||||
required this.trailingExtraMargin,
|
required this.trailingExtraMargin,
|
||||||
required this.showExecutionStates,
|
required this.showExecutionStates,
|
||||||
});
|
}) : super(key: key);
|
||||||
@override
|
@override
|
||||||
State<TrailingWidget> createState() => _TrailingWidgetState();
|
State<TrailingWidget> createState() => _TrailingWidgetState();
|
||||||
}
|
}
|
||||||
@@ -101,11 +101,11 @@ class ExpansionTrailingIcon extends StatelessWidget {
|
|||||||
final IconData? trailingIcon;
|
final IconData? trailingIcon;
|
||||||
final Color? trailingIconColor;
|
final Color? trailingIconColor;
|
||||||
const ExpansionTrailingIcon({
|
const ExpansionTrailingIcon({
|
||||||
super.key,
|
Key? key,
|
||||||
required this.isExpanded,
|
required this.isExpanded,
|
||||||
this.trailingIcon,
|
this.trailingIcon,
|
||||||
this.trailingIconColor,
|
this.trailingIconColor,
|
||||||
});
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -138,12 +138,12 @@ class LeadingWidget extends StatelessWidget {
|
|||||||
// leadIconSize deafult value is 20.
|
// leadIconSize deafult value is 20.
|
||||||
final double leadingIconSize;
|
final double leadingIconSize;
|
||||||
const LeadingWidget({
|
const LeadingWidget({
|
||||||
super.key,
|
Key? key,
|
||||||
required this.leadingIconSize,
|
required this.leadingIconSize,
|
||||||
this.leadingIcon,
|
this.leadingIcon,
|
||||||
this.leadingIconColor,
|
this.leadingIconColor,
|
||||||
this.leadingIconWidget,
|
this.leadingIconWidget,
|
||||||
});
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|||||||
@@ -86,8 +86,8 @@ class MenuItemWidget extends StatefulWidget {
|
|||||||
this.showOnlyLoadingState = false,
|
this.showOnlyLoadingState = false,
|
||||||
this.surfaceExecutionStates = true,
|
this.surfaceExecutionStates = true,
|
||||||
this.alwaysShowSuccessState = false,
|
this.alwaysShowSuccessState = false,
|
||||||
super.key,
|
Key? key,
|
||||||
});
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<MenuItemWidget> createState() => _MenuItemWidgetState();
|
State<MenuItemWidget> createState() => _MenuItemWidgetState();
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user