Compare commits

..

75 Commits

Author SHA1 Message Date
laurenspriem
a694bf9b6c flutter downgrade dependencies 2025-07-24 09:02:05 +02:00
laurenspriem
bfcfa691a2 upgrade frb and run frb gen command 2025-07-23 17:55:07 +02:00
laurenspriem
2703c6a33a Bump to be up to date with internal branch 2025-07-23 17:39:19 +02:00
laurenspriem
3ad94f362a Resolve merge conflicts 2025-07-23 17:06:54 +02:00
laurenspriem
8508ca74f2 Merge branch 'main' into usearch_again 2025-07-23 15:05:06 +02:00
laurenspriem
eed12c2089 Merge branch 'internal-15_06_2025' into usearch_again 2025-06-09 12:34:55 +05:30
laurenspriem
889aed6024 Bump for internal release 2025-06-09 12:34:29 +05:30
laurenspriem
ac7840cbfd Merge branch 'internal-15_06_2025' into usearch_again 2025-06-09 12:33:03 +05:30
laurenspriem
1f1304ca5b Upgrade usearch to fix Armv8-R issues 2025-06-09 12:31:38 +05:30
Neeraj Gupta
94098d8a07 Bump version 2025-06-06 12:35:30 +05:30
Neeraj Gupta
4b9c5fcb73 Merge branch 'internal-15_06_2025' of https://github.com/ente-io/auth into internal-15_06_2025 2025-06-06 12:33:48 +05:30
Neeraj Gupta
6ed16e5e02 Merge branch 'main' into internal-15_06_2025 2025-06-06 12:33:21 +05:30
laurenspriem
82a8e504af Merge branch 'internal-15_06_2025' into usearch_again 2025-06-04 22:14:26 +05:30
ashilkn
cc1660d9af bump up build number" 2025-06-04 18:24:59 +05:30
ashilkn
52b6fc108b Merge branch 'memory_improvement' into internal-15_06_2025 2025-06-04 18:19:37 +05:30
Neeraj Gupta
8b3b20aa93 Remove unsued type 2025-06-02 18:38:45 +05:30
Neeraj Gupta
408b0bfe2d Merge branch 'internal-15_06_2025' of https://github.com/ente-io/auth into internal-15_06_2025 2025-06-02 17:55:33 +05:30
Neeraj Gupta
655be76428 Bump version 2025-06-02 17:55:06 +05:30
Neeraj Gupta
9fedf8d6b7 Merge branch 'main' into internal-15_06_2025 2025-06-02 17:54:47 +05:30
ashilkn
7c4e775872 Bump build number 2025-06-02 14:56:04 +05:30
Neeraj Gupta
ecfa640c28 Bump version 2025-05-30 15:44:46 +05:30
Neeraj Gupta
1997eb20f3 Merge branch 'main' into internal-15_06_2025 2025-05-30 15:44:30 +05:30
laurenspriem
726425bbb6 Put vector db behind feature flag internal 2025-05-27 14:07:34 +05:30
laurenspriem
c068f26604 Aggressive logging of vectorDB migration 2025-05-22 11:32:36 +05:30
laurenspriem
e60c2b1192 GC hints 2025-05-22 11:19:19 +05:30
laurenspriem
beb049f817 Reduce batch size in migration 2025-05-22 10:38:43 +05:30
laurenspriem
7021c9fe02 Bump for internal release 2025-05-12 17:05:31 +05:30
laurenspriem
c2d5dece9e Merge branch 'main' into usearch_again 2025-05-12 17:04:42 +05:30
laurenspriem
b76d41b84d Specify rust version in readme 2025-05-12 15:48:27 +05:30
laurenspriem
3b9c76649d Update readme to include rust 2025-05-12 15:12:31 +05:30
laurenspriem
62ed8b6975 Log vector db stats when opening connection 2025-05-12 14:46:55 +05:30
laurenspriem
2422dba4d4 vector db more stats logging 2025-05-12 14:23:16 +05:30
laurenspriem
eb1916e3a3 integrate vector db in magic search 2025-05-12 11:39:02 +05:30
laurenspriem
df0d9137a6 Integration clip embeddings in vector db 2025-05-11 13:09:56 +05:30
laurenspriem
fc36b87965 Clip migration method 2025-05-11 13:09:20 +05:30
laurenspriem
63d90ea275 Class for vector db stats 2025-05-09 16:36:39 +05:30
laurenspriem
bb7f8a5eef More testing 2025-05-09 15:59:46 +05:30
laurenspriem
2f5a02ec43 delete table option 2025-05-09 12:57:44 +05:30
laurenspriem
d411d91966 vector db api ensure capacity safety 2025-05-09 12:56:59 +05:30
laurenspriem
54b712953a vector db api let clear include capacity reset 2025-05-09 10:49:03 +05:30
laurenspriem
27ad020adc Testing clip migration to vector DB 2025-05-08 17:40:01 +05:30
laurenspriem
ce112bd4d7 Index stats method 2025-05-08 15:23:23 +05:30
laurenspriem
2ffb73d053 Consistent method parameters 2025-05-08 15:07:50 +05:30
laurenspriem
6478d438b5 vector db api ensure never duplicate keys 2025-05-08 14:30:51 +05:30
laurenspriem
d87069eb4c vectordb api add documentation 2025-05-08 12:31:09 +05:30
laurenspriem
5447350ab1 vector db api add check for key 2025-05-08 12:29:41 +05:30
laurenspriem
ea1a2960bf First implementation of clip vector db 2025-05-08 12:08:55 +05:30
laurenspriem
832f2c451e Add bulk get method to vector db api 2025-05-08 11:47:50 +05:30
laurenspriem
715c7c23a7 Add bulk remove embeddings api 2025-05-08 10:29:25 +05:30
laurenspriem
e9c2e40a43 Update to latest usearch 2025-05-07 13:25:40 +05:30
laurenspriem
603c275c09 Update basic usearch test 2025-05-07 12:01:45 +05:30
laurenspriem
7b9d6df2fd Fix ios build issue 2025-05-07 11:32:53 +05:30
laurenspriem
a4afecef3d Fix ios config 2025-05-07 10:50:39 +05:30
laurenspriem
4d9bfb89ae macos config 2025-05-07 10:36:17 +05:30
laurenspriem
f2a74bd35e Merge branch 'main' into usearch_again 2025-05-06 15:34:59 +05:30
laurenspriem
8c65a21b86 don't generate for web 2025-04-10 13:03:52 +05:30
laurenspriem
a07e8477fb format 2025-04-09 15:34:06 +05:30
laurenspriem
8b489e9ced Give distances in bulk search 2025-04-09 15:31:03 +05:30
laurenspriem
77e2bb1d46 Stop our own vector comparisons in benchmark 2025-04-09 15:21:20 +05:30
laurenspriem
4ce24e080a logging of benchmarking 2025-04-09 14:22:39 +05:30
laurenspriem
4e5ca3dca6 Benchmark face embeddings 2025-04-09 13:43:39 +05:30
laurenspriem
2ed155ab47 ignore trailing commas in generated code 2025-04-09 13:14:26 +05:30
laurenspriem
65e71e3caf Reintroduce reset_index method 2025-04-09 10:49:50 +05:30
laurenspriem
ee5efbcfcc Don't consume index if not needed 2025-04-09 10:43:07 +05:30
laurenspriem
6cf4530f7d Remove reset index 2025-04-09 10:06:46 +05:30
laurenspriem
e6ee09ca30 Back to basic error handling 2025-04-08 17:03:41 +05:30
laurenspriem
6d2f53b86c Update usearch 2025-04-08 14:56:47 +05:30
laurenspriem
6500748c5a Make vector db stateful in rust 2025-04-08 14:48:30 +05:30
laurenspriem
120dbeb4fc Fix null pointer crash 2025-04-05 16:56:14 +05:30
laurenspriem
c42807487b Upgrade Android NDK to r28 latest stable 2025-04-05 16:11:03 +05:30
laurenspriem
e707e24da9 first integration of usearch 2025-04-05 16:10:39 +05:30
laurenspriem
af817ec439 Test rust 2025-04-04 11:49:54 +05:30
laurenspriem
ddb44d8fd7 Integrate flutter_rust_bridge 2025-03-31 15:56:03 +05:30
laurenspriem
778822b12d Upgrade sdk 2025-03-31 15:47:51 +05:30
laurenspriem
9599ec3236 dart format 2025-03-31 15:34:48 +05:30
679 changed files with 16911 additions and 75545 deletions

View File

@@ -4,7 +4,7 @@ on:
workflow_dispatch: # Allow manually running the action
env:
FLUTTER_VERSION: "3.32.5"
FLUTTER_VERSION: "3.24.3"
RUST_VERSION: "1.85.1"
permissions:

View File

@@ -4,7 +4,7 @@ on:
workflow_dispatch: # Allow manually running the action
env:
FLUTTER_VERSION: "3.32.5"
FLUTTER_VERSION: "3.24.3"
permissions:
contents: write

View File

@@ -8,7 +8,7 @@ on:
- ".github/workflows/mobile-lint.yml"
env:
FLUTTER_VERSION: "3.32.5"
FLUTTER_VERSION: "3.24.3"
permissions:
contents: read

View File

@@ -9,7 +9,7 @@ on:
- "photos-v*"
env:
FLUTTER_VERSION: "3.32.5"
FLUTTER_VERSION: "3.24.3"
permissions:
contents: write

View File

@@ -93,3 +93,13 @@ jobs:
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
command: pages deploy --project-name=ente --commit-dirty=true --branch=deploy/payments web/apps/payments/dist
- name: Build locker
run: yarn build:locker
- name: Publish locker
uses: cloudflare/wrangler-action@v3
with:
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
command: pages deploy --project-name=ente --commit-dirty=true --branch=deploy/locker web/apps/locker/out

View File

@@ -63,7 +63,7 @@ See [docs/](docs/README.md) for how to edit these documents.
Code is a small aspect of community, and the ways mentioned above are more
important in helping us. But if you'd _really_ like to contribute code, it is
best to start small. Consider some well-scoped changes, say like adding more
[custom icons to auth](mobile/apps/auth/docs/adding-icons.md).
[custom icons to auth](auth/docs/adding-icons.md).
Each of the individual product/platform specific directories in this repository
have instructions on setting up a dev environment.

Submodule auth/assets/simple-icons added at 6dcfdc2f58

1
auth/flutter Submodule

Submodule auth/flutter added at 5874a72aa4

View File

@@ -12,7 +12,7 @@ This page outlines the key features available in Ente Auth.
Ente Auth supports the icon pack provided by
[simple-icons](https://github.com/simple-icons/simple-icons). If an icon you
need is missing, please refer to the
[docs/adding-icons](https://github.com/ente-io/ente/blob/main/auth/docs/adding-icons.md)
[docs/adding-icons](https://github.com/ente-io/ente/blob/main/mobile/apps/auth/docs/adding-icons.md)
guide for instructions on how to contribute.
### Search
@@ -136,4 +136,4 @@ Easily add or share entries using QR codes:
right-click (desktop) a code and select "QR". This allows you to easily share
the complete entry (including the secret) with others by letting them scan the
displayed QR code. This can also be used to easily add the same entry to
another authenticatior app or service.
another authenticator app or service.

View File

@@ -1 +0,0 @@
surprise/

View File

@@ -44,5 +44,4 @@ android/key.properties
dist/
# FVM Version Cache
.fvm/
lib/l10n/arb/*.dart
.fvm/

View File

@@ -19,16 +19,16 @@ without relying on third party stores.
You can alternatively install the build from PlayStore or F-Droid.
<a href="https://play.google.com/store/apps/details?id=io.ente.auth">
<img height="59" src="../../../.github/assets/play-store-badge.png">
<img height="59" src="../.github/assets/play-store-badge.png">
</a>
<a href="https://f-droid.org/packages/io.ente.auth/">
<img height="59" src="../../../.github/assets/f-droid-badge.png">
<img height="59" src="../.github/assets/f-droid-badge.png">
</a>
### iOS / Apple Silicon macOS
<a href="https://apps.apple.com/us/app/ente-authenticator/id6444121398">
<img height="59" src="../../../.github/assets/app-store-badge.svg">
<img height="59" src="../.github/assets/app-store-badge.svg">
</a>
### Desktop
@@ -98,7 +98,7 @@ more, see [docs/adding-icons](docs/adding-icons.md).
The best way to support this project is by checking out [Ente
Photos](../mobile/README.md) or spreading the word.
For more ways to contribute, see [../../../CONTRIBUTING.md](../../../CONTRIBUTING.md).
For more ways to contribute, see [../CONTRIBUTING.md](../CONTRIBUTING.md).
## Certificate Fingerprints
@@ -113,4 +113,4 @@ apksigner verify --print-certs <path_to_apk>
## ⭐️ About
To know more about Ente and the ways to get in touch or seek help, see [our main
README](../../../README.md) or visit [ente.io](https://ente.io).
README](../README.md) or visit [ente.io](https://ente.io).

View File

@@ -30,11 +30,10 @@ if (keystorePropertiesFile.exists()) {
android {
namespace "io.ente.auth"
compileSdk 35
compileSdk 34
ndkVersion flutter.ndkVersion
compileOptions {
coreLibraryDesugaringEnabled = true
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
@@ -57,7 +56,7 @@ android {
applicationId "io.ente.auth"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion 22
minSdkVersion 21
targetSdkVersion 34
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
@@ -116,6 +115,4 @@ flutter {
source '../..'
}
dependencies {
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
}
dependencies {}

View File

@@ -6,19 +6,6 @@ allprojects {
}
rootProject.buildDir = '../build'
subprojects {
afterEvaluate { project ->
if (project.hasProperty('android')) {
project.android {
if (namespace == null) {
namespace project.group
}
}
}
}
}
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}

View File

@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip

View File

@@ -19,7 +19,7 @@ pluginManagement {
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "8.2.0" apply false
id "com.android.application" version "7.3.0" apply false
id "org.jetbrains.kotlin.android" version "1.8.22" apply false
}

View File

@@ -35,20 +35,6 @@
{
"title": "Amazon"
},
{
"title": "Amtrak",
"slug": "amtrak",
"hex": "003A5D"
},
{
"title": "Animal Crossing",
"slug:": "animal_crossing",
"altNames": [
"AnimalCrossing",
"Bell Tree Forums",
"BellTree Forums"
]
},
{
"title": "Ankama",
"slug": "ankama"
@@ -70,13 +56,6 @@
"Docaposte AR24"
]
},
{
"title": "Art Fight",
"slug": "art_fight",
"altNames": [
"ArtFight"
]
},
{
"title": "Aruba",
"slug": "aruba",
@@ -298,19 +277,6 @@
"slug": "cih",
"hex": "D14633"
},
{
"title": "Chucklefish"
},
{
"title": "Clipper",
"slug": "clippercard",
"altNames": [
"ClipperCard",
"clipper-card",
"Clipper Card"
],
"hex": "006298"
},
{
"title": "CloudAMQP"
},
@@ -1241,9 +1207,6 @@
{
"title": "Skinport"
},
{
"title": "Smogon"
},
{
"title": "SMSPool",
"slug": "sms_pool_net",
@@ -1337,6 +1300,10 @@
"title": "Termius",
"hex": "858585"
},
{
"title": "Temu",
"slug": "temu"
},
{
"title": "tianyiyun",
"altNames": [
@@ -1352,12 +1319,6 @@
{
"title": "TorGuard"
},
{
"title": "Toyhouse",
"altNames": [
"Toyhou.se"
]
},
{
"title": "Trading 212"
},
@@ -1393,12 +1354,6 @@
"Twitch tv"
]
},
{
"title": "Twitter",
"altNames": [
"X",
]
},
{
"title": "Ubiquiti",
"slug": "ubiquiti",
@@ -1560,6 +1515,10 @@
},
{
"title": "Zitadel"
},
{
"title": "Co-Wheels",
"slug": "cowheels"
}
]
}

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 326.24 311.07"><path fill="#1aae5e" d="M266.38 1.26c-.93 9.12-7.71 47.21-27.28 83.44a1 1 0 0 1-1.7-.06c-4-8.24-24.86-44.58-79.29-44.91C96.71 39.35 66.67 87 66.67 87s-23.45 32.93-33.49 94.89-22.61 79-22.61 79L.39 277a2.54 2.54 0 0 0 1.11 3.67c13 5.81 72.68 30.38 151.68 30.38 53.8 0 70-7.58 74.25-10.55a.61.61 0 0 0-.35-1.12c-10.55 1.16-51.2-5.26-51.2-44 0-34.79 25.3-51.62 51.81-52.83 12.56-.58 39.35 4.74 47.72 29.3 7 20.66-2.31 39.15-5.45 44.5a.62.62 0 0 0 .77.89c9.52-3.84 53.71-25.77 55.47-93.95 1.55-59.91-47.64-85.62-70.8-86.71a1.18 1.18 0 0 1-1-1.62c6.23-15.94 45-57.27 51.84-64.46a1.57 1.57 0 0 0 0-2.17A159.05 159.05 0 0 0 268.46.17a1.42 1.42 0 0 0-2.08 1.09Z"/></svg>

Before

Width:  |  Height:  |  Size: 730 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 35 KiB

View File

@@ -1,13 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1000" height="584.48486" >
<g transform="translate(-137.5,-22.390163)">
<path style="fill:#1c252c" d="m 137.5,29.631487 0,252.320873 132.1114,0 c 51.99819,0 100.05059,-18.25003 100.05059,-71.85789 0,-36.24066 -26.10767,-52.7841 -54.82326,-61.4437 17.4955,-7.01771 37.3423,-21.76056 37.3423,-52.666034 0,-39.55613 -39.24823,-66.353249 -92.09117,-66.353249 l -122.58986,0 z m 80.93311,58.76577 29.75482,0 c 12.11271,0 21.34908,9.487931 21.34908,19.117473 0,8.94677 -9.59745,18.52237 -21.34908,18.52237 l -29.75482,0 0,-37.639843 z m 0,90.008333 39.27636,0 c 13.71131,0 25.21721,10.41366 25.21721,22.31611 0,12.63921 -10.76115,23.13438 -27.59759,23.13438 l -36.89598,0 0,-45.45049 z" id="path2998" />
<path id="path3005" d="m 202.9606,309.47481 0,252.32088 132.11141,0 c 51.99819,0 100.05058,-18.25004 100.05058,-71.8579 0,-36.24065 -26.10766,-52.78409 -54.82326,-61.4437 17.4955,-7.01771 37.3423,-21.76056 37.3423,-52.66603 0,-39.55613 -39.24822,-66.35325 -92.09117,-66.35325 l -122.58986,0 z m 80.93312,58.76577 29.75482,0 c 12.11271,0 21.34908,9.48793 21.34908,19.11747 0,8.94678 -9.59745,18.52238 -21.34908,18.52238 l -29.75482,0 0,-37.63985 z m 0,90.00833 39.27636,0 c 13.71131,0 25.21721,10.41366 25.21721,22.31612 0,12.6392 -10.76115,23.13437 -27.5976,23.13437 l -36.89597,0 0,-45.45049 z" style="fill:#1c252c" />
<path style="fill:#1c252c" d="m 376.01464,281.92185 0,-252.32088 203.52297,0 0,60.937872 -122.8279,0 0,33.087358 99.73816,0 0,57.12926 -99.73816,0 0,40.46655 122.8279,0 0,60.69984 z" id="path3007"/>
<path style="fill:#1c252c" d="m 689.78812,289.1442 c 57.28767,0 103.11071,-32.67744 103.11071,-85.63169 0,-85.46143 -111.17753,-72.31037 -111.17753,-98.91916 0,-10.278288 10.80934,-15.732656 21.89462,-15.732656 19.10438,0 32.9075,12.586126 32.9075,12.586126 L 784.35446,55.903565 C 765.2233,37.697019 735.00886,22.390163 694.30743,22.390163 c -61.12759,0 -101.11859,36.280243 -101.11859,80.044867 0,86.54828 109.57491,73.98694 109.57491,101.14304 0,9.51935 -9.15835,19.09619 -25.76901,19.09619 -18.85922,0 -33.79879,-11.38479 -45.42578,-21.04418 l -48.11128,45.86837 c 19.37303,18.87022 50.47517,41.64575 106.33044,41.64575 z" id="path3009" />
<path style="fill:#1c252c" d="m 855.18627,281.92185 0,-191.621047 -66.6508,0 0,-60.699833 214.23473,0 0,60.699833 -66.65082,0 0,191.621047 z" id="path3011" />
<path style="fill:#1c252c" d="m 437.90467,309.29628 80.69507,0 0,151.15449 c 0,15.34925 15.27608,29.49386 31.20285,29.49386 15.02419,0 30.2111,-12.77313 30.2111,-30.30098 l 0,-150.34737 80.45703,0 0,149.31902 c 0,59.25051 -49.19276,106.51359 -112.80036,106.51359 -63.96969,0 -109.76569,-51.43651 -109.76569,-109.74208 z" id="path3013" />
<path style="fill:#1c252c;fill-opacity:1;stroke:none" d="m 759.97084,561.61716 0,-90.16156 -94.84461,-162.15932 81.6448,0 53.66637,86.88408 53.85177,-86.88408 81.83021,0 -95.21543,163.08638 0,89.2345 z" id="path3015" />
<path style="fill:#ffed31" d="m 936.11938,447.38916 -47.60772,47.60772 0,64.27041 47.60772,47.60771 201.38062,0 0,-159.48584 z" id="path3017" />
<path style="fill:#1c252c" id="path3024" d="m 469,573.36218 c 0,2.20914 -1.79086,4 -4,4 -2.20914,0 -4,-1.79086 -4,-4 0,-2.20914 1.79086,-4 4,-4 2.20914,0 4,1.79086 4,4 z" transform="matrix(2.6779338,0,0,2.6779338,-327.65077,-1008.3244)" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.3 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,9 @@
<svg viewBox="0 0 135.47 135.47" xmlns="http://www.w3.org/2000/svg">
<g>
<path fill="none" stroke="#ff5e00" stroke-width="19.85" d="m 67.6,-67.73 a 28.84,28.84 0 0 1 -24.98,-14.42 28.84,28.84 0 0 1 0,-28.84 28.84,28.84 0 0 1 24.98,-14.42" transform="rotate(90)"/>
<path fill="none" stroke="#ffb71c" stroke-width="19.85" d="m -67.6,125.41 a 28.84,28.84 0 0 1 -24.98,-14.42 28.84,28.84 0 0 1 0,-28.84 28.84,28.84 0 0 1 24.98,-14.42" transform="rotate(-90)"/>
<path fill="none" stroke="#d92a1c" stroke-width="19.84" d="M -67.6,67.73 A 28.84,28.97 0 0 1 -87.99,59.25 28.84,28.97 0 0 1 -96.44,38.76" transform="rotate(-90)"/>
<path fill="none" stroke="#a130cd" stroke-width="19.84" d="m 38.76,96.44 a 28.84,28.84 0 0 1 -24.98,-14.42 28.84,28.84 0 0 1 0,-28.84 28.84,28.84 0 0 1 24.98,-14.42"/>
<path fill="#a130cd" d="m 38.70,28.84 -0.001,19.84 c 3.01,0 5.07,0.46 7.84,1.56 2.21,-8.13 6.14,-12.85 9.61,-16.95 -0.02,-0.05 -6.98,-4.45 -17.45,-4.45 z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 981 B

View File

@@ -1,170 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" width="451.00259" height="507.332" viewBox="0 0 451.00259 507.332" id="svg2">
<defs id="defs4"/>
<metadata id="metadata7">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<dc:title/>
</cc:Work>
</rdf:RDF>
</metadata>
<g transform="translate(-197.35585,-58.696183)" id="layer1">
<g transform="translate(28.601602,6.2831826)" id="g2986">
<g id="g2988">
<g id="g2990">
<path d="m 284.953,425.625 c -41.441,-0.008 -74.219,-15.391 -94.77,-44.48 -23.637,-33.461 -28.066,-83.176 -11.559,-129.75 21.262,-60.016 68.266,-89.355 68.742,-89.641 l 2.801,-1.703 4.555,-14.98 21.422,4.02 14.844,29.734 -0.773,5.273 c -0.738,4.98 -1.48,9.875 -2.215,14.695 l -0.191,1.238 c -5.277,34.695 -9.832,64.66 -6.102,82.488 2.242,10.711 7.199,17.898 15.895,22.906 2.984,-3.594 5.02,-5.305 7.535,-5.305 h 0.812 l 0.98,0.34 c 3.582,1.434 4.047,5.508 3.922,10.246 0.348,0.098 0.703,0.199 1.07,0.289 6.363,1.621 11.062,5.375 13.602,10.852 4.191,9.082 0.234,17.125 -3.254,24.215 -2.578,5.238 -6.379,12.953 -8.754,23.418 3.586,6.008 4.141,11.75 3.934,15.641 -0.297,5.516 -2.406,11.066 -5.996,15.848 1.102,4.875 0.484,9.473 -1.844,13.688 -5.285,9.566 -15.68,10.465 -19.094,10.754 -1.574,0.137 -3.34,0.215 -5.262,0.215 h -0.3 z" id="path2992" style="fill:#260859"/>
</g>
<g id="g2994">
<path d="m 506.766,425.695 c -6.77,0 -12.57,-2.73 -16.773,-7.902 -8.246,-10.133 -6.812,-26.621 -3.918,-41.938 -3.145,-2.297 -7.34,-5.594 -9.633,-10.934 l -5.355,-12.438 11.875,-6.527 c 24.914,-13.723 34.227,-39.723 27.668,-77.289 -2.957,-4.148 -7.086,-11.844 -10.824,-25.562 -2.727,-10.008 -13.16,-72.691 -7.508,-83.227 1.012,-1.898 5.035,-8.109 14.039,-8.109 5.695,0 19.23,3.664 28.004,12.398 0.246,-0.012 0.488,-0.016 0.734,-0.016 5.488,0 9.648,2.668 13.672,5.242 l 0.477,0.301 c 1.07,0.68 2.219,1.41 3.492,2.164 2.246,1.336 6.668,3.961 10.598,9.719 6.301,1.035 11.359,5.949 20.273,15.086 0.207,0.219 0.996,1.027 1.203,1.227 2.219,2.195 3.977,5.051 5.602,9.125 11.438,4.383 18.34,21.609 19.629,27.852 l 0.508,1.938 c 15.383,58.23 11.477,106.727 -11.297,140.25 -18.512,27.266 -48.637,43.574 -89.535,48.465 -1.045,0.117 -1.994,0.175 -2.931,0.175 l 0,0 z" id="path2996" style="fill:#260859"/>
</g>
</g>
<path d="M 382.699,555.371 C 362.824,548.375 188.613,484.344 188.613,390.375 l 0.012,-281.641 c 0,-25.367 21.531,-46.004 47.996,-46.004 16.855,0 32.289,8.32 40.957,21.902 31.453,-3.801 73.332,-11.934 99.66,-23.809 l 2.559,-1.129 15.438,-7.281 16.172,7.629 c 26.121,12.301 69,20.699 101.461,24.602 8.672,-13.59 24.102,-21.914 40.949,-21.914 26.473,0 48.008,20.551 48.008,45.812 l -0.027,281.832 c 0,94.301 -174.176,158.012 -194.039,164.965 l -12.523,4.406 -12.537,-4.374 z" id="path2998" style="fill:#260859"/>
<path d="M 389.637,535.469 C 371.207,528.989 209.688,469.871 209.688,390.375 L 209.7,108.734 c 0,-13.93 12.086,-24.93 26.922,-24.93 14.336,0 26.137,10.309 26.867,23.469 l 0.004,0.012 c 35.363,-3.055 88.648,-12.023 122.41,-27.25 l 2.426,-1.07 6.906,-3.254 7.176,3.387 c 33.441,15.754 87.988,25.031 124.539,28.188 h 0.004 c 0.73,-13.168 12.527,-23.48 26.863,-23.48 14.852,0 26.934,11.098 26.934,24.738 l -0.027,281.832 c 0,79.676 -161.52,138.625 -179.938,145.082 l -5.57,1.961 -5.579,-1.95 z" id="path3000" style="fill:#7c3a00"/>
<path d="m 395.156,112.82 -1.527,0.621 c -37.711,15.406 -92.113,24.406 -127.242,27.43 l -22.969,1.984 -0.008,130.859 H 395.156 V 112.82 z" id="path3002" style="fill:#ffef6f"/>
<path d="m 395.223,75.715 -6.895,3.25 -2.426,1.07 c -33.762,15.227 -87.047,24.195 -122.41,27.25 l -0.004,-0.012 c -0.73,-13.16 -12.531,-23.469 -26.867,-23.469 -14.836,0 -26.922,11 -26.922,24.93 l -0.012,281.641 c 0,79.496 161.52,138.613 179.949,145.094 l 5.578,1.949 0.008,-0.004 V 75.715 z" id="path3004" style="fill:#e8941a"/>
<g id="g3006">
<path d="m 553.816,100.664 c -5.395,0 -9.801,3.312 -10.035,7.539 l -0.941,17.375 -17.34,-1.5 c -38.246,-3.301 -94.871,-13.051 -130.277,-29.734 -0.773,0.363 -1.609,0.707 -2.402,1.062 0.016,-0.004 0.031,-0.012 0.043,-0.016 v 307.098 l 171.008,-126.707 0.02,-167.238 c -0.001,-4.27 -4.615,-7.879 -10.076,-7.879 z" id="path3008" style="fill:#e8941a"/>
</g>
<path d="M 392.863,397.402 V 95.391 c -35.527,16.031 -90.5,25.457 -127.926,28.688 l -17.336,1.5 -0.941,-17.367 c -0.234,-4.234 -4.645,-7.547 -10.039,-7.547 -5.766,0 -10.082,3.891 -10.09,7.219 l 0.027,0.852 -0.004,152.738 166.309,135.928 z" id="path3010" style="fill:#ffef6f"/>
<path d="m 525.5,124.078 c -38.246,-3.301 -94.871,-13.051 -130.277,-29.734 -0.492,0.23 -1.031,0.449 -1.527,0.676 v 41.531 l 30.055,-1.555 113.195,21.496 10.082,-13.633 -4.188,-17.281 -17.34,-1.5 z" id="path3012" style="fill:#e8941a"/>
<path d="m 393.695,95.02 c -35.492,16.262 -91.043,25.805 -128.758,29.059 l -17.336,1.5 -4.184,17.277 19.438,6.918 102.445,-11.754 28.395,-1.469 V 95.02 z" id="path3014" style="fill:#ffffff"/>
<path d="m 226.555,243.211 -0.008,147.652 c 0,55.215 105.336,106.918 168.656,129.176 63.324,-22.238 168.66,-73.918 168.66,-129.176 l 0.016,-147.652 H 226.555 z" id="path3016" style="fill:#ffd26c"/>
<path d="M 393.746,508.047 C 318.914,480.473 243.406,435.543 243.406,397.313 l 0.012,-254.457 22.969,-1.984 c 35.129,-3.023 89.531,-12.023 127.242,-27.43 l 1.594,-0.648 1.59,0.648 c 37.711,15.406 92.109,24.406 127.242,27.43 l 22.973,1.988 -0.023,254.453 c 0,38.266 -75.508,83.188 -150.344,110.734 l -1.457,0.535 -1.458,-0.535 z" id="path3018" style="fill:#ffffff"/>
<path d="m 524.055,140.871 c -35.133,-3.023 -89.531,-12.023 -127.242,-27.43 l -1.59,-0.648 -1.594,0.648 1.527,-0.621 v 160.895 h 151.859 l 0.012,-130.855 -22.972,-1.989 z" id="path3020" style="fill:#ffd26c"/>
<path d="m 395.156,112.82 -1.527,0.621 c -37.711,15.406 -92.113,24.406 -127.242,27.43 l -22.969,1.984 -0.008,130.859 H 395.156 V 112.82 z" id="path3022" style="fill:#ffef6f"/>
<linearGradient x1="395.21631" y1="132.92529" x2="395.21631" y2="491.23761" id="SVGID_1_" gradientUnits="userSpaceOnUse">
<stop id="stop3025" style="stop-color:#e8941a;stop-opacity:1" offset="0"/>
<stop id="stop3027" style="stop-color:#ffef6f;stop-opacity:1" offset="1"/>
</linearGradient>
<path d="M 395.203,494.371 C 315.765,464.609 256.051,422.691 256.051,396.586 l 0.012,-242.137 11.41,-0.984 c 34.82,-2.996 88.879,-11.863 127.75,-27.043 38.859,15.18 92.914,24.047 127.742,27.043 l 11.418,0.988 -0.023,242.133 c -10e-4,26.133 -59.715,68.051 -139.157,97.785 l 0,0 z" id="path3029" style="fill:url(#SVGID_1_)"/>
<path d="m 534.359,377.328 c 0,26.133 -59.715,68.051 -139.156,97.785 C 315.777,445.355 256.07,403.449 256.051,377.34 v 23.805 c 0,26.102 59.715,68.02 139.152,97.785 79.441,-29.734 139.156,-71.652 139.156,-97.785 l 0.023,-246.691 0,0 -0.023,222.874 z" id="path3031" style="fill:#260859"/>
<path d="m 395.156,126.445 c -38.867,15.164 -92.883,24.023 -127.684,27.02 l -11.41,0.984 v 16.746 l 10.133,-0.867 c 35.152,-2.996 89.707,-11.855 128.961,-27.02 v -16.863 z" id="path3033" style="fill:#a84d10"/>
<path d="m 522.965,153.465 c -34.828,-2.996 -88.883,-11.863 -127.742,-27.043 -0.223,0.086 -0.457,0.164 -0.68,0.25 0.199,-0.074 0.41,-0.148 0.613,-0.227 v 16.863 c 0.02,-0.008 0.043,-0.016 0.066,-0.023 39.25,15.18 93.844,24.047 129.023,27.043 l 10.137,0.871 v -16.746 l -11.417,-0.988 z" id="path3035" style="fill:#a84d10"/>
<path d="m 256.062,158.086 -0.012,238.5 c 0,7.613 5.105,16.57 14.262,26.176 1.164,0.109 2.312,0.242 3.492,0.328 0.074,1.215 0.168,2.344 0.277,3.438 23.133,22.137 66.586,47.148 118.852,66.977 l -0.059,-1.766 c -0.59,-18.352 -2.348,-35.594 -4.691,-46.121 -5.438,-24.406 -22.613,-48.82 -37.359,-62.367 -6.184,-15.457 -1.742,-25.371 4.391,-39.07 1.871,-4.168 3.992,-8.887 5.699,-13.855 5.145,-14.93 4.266,-38.211 -1.984,-53.98 -2.898,-9.637 -9.184,-16.973 -16.832,-19.625 -2.328,-0.777 -5.027,-1.312 -8.23,-1.625 -3.215,-10.938 -1.609,-22.059 -1.598,-22.148 l 1.754,-10.398 -9.758,-3.961 c -0.977,-0.398 -9.941,-3.902 -23.02,-3.902 -6.027,0 -12.105,0.785 -18.117,2.34 0.82,-5.617 1.695,-11.391 2.598,-17.309 0,0 1.664,-10.949 2.402,-15.922 l 0.676,-4.617 -15.496,-26.328 c 0,0 -3.242,0.398 -5.836,0.617 l -11.411,4.618 z" id="path3037" style="fill:#260859"/>
<path d="m 514.695,428.105 c 0.016,-1.758 0.035,-3.516 -0.082,-5.402 2.242,-0.328 4.438,-0.699 6.609,-1.102 8.461,-9.164 13.137,-17.707 13.137,-25.016 l -1.562,-226.52 c 0,0 2.516,-0.133 2.234,-0.102 -3.75,-10.566 -23.34,-16.086 -28.695,-16.086 -7.617,0 -11.059,4.895 -12.184,6.996 -5.09,9.496 4.488,69.965 7.688,81.676 0.309,1.141 0.621,2.191 0.938,3.234 l -1.672,8.418 c -14.316,-13.652 -32.395,-28.645 -42.766,-28.645 -0.656,0 -1.289,0.055 -1.898,0.156 -2.648,0.461 -4.789,1.961 -6.016,4.215 -1.777,3.258 -1.703,7.801 0.27,14.133 -5.043,-1.027 -11.129,-1.98 -16.34,-1.98 -6.387,0 -10.586,1.453 -12.836,4.434 -4.473,5.934 -2.328,15.238 6.367,27.66 2.562,3.668 5.789,7.285 8.969,10.848 0.66,0.746 1.328,1.492 1.992,2.242 -9.152,14.402 -11.535,36.027 -6.352,58.285 2.27,9.738 5.41,16.656 8.188,22.77 1.766,3.883 3.203,7.047 4.09,10.273 -21.77,18.578 -37.285,42.938 -42.648,67.016 -2.184,9.785 -3.836,25.16 -4.547,42.199 l -0.23,5.742 c 51.096,-19.35 93.811,-43.674 117.346,-65.444 z" id="path3039" style="fill:#260859"/>
<g id="g3041">
<path d="m 321.699,323.621 c -1.984,-4.289 -5.723,-7.242 -10.809,-8.539 -19.477,-4.91 -29.746,-14.688 -33.309,-31.699 -3.887,-18.574 0.723,-48.887 6.062,-83.988 l 0.188,-1.234 c 0.734,-4.816 1.477,-9.707 2.211,-14.672 l 0.582,-3.965 -13.316,-26.676 -15.652,-2.938 -3.922,12.906 -4.168,2.527 c -0.457,0.281 -46.105,28.566 -66.969,87.457 -16.059,45.312 -11.832,93.551 11.023,125.91 19.73,27.922 51.32,42.691 91.348,42.699 1.902,0.008 3.645,-0.066 5.184,-0.195 4.969,-0.43 12.004,-1.789 15.77,-8.598 2.121,-3.844 2.418,-7.98 0.891,-12.609 3.871,-4.461 6.141,-9.797 6.426,-15.113 0.195,-3.613 -0.395,-9.031 -4.211,-14.613 2.449,-11.852 6.652,-20.379 9.449,-26.062 3.375,-6.86 6.57,-13.344 3.222,-20.598 z" id="path3043" style="fill:#260859"/>
<g id="g3045">
<path d="m 199.93,259.562 c 14.34,-66.531 55.953,-77.508 55.953,-77.508 l -1.012,36.066 0.816,76.336 c 0,0 -2.602,60.848 30.262,73.891 32.863,13.039 6.941,42.215 -36.23,31.109 C 208.5,389.602 184.465,330.16 199.93,259.562 z" id="path3047" style="fill:#ffffff"/>
<g id="g3049">
<path d="m 228.879,317.406 c -10.066,-45.742 11.844,-91.457 22.941,-105.285 l 13.945,-45.91 7.914,15.453 c -4.523,30.586 -9.375,58.598 -9.957,80.766 -0.121,4.418 0.922,20.125 1.629,23.516 3.336,15.93 11.039,27.066 24.359,34.395 2.242,1.234 13.094,-16.984 15.656,-15.965 3.352,1.344 -1.461,21.836 2.469,22.828 9.133,2.305 -6.898,13.684 -11.938,45.445 -21.198,1.91 -56.975,-9.61 -67.018,-55.243 z" id="path3051" style="fill:#f26531"/>
</g>
<path d="m 194.371,256.984 c 19.648,-55.469 61.68,-80.957 61.68,-80.957 l -0.434,15.422 c -15.742,12.766 -62.27,71.184 -44.375,141.27 13.469,48.656 52.797,63.223 80.523,57.949 -4.102,9.059 14.18,18.254 -6.789,18.254 -91.675,-0.02 -114.8,-83.645 -90.605,-151.938 z" id="path3053" style="fill:#7ac143"/>
</g>
<polygon points="213.434,210.594 205.309,222.129 239.594,232.855 " id="polygon3055" style="fill:#260859"/>
<polygon points="191.312,240.535 186.168,257.379 230.988,256.758 " id="polygon3057" style="fill:#260859"/>
<polygon points="183.648,279.039 183.082,296.645 226.18,284.332 " id="polygon3059" style="fill:#260859"/>
<polygon points="180.789,326.434 186,346.004 229.383,318.148 " id="polygon3061" style="fill:#260859"/>
<polygon points="201.324,373.969 214.367,389.453 241.773,345.785 " id="polygon3063" style="fill:#260859"/>
<polygon points="241.258,405.695 259.137,409.742 267.953,367.039 " id="polygon3065" style="fill:#260859"/>
</g>
<path d="m 324.59,268.086 c 0.148,0.016 0.281,0.008 0.438,0.023 -9.18,-17.043 -5.715,-37.352 -5.715,-37.352 0,0 -16.691,-6.77 -37.332,0.254 -24.391,8.301 -27.098,26.691 -24.246,32.094 2.688,5.09 9.637,6.027 15.934,2.34 3.547,9.219 8,15.848 18.32,24.191 l 34.66,-20.137 c -10e-4,10e-4 -0.837,-0.394 -2.059,-1.413 z" id="path3067" style="fill:#fff200"/>
<path d="m 274.98,347.574 c -5.398,0 -8.523,-1.82 -9.355,-2.379 l -9.891,-6.449 12.168,-6.41 c 5.203,-1.285 10.617,-7.621 15.832,-14.719 -1.098,0.18 -2.164,0.363 -3.195,0.535 -6.023,1.031 -11.086,1.895 -14.895,1.895 -0.688,0 -1.328,-0.031 -1.918,-0.082 -0.203,-0.02 -0.395,-0.027 -0.59,-0.027 -4.445,0 -10.258,3.727 -12.125,5.164 l -6.609,5.445 -4.531,-8.77 c -0.234,-0.688 -2.207,-6.969 1.93,-14.664 3.105,-5.758 12.41,-13.875 23.41,-13.875 1.82,0 3.633,0.219 5.387,0.652 0.594,0.148 1.184,0.223 1.734,0.223 2.828,0 5.781,-1.98 8.781,-5.883 0.738,-1.004 1.242,-1.891 1.648,-2.598 0.473,-0.82 0.875,-1.5 1.395,-2.191 1.762,-2.109 4.008,-3.098 6.93,-3.098 5.004,0 11.262,2.648 13.82,10.074 l 0,0 c 0.008,0 0.719,1.465 4.141,4.703 3.637,0.766 6.613,2.043 8.863,3.809 3.297,2.598 5.059,6.297 4.953,10.43 -0.207,8.285 -7.871,17.668 -23.434,28.684 -11.148,7.875 -19.449,9.531 -24.449,9.531 l 0,0 z" id="path3069" style="fill:#260859"/>
<path d="m 285.051,298.215 c -8.113,-6.562 -13.383,-12.48 -17.387,-19.559 -0.559,0.035 -1.121,0.059 -1.676,0.059 -7.941,0 -14.508,-3.812 -18.008,-10.461 -2.953,-5.582 -2.887,-13.828 0.176,-21.527 2.715,-6.844 10.266,-19.348 30.266,-26.16 7.363,-2.504 15.043,-3.773 22.824,-3.773 12.699,0 21.285,3.363 22.227,3.746 l 8.191,3.324 -1.473,8.734 c -0.02,0.137 -2.246,15.293 3.441,28.035 l 16.324,8.004 -17.734,10.391 -41.266,23.961 -5.905,-4.774 z" id="path3071" style="fill:#260859"/>
<path d="m 313.297,327.484 c -4.66,0 -5.777,-4.406 -6.758,-8.293 -0.754,-2.98 -1.535,-6.062 -3.594,-8.117 -7.707,-7.703 -18.688,-12.191 -18.801,-12.238 l -1.008,-0.406 -0.25,-1.062 c -1.91,-8.031 -0.074,-15.875 5.312,-22.695 8.305,-10.508 25,-17.848 40.602,-17.848 4.613,0 8.863,0.637 12.629,1.895 11.438,3.969 18.797,19.078 16.785,34.398 -2.59,19.672 -19.195,32.516 -44.418,34.352 l -0.499,0.014 z" id="path3073" style="fill:#260859"/>
<path d="m 390.77,491.805 c -0.586,-18.211 -2.32,-35.305 -4.645,-45.73 -5.391,-24.207 -22.441,-48.285 -37.074,-61.586 -6.785,-16.547 -1.922,-27.41 4.227,-41.141 1.859,-4.137 3.961,-8.82 5.645,-13.711 6.371,-18.496 3.301,-52.016 -9.367,-64.766 l -0.875,-0.883 -17.277,4.875 -0.285,1.207 c -3.629,15.34 -12.52,35.328 -19.969,40.625 l -5.223,3.711 0.672,6.355 c 1.371,13.16 -5.613,28.898 -23.094,64.328 -6.949,14.074 -9.164,28.023 -7.043,43.664 23.328,21.27 64.762,44.902 114.336,63.93 l -0.028,-0.878 z" id="path3075" style="fill:#260859"/>
<path d="m 286.379,296.578 c -8.293,-6.707 -13.605,-12.801 -17.535,-20.16 -0.957,0.125 -1.914,0.188 -2.855,0.188 -7.129,0 -13.016,-3.398 -16.145,-9.336 -2.672,-5.051 -2.57,-12.621 0.27,-19.766 2.586,-6.512 9.793,-18.406 28.988,-24.941 7.148,-2.434 14.594,-3.664 22.145,-3.664 12.223,0 20.52,3.219 21.426,3.59 l 6.637,2.691 -1.195,7.066 c -0.023,0.141 -2.43,16.492 3.918,29.945 l 13.477,6.609 -14.352,8.406 -40,23.23 -4.779,-3.858 z" id="path3077" style="fill:#260859"/>
<path d="m 324.59,268.086 c 0.148,0.016 0.281,0.008 0.438,0.023 -9.18,-17.043 -5.715,-37.352 -5.715,-37.352 0,0 -16.691,-6.77 -37.332,0.254 -24.391,8.301 -27.098,26.691 -24.246,32.094 2.688,5.09 9.637,6.027 15.934,2.34 3.547,9.219 8,15.848 18.32,24.191 l 34.66,-20.137 c -10e-4,10e-4 -0.837,-0.394 -2.059,-1.413 z" id="path3079" style="fill:#fff200"/>
<path d="m 291.426,228.664 c -3.023,0.5 -6.176,1.234 -9.445,2.348 -1.793,0.609 -3.449,1.281 -5.012,1.988 0.133,0.762 0.27,1.52 0.402,2.305 2.273,13.105 5.395,30.973 23.766,49.016 l 3.77,-2.191 4.488,-4.691 c -0.477,-0.453 -0.945,-0.914 -1.398,-1.371 -15.052,-15.072 -17.841,-27.463 -16.571,-47.404 z" id="path3081" style="fill:#f26531"/>
<path d="m 319.312,230.758 c 0,0 -4.73,-1.898 -12.234,-2.641 -2.945,19.641 0.68,32.223 5.871,43.66 2.867,-2.016 6.352,-4.246 12.078,-3.668 -9.179,-17.043 -5.715,-37.351 -5.715,-37.351 z" id="path3083" style="fill:#f26531"/>
<path d="m 340.762,260.719 c -22.492,-7.508 -62.18,9.445 -55.824,36.164 0,0 11.375,4.582 19.496,12.699 5.488,5.477 3.191,16.223 9.211,15.781 55.078,-4.008 48.554,-57.203 27.117,-64.644 z" id="path3085" style="fill:#260859"/>
<path d="m 384.066,446.531 c -5.324,-23.91 -22.367,-47.836 -36.781,-60.797 -7.402,-17.637 -2.094,-29.484 4.07,-43.25 1.84,-4.102 3.926,-8.746 5.574,-13.535 6.051,-17.562 3.086,-50.559 -8.871,-62.594 l -14.891,4.199 c -3.703,15.652 -12.715,36.105 -20.797,41.855 l -4.219,3 0.543,5.133 c 1.426,13.711 -5.629,29.66 -23.301,65.484 -7.051,14.281 -9.199,28.594 -6.531,44.879 23.355,20.395 62.828,42.727 109.797,60.965 -0.671,-20.983 -2.706,-36.886 -4.593,-45.339 z" id="path3087" style="fill:#260859"/>
<path d="m 375.43,448.457 c -4.688,-21.027 -20.555,-44.578 -35.52,-57.488 -0.02,-0.051 -0.031,-0.094 -0.059,-0.145 -12.793,-28.352 1.895,-44.961 8.711,-64.754 5.188,-15.062 2.301,-44.332 -6.785,-53.473 -2.863,12.109 -12.199,38.438 -24.281,47.027 1.711,16.387 -6.383,34.281 -24.168,70.316 -7.707,15.625 -8.574,31.184 -3.348,49.906 22.02,16.477 53.324,33.613 89.688,48.441 -0.727,-17.475 -2.406,-31.607 -4.238,-39.83 z" id="path3089" style="fill:#f26531"/>
<path d="m 336.758,268.195 c -10.891,-5.066 -39.801,0.785 -49.402,24.723 0,0 8.891,5.105 14.688,12.75 3.91,5.168 3.672,14.082 8.707,14.211 42.866,1.105 42.554,-43.981 26.007,-51.684 z" id="path3091" style="fill:#f26531"/>
<path d="m 272.738,340.402 c -1.93,0 -3.652,-0.895 -4.738,-2.457 -1.965,-2.816 -0.754,-6.086 -0.109,-7.84 3.441,-9.297 12.891,-18.176 21.5,-20.219 l 16.016,-4.246 1.781,0.863 c 4.453,2.152 2.496,-3.094 5.336,-1.969 11.52,4.566 5.867,13.898 4.336,16.051 -3.465,4.875 -7.785,4.871 -10.066,5.266 -9.285,1.57 -14.492,4.105 -18.293,5.965 -1.504,0.734 -2.801,1.367 -4.184,1.898 -0.977,0.379 -3.066,2.02 -4.32,3 -2.829,2.224 -4.7,3.688 -7.259,3.688 l 0,0 z" id="path3093" style="fill:#260859"/>
<path d="m 304.859,311.324 -14.234,3.773 c -6.875,1.625 -14.824,9.062 -17.707,16.859 -2.891,7.805 4.785,-1.43 9.469,-3.234 4.668,-1.812 10.121,-5.887 23.5,-8.156 13.379,-2.265 -1.028,-9.242 -1.028,-9.242 z" id="path3095" style="fill:#f26531"/>
<path d="m 290.859,310.074 c 0,0 -11.062,22.945 -25.168,28.18 l 10.75,1.375 c 0,0 15.59,-9.383 18.441,-19.707 2.852,-10.312 2.551,-15.832 2.551,-15.832 l -6.574,5.984 z" id="path3097" style="fill:#260859"/>
<path d="m 313.492,304.547 c -6.148,-4.84 -21.82,-2.699 -21.82,-2.699 l 8.758,5.172 c -8.391,9.762 -18.191,29.141 -30.828,32.25 0,0 8.082,5.398 25.703,-7.062 17.621,-12.46 24.347,-22.825 18.187,-27.661 z" id="path3099" style="fill:#fff200"/>
<path d="m 289.879,287.703 c -0.773,1.035 -1.34,2.477 -3.102,4.871 -2.613,3.402 -8.828,10.496 -17.902,8.246 -9.074,-2.25 -18.012,4.523 -20.785,9.676 -2.77,5.152 -1.465,8.965 -1.465,8.965 0,0 9.391,-7.359 17.73,-6.609 8.348,0.758 32.059,-6.914 50.258,-4.016 0,0 -14.355,-10.16 -16.457,-16.086 -2.011,-5.848 -7.902,-5.488 -8.277,-5.047 z" id="path3101" style="fill:#fff200"/>
<g id="g3103">
<path d="m 318.129,275.305 c -8.238,10.934 -12.203,12.973 -19.973,17.445 18.227,33.57 52.637,-9.355 19.973,-17.445 z" id="path3105" style="fill:#260859"/>
</g>
<g id="g3107">
<path d="m 315.082,300.051 c -3.988,0 -7.168,-2.73 -9.383,-5.508 5.203,-3.199 9.039,-6.363 14.363,-13.004 4.559,1.859 6.637,4.664 6.188,8.344 -0.574,4.703 -5.578,10.168 -11.168,10.168 l 0,0 z" id="path3109" style="fill:#ffffff"/>
</g>
<path d="m 317.875,282.395 c -2.621,1.168 -5.152,2.633 -7.699,3.727 -1.414,0.609 -3.875,2.562 -3.633,3.16 1.148,2.824 3.023,5.551 6.082,6.75 0.297,0.117 0.602,0.211 0.906,0.289 0.695,-1.246 1.336,-2.582 1.898,-4.008 1.118,-2.86 2.009,-6.751 2.446,-9.918 z" id="path3111" style="fill:#260859"/>
<path d="m 313.707,296.379 c 0,-2.535 -0.289,-4.934 -0.781,-7.129 -2.305,2.059 -4.594,3.672 -7.227,5.293 1.91,2.398 4.551,4.75 7.801,5.352 0.125,-1.137 0.207,-2.305 0.207,-3.516 z" id="path3113" style="fill:#f37344"/>
<path d="m 280.828,251.062 c 0.438,9.141 4.355,19.59 12.043,29.02 l 4.793,-4.211 c -11.609,-7.101 -16.836,-24.809 -16.836,-24.809 z" id="path3115" style="fill:#260859"/>
<path d="m 291.141,238.789 c -1.156,10.66 1.484,23.461 8.672,35.766 l 6.305,-4 c -12.141,-10.336 -14.977,-31.766 -14.977,-31.766 z" id="path3117" style="fill:#260859"/>
<path d="m 306.738,231.996 c -2.785,10.348 -2.152,23.402 3.047,36.672 l 6.844,-2.973 c -10.399,-12.082 -9.891,-33.699 -9.891,-33.699 z" id="path3119" style="fill:#260859"/>
<path d="m 334.832,367.363 c 0.266,-6.996 2.133,-13.312 4.527,-19.387 0.395,-1.004 -7.34,5.734 -15.191,7.973 -7.262,2.07 -14.66,-0.41 -14.953,0.293 -2.434,5.727 -5.387,12.094 -8.852,19.297 19.012,1.656 27.176,-2.223 34.469,-8.176 z" id="path3121" style="fill:#7ac143"/>
<path d="m 337.445,353.391 c 1.25,-3.918 2.812,-7.699 4.465,-11.438 -12.93,7.711 -20.523,10.559 -30.961,10.07 -1.262,3.164 -2.719,6.547 -4.34,10.152 13.114,1.403 24.301,-3.511 30.836,-8.784 z" id="path3123" style="fill:#ffffff"/>
<path d="m 293.305,389.996 c -7.684,15.605 -8.547,31.148 -3.324,49.852 12.047,9.012 26.852,18.227 43.773,27.152 12.125,-36.82 -2.57,-65.309 -8.16,-75.688 -8.168,2.25 -33.199,11.153 -32.289,-1.316 z" id="path3125" style="fill:#ffffff"/>
<g id="g3127">
<path d="m 426.086,226.523 c -3.566,0 -6.266,-1.336 -7.719,-2.051 -0.879,-0.445 -2,-1.012 -7.145,-4.84 -5.227,1.496 -10.617,2.254 -16.043,2.254 -5.52,0 -10.984,-0.777 -16.266,-2.309 -5.227,3.891 -6.375,4.465 -7.148,4.855 -1.371,0.684 -4.199,2.09 -7.797,2.09 -4.008,0 -7.691,-1.621 -10.949,-4.805 -4.895,-4.824 -6.535,-9.34 -5.664,-15.602 0.137,-0.996 0.441,-2.527 1.691,-6.785 -1.613,-2.074 -3.102,-4.285 -4.441,-6.59 -9.844,-3.238 -12.664,-4.578 -14.113,-5.461 -4.582,-2.82 -6.859,-8.195 -6.98,-16.441 -0.117,-8.219 1.965,-13.617 6.363,-16.496 0.707,-0.461 1.859,-1.211 8.289,-4.105 0.246,-1.074 0.527,-2.141 0.832,-3.195 -5.164,-7.422 -5.906,-9.02 -6.309,-9.887 -1.41,-3.07 -2.191,-8.109 2.059,-14.746 1.551,-2.414 7.227,-10.281 15.102,-10.281 h 0.797 l 1.406,0.211 c 0.941,0.184 2.262,0.449 9.582,3.125 4.242,-2.965 8.844,-5.352 13.707,-7.109 2.277,-5.695 3.047,-6.969 3.422,-7.594 2.812,-4.617 8.301,-6.957 16.328,-6.957 8.25,0 13.824,2.352 16.57,6.988 0.398,0.676 1.055,1.781 3.363,7.566 4.855,1.758 9.449,4.141 13.676,7.098 7.227,-2.648 8.523,-2.91 9.391,-3.09 l 0.734,-0.148 1.395,-0.086 c 7.91,0 13.363,7.188 15.348,10.277 4.25,6.629 3.473,11.66 2.07,14.715 -0.418,0.906 -1.176,2.527 -6.289,9.871 0.312,1.074 0.594,2.156 0.844,3.246 6.473,2.906 7.566,3.625 8.297,4.105 4.398,2.875 6.48,8.273 6.359,16.496 -0.113,8.125 -2.453,13.648 -6.957,16.414 -1.016,0.625 -2.891,1.785 -14.141,5.484 -1.391,2.406 -2.957,4.711 -4.656,6.879 1.348,4.625 1.508,5.785 1.605,6.496 0.871,6.266 -0.762,10.777 -5.645,15.582 -3.269,3.201 -6.956,4.826 -10.968,4.826 l 0,0 z" id="path3129" style="fill:#260859"/>
<path d="m 426.086,224.414 c -3.094,0 -5.512,-1.203 -6.812,-1.844 -0.746,-0.379 -1.895,-0.953 -7.633,-5.262 -5.348,1.641 -10.875,2.473 -16.461,2.473 -5.676,0 -11.273,-0.848 -16.676,-2.527 -5.824,4.367 -6.977,4.941 -7.691,5.301 -1.324,0.656 -3.75,1.859 -6.844,1.859 -3.438,0 -6.621,-1.41 -9.477,-4.203 -4.41,-4.344 -5.828,-8.211 -5.051,-13.801 0.137,-1 0.48,-2.645 1.938,-7.531 -1.992,-2.461 -3.781,-5.105 -5.344,-7.887 -10.289,-3.363 -13.133,-4.711 -14.441,-5.512 -3.922,-2.41 -5.867,-7.207 -5.973,-14.668 -0.109,-7.441 1.664,-12.254 5.41,-14.707 0.672,-0.438 1.848,-1.199 8.965,-4.375 0.359,-1.711 0.801,-3.402 1.309,-5.059 -5.598,-8.008 -6.336,-9.613 -6.707,-10.41 -1.188,-2.582 -1.816,-6.879 1.922,-12.715 1.41,-2.195 6.527,-9.309 13.328,-9.309 h 0.797 l 1.051,0.18 c 0.832,0.164 2.242,0.457 10.258,3.406 4.551,-3.312 9.668,-5.977 14.992,-7.801 2.492,-6.301 3.273,-7.594 3.621,-8.176 2.402,-3.941 7.281,-5.934 14.523,-5.934 7.453,0 12.418,2.004 14.754,5.953 0.371,0.625 1.039,1.754 3.574,8.16 5.312,1.828 10.418,4.48 14.965,7.789 8.012,-2.953 9.32,-3.219 10.129,-3.383 l 0.738,-0.152 1.047,-0.039 c 7.012,0 12.098,7.125 13.496,9.305 3.738,5.828 3.109,10.125 1.93,12.703 -0.387,0.828 -1.137,2.441 -6.684,10.375 0.52,1.68 0.957,3.387 1.32,5.109 7.172,3.195 8.281,3.922 8.969,4.375 3.746,2.449 5.52,7.262 5.414,14.707 -0.105,7.359 -2.109,12.285 -5.953,14.648 -0.922,0.566 -2.789,1.711 -14.469,5.527 -1.621,2.895 -3.48,5.625 -5.559,8.168 1.59,5.371 1.758,6.566 1.852,7.25 0.777,5.59 -0.633,9.457 -5.035,13.785 -2.863,2.808 -6.055,4.222 -9.492,4.222 l 0,0 z" id="path3131" style="fill:#260859"/>
<path d="m 409.34,122.93 c 0,0 -5.383,-14.453 -6.75,-16.773 -1.375,-2.324 -13.246,-2.52 -14.793,0.031 -1.535,2.566 -6.75,16.742 -6.75,16.742 h 28.293 z" id="path3133" style="fill:#7f3f98"/>
<path d="m 380.41,205.176 c 0,0 -11.297,8.801 -13.367,9.836 -2.066,1.031 -3.934,1.836 -6.656,-0.824 -2.676,-2.637 -2.969,-3.941 -2.598,-6.621 0.371,-2.668 4.875,-16.578 4.875,-16.578 l 17.746,14.187 z" id="path3135" style="fill:#7f3f98"/>
<path d="m 409.645,205.176 c 0,0 11.297,8.801 13.367,9.836 2.066,1.031 3.938,1.836 6.656,-0.824 2.676,-2.637 2.969,-3.941 2.598,-6.621 -0.371,-2.668 -4.875,-16.578 -4.875,-16.578 l -17.746,14.187 z" id="path3137" style="fill:#7f3f98"/>
<path d="m 352.539,155.469 c 0,0 -14.668,6.238 -16.891,7.691 -2.211,1.449 -2.164,13.59 0.336,15.125 2.5,1.531 17.105,6.121 17.105,6.121 l -0.55,-28.937 z" id="path3139" style="fill:#7f3f98"/>
<path d="m 437.82,155.469 c 0,0 14.668,6.238 16.891,7.691 2.215,1.449 2.164,13.59 -0.336,15.125 -2.5,1.531 -17.105,6.121 -17.105,6.121 l 0.55,-28.937 z" id="path3141" style="fill:#7f3f98"/>
<path d="m 348.719,153.543 -7.418,3.023 c -0.465,4.172 0.828,11.812 6.457,16.184 5.629,4.359 0.961,-19.207 0.961,-19.207 z" id="path3143" style="fill:#260859"/>
<path d="m 441.246,153.543 7.418,3.023 c 0.465,4.172 -0.828,11.812 -6.457,16.184 -5.629,4.359 -0.961,-19.207 -0.961,-19.207 z" id="path3145" style="fill:#260859"/>
<path d="m 367.875,129.066 c 0,0 -15.164,-5.836 -17.758,-6.371 -2.598,-0.547 -9.082,7.383 -7.863,10.039 1.223,2.66 10.508,15.508 10.508,15.508 l 15.113,-19.176 z" id="path3147" style="fill:#7f3f98"/>
<path d="m 422.438,129.066 c 0,0 15.164,-5.836 17.754,-6.371 2.598,-0.547 9.086,7.383 7.867,10.039 -1.223,2.66 -10.508,15.508 -10.508,15.508 l -15.113,-19.176 z" id="path3149" style="fill:#7f3f98"/>
<path d="m 359.793,121.949 c -2.27,3.754 -6.828,9.09 -3.359,17.332 2.875,-3.961 11.133,-15.062 11.43,-16.781 l -8.071,-0.551 z" id="path3151" style="fill:#260859"/>
<path d="m 423.164,198.848 10.629,-4.398 3.391,4.844 c -4.156,3.188 -7.762,6.547 -14.637,6.121 -8.692,-0.552 0.617,-6.567 0.617,-6.567 z" id="path3153" style="fill:#260859"/>
<path d="m 366.891,198.848 -10.629,-4.398 -3.391,4.844 c 4.156,3.188 7.762,6.547 14.637,6.121 8.695,-0.552 -0.617,-6.567 -0.617,-6.567 z" id="path3155" style="fill:#260859"/>
<path d="m 430.566,121.949 c 2.266,3.754 6.824,9.09 3.352,17.332 -2.867,-3.961 -11.133,-15.062 -11.43,-16.781 l 8.078,-0.551 z" id="path3157" style="fill:#260859"/>
<path d="m 443.168,163.367 c 0,26.504 -21.488,47.984 -47.988,47.984 -26.508,0 -47.992,-21.48 -47.992,-47.984 0,-26.504 21.484,-47.996 47.992,-47.996 26.5,0 47.988,21.492 47.988,47.996 z" id="path3159" style="fill:#7f3f98"/>
</g>
<path d="m 499.035,379.352 c -24.148,-14.574 -50.754,-40.875 -46.246,-67.043 1.824,-10.57 -6.406,-19.781 -14.363,-28.688 -3.098,-3.477 -6.305,-7.066 -8.812,-10.648 -5.574,-7.969 -11.219,-18.809 -6.41,-25.188 1.828,-2.426 5.477,-3.598 11.152,-3.598 6.438,0 14.301,1.52 19.57,2.734 -2.094,-5.496 -3.824,-12.004 -1.652,-15.984 0.93,-1.707 2.5,-2.793 4.531,-3.148 0.484,-0.082 1,-0.121 1.535,-0.121 10.691,0 31.27,18.254 44.082,30.727 l 2.691,-13.578 1.098,2.488 c 0.855,0.215 21,5.586 31.73,37.02 15.328,14.332 30.715,37.719 28.375,60.863 l -1.391,13.645 -24.707,-4.203 -10.504,-1.258 10.469,12.742 -6.188,10.285 -10.91,2.156 c -4.785,0.953 -10.098,1.809 -15.793,2.543 l -4.441,0.562 -3.816,-2.308 z" id="path3161" style="fill:#260859"/>
<path d="m 499.883,380.711 c -8.453,-18.031 -15.75,-33.605 -11.617,-49.598 5.477,-21.191 4.926,-37.324 -1.633,-47.938 -5.246,-8.492 -12.496,-10.414 -13.895,-10.715 l -2.395,-0.512 -14.07,3.293 -0.402,0.141 c -17.102,5.828 -29.754,33.488 -21.32,69.688 2.219,9.543 5.305,16.332 8.051,22.375 2.02,4.445 3.656,8.047 4.535,11.91 -21.969,18.406 -37.598,42.656 -42.953,66.711 -2.16,9.668 -3.797,24.91 -4.496,41.824 l -0.195,4.844 c 48.699,-18.664 89.555,-41.785 113.125,-62.766 0.469,-12.918 -2.551,-27.004 -8.867,-40.922 -1.278,-2.812 -2.579,-5.585 -3.868,-8.335 z" id="path3163" style="fill:#260859"/>
<path d="m 506.766,421.48 c -5.457,0 -10.125,-2.191 -13.5,-6.348 -7.531,-9.25 -5.598,-25.848 -2.543,-41.09 -0.52,-0.402 -1.082,-0.809 -1.566,-1.16 -2.887,-2.09 -6.832,-4.945 -8.84,-9.621 l -3.844,-8.93 8.52,-4.684 c 26.812,-14.766 36.797,-42.465 29.68,-82.332 -0.055,-0.164 -0.105,-0.328 -0.164,-0.488 -2.809,-3.723 -6.871,-11.012 -10.637,-24.828 -3.523,-12.914 -12.309,-71.832 -7.859,-80.129 0.945,-1.77 3.852,-5.887 10.324,-5.887 4.68,0 18.578,3.645 26.375,12.609 0.766,-0.152 1.562,-0.227 2.363,-0.227 4.25,0 7.562,2.121 11.398,4.578 1.246,0.789 2.582,1.645 4.094,2.543 2.199,1.305 6.66,3.945 10.301,10.102 5.594,0.066 9.668,3.734 19.703,14.016 0.09,0.102 1.055,1.09 1.293,1.32 2.059,2.035 3.676,4.914 5.316,9.516 10.035,2.156 17.312,18.496 18.711,25.281 0.051,0.262 0.152,0.633 0.289,1.145 l 0.273,1.02 c 15.062,57.008 11.363,104.316 -10.707,136.801 -17.801,26.219 -46.922,41.906 -86.551,46.645 -0.859,0.102 -1.648,0.148 -2.429,0.148 l 0,0 z" id="path3165" style="fill:#260859"/>
<path d="m 595.402,237.887 c -1.785,-8.676 -10.664,-20.074 -11.051,-16.191 -0.199,2.031 1.609,13.535 2.16,20.777 -0.961,-0.691 -2.066,-1.449 -2.703,-1.969 -2.98,-14.074 -6.547,-29.066 -9.473,-31.961 -2.211,-2.176 -14.605,-15.398 -13.977,-11.551 2.879,17.59 7.48,30.176 11.324,39.035 -3.078,-2.262 -4.473,-3.328 -6.055,-4.422 -6.062,-4.195 -8.738,-30.359 -13.359,-39.422 -2.363,-4.652 -5.629,-6.57 -7.172,-7.484 -9.922,-5.895 -12.629,-9.863 -10.863,2.387 1.348,9.336 2.344,21.816 14.742,47.855 l -8.207,-6.227 c -6.785,-5.465 -11.07,-36.008 -13.523,-48.082 -2.031,-10.043 -21.027,-15.141 -21.797,-13.699 -1.18,2.203 3.742,53.883 8.754,72.25 5.008,18.359 10.426,24.254 10.836,25.301 3.816,20.711 5.387,47.844 -8.359,70.012 -2.25,3.625 -31.09,-18.539 -34.219,-15.227 -3.129,3.312 19.469,32.105 15.348,34.992 -2.375,1.672 -4.922,3.262 -7.656,4.766 1.898,4.414 10.406,6.801 11.91,12.598 0.566,0 -11.004,41.09 5.863,39.07 136.755,-16.336 89.27,-164.133 87.477,-172.808 z" id="path3167" style="fill:#ffffff"/>
<path d="m 519.477,379.414 c -7.062,-21.422 -20.711,-50.16 -20.711,-50.16 l -28.637,-11.207 19.031,63.051 -6.086,-5.641 -1.121,4.543 10.562,19.551 c 0,0 27.895,4.254 31.422,-6.102 -1.546,-5.48 -2.132,-6.969 -4.46,-14.035 z" id="path3169" style="fill:#aaa4c4"/>
<path d="m 506.004,389.176 c -3.055,0.156 -3.531,-6.988 -5.977,-6.602 -1.98,12.016 -3.234,29.453 7.898,28.121 105.012,-12.539 101.387,-102.594 93.238,-147.656 -1.339,28.816 1.314,121.039 -95.159,126.137 z" id="path3171" style="fill:#aaa4c4"/>
<path d="m 499.363,403.242 c 1.051,4.82 3.543,8.055 8.562,7.453 48.184,-5.754 73.453,-27.84 85.957,-54.395 -20.577,34.462 -64.964,42.055 -94.519,46.942 z" id="path3173" style="fill:#f2f1f8"/>
<path d="m 536.113,285.504 c -10.438,-31.156 -30.414,-36.156 -30.414,-36.156 l -1.723,13.531 c -1.992,-1.926 -34.645,-35.117 -46.812,-33.012 -8.293,1.434 -0.457,18.242 0.266,20.086 -6.105,-1.598 -28.066,-6.832 -32.543,-0.898 -4.473,5.93 2.891,17.617 6.457,22.711 8.84,12.648 26.277,24.93 23.523,40.898 -4.41,25.586 22.328,51.047 45.258,64.883 l 3.191,1.93 3.703,-0.469 c 5.539,-0.715 10.805,-1.555 15.66,-2.52 l 9.992,-1.977 4.938,-8.211 -12.848,-15.648 15.812,1.898 22.48,3.824 1.168,-11.398 c 2.088,-20.64 -11.045,-43.691 -28.108,-59.472 z" id="path3175" style="fill:#260859"/>
<path d="m 511.441,262.371 0.492,25.715 20.984,8.66 C 522.91,277.422 511.441,262.371 511.441,262.371 z" id="path3177" style="fill:#6b5f91"/>
<path d="m 497.977,381.613 c -8.625,-18.406 -16.074,-34.297 -11.754,-51.027 5.328,-20.621 4.859,-36.199 -1.383,-46.305 -4.797,-7.758 -11.289,-9.492 -12.543,-9.758 l -1.934,-0.414 -13.398,3.125 -0.414,0.145 c -16.215,5.523 -28.109,32.18 -19.945,67.215 2.172,9.352 5.219,16.047 7.902,21.953 2.297,5.055 4.102,9.027 4.949,13.617 -21.887,18.016 -37.902,42.508 -43.219,66.363 -2.129,9.539 -3.754,24.648 -4.445,41.449 l -0.16,3.938 c 46.297,-17.949 85.312,-39.887 108.82,-60.047 0.855,-12.961 -2.055,-27.484 -8.621,-41.949 -1.273,-2.801 -2.57,-5.566 -3.855,-8.305 z" id="path3179" style="fill:#260859"/>
<path d="m 414.879,448.457 c -1.84,8.227 -3.52,22.379 -4.238,39.879 36.367,-14.809 67.703,-31.938 89.738,-48.418 3.441,-14 0.355,-31.008 -6.609,-46.34 -10.18,-22.418 -21.957,-42.605 -16.117,-65.207 10.691,-41.363 -7.207,-45.191 -7.207,-45.191 l -11.039,2.574 c -11.812,4.027 -21.172,26.828 -14.184,56.836 4.383,18.836 12.867,26.305 13.566,41.469 -24.969,19.054 -39.453,44.386 -43.91,64.398 z" id="path3181" style="fill:#ffffff"/>
<polygon points="524.949,339.023 553.559,343.895 540.949,327.73 497.863,316.172 478.828,322.18 505.211,341.258 515.43,341.129 518.512,346.129 " id="polygon3183" style="fill:#42316f"/>
<path d="m 512.535,370.281 c -0.336,-0.113 -1.629,-0.211 -2.324,-0.262 -3.934,-0.297 -11.246,-0.84 -16.621,-8.176 -9.293,-12.676 -18.852,-30.539 -18.945,-30.719 l -1.918,-3.586 16.203,-15.387 3.598,2.605 c 10.914,7.887 31.23,22.578 27.18,51.25 l -0.93,6.586 -6.243,-2.311 z" id="path3185" style="fill:#260859"/>
<path d="m 507.996,273.875 c -8.719,-4.91 -1.633,8.113 -1.633,8.113 l 17.391,8.082 c 0,0 -7.035,-11.285 -15.758,-16.195 z" id="path3187" style="fill:#260859"/>
<path d="m 497.902,358.684 c 5.445,7.418 12.918,5.246 16.504,6.578 3.41,-24.145 -12.492,-37.109 -25.016,-46.168 l -10.02,9.52 c 10e-4,-10e-4 9.341,17.515 18.532,30.07 z" id="path3189" style="fill:#f6a0a6"/>
<polygon points="494.504,330.309 504.152,329.352 500.211,320.141 490.449,320.98 " id="polygon3191" style="fill:#260859"/>
<path d="m 477.652,328.371 c 0.344,-1.586 0.645,-3.094 0.914,-4.574 l -0.824,-32.625 -7.129,-5.695 -4.527,-1.277 -6.68,1.555 c -3.07,1.051 -5.98,3.387 -8.48,6.777 4.602,7.199 9.676,10.191 8.43,22.199 -1.32,12.793 10.867,25.336 16.84,28.691 -0.165,-4.742 0.284,-9.649 1.456,-15.051 z" id="path3193" style="fill:#aaa4c4"/>
<path d="m 515.441,362.789 c 2.875,-0.449 5.562,-0.922 8.031,-1.41 -11.984,-10.055 -24.457,-25.418 -30.348,-40.766 1.129,-0.16 2.285,-0.285 3.461,-0.387 l -12.074,-9.066 -1.441,17.137 20.973,26.426 11.398,8.066 z" id="path3195" style="fill:#260859"/>
<g id="g3197">
<path d="m 520.594,365.984 c -11.988,-10.059 -24.457,-25.414 -30.348,-40.773 14.125,-2 31.789,-0.09 63.312,18.684 2.402,-23.664 -8.188,-64.223 -120.211,-88.75 19.531,31.617 35.305,33.953 32.133,58.875 -2.477,19.504 17.742,40.82 40.18,54.359 5.613,-0.715 10.582,-1.535 14.934,-2.395 z" id="path3199" style="fill:#ffffff"/>
</g>
<polygon points="502.367,321.293 514.109,336.367 528.445,325.293 " id="polygon3201" style="fill:#ffffff"/>
<polygon points="474.906,256.566 459.891,260.328 490.523,302.746 " id="polygon3203" style="fill:#260859"/>
<polygon points="519.023,350.121 506.781,357.129 514.066,363.012 " id="polygon3205" style="fill:#ffffff"/>
<polygon points="515.023,361.746 520.594,365.984 523.348,358.102 " id="polygon3207" style="fill:#ffffff"/>
<g id="g3209">
<path d="m 462.426,239.809 c 11.012,28.695 21.609,50.254 24.316,56.68 3.309,7.875 6.352,11.246 16.789,16.836 6.863,3.676 19.824,10.402 31.555,10.422 -8.766,-21.824 -16.195,-32.91 -38.211,-53.188 -11.328,-10.43 -24.145,-21.758 -34.449,-30.75 z" id="path3211" style="fill:#594c82"/>
</g>
<g id="g3213">
<path d="M 521.926,322.051 C 505.781,318.36 496.391,306.5 495.473,288.668 l -0.387,-7.441 6.039,4.363 c 20.16,14.57 24.406,26.645 25.137,32.543 l 0.633,5.062 -4.969,-1.144 z" id="path3215" style="fill:#260859"/>
</g>
<g id="g3217">
<path d="m 504.73,294.418 c 0.742,14.297 7.789,26.453 23.688,30.094 -0.844,-6.774 -6.383,-17.582 -23.688,-30.094 z" id="path3219" style="fill:#ffffff"/>
</g>
<path d="m 521.02,307.242 -1.926,-0.84 c -0.336,0.574 -0.645,1.184 -0.918,1.844 -1.785,4.355 -1.414,8.625 0.836,9.559 1.629,0.664 3.961,-0.762 5.711,-3.18 l -3.703,-7.383 z" id="path3221" style="fill:#260859"/>
<path d="m 493.77,393.578 c -0.039,-0.094 -0.086,-0.184 -0.125,-0.273 -1.496,2.672 -2.945,5.547 -2.945,5.547 0,0 -19.02,-6.207 -25.148,-7.895 -5.867,10.895 -20.238,38.785 -8.461,75.781 16.672,-8.82 31.375,-17.91 43.289,-26.82 3.44,-14 0.354,-31.008 -6.61,-46.34 z" id="path3223" style="fill:#6b5f91"/>
<path d="m 282.871,437.34 c 22.973,18.836 59.043,39.004 101.59,55.91 v -12.582 c -45.699,-18.512 -81.504,-40.121 -101.59,-58.043 v 14.715 z" id="path3225" style="fill:#260859"/>
<path d="m 406.188,493.156 c 42.293,-16.809 78.164,-36.848 101.102,-55.594 v -14.703 c -19.926,17.703 -55.094,38.953 -99.949,57.25 l -1.153,13.047 z" id="path3227" style="fill:#260859"/>
<path d="m 403.402,148.789 c -0.047,-0.816 0.004,-1.645 0.133,-2.477 1.031,-5.996 6.719,-10.016 12.719,-8.984 5.887,1.008 9.855,6.508 9.039,12.367 -7.613,-1.144 -14.949,-1.629 -21.891,-0.906 z" id="path3229" style="fill:#ffffff"/>
<path d="m 365.43,162.512 c 6.816,9.141 17.707,15.051 29.973,15.051 12.27,0 23.16,-5.91 29.973,-15.051 H 365.43 z" id="path3231" style="fill:#260859"/>
<path d="m 416.684,170.902 c -4.809,-1.645 -12.762,-2.707 -21.766,-2.707 -8.594,0 -16.242,0.969 -21.102,2.484 6.102,4.32 13.543,6.883 21.586,6.883 7.907,0 15.239,-2.472 21.282,-6.66 z" id="path3233" style="fill:#b30838"/>
<path d="m 359.141,162.512 0.438,1.328 c 3.145,9.539 4.848,12.016 8.293,12.043 h 0.027 c 3.363,0 5.168,-2.57 8.371,-11.938 l 0.496,-1.434 h -17.625 z" id="path3235" style="fill:#260859"/>
<g id="g3237">
<path d="m 363.828,162.43 c 0,0 2.953,8.965 4.055,8.973 1.102,0.012 4.156,-8.914 4.156,-8.914 l -8.211,-0.059 z" id="path3239" style="fill:#ffffff"/>
</g>
<path d="m 414.266,162.512 0.438,1.328 c 3.145,9.539 4.848,12.016 8.293,12.043 h 0.027 c 3.363,0 5.168,-2.57 8.375,-11.938 l 0.492,-1.434 h -17.625 z" id="path3241" style="fill:#260859"/>
<g id="g3243">
<path d="m 418.953,162.43 c 0,0 2.953,8.965 4.055,8.973 1.105,0.012 4.156,-8.914 4.156,-8.914 l -8.211,-0.059 z" id="path3245" style="fill:#ffffff"/>
</g>
<path d="m 411.148,196.828 c 0,4.668 -7.133,8.449 -15.926,8.449 -8.797,0 -15.93,-3.781 -15.93,-8.449 0,-4.668 7.133,-8.449 15.93,-8.449 8.794,0 15.926,3.781 15.926,8.449 z" id="path3247" style="fill:#ffef6f"/>
<path d="m 400.66,196.828 c 0,1.594 -2.434,2.883 -5.438,2.883 -3,0 -5.438,-1.289 -5.438,-2.883 0,-1.598 2.438,-2.891 5.438,-2.891 3.005,10e-4 5.438,1.293 5.438,2.891 z" id="path3249" style="fill:#7f3f98"/>
<path d="m 410.676,141.695 c 0.078,2.168 1.301,3.871 2.711,3.816 1.422,-0.066 2.496,-1.863 2.402,-4.031 -0.082,-2.176 -1.309,-3.895 -2.727,-3.832 -1.414,0.059 -2.492,1.868 -2.386,4.047 z" id="path3251" style="fill:#260859"/>
<path d="m 386.906,148.789 c 0.051,-0.816 -0.004,-1.645 -0.133,-2.477 -1.027,-5.996 -6.715,-10.016 -12.715,-8.984 -5.887,1.008 -9.855,6.508 -9.039,12.367 7.614,-1.144 14.95,-1.629 21.887,-0.906 z" id="path3253" style="fill:#ffffff"/>
<path d="m 379.633,141.695 c -0.082,2.168 -1.301,3.871 -2.707,3.816 -1.426,-0.066 -2.504,-1.863 -2.402,-4.031 0.082,-2.176 1.309,-3.895 2.723,-3.832 1.417,0.059 2.495,1.868 2.386,4.047 z" id="path3255" style="fill:#260859"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 39 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -1,49 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="197.72693mm"
height="190.30197mm"
viewBox="0 0 197.72693 190.30197"
version="1.1"
id="svg5"
xml:space="preserve"
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
sodipodi:docname="Toyhouse.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.5"
inkscape:cx="969"
inkscape:cy="450"
inkscape:window-width="1920"
inkscape:window-height="1009"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" /><defs
id="defs2" /><g
inkscape:label="Capa 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(6.2923729,-54.858845)"><g
id="g1824"><path
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M -6.2923729,154.22213 92.377447,54.858845 121.69053,84.093271 V 69.076617 h 31.14565 v 46.273543 l 38.59837,38.59837 h -32.36923 v 91.21228 H 25.02776 v -91.10105 z"
id="path428"
sodipodi:nodetypes="cccccccccccc" /><path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 54.338047,170.6337 v 8.39821 H 70.80075 v 53.94872 h 11.012215 v -54.00434 h 17.920872 c 0,0 2.72e-4,-0.23357 2.72e-4,2.12606 v 51.91208 h 8.966631 v -29.12162 h 26.03469 v 28.76768 h 9.28125 v -61.66526 h -8.96663 v 25.72008 h -26.42796 l 0.22247,-26.09986 z"
id="path526"
sodipodi:nodetypes="cccccccscccccccccccc" /></g></g></svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 248 204">
<path fill="#1d9bf0" d="M221.95 51.29c.15 2.17.15 4.34.15 6.53 0 66.73-50.8 143.69-143.69 143.69v-.04c-27.44.04-54.31-7.82-77.41-22.64 3.99.48 8 .72 12.02.73 22.74.02 44.83-7.61 62.72-21.66-21.61-.41-40.56-14.5-47.18-35.07 7.57 1.46 15.37 1.16 22.8-.87-23.56-4.76-40.51-25.46-40.51-49.5v-.64c7.02 3.91 14.88 6.08 22.92 6.32C11.58 63.31 4.74 33.79 18.14 10.71c25.64 31.55 63.47 50.73 104.08 52.76-4.07-17.54 1.49-35.92 14.61-48.25 20.34-19.12 52.33-18.14 71.45 2.19 11.31-2.23 22.15-6.38 32.07-12.26-3.77 11.69-11.66 21.62-22.2 27.93 10.01-1.18 19.79-3.86 29-7.95-6.78 10.16-15.32 19.01-25.2 26.16z"/>
</svg>

Before

Width:  |  Height:  |  Size: 732 B

View File

@@ -6,14 +6,14 @@
"request": "launch",
"type": "dart",
"flutterMode": "debug",
"program": "mobile/apps/auth/lib/main.dart",
"program": "auth/lib/main.dart",
"args": ["--dart-define", "endpoint=http://localhost:8080"]
},
{
"name": "Auth Android Dev",
"request": "launch",
"type": "dart",
"program": "mobile/apps/auth/lib/main.dart",
"program": "auth/lib/main.dart",
"args": [
"--dart-define",
"endpoint=http://192.168.1.3:8080",
@@ -25,21 +25,21 @@
"name": "Auth iOS Dev",
"request": "launch",
"type": "dart",
"program": "mobile/apps/auth/lib/main.dart",
"program": "auth/lib/main.dart",
"args": ["--dart-define", "endpoint=http://192.168.1.30:8080"]
},
{
"name": "Auth iOS Prod",
"request": "launch",
"type": "dart",
"program": "mobile/apps/auth/lib/main.dart",
"program": "auth/lib/main.dart",
"args": ["--target", "lib/main.dart"]
},
{
"name": "Auth Android Prod",
"request": "launch",
"type": "dart",
"program": "mobile/apps/auth/lib/main.dart",
"program": "auth/lib/main.dart",
"args": ["--target", "lib/main.dart", "--flavor", "independent"]
}
]

View File

@@ -1,32 +0,0 @@
#
# Generated file, do not edit.
#
import lldb
def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict):
"""Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages."""
base = frame.register["x0"].GetValueAsAddress()
page_len = frame.register["x1"].GetValueAsUnsigned()
# Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the
# first page to see if handled it correctly. This makes diagnosing
# misconfiguration (e.g. missing breakpoint) easier.
data = bytearray(page_len)
data[0:8] = b'IHELPED!'
error = lldb.SBError()
frame.GetThread().GetProcess().WriteMemory(base, data, error)
if not error.Success():
print(f'Failed to write into {base}[+{page_len}]', error)
return
def __lldb_init_module(debugger: lldb.SBDebugger, _):
target = debugger.GetDummyTarget()
# Caveat: must use BreakpointCreateByRegEx here and not
# BreakpointCreateByName. For some reasons callback function does not
# get carried over from dummy target for the later.
bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$")
bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__))
bp.SetAutoContinue(True)
print("-- LLDB integration loaded --")

View File

@@ -1,5 +0,0 @@
#
# Generated file, do not edit.
#
command script import --relative-to-command-file flutter_lldb_helper.py

View File

@@ -3,6 +3,7 @@ PODS:
- Flutter
- connectivity_plus (0.0.1):
- Flutter
- FlutterMacOS
- cupertino_http (0.0.1):
- Flutter
- FlutterMacOS
@@ -60,12 +61,13 @@ PODS:
- Flutter
- flutter_local_notifications (0.0.1):
- Flutter
- flutter_native_splash (2.4.3):
- flutter_native_splash (0.0.1):
- Flutter
- flutter_secure_storage (6.0.0):
- Flutter
- fluttertoast (0.0.2):
- Flutter
- Toast
- local_auth_darwin (0.0.1):
- Flutter
- FlutterMacOS
@@ -85,9 +87,9 @@ PODS:
- qr_code_scanner (0.2.0):
- Flutter
- MTBBarcodeScanner
- SDWebImage (5.21.1):
- SDWebImage/Core (= 5.21.1)
- SDWebImage/Core (5.21.1)
- SDWebImage (5.21.0):
- SDWebImage/Core (= 5.21.0)
- SDWebImage/Core (5.21.0)
- Sentry/HybridSDK (8.46.0)
- sentry_flutter (8.14.2):
- Flutter
@@ -100,38 +102,35 @@ PODS:
- FlutterMacOS
- sodium_libs (2.2.1):
- Flutter
- sqflite_darwin (0.0.4):
- sqflite (0.0.3):
- Flutter
- FlutterMacOS
- sqlite3 (3.50.2):
- sqlite3/common (= 3.50.2)
- sqlite3/common (3.50.2)
- sqlite3/dbstatvtab (3.50.2):
- "sqlite3 (3.46.1+1)":
- "sqlite3/common (= 3.46.1+1)"
- "sqlite3/common (3.46.1+1)"
- "sqlite3/dbstatvtab (3.46.1+1)":
- sqlite3/common
- sqlite3/fts5 (3.50.2):
- "sqlite3/fts5 (3.46.1+1)":
- sqlite3/common
- sqlite3/math (3.50.2):
- "sqlite3/perf-threadsafe (3.46.1+1)":
- sqlite3/common
- sqlite3/perf-threadsafe (3.50.2):
- sqlite3/common
- sqlite3/rtree (3.50.2):
- "sqlite3/rtree (3.46.1+1)":
- sqlite3/common
- sqlite3_flutter_libs (0.0.1):
- Flutter
- FlutterMacOS
- sqlite3 (~> 3.50.1)
- "sqlite3 (~> 3.46.0+1)"
- sqlite3/dbstatvtab
- sqlite3/fts5
- sqlite3/math
- sqlite3/perf-threadsafe
- sqlite3/rtree
- SwiftyGif (5.4.5)
- Toast (4.1.1)
- url_launcher_ios (0.0.1):
- Flutter
DEPENDENCIES:
- app_links (from `.symlinks/plugins/app_links/ios`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/darwin`)
- cupertino_http (from `.symlinks/plugins/cupertino_http/darwin`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- file_picker (from `.symlinks/plugins/file_picker/ios`)
@@ -156,8 +155,8 @@ DEPENDENCIES:
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sodium_libs (from `.symlinks/plugins/sodium_libs/ios`)
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
- sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/darwin`)
- 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`)
SPEC REPOS:
@@ -170,12 +169,13 @@ SPEC REPOS:
- Sentry
- sqlite3
- SwiftyGif
- Toast
EXTERNAL SOURCES:
app_links:
:path: ".symlinks/plugins/app_links/ios"
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/ios"
:path: ".symlinks/plugins/connectivity_plus/darwin"
cupertino_http:
:path: ".symlinks/plugins/cupertino_http/darwin"
device_info_plus:
@@ -224,51 +224,52 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
sodium_libs:
:path: ".symlinks/plugins/sodium_libs/ios"
sqflite_darwin:
:path: ".symlinks/plugins/sqflite_darwin/darwin"
sqflite:
:path: ".symlinks/plugins/sqflite/darwin"
sqlite3_flutter_libs:
:path: ".symlinks/plugins/sqlite3_flutter_libs/darwin"
:path: ".symlinks/plugins/sqlite3_flutter_libs/ios"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"
SPEC CHECKSUMS:
app_links: f3e17e4ee5e357b39d8b95290a9b2c299fca71c6
connectivity_plus: 2a701ffec2c0ae28a48cf7540e279787e77c447d
cupertino_http: 947a233f40cfea55167a49f2facc18434ea117ba
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
app_links: 3da4c36b46cac3bf24eb897f1a6ce80bda109874
connectivity_plus: 3f6c9057f4cd64198dc826edfb0542892f825343
cupertino_http: 94ac07f5ff090b8effa6c5e2c47871d48ab7c86c
device_info_plus: 335f3ce08d2e174b9fdc3db3db0f4e3b1f66bd89
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
file_picker: b159e0c068aef54932bb15dc9fd1571818edaf49
file_saver: 503e386464dbe118f630e17b4c2e1190fa0cf808
fk_user_agent: 1f47ec39291e8372b1d692b50084b0d54103c545
file_picker: 9b3292d7c8bc68c8a7bf8eb78f730e49c8efc517
file_saver: 6cdbcddd690cb02b0c1a0c225b37cd805c2bf8b6
fk_user_agent: 137145b086229251761678fe034da53753f4ce59
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_email_sender: e03bdda7637bcd3539bfe718fddd980e9508efaa
flutter_inappwebview_ios: 6f63631e2c62a7c350263b13fa5427aedefe81d4
flutter_local_authentication: 1172a4dd88f6306dadce067454e2c4caf07977bb
flutter_local_notifications: df98d66e515e1ca797af436137b4459b160ad8c9
flutter_native_splash: df59bb2e1421aa0282cb2e95618af4dcb0c56c29
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
fluttertoast: 21eecd6935e7064cc1fcb733a4c5a428f3f24f0f
local_auth_darwin: fa4b06454df7df8e97c18d7ee55151c57e7af0de
move_to_background: 39a5b79b26d577b0372cbe8a8c55e7aa9fcd3a2d
flutter_email_sender: 2397f5e84aaacfb61af569637a963e7c687858d8
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
flutter_local_authentication: 989278c681612f1ee0e36019e149137f114b9d7f
flutter_local_notifications: ad39620c743ea4c15127860f4b5641649a988100
flutter_native_splash: 35ddbc7228eafcb3969dcc5f1fbbe27c1145a4f0
flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
fluttertoast: 76fea30fcf04176325f6864c87306927bd7d2038
local_auth_darwin: 553ce4f9b16d3fdfeafce9cf042e7c9f77c1c391
move_to_background: 155f7bfbd34d43ad847cb630d2d2d87c17199710
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
objective_c: 77e887b5ba1827970907e10e832eec1683f3431d
objective_c: 89e720c30d716b036faf9c9684022048eee1eee2
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
privacy_screen: 1a131c052ceb3c3659934b003b0d397c2381a24e
qr_code_scanner: bb67d64904c3b9658ada8c402e8b4d406d5d796e
SDWebImage: f29024626962457f3470184232766516dee8dfea
package_info_plus: 580e9a5f1b6ca5594e7c9ed5f92d1dfb2a66b5e1
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
privacy_screen: 3159a541f5d3a31bea916cfd4e58f9dc722b3fd4
qr_code_scanner: d77f94ecc9abf96d9b9b8fc04ef13f611e5a147a
SDWebImage: f84b0feeb08d2d11e6a9b843cb06d75ebf5b8868
Sentry: da60d980b197a46db0b35ea12cb8f39af48d8854
sentry_flutter: 2df8b0aab7e4aba81261c230cbea31c82a62dd1b
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
sodium_libs: 1faae17af662384acbd13e41867a0008cd2e2318
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
sqlite3: 3e82a2daae39ba3b41ae6ee84a130494585460fc
sqlite3_flutter_libs: 2c48c4ee7217fd653251975e43412250d5bcbbe2
sentry_flutter: 27892878729f42701297c628eb90e7c6529f3684
share_plus: 011d6fb4f9d2576b83179a3a5c5e323202cdabcf
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
sodium_libs: 6c6d0e83f4ee427c6464caa1f1bdc2abf3ca0b7f
sqflite: c35dad70033b8862124f8337cc994a809fcd9fa3
sqlite3: 0bb0e6389d824e40296f531b858a2a0b71c0d2fb
sqlite3_flutter_libs: 9379996d65aa23dcda7585a5b58766cebe0aa042
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
PODFILE CHECKSUM: 78f002751f1a8f65042b8da97902ba4124271c5a

View File

@@ -26,7 +26,6 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
@@ -44,13 +43,11 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
enableGPUValidationMode = "1"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">

View File

@@ -2,20 +2,20 @@ import 'dart:async';
import 'dart:io';
import 'package:adaptive_theme/adaptive_theme.dart';
import 'package:ente_accounts/services/user_service.dart';
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/core/event_bus.dart';
import 'package:ente_auth/ente_theme_data.dart';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/events/signed_in_event.dart';
import 'package:ente_auth/events/signed_out_event.dart';
import "package:ente_auth/l10n/l10n.dart";
import 'package:ente_auth/locale.dart';
import "package:ente_auth/onboarding/view/onboarding_page.dart";
import 'package:ente_auth/services/authenticator_service.dart';
import 'package:ente_auth/services/update_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/settings/app_update_dialog.dart';
import 'package:ente_events/event_bus.dart';
import 'package:ente_events/models/signed_in_event.dart';
import 'package:ente_events/models/signed_out_event.dart';
import 'package:flutter/foundation.dart';
import "package:flutter/material.dart";
import 'package:flutter_localizations/flutter_localizations.dart';
@@ -40,7 +40,7 @@ class _AppState extends State<App>
late StreamSubscription<SignedOutEvent> _signedOutEvent;
late StreamSubscription<SignedInEvent> _signedInEvent;
Locale? locale;
void setLocale(Locale newLocale) {
setLocale(Locale newLocale) {
setState(() {
locale = newLocale;
});

View File

@@ -27,8 +27,10 @@ Future<void> bootstrap(FutureOr<Widget> Function() builder) async {
await runZonedGuarded(
() async {
Bloc.observer = AppBlocObserver();
runApp(await builder());
await BlocOverrides.runZoned(
() async => runApp(await builder()),
blocObserver: AppBlocObserver(),
);
},
(error, stackTrace) => log(error.toString(), stackTrace: stackTrace),
);

View File

@@ -1,36 +1,95 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io' as io;
import 'dart:typed_data';
import 'package:ente_base/models/database.dart';
import 'package:ente_configuration/base_configuration.dart';
import 'package:bip39/bip39.dart' as bip39;
import 'package:ente_auth/core/constants.dart';
import 'package:ente_auth/core/event_bus.dart';
import 'package:ente_auth/events/endpoint_updated_event.dart';
import 'package:ente_auth/events/signed_in_event.dart';
import 'package:ente_auth/events/signed_out_event.dart';
import 'package:ente_auth/models/key_attributes.dart';
import 'package:ente_auth/models/key_gen_result.dart';
import 'package:ente_auth/models/private_key_attributes.dart';
import 'package:ente_auth/store/authenticator_db.dart';
import 'package:ente_auth/utils/directory_utils.dart';
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:logging/logging.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
import 'package:tuple/tuple.dart';
class Configuration extends BaseConfiguration {
class Configuration {
Configuration._privateConstructor();
static final Configuration instance = Configuration._privateConstructor();
static const endpoint = String.fromEnvironment(
"endpoint",
defaultValue: kDefaultProductionEndpoint,
);
static const emailKey = "email";
static const keyAttributesKey = "key_attributes";
static const lastTempFolderClearTimeKey = "last_temp_folder_clear_time";
static const keyKey = "key";
static const secretKeyKey = "secret_key";
static const authSecretKeyKey = "auth_secret_key";
static const offlineAuthSecretKey = "offline_auth_secret_key";
static const tokenKey = "token";
static const encryptedTokenKey = "encrypted_token";
static const userIDKey = "user_id";
static const hasMigratedSecureStorageKey = "has_migrated_secure_storage";
static const hasOptedForOfflineModeKey = "has_opted_for_offline_mode";
static const endPointKey = "endpoint";
final List<String> onlineSecureKeys = [
keyKey,
secretKeyKey,
authSecretKeyKey,
];
final kTempFolderDeletionTimeBuffer = const Duration(days: 1).inMicroseconds;
static final _logger = Logger("Configuration");
String? _cachedToken;
late SharedPreferences _preferences;
String? _key;
String? _secretKey;
String? _authSecretKey;
String? _offlineAuthKey;
late FlutterSecureStorage _secureStorage;
late String _tempDirectory;
@override
Future<void> init(List<EnteBaseDatabase> dbs) async {
await super.init(dbs);
String? _volatilePassword;
Future<void> init() async {
_preferences = await SharedPreferences.getInstance();
sqfliteFfiInit();
_secureStorage = const FlutterSecureStorage(
iOptions: IOSOptions(
accessibility: KeychainAccessibility.first_unlock_this_device,
),
);
sqfliteFfiInit();
_tempDirectory = (await DirectoryUtils.getDirectoryForInit()).path;
final tempDirectory = io.Directory(_tempDirectory);
try {
final currentTime = DateTime.now().microsecondsSinceEpoch;
if (tempDirectory.existsSync() &&
(_preferences.getInt(lastTempFolderClearTimeKey) ?? 0) <
(currentTime - kTempFolderDeletionTimeBuffer)) {
await tempDirectory.delete(recursive: true);
await _preferences.setInt(lastTempFolderClearTimeKey, currentTime);
_logger.info("Cleared temp folder");
} else {
_logger.info("Skipping temp folder clear");
}
} catch (e) {
_logger.warning(e);
}
tempDirectory.createSync(recursive: true);
await _initOnlineAccount();
await _initOfflineAccount();
}
@@ -40,10 +99,303 @@ class Configuration extends BaseConfiguration {
);
}
@override
Future<void> _initOnlineAccount() async {
if (!_preferences.containsKey(tokenKey)) {
for (final key in onlineSecureKeys) {
unawaited(
_secureStorage.delete(
key: key,
),
);
}
} else {
_key = await _secureStorage.read(
key: keyKey,
);
_secretKey = await _secureStorage.read(
key: secretKeyKey,
);
_authSecretKey = await _secureStorage.read(
key: authSecretKeyKey,
);
if (_key == null) {
await logout(autoLogout: true);
}
}
}
Future<void> logout({bool autoLogout = false}) async {
await _preferences.clear();
for (String key in onlineSecureKeys) {
await _secureStorage.delete(
key: key,
);
}
await AuthenticatorDB.instance.clearTable();
_key = null;
_cachedToken = null;
_secretKey = null;
_authSecretKey = null;
await super.logout();
Bus.instance.fire(SignedOutEvent());
}
Future<KeyGenResult> generateKey(String password) async {
// Create a master key
final masterKey = CryptoUtil.generateKey();
// Create a recovery key
final recoveryKey = CryptoUtil.generateKey();
// Encrypt master key and recovery key with each other
final encryptedMasterKey = CryptoUtil.encryptSync(masterKey, recoveryKey);
final encryptedRecoveryKey = CryptoUtil.encryptSync(recoveryKey, masterKey);
// Derive a key from the password that will be used to encrypt and
// decrypt the master key
final kekSalt = CryptoUtil.getSaltToDeriveKey();
final derivedKeyResult = await CryptoUtil.deriveSensitiveKey(
utf8.encode(password),
kekSalt,
);
final loginKey = await CryptoUtil.deriveLoginKey(derivedKeyResult.key);
// Encrypt the key with this derived key
final encryptedKeyData =
CryptoUtil.encryptSync(masterKey, derivedKeyResult.key);
// Generate a public-private keypair and encrypt the latter
final keyPair = CryptoUtil.generateKeyPair();
final encryptedSecretKeyData =
CryptoUtil.encryptSync(keyPair.secretKey.extractBytes(), masterKey);
final attributes = KeyAttributes(
CryptoUtil.bin2base64(kekSalt),
CryptoUtil.bin2base64(encryptedKeyData.encryptedData!),
CryptoUtil.bin2base64(encryptedKeyData.nonce!),
CryptoUtil.bin2base64(keyPair.publicKey),
CryptoUtil.bin2base64(encryptedSecretKeyData.encryptedData!),
CryptoUtil.bin2base64(encryptedSecretKeyData.nonce!),
derivedKeyResult.memLimit,
derivedKeyResult.opsLimit,
CryptoUtil.bin2base64(encryptedMasterKey.encryptedData!),
CryptoUtil.bin2base64(encryptedMasterKey.nonce!),
CryptoUtil.bin2base64(encryptedRecoveryKey.encryptedData!),
CryptoUtil.bin2base64(encryptedRecoveryKey.nonce!),
);
final privateAttributes = PrivateKeyAttributes(
CryptoUtil.bin2base64(masterKey),
CryptoUtil.bin2hex(recoveryKey),
CryptoUtil.bin2base64(keyPair.secretKey.extractBytes()),
);
return KeyGenResult(attributes, privateAttributes, loginKey);
}
Future<Tuple2<KeyAttributes, Uint8List>> getAttributesForNewPassword(
String password,
) async {
// Get master key
final masterKey = getKey();
// Derive a key from the password that will be used to encrypt and
// decrypt the master key
final kekSalt = CryptoUtil.getSaltToDeriveKey();
final derivedKeyResult = await CryptoUtil.deriveSensitiveKey(
utf8.encode(password),
kekSalt,
);
final loginKey = await CryptoUtil.deriveLoginKey(derivedKeyResult.key);
// Encrypt the key with this derived key
final encryptedKeyData =
CryptoUtil.encryptSync(masterKey!, derivedKeyResult.key);
final existingAttributes = getKeyAttributes();
final updatedAttributes = existingAttributes!.copyWith(
kekSalt: CryptoUtil.bin2base64(kekSalt),
encryptedKey: CryptoUtil.bin2base64(encryptedKeyData.encryptedData!),
keyDecryptionNonce: CryptoUtil.bin2base64(encryptedKeyData.nonce!),
memLimit: derivedKeyResult.memLimit,
opsLimit: derivedKeyResult.opsLimit,
);
return Tuple2(updatedAttributes, loginKey);
}
// decryptSecretsAndGetLoginKey decrypts the master key and recovery key
// with the given password and save them in local secure storage.
// This method also returns the keyEncKey that can be used for performing
// SRP setup for existing users.
Future<Uint8List> decryptSecretsAndGetKeyEncKey(
String password,
KeyAttributes attributes, {
Uint8List? keyEncryptionKey,
}) async {
_logger.info('Start decryptAndSaveSecrets');
keyEncryptionKey ??= await CryptoUtil.deriveKey(
utf8.encode(password),
CryptoUtil.base642bin(attributes.kekSalt),
attributes.memLimit,
attributes.opsLimit,
);
_logger.info('user-key done');
Uint8List key;
try {
key = CryptoUtil.decryptSync(
CryptoUtil.base642bin(attributes.encryptedKey),
keyEncryptionKey,
CryptoUtil.base642bin(attributes.keyDecryptionNonce),
);
} catch (e) {
_logger.severe('master-key failed, incorrect password?', e);
throw Exception("Incorrect password");
}
_logger.info("master-key done");
await setKey(CryptoUtil.bin2base64(key));
final secretKey = CryptoUtil.decryptSync(
CryptoUtil.base642bin(attributes.encryptedSecretKey),
key,
CryptoUtil.base642bin(attributes.secretKeyDecryptionNonce),
);
_logger.info("secret-key done");
await setSecretKey(CryptoUtil.bin2base64(secretKey));
final token = CryptoUtil.openSealSync(
CryptoUtil.base642bin(getEncryptedToken()!),
CryptoUtil.base642bin(attributes.publicKey),
secretKey,
);
_logger.info('appToken done');
await setToken(
CryptoUtil.bin2base64(token, urlSafe: true),
);
return keyEncryptionKey;
}
Future<void> recover(String recoveryKey) async {
// check if user has entered mnemonic code
if (recoveryKey.contains(' ')) {
final split = recoveryKey.split(' ');
if (split.length != mnemonicKeyWordCount) {
String wordThatIsFollowedByEmptySpaceInSplit = '';
for (int i = 0; i < split.length; i++) {
String word = split[i];
if (word.isEmpty) {
wordThatIsFollowedByEmptySpaceInSplit =
'\n\nExtra space after word at position $i';
break;
}
}
throw AssertionError(
'\nRecovery code should have $mnemonicKeyWordCount words, '
'found ${split.length} words instead.$wordThatIsFollowedByEmptySpaceInSplit',
);
}
recoveryKey = bip39.mnemonicToEntropy(recoveryKey);
}
final attributes = getKeyAttributes();
Uint8List masterKey;
try {
masterKey = await CryptoUtil.decrypt(
CryptoUtil.base642bin(attributes!.masterKeyEncryptedWithRecoveryKey),
CryptoUtil.hex2bin(recoveryKey),
CryptoUtil.base642bin(attributes.masterKeyDecryptionNonce),
);
} catch (e) {
_logger.severe(e);
rethrow;
}
await setKey(CryptoUtil.bin2base64(masterKey));
final secretKey = CryptoUtil.decryptSync(
CryptoUtil.base642bin(attributes.encryptedSecretKey),
masterKey,
CryptoUtil.base642bin(attributes.secretKeyDecryptionNonce),
);
await setSecretKey(CryptoUtil.bin2base64(secretKey));
final token = CryptoUtil.openSealSync(
CryptoUtil.base642bin(getEncryptedToken()!),
CryptoUtil.base642bin(attributes.publicKey),
secretKey,
);
await setToken(
CryptoUtil.bin2base64(token, urlSafe: true),
);
}
String getHttpEndpoint() {
return _preferences.getString(endPointKey) ?? endpoint;
}
Future<void> setHttpEndpoint(String endpoint) async {
await _preferences.setString(endPointKey, endpoint);
Bus.instance.fire(EndpointUpdatedEvent());
}
String? getToken() {
_cachedToken ??= _preferences.getString(tokenKey);
return _cachedToken;
}
bool isLoggedIn() {
return getToken() != null;
}
Future<void> setToken(String token) async {
_cachedToken = token;
await _preferences.setString(tokenKey, token);
Bus.instance.fire(SignedInEvent());
}
Future<void> setEncryptedToken(String encryptedToken) async {
await _preferences.setString(encryptedTokenKey, encryptedToken);
}
String? getEncryptedToken() {
return _preferences.getString(encryptedTokenKey);
}
String? getEmail() {
return _preferences.getString(emailKey);
}
Future<void> setEmail(String email) async {
await _preferences.setString(emailKey, email);
}
int? getUserID() {
return _preferences.getInt(userIDKey);
}
Future<void> setUserID(int userID) async {
await _preferences.setInt(userIDKey, userID);
}
Future<void> setKeyAttributes(KeyAttributes attributes) async {
await _preferences.setString(keyAttributesKey, attributes.toJson());
}
KeyAttributes? getKeyAttributes() {
final jsonValue = _preferences.getString(keyAttributesKey);
if (jsonValue == null) {
return null;
} else {
return KeyAttributes.fromJson(jsonValue);
}
}
Future<void> setKey(String key) async {
_key = key;
await _secureStorage.write(
key: keyKey,
value: key,
);
}
Future<void> setSecretKey(String? secretKey) async {
_secretKey = secretKey;
await _secureStorage.write(
key: secretKeyKey,
value: secretKey,
);
}
Future<void> setAuthSecretKey(String? authSecretKey) async {
@@ -54,6 +406,14 @@ class Configuration extends BaseConfiguration {
);
}
Uint8List? getKey() {
return _key == null ? null : CryptoUtil.base642bin(_key!);
}
Uint8List? getSecretKey() {
return _secretKey == null ? null : CryptoUtil.base642bin(_secretKey!);
}
Uint8List? getAuthSecretKey() {
return _authSecretKey == null
? null
@@ -66,6 +426,24 @@ class Configuration extends BaseConfiguration {
: CryptoUtil.base642bin(_offlineAuthKey!);
}
Uint8List getRecoveryKey() {
final keyAttributes = getKeyAttributes()!;
return CryptoUtil.decryptSync(
CryptoUtil.base642bin(keyAttributes.recoveryKeyEncryptedWithMasterKey),
getKey()!,
CryptoUtil.base642bin(keyAttributes.recoveryKeyDecryptionNonce),
);
}
// Caution: This directory is cleared on app start
String getTempDirectory() {
return _tempDirectory;
}
bool hasConfiguredAccount() {
return getToken() != null && _key != null;
}
bool hasOptedForOfflineMode() {
return _preferences.getBool(hasOptedForOfflineModeKey) ?? false;
}
@@ -86,4 +464,16 @@ class Configuration extends BaseConfiguration {
}
await _preferences.setBool(hasOptedForOfflineModeKey, true);
}
void setVolatilePassword(String volatilePassword) {
_volatilePassword = volatilePassword;
}
void resetVolatilePassword() {
_volatilePassword = null;
}
String? getVolatilePassword() {
return _volatilePassword;
}
}

View File

@@ -3,12 +3,13 @@ import 'dart:collection';
import 'dart:core';
import 'dart:io';
import 'package:ente_logging/tunneled_transport.dart';
import 'package:ente_auth/core/logging/tunneled_transport.dart';
import 'package:ente_auth/models/typedefs.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:http/http.dart' as http;
import 'package:intl/intl.dart';
import 'package:logging/logging.dart' as log;
import 'package:logging/logging.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
@@ -16,9 +17,6 @@ import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:uuid/uuid.dart';
/// Type definition for functions that may return a value or Future
typedef FutureOrVoidCallback = dynamic Function();
extension SuperString on String {
Iterable<String> chunked(int chunkSize) sync* {
var start = 0;
@@ -36,7 +34,7 @@ extension SuperString on String {
}
}
extension SuperLogRecord on log.LogRecord {
extension SuperLogRecord on LogRecord {
String toPrettyString([String? extraLines]) {
final header = "[$loggerName] [$level] [$time]";
@@ -138,7 +136,7 @@ class LogConfig {
class SuperLogging {
/// The logger for SuperLogging
static final $ = log.Logger('ente_logging');
static final $ = Logger('ente_logging');
/// The current super logging configuration
static late LogConfig config;
@@ -170,8 +168,8 @@ class SuperLogging {
setupSentry().ignore();
}
log.Logger.root.level = log.Level.ALL;
log.Logger.root.onRecord.listen(onLogRecord);
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen(onLogRecord);
if (!enable) {
$.info("detected debug mode; sentry & file logging disabled.");
@@ -226,7 +224,7 @@ class SuperLogging {
static String _lastExtraLines = '';
static Future onLogRecord(log.LogRecord rec) async {
static Future onLogRecord(LogRecord rec) async {
// log misc info if it changed
String? extraLines = "app version: '$appVersion'\n";
if (extraLines != _lastExtraLines) {

View File

@@ -1,13 +1,14 @@
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:ente_configuration/base_configuration.dart';
import 'package:ente_events/event_bus.dart';
import 'package:ente_events/models/endpoint_updated_event.dart';
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/core/event_bus.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:flutter/foundation.dart';
import 'package:native_dio_adapter/native_dio_adapter.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:ua_client_hints/ua_client_hints.dart';
import 'package:uuid/uuid.dart';
int kConnectTimeout = 15000;
@@ -16,19 +17,20 @@ class Network {
late Dio _dio;
late Dio _enteDio;
Future<void> init(BaseConfiguration configuration) async {
final String ua = await userAgent();
final packageInfo = await PackageInfo.fromPlatform();
final version = packageInfo.version;
final packageName = packageInfo.packageName;
final endpoint = configuration.getHttpEndpoint();
final isMobile = Platform.isAndroid || Platform.isIOS;
Future<void> init() async {
if (PlatformUtil.isMobile()) await FkUserAgent.init();
final packageInfo = await PackageInfoUtil().getPackageInfo();
final version = PackageInfoUtil().getVersion(packageInfo);
final packageName = PackageInfoUtil().getPackageName(packageInfo);
final endpoint = Configuration.instance.getHttpEndpoint();
_dio = Dio(
BaseOptions(
connectTimeout: Duration(milliseconds: kConnectTimeout),
headers: {
HttpHeaders.userAgentHeader: isMobile ? ua : Platform.operatingSystem,
HttpHeaders.userAgentHeader: PlatformUtil.isMobile()
? FkUserAgent.userAgent
: Platform.operatingSystem,
'X-Client-Version': version,
'X-Client-Package': packageName,
},
@@ -40,8 +42,8 @@ class Network {
baseUrl: endpoint,
connectTimeout: Duration(milliseconds: kConnectTimeout),
headers: {
if (isMobile)
HttpHeaders.userAgentHeader: ua
if (PlatformUtil.isMobile())
HttpHeaders.userAgentHeader: FkUserAgent.userAgent
else
HttpHeaders.userAgentHeader: Platform.operatingSystem,
'X-Client-Version': version,
@@ -53,12 +55,12 @@ class Network {
_dio.httpClientAdapter = NativeAdapter();
_enteDio.httpClientAdapter = NativeAdapter();
_setupInterceptors(configuration);
_setupInterceptors(endpoint);
Bus.instance.on<EndpointUpdatedEvent>().listen((event) {
final endpoint = configuration.getHttpEndpoint();
final endpoint = Configuration.instance.getHttpEndpoint();
_enteDio.options.baseUrl = endpoint;
_setupInterceptors(configuration);
_setupInterceptors(endpoint);
});
}
@@ -69,12 +71,12 @@ class Network {
Dio getDio() => _dio;
Dio get enteDio => _enteDio;
void _setupInterceptors(BaseConfiguration configuration) {
void _setupInterceptors(String endpoint) {
_dio.interceptors.clear();
_dio.interceptors.add(RequestIdInterceptor());
_enteDio.interceptors.clear();
_enteDio.interceptors.add(EnteRequestInterceptor(configuration));
_enteDio.interceptors.add(EnteRequestInterceptor(endpoint));
}
}
@@ -88,21 +90,21 @@ class RequestIdInterceptor extends Interceptor {
}
class EnteRequestInterceptor extends Interceptor {
final BaseConfiguration configuration;
final String endpoint;
EnteRequestInterceptor(this.configuration);
EnteRequestInterceptor(this.endpoint);
@override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
if (kDebugMode) {
assert(
options.baseUrl == configuration.getHttpEndpoint(),
options.baseUrl == endpoint,
"interceptor should only be used for API endpoint",
);
}
options.headers
.putIfAbsent("x-request-id", () => const Uuid().v4().toString());
final String? tokenValue = configuration.getToken();
final String? tokenValue = Configuration.instance.getToken();
if (tokenValue != null) {
options.headers.putIfAbsent("X-Auth-Token", () => tokenValue);
}

View File

@@ -38,7 +38,7 @@ final lightThemeData = ThemeData(
bodyMedium: const TextStyle(color: Colors.yellow),
bodyLarge: const TextStyle(color: Colors.orange),
),
cardColor: const Color.fromRGBO(250, 250, 250, 1),
cardColor: const Color.fromRGBO(250, 250, 250, 1.0),
dialogTheme: const DialogTheme().copyWith(
backgroundColor: const Color.fromRGBO(250, 250, 250, 1.0), //
titleTextStyle: const TextStyle(

View File

@@ -1,3 +1,3 @@
import 'package:ente_events/models/event.dart';
import 'package:ente_auth/events/event.dart';
class CodesUpdatedEvent extends Event {}

View File

@@ -0,0 +1,3 @@
import 'package:ente_auth/events/event.dart';
class EndpointUpdatedEvent extends Event {}

View File

@@ -1,3 +1,3 @@
import 'package:ente_events/models/event.dart';
import 'package:ente_auth/events/event.dart';
class IconsChangedEvent extends Event {}

View File

@@ -0,0 +1,5 @@
import 'package:ente_auth/events/event.dart';
// NotificationEvent event is used to re-fresh the UI to show latest notification
// (if any)
class NotificationEvent extends Event {}

View File

@@ -0,0 +1,3 @@
import 'package:ente_auth/events/event.dart';
class SignedInEvent extends Event {}

View File

@@ -1 +1,3 @@
// TODO Implement this library.
import 'package:ente_auth/events/event.dart';
class SignedOutEvent extends Event {}

View File

@@ -1,3 +1,3 @@
import 'package:ente_events/models/event.dart';
import 'package:ente_auth/events/event.dart';
class TriggerLogoutEvent extends Event {}

View File

@@ -1,3 +1,3 @@
import 'package:ente_events/models/event.dart';
import 'package:ente_auth/events/event.dart';
class UserDetailsChangedEvent extends Event {}

View File

@@ -1,8 +1,8 @@
import 'package:dio/dio.dart';
import 'package:ente_auth/core/errors.dart';
import 'package:ente_auth/core/network.dart';
import 'package:ente_auth/models/authenticator/auth_entity.dart';
import 'package:ente_auth/models/authenticator/auth_key.dart';
import 'package:ente_network/network.dart';
class AuthenticatorGateway {
late Dio _enteDio;

View File

@@ -177,24 +177,50 @@
"systemTheme": "Сістэманая",
"invalidKey": "Памылковы ключ",
"tryAgain": "Паспрабуйце яшчэ раз",
"confirmYourRecoveryKey": "Пацвердзіце свой ключ аднаўлення",
"confirm": "Пацвердзіць",
"emailYourLogs": "Адправіць журналы",
"pleaseSendTheLogsTo": "Адпраўце журналы на \n{toEmail}",
"copyEmailAddress": "Скапіяваць адрас электроннай пошты",
"exportLogs": "Экспартаваць журналы",
"enterYourRecoveryKey": "Увядзіце свой ключ аднаўлення",
"tempErrorContactSupportIfPersists": "Здаецца, што нешта пайшло не так. Паспрабуйце яшчэ раз крыху пазней. Звяжыцеся са службай падтрымкі, калі праблема застанецца.",
"networkHostLookUpErr": "Немагчыма падключыцца да сервера Ente. Праверце свае сеткавыя налады. Звяжыцеся са службай падтрымкі, калі праблема застанецца.",
"about": "Аб праграме",
"weAreOpenSource": "Наша праграма мае адкрыты зыходны код!",
"privacy": "Прыватнасць",
"terms": "Умовы",
"checkForUpdates": "Праверыць абнаўленні",
"checkStatus": "Праверыць статус",
"downloadUpdate": "Спампаваць",
"criticalUpdateAvailable": "Даступна крытычнае абнаўленне",
"updateAvailable": "Даступна абнаўленне",
"update": "Абнавіць",
"checking": "Праверка...",
"youAreOnTheLatestVersion": "У вас апошняя версія праграмы",
"warning": "Папярэджанне",
"exportWarningDesc": "Экспартаваны файл змяшчае канфідэнцыяльныя звесткі. Захоўвайце яго ў надзейным месцы.",
"iUnderStand": "Ясна",
"@iUnderStand": {
"description": "Text for the button to confirm the user understands the warning"
},
"authToExportCodes": "Прайдзіце аўтэнтыфікацыю, каб экспартаваць свае коды",
"importSuccessTitle": "Ура!",
"importSuccessDesc": "Коды паспяхова імпартаваны ({count} шт.)!",
"@importSuccessDesc": {
"placeholders": {
"count": {
"description": "The number of codes imported",
"type": "int",
"example": "1"
}
}
},
"sorry": "Прабачце",
"importFailureDesc": "Не ўдалося прааналізаваць выбраны файл.\nНапішыце нам на support@ente.io, калі вам патрэбная дапамога!",
"pendingSyncs": "Папярэджанне",
"pendingSyncsWarningBody": "Некаторыя вашы коды не былі зарэзерваваны.\n\nПераканайцеся, што ў вас ёсць іх рэзервовая копія перад выхадам з сістэмы.",
"checkInboxAndSpamFolder": "Праверце свае ўваходныя лісты (і спам) для завяршэння праверкі",
"resendEmail": "Адправіць ліст яшчэ раз",
"manualSort": "Карыстальніцкая",
"editOrder": "Рэдагаваць заказ",
@@ -205,12 +231,24 @@
"thisDevice": "Гэта прылада",
"incorrectCode": "Няправільны код",
"enterPassword": "Увядзіце пароль",
"selectExportFormat": "Выберыце фармат экспартавання",
"exportDialogDesc": "Зашыфраванае экспартаванне будзе абаронена паролем, які вы выберыце.",
"encrypted": "Зашыфравана",
"plainText": "Звычайны тэкст",
"passwordToEncryptExport": "Пароль для шыфравання экспартавання",
"export": "Экспартаваць",
"useOffline": "Выкарыстоўваць без рэзервовых копій",
"signInToBackup": "Увайсці для рэзервовага капіявання кодаў",
"singIn": "Увайсці",
"sigInBackupReminder": "Экспартуйце свае коды, каб у вас была рэзервовая копія з якой вы заўсёды зможаце аднавіць іх.",
"offlineModeWarning": "Вы выбралі працягнуць без рэзервовых копій. Зрабіце рэзервовую копію кодаў уручную, каб не згубіць доступ да іх.",
"showLargeIcons": "Паказваць вялікі значкі",
"compactMode": "Кампактны рэжым",
"shouldHideCode": "Схаваць коды",
"doubleTapToViewHiddenCode": "Можна двойчы націснуць на запіс, каб паглядзець код",
"focusOnSearchBar": "Сфакусіравацца на пошуку пры запуску праграмы",
"confirmUpdatingkey": "Вы сапраўды хочаце абнавіць сакрэтны ключ?",
"minimizeAppOnCopy": "Згортваць праграму пры капіяванні",
"androidBiometricHint": "Праверыць ідэнтыфікацыю",
"@androidBiometricHint": {
"description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters."

View File

@@ -173,6 +173,7 @@
"invalidQRCode": "Codi QR invàlid",
"noRecoveryKeyTitle": "No tens clau de recuperació?",
"enterEmailHint": "Introdueix la teva adreça de correu electrònic",
"enterNewEmailHint": "Introdueix la teva nova adreça de correu electrònic",
"invalidEmailTitle": "Adreça de correu electrònic no vàlida",
"invalidEmailMessage": "Si us plau, introdueix una adreça de correu electrònic vàlida.",
"deleteAccount": "Elimina el compte",
@@ -499,16 +500,22 @@
"appLockOfflineModeWarning": "Has triat procedir sense còpies de seguretat. Si oblides el bloqueig de l'aplicació, no podràs accedir a les teves dades.",
"duplicateCodes": "Codis duplicats",
"noDuplicates": "✨ Sense duplicats",
"youveNoDuplicateCodesThatCanBeCleared": "No teniu cap codi duplicat que es puguin esborrar",
"deduplicateCodes": "Desduplica codis",
"deselectAll": "Desselecciona-ho tot",
"selectAll": "Seleccionar-ho tot",
"deleteDuplicates": "Elimina duplicats",
"plainHTML": "HTML pla",
"tellUsWhatYouThink": "Digueu-nos què us sembla",
"dropReviewiOS": "Deixa una ressenya a l'App Store",
"dropReviewAndroid": "Deixa una ressenya a la Play Store",
"supportEnte": "Donar suport a <bold-green>ente</bold-green>",
"giveUsAStarOnGithub": "Dona'ns una estrella a Github",
"free5GB": "5 GB gratuïts a <bold-green>ente</bold-green> Photos",
"loginWithAuthAccount": "Inicieu sessió amb el vostre compte Auth",
"freeStorageOffer": "10% de descompte a <bold-green>ente</bold-green> photos",
"freeStorageOfferDescription": "Utilitzeu el codi \"AUTH\" per obtenir un 10% de descompte el primer any"
"freeStorageOfferDescription": "Utilitzeu el codi \"AUTH\" per obtenir un 10% de descompte el primer any",
"advanced": "Avançat",
"algorithm": "Algorisme",
"type": "Tipus"
}

View File

@@ -0,0 +1 @@
{}

View File

@@ -1,28 +1,28 @@
{
"account": "Tài khoản",
"unlock": "Mở khóa",
"recoveryKey": "Khóa khôi phục",
"counterAppBarTitle": "Bộ Đếm",
"recoveryKey": " khôi phục",
"counterAppBarTitle": "Đếm ngược",
"@counterAppBarTitle": {
"description": "Text shown in the AppBar of the Counter Page"
},
"onBoardingBody": "Sao lưu mã 2FA của bạn một cách an toàn",
"onBoardingBody": "Sao lưu an toàn mã 2FA của bạn",
"onBoardingGetStarted": "Bắt đầu",
"setupFirstAccount": "Thiết lập tài khoản đầu tiên của bạn",
"importScanQrCode": "Quét mã QR",
"qrCode": "Mã QR",
"importEnterSetupKey": "Nhập khóa thiết lập",
"importAccountPageTitle": "Nhập chi tiết tài khoản",
"secretCanNotBeEmpty": "Khoá bí mật không được để trống",
"bothIssuerAndAccountCanNotBeEmpty": "Cả nhà phát hành và tài khoản không được để trống",
"incorrectDetails": "Thông tin chi tiết không chính xác",
"secretCanNotBeEmpty": "Không được để trống khóa bí mật",
"bothIssuerAndAccountCanNotBeEmpty": "Không được để trống dịch vụ và tài khoản",
"incorrectDetails": "Thông tin không chính xác",
"pleaseVerifyDetails": "Vui lòng xác minh chi tiết và thử lại",
"codeIssuerHint": "Bên phát hành",
"codeIssuerHint": "Dịch vụ",
"codeSecretKeyHint": "Khóa bí mật",
"secret": "Khóa bí mật",
"all": "Tất cả",
"notes": "Ghi chú",
"notesLengthLimit": "Ghi chú có thể dài tối đa {count} ký tự",
"notesLengthLimit": "Ghi chú dài tối đa {count} ký tự",
"@notesLengthLimit": {
"description": "Text to indicate the maximum number of characters allowed for notes",
"placeholders": {
@@ -33,10 +33,10 @@
}
}
},
"codeAccountHint": "Tài khoản (bạn@miền.com)",
"codeAccountHint": "Tài khoản (you@domain.com)",
"codeTagHint": "Thẻ",
"accountKeyType": "Loại khóa",
"sessionExpired": "Phiên làm việc đã hết hạn",
"sessionExpired": "Phiên đăng nhập đã hết hạn",
"@sessionExpired": {
"description": "Title of the dialog when the users current session is invalid/expired"
},
@@ -47,15 +47,15 @@
"saveAction": "Lưu",
"nextTotpTitle": "tiếp theo",
"deleteCodeTitle": "Xóa mã?",
"deleteCodeMessage": "Bạn có chắc chắn muốn xóa mã này không? Hành động này không thể đảo ngược.",
"deleteCodeMessage": "Bạn có chắc muốn xóa mã này không? Hành động này không thể hoàn tác.",
"trashCode": "Xóa mã?",
"trashCodeMessage": "Bạn có chắc chắn muốn xóa mã cho {account} không?",
"trashCodeMessage": "Bạn có chắc muốn xóa mã cho {account} không?",
"trash": "Xóa",
"viewLogsAction": "Xem nhật ký",
"sendLogsDescription": "Thao tác này sẽ gửi nhật ký để giúp chúng tôi gỡ lỗi sự cố của bạn. Mặc dù chúng tôi thực hiện các biện pháp phòng ngừa để đảm bảo rằng thông tin nhạy cảm không được ghi lại, nhưng chúng tôi khuyến khích bạn xem các nhật ký này trước khi chia sẻ chúng.",
"preparingLogsTitle": "Đang chuẩn bị nhật ký...",
"sendLogsDescription": "Gửi nhật ký để giúp chúng tôi xử lý vấn đề của bạn. Dù chúng tôi thực hiện các biện pháp phòng ngừa để đảm bảo rằng thông tin nhạy cảm không được ghi lại, bạn vẫn nên xem các nhật ký này trước khi chia sẻ chúng.",
"preparingLogsTitle": "Đang ghi nhật ký...",
"emailLogsTitle": "Nhật ký email",
"emailLogsMessage": "Vui lòng gửi nhật ký tới {email}",
"emailLogsMessage": "Vui lòng gửi nhật ký đến {email}",
"@emailLogsMessage": {
"placeholders": {
"email": {
@@ -65,7 +65,7 @@
},
"copyEmailAction": "Sao chép email",
"exportLogsAction": "Xuất nhật ký",
"reportABug": "Báo cáo lỗi",
"reportABug": "Báo lỗi",
"crashAndErrorReporting": "Báo cáo sự cố & lỗi",
"reportBug": "Báo lỗi",
"emailUsMessage": "Vui lòng gửi email cho chúng tôi tại {email}",
@@ -79,88 +79,88 @@
"contactSupport": "Liên hệ hỗ trợ",
"rateUsOnStore": "Đánh giá chúng tôi trên {storeName}",
"blog": "Blog",
"merchandise": "Hàng hóa",
"verifyPassword": "Xác nhận mật khẩu",
"merchandise": "Vật phẩm",
"verifyPassword": "Xác minh mật khẩu",
"pleaseWait": "Vui lòng chờ...",
"generatingEncryptionKeysTitle": "Đang tạo khóa mã hóa...",
"recreatePassword": "Tạo lại mật khẩu",
"recreatePasswordMessage": "Thiết bị hiện tại không đủ mạnh để xác minh mật khẩu của bạn, vì vậy chúng tôi cần tạo lại mật khẩu một lần theo cách hoạt động với tất cả các thiết bị.\n\nVui lòng đăng nhập bằng khóa khôi phục và tạo lại mật khẩu của bạn (bạn có thể sử dụng lại cùng một mật khẩu nếu muốn).",
"useRecoveryKey": "Dùng khóa khôi phục",
"recreatePasswordMessage": "Thiết bị hiện tại không đủ mạnh để xác minh mật khẩu của bạn, nhưng chúng tôi có thể tạo lại để nó hoạt động với tất cả thiết bị.\n\nVui lòng đăng nhập bằng khôi phục và tạo lại mật khẩu (bạn có thể ng lại mật khẩu nếu muốn).",
"useRecoveryKey": "Dùng khôi phục",
"incorrectPasswordTitle": "Mật khẩu không đúng",
"welcomeBack": "Chào mừng trở lại!",
"emailAlreadyRegistered": "Email đã được đăng kí.",
"emailNotRegistered": "Email chưa được đăng kí.",
"madeWithLoveAtPrefix": "được làm bằng ❤️ tại ",
"emailAlreadyRegistered": "Email đã được đăng ký.",
"emailNotRegistered": "Email chưa được đăng ký.",
"madeWithLoveAtPrefix": "lập trình bằng ❤️ bởi ",
"supportDevs": "Đăng ký <bold-green>ente</bold-green> để hỗ trợ dự án này.",
"supportDiscount": "Sử dụng mã giảm giá \"AUTH\" để được giảm 10% trong năm đầu tiên",
"changeEmail": "Thay đổi email",
"supportDiscount": "Dùng mã \"AUTH\" để được giảm 10% trong năm đầu tiên",
"changeEmail": "Đổi email",
"changePassword": "Đổi mật khẩu",
"data": "Dữ liệu",
"importCodes": "Nhập mã",
"importTypePlainText": "Văn bản thuần",
"importTypeEnteEncrypted": "Xuất key đã được mã hóa",
"passwordForDecryptingExport": "Mật khẩu để giải mã xuất",
"importTypeEnteEncrypted": "Ente Auth",
"passwordForDecryptingExport": "Mật khẩu để giải mã tệp xuất",
"passwordEmptyError": "Mật khẩu không thể để trống",
"importFromApp": "Nhập mã từ {appName}",
"importGoogleAuthGuide": "Xuất dữ liệu tài khoản của bạn từ Google Authenticator sang mã QR bằng tùy chọn \"Chuyển tài khoản\". Sau đó dùng thiết bị khác quét mã QR.",
"importGoogleAuthGuide": "Xuất dữ liệu tài khoản Google Authenticator của bạn thành mã QR bằng tùy chọn \"Chuyển tài khoản\". Sau đó, dùng thiết bị khác quét mã QR.",
"importSelectJsonFile": "Chọn tệp JSON",
"importSelectAppExport": "Chọn {appName} tệp dữ liệu xuất",
"importEnteEncGuide": "Chọn tệp JSON được mã hóa đã xuất từ ente",
"importRaivoGuide": "Sử dụng tùy chọn \"Xuất OTP sang lưu trữ Zip\" trong cài đặt của Raivo.",
"importBitwardenGuide": "Sử dụng tùy chọn \"Xuất vault\" trong công cụ Bitwarden và nhập tệp JSON không được mã hóa.",
"importAegisGuide": "Nếu vault của bạn được mã hóa, bạn sẽ cần nhập mật khẩu vault để giải mã vault.",
"import2FasGuide": "Sử dụng tùy chọn \"Cài đặt->Sao lưu -Xuất dữ liệu\" trong 2FAS.\n\nNếu bản sao lưu của bạn được mã hóa, bạn sẽ cần nhập mật khẩu để giải mã bản sao lưu",
"importLastpassGuide": "Sử dụng tùy chọn \"Chuyển tài khoản\" trong Cài đặt Lastpass Authenticator và nhấn \"Xuất tài khoản ra tệp\". Nhập tệp JSON đã tải xuống.",
"importSelectAppExport": "Chọn tệp xuất từ {appName}",
"importEnteEncGuide": "Chọn tệp JSON Ente đã mã hóa",
"importRaivoGuide": "ng tùy chọn \"Xuất OTP thành tệp Zip\" của Raivo.\n\nGiải nén tệp Zip đó và nhập tệp JSON.",
"importBitwardenGuide": "ng tùy chọn \"Xuất kho\" của Bitwarden và nhập tệp JSON không có mã hóa.",
"importAegisGuide": "Dùng tùy chọn \"Xuất kho\" của Aegis.\n\nNếu kho của bạn có mã hóa, bạn phải có mật khẩu để giải mã.",
"import2FasGuide": "ng tùy chọn \"Cài đặt->Sao lưu -Xuất dữ liệu\" của 2FAS.\n\nNếu bản sao lưu của bạn có mã hóa, bạn phải có mật khẩu để giải mã.",
"importLastpassGuide": "ng tùy chọn \"Chuyển tài khoản\" của Lastpass Authenticator và nhấn \"Xuất tài khoản ra tệp\". Nhập tệp JSON đã tải xuống.",
"exportCodes": "Xuất mã",
"importLabel": "Nhập",
"importInstruction": "Vui lòng chọn tệp chứa danh sách mã của bạn định dạng sau",
"importCodeDelimiterInfo": "Các mã có thể được phân tách bằng một dấu phẩy hoặc một dòng mới",
"selectFile": "Chọn tập tin",
"emailVerificationToggle": "Email xác thực",
"importInstruction": "Vui lòng chọn tệp chứa danh sách mã của bạn định dạng",
"importCodeDelimiterInfo": "Phân tách bằng dấu phẩy hoặc xuống dòng",
"selectFile": "Chọn tệp",
"emailVerificationToggle": "Xác minh email",
"emailVerificationEnableWarning": "Để tránh bị khóa tài khoản, hãy đảm bảo lưu trữ bản sao email 2FA của bạn bên ngoài Ente Auth trước khi bật xác minh email.",
"authToChangeEmailVerificationSetting": "Vui lòng xác thực để thay đổi email",
"authToChangeEmailVerificationSetting": "Vui lòng xác thực để đổi email",
"authenticateGeneric": "Vui lòng xác thực",
"authToViewYourRecoveryKey": "Vui lòng xác thực để xem khóa khôi phục của bạn",
"authToViewYourRecoveryKey": "Vui lòng xác thực để xem khôi phục",
"authToChangeYourEmail": "Vui lòng xác thực để thay đổi email của bạn",
"authToChangeYourPassword": "Vui lòng xác thực để thay đổi mật khẩu của bạn",
"authToViewSecrets": "Vui lòng xác thực để xem bí mật của bạn",
"authToInitiateSignIn": "Vui lòng xác thực để bắt đầu đăng nhập nhằm sao lưu.",
"ok": "Đồng ý",
"authToChangeYourPassword": "Vui lòng xác thực để đổi mật khẩu",
"authToViewSecrets": "Vui lòng xác thực để xem khóa bí mật",
"authToInitiateSignIn": "Vui lòng xác thực để bắt đầu sao lưu",
"ok": "Được",
"cancel": "Hủy",
"yes": "Đúng",
"yes": "",
"no": "Không",
"email": "Thư điện tử",
"email": "Email",
"support": "Hỗ trợ",
"general": "Tổng quan",
"general": "Chung",
"settings": "Cài đặt",
"copied": "Đã sao chép",
"pleaseTryAgain": "Vui lòng thử lại",
"existingUser": "Người dùng hiện tại",
"newUser": "Mới tham gia Ente",
"existingUser": "Đã có tài khoản",
"newUser": "Mới dùng Ente",
"delete": "Xóa",
"enterYourPasswordHint": "Nhập mật khẩu của bạn",
"forgotPassword": "Quên mật khẩu",
"oops": "Rất tiếc",
"suggestFeatures": "Gợi ý tính năng",
"oops": "Ốii!",
"suggestFeatures": "Đề xuất tính năng",
"faq": "Câu hỏi thường gặp",
"somethingWentWrongMessage": "Đã xảy ra lỗi, xin thử lại",
"leaveFamily": "Rời khỏi gia đình",
"leaveFamilyMessage": "Bạn có chắc chắn muốn thoát khỏi gói dành cho gia đình không?",
"inFamilyPlanMessage": "Bạn đang sử dụng gói dành cho gia đình!",
"hintForMobile": "Nhấn giữ trên mã để chỉnh sửa hoặc xóa.",
"hintForDesktop": "Nhấp chuột phải vào mã để chỉnh sửa hoặc xóa.",
"somethingWentWrongMessage": "Có gì đó không ổn, vui lòng thử lại",
"leaveFamily": "Rời khỏi gói gia đình",
"leaveFamilyMessage": "Bạn có chắc muốn rời khỏi gói gia đình không?",
"inFamilyPlanMessage": "Bạn đang ng gói gia đình!",
"hintForMobile": "Nhấn giữ mã để chỉnh sửa hoặc xóa.",
"hintForDesktop": "Nhấn chuột phải vào mã để chỉnh sửa hoặc xóa.",
"scan": "Quét",
"scanACode": "Quét mã",
"verify": "Xác minh",
"verifyEmail": "Xác nhận địa chỉ Email",
"enterCodeHint": "Nhập mã gồm 6 chữ số từ ứng dụng xác thực của bạn",
"verifyEmail": "Xác minh email",
"enterCodeHint": "Nhập mã 6 chữ số từ\nứng dụng xác thực của bạn",
"lostDeviceTitle": "Mất thiết bị?",
"twoFactorAuthTitle": "Xác thực hai yếu tố",
"passkeyAuthTitle": "Xác minh khóa",
"verifyPasskey": "Xác minh khóa",
"twoFactorAuthTitle": "Xác thực 2 bước",
"passkeyAuthTitle": "Xác minh khóa truy cập",
"verifyPasskey": "Xác minh khóa truy cập",
"loginWithTOTP": "Đăng nhập bằng TOTP",
"recoverAccount": "Khôi phục tài khoản",
"enterRecoveryKeyHint": "Nhập khóa khôi phục của bạn",
"enterRecoveryKeyHint": "Nhập khôi phục của bạn",
"recover": "Khôi phục",
"contactSupportViaEmailMessage": "Vui lòng gửi email đến {email} từ địa chỉ email đã đăng ký của bạn",
"@contactSupportViaEmailMessage": {
@@ -171,12 +171,13 @@
}
},
"invalidQRCode": "Mã QR không hợp lệ",
"noRecoveryKeyTitle": "Không có khóa khôi phục?",
"noRecoveryKeyTitle": "Không có khôi phục?",
"enterEmailHint": "Nhập địa chỉ email của bạn",
"enterNewEmailHint": "Nhập địa chỉ email mới của bạn",
"invalidEmailTitle": "Địa chỉ email không hợp lệ",
"invalidEmailMessage": "Xin vui lòng nhập một địa chỉ email hợp lệ.",
"deleteAccount": "X tài khoản",
"deleteAccountQuery": "Chúng tôi sẽ rất tiếc khi thấy bạn đi. Bạn đang phải đối mặt với một số vấn đề?",
"invalidEmailMessage": "Vui lòng nhập một địa chỉ email hợp lệ.",
"deleteAccount": "Xóa tài khoản",
"deleteAccountQuery": "Chúng tôi rất tiếc khi thấy bạn ra đi. Có phải bạn gặp vấn đề?",
"yesSendFeedbackAction": "Có, gửi phản hồi",
"noDeleteAccountAction": "Không, xóa tài khoản",
"initiateAccountDeleteTitle": "Vui lòng xác thực để bắt đầu xóa tài khoản",
@@ -187,40 +188,40 @@
"moderateStrength": "Trung bình",
"confirmPassword": "Xác nhận mật khẩu",
"close": "Đóng",
"oopsSomethingWentWrong": "Rất tiếc, Đã xảy ra lỗi.",
"oopsSomethingWentWrong": "Ốii!, có gì đó không ổn.",
"selectLanguage": "Chọn ngôn ngữ",
"language": "Ngôn ngữ",
"social": "Xã hội",
"social": "Mạng xã hội",
"security": "Bảo mật",
"lockscreen": "Màn hình khoá",
"authToChangeLockscreenSetting": "Vui lòng xác thực để thay đổi cài đặt màn hình khóa",
"lockscreen": "Khóa màn hình",
"authToChangeLockscreenSetting": "Vui lòng xác thực để thay đổi cài đặt khóa màn hình",
"deviceLockEnablePreSteps": "Để bật khoá thiết bị, vui lòng thiết lập mật khẩu thiết bị hoặc khóa màn hình trong cài đặt hệ thống của bạn.",
"viewActiveSessions": "Xem danh sách phiên làm việc hiện tại",
"authToViewYourActiveSessions": "Vui lòng xác thực để xem danh sách phiên làm việc của bạn",
"viewActiveSessions": "Xem phiên hoạt động",
"authToViewYourActiveSessions": "Vui lòng xác thực để xem các phiên hoạt động",
"searchHint": "Tìm kiếm...",
"search": "Tìm kiếm",
"sorryUnableToGenCode": "Rất tiếc, không thể tạo mã cho {issuerName}",
"noResult": "Không có kết quả",
"addCode": "Thêm mã",
"scanAQrCode": "Quét mã QR",
"enterDetailsManually": "Nhập chi tiết thủ công",
"enterDetailsManually": "Nhập thủ công",
"edit": "Sửa",
"share": "Chia sẻ",
"shareCodes": "Chia sẻ mã",
"shareCodesDuration": "Chọn khoảng thời gian mà bạn muốn chia sẻ mã.",
"shareCodesDuration": "Chọn thời hạn bạn muốn chia sẻ mã.",
"restore": "Khôi phục",
"copiedToClipboard": "Đ sao chp vào khay nhớ tạm",
"copiedNextToClipboard": "Đã sao chép mã tiếp theo vào bảng nhớ tạm",
"copiedToClipboard": "Đã sao chép vào bộ nhớ tạm",
"copiedNextToClipboard": "Đã sao chép mã tiếp theo vào b nhớ tạm",
"error": "Lỗi",
"recoveryKeyCopiedToClipboard": "Đã sao chép khóa khôi phục vào bộ nhớ tạm",
"recoveryKeyOnForgotPassword": "Nếu bạn quên mật khẩu, cách duy nhất bạn có thể khôi phục dữ liệu của mình là sử dụng khóa này.",
"recoveryKeySaveDescription": "Chúng tôi không lưu trữ khóa này, vui lòng lưu khóa 24 từ này ở nơi an toàn.",
"recoveryKeyCopiedToClipboard": "Đã sao chép khôi phục vào bộ nhớ tạm",
"recoveryKeyOnForgotPassword": "Nếu bạn quên mật khẩu, cách duy nhất để khôi phục dữ liệu của bạn là dùng mã này.",
"recoveryKeySaveDescription": "Chúng tôi không lưu trữ này, nên hãy lưu nó ở nơi an toàn.",
"doThisLater": "Để sau",
"saveKey": "Lưu khóa",
"saveKey": "Lưu ",
"save": "Lưu",
"send": "Gửi",
"saveOrSendDescription": "Bạn muốn lưu vào bộ nhớ (Mặc định lưu vào thư mục Tải về) hoặc chuyển qua ứng dụng khác?",
"saveOnlyDescription": "Bạn muốn lưu vào bộ nhớ không (Mặc định lưu vào thư mục Tải về)?",
"saveOrSendDescription": "Bạn muốn lưu vào bộ nhớ (Mặc định là thư mục Tải xuống) hay gửi qua ứng dụng khác?",
"saveOnlyDescription": "Bạn muốn lưu vào bộ nhớ (Mặc định là thư mục Tải xuống)?",
"back": "Quay lại",
"createAccount": "Tạo tài khoản",
"passwordStrength": "Độ mạnh mật khẩu: {passwordStrengthValue}",
@@ -236,76 +237,76 @@
"message": "Password Strength: {passwordStrengthText}"
},
"password": "Mật khẩu",
"signUpTerms": "Tôi đồng ý với <u-terms>điều khoản dịch vụ</u-terms> và <u-policy>chính sách quyền riêng tư</u-policy>",
"signUpTerms": "Tôi đồng ý với <u-terms>điều khoản</u-terms> và <u-policy>chính sách bảo mật</u-policy>",
"privacyPolicyTitle": "Chính sách bảo mật",
"termsOfServicesTitle": "Điều khoản",
"encryption": "Mã hóa",
"setPasswordTitle": "Đặt mật khẩu",
"changePasswordTitle": "Thay đổi mật khẩu",
"changePasswordTitle": "Đổi mật khẩu",
"resetPasswordTitle": "Đặt lại mật khẩu",
"encryptionKeys": "Khóa mã hóa",
"passwordWarning": "Chúng tôi không lưu trữ mật khẩu này, vì vậy nếu bạn quên, <underline>chúng tôi không thể giải mã dữ liệu của bạn</underline>",
"enterPasswordToEncrypt": "Nhập mật khẩu mà chúng tôi có thể sử dụng để mã hóa dữ liệu của bạn",
"enterNewPasswordToEncrypt": "Nhập một mật khẩu mới mà chúng tôi có thể sử dụng để mã hóa dữ liệu của bạn",
"passwordChangedSuccessfully": "Thay đổi mật khẩu thành công",
"passwordWarning": "Chúng tôi không lưu trữ mật khẩu này, nên nếu bạn quên, <underline>chúng tôi không thể giải mã dữ liệu của bạn</underline>",
"enterPasswordToEncrypt": "Vui lòng nhập một mật khẩu dùng để mã hóa dữ liệu của bạn",
"enterNewPasswordToEncrypt": "Vui lòng nhập một mật khẩu mới để mã hóa dữ liệu của bạn",
"passwordChangedSuccessfully": "Đã thay đổi mật khẩu thành công",
"generatingEncryptionKeys": "Đang tạo khóa mã hóa...",
"continueLabel": "Tiếp tục",
"insecureDevice": "Thiết bị không an toàn",
"sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "Rất tiếc, chúng tôi không thể tạo khóa bảo mật trên thiết bị này.\n\nvui lòng đăng ký từ một thiết bị khác.",
"sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "Rất tiếc, chúng tôi không thể tạo khóa an toàn trên thiết bị này.\n\nVui lòng đăng ký từ một thiết bị khác.",
"howItWorks": "Cách thức hoạt động",
"ackPasswordLostWarning": "Tôi hiểu rằng việc mất mật khẩu có thể đồng nghĩa với việc mất dữ liệu của tôi vì dữ liệu của tôi được <underline>mã hóa hai đầu</underline>.",
"loginTerms": "Bằng cách nhấp vào đăng nhập, tôi đồng ý với <u-terms>điều khoản dịch vụ</u-terms> và <u-policy>chính sách quyền riêng tư</u-policy>",
"ackPasswordLostWarning": "Tôi hiểu rằng nếu mất mật khẩu, dữ liệu của tôi sẽ mất vì nó được <underline>mã hóa đầu cuối</underline>.",
"loginTerms": "Nhấn vào đăng nhập, tôi đồng ý với <u-terms>điều khoản</u-terms> và <u-policy>chính sách bảo mật</u-policy>",
"logInLabel": "Đăng nhập",
"logout": "Đăng xuất",
"areYouSureYouWantToLogout": "Bạn có chắc chắn muốn đăng xuất?",
"areYouSureYouWantToLogout": "Bạn có chắc muốn đăng xuất không?",
"yesLogout": "Có, đăng xuất",
"exit": "Thoát",
"theme": "Chủ đề",
"lightTheme": "Sáng",
"darkTheme": "Tối",
"systemTheme": "Hệ thống",
"verifyingRecoveryKey": "Đang xác minh khóa khôi phục...",
"recoveryKeyVerified": "Khóa khôi phục đã được xác thực",
"recoveryKeySuccessBody": "Tuyệt vời! Khóa khôi phục của bạn hợp lệ. Cảm ơn bạn đã xác minh.\n\nHãy nhớ sao lưu khóa khôi phục của bạn một cách an toàn.",
"invalidRecoveryKey": "Khóa khôi phục bạn đã nhập không hợp lệ. Vui lòng đảm bảo rằng nó chứa 24 từ và kiểm tra chính tả của từng từ.\n\nNếu bạn đã nhập mã khôi phục cũ hơn, hãy đảm bảo mã đó dài 64 ký tự và kiểm tra từng ký tự.",
"systemTheme": "Giống hệ thống",
"verifyingRecoveryKey": "Đang xác minh khôi phục...",
"recoveryKeyVerified": " khôi phục đã được xác minh",
"recoveryKeySuccessBody": "Tuyệt! Mã khôi phục của bạn hợp lệ. Cảm ơn đã xác minh.\n\nNhớ lưu giữ mã khôi phục của bạn ở nơi an toàn.",
"invalidRecoveryKey": " khôi phục không hợp lệ. Vui lòng đảm bảo nó chứa 24 từ,đúng chính tả từng từ.\n\nNếu bạn nhập loại mã khôi phục cũ, hãy đảm bảo nó dài 64 ký tự, và kiểm tra từng ký tự.",
"recreatePasswordTitle": "Tạo lại mật khẩu",
"recreatePasswordBody": "Thiết bị hiện tại không đủ mạnh để xác minh mật khẩu của bạn nhưng chúng tôi có thể tạo lại mật khẩu theo cách hoạt động với tất cả các thiết bị.\n\nVui lòng đăng nhập bằng khóa khôi phục và tạo lại mật khẩu của bạn (bạn có thể sử dng lại cùng một mật khẩu nếu muốn).",
"invalidKey": "Khoá không hợp lệ",
"recreatePasswordBody": "Thiết bị hiện tại không đủ mạnh để xác minh mật khẩu của bạn, nhưng chúng tôi có thể tạo lại để nó hoạt động với tất cả thiết bị.\n\nVui lòng đăng nhập bằng khôi phục và tạo lại mật khẩu (bạn có thể dùng lại mật khẩu nếu muốn).",
"invalidKey": " không hợp lệ",
"tryAgain": "Thử lại",
"viewRecoveryKey": "Hiển thị khóa khôi phục",
"confirmRecoveryKey": "Xác nhận khóa khôi phục",
"recoveryKeyVerifyReason": "Nếu bạn quên mật khẩu, khóa khôi phục là cách duy nhất để khôi phục ảnh của bạn. Bạn có thể tìm thấy khóa khôi phục của mình trong Cài đặt > Tài khoản.\n\nVui lòng nhập khóa khôi phục của bạn tại đây để xác minh rằng bạn đã lưu chính xác.",
"confirmYourRecoveryKey": "Xác nhận khóa khôi phục",
"viewRecoveryKey": "Xem mã khôi phục",
"confirmRecoveryKey": "Xác nhận khôi phục",
"recoveryKeyVerifyReason": " khôi phục là cách duy nhất để khôi phục ảnh của bạn nếu bạn quên mật khẩu. Bạn có thể xem mã khôi phục của mình trong Cài đặt > Tài khoản.\n\nVui lòng nhập khôi phục của bạn đây để xác minh rằng bạn đã lưu nó đúng cách.",
"confirmYourRecoveryKey": "Xác nhận khôi phục của bạn",
"confirm": "Xác nhận",
"emailYourLogs": "Gửi email nhật ký của bạn",
"emailYourLogs": "Gửi nhật ký qua email",
"pleaseSendTheLogsTo": "Vui lòng gửi nhật ký đến \n{toEmail}",
"copyEmailAddress": "Sao chép địa chỉ email",
"exportLogs": "Xuất nhật ký",
"enterYourRecoveryKey": "Nhập khóa khôi phục của bạn",
"tempErrorContactSupportIfPersists": "Có vẻ như đã xảy ra sự cố. Vui lòng thử lại sau một thời gian. Nếu lỗi vẫn tiếp diễn, vui lòng liên hệ với nhóm hỗ trợ của chúng tôi.",
"networkHostLookUpErr": "Không thể kết nối đến Ente, vui lòng kiểm tra lại kết nối mạng. Nếu vẫn còn lỗi, xin vui lòng liên hệ hỗ trợ.",
"networkConnectionRefusedErr": "Không thể kết nối đến Ente, vui lòng thử lại sau. Nếu vẫn còn lỗi, xin vui lòng liên hệ hỗ trợ.",
"itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Có vẻ như đã xảy ra sự cố. Vui lòng thử lại sau một thời gian. Nếu lỗi vẫn tiếp diễn, vui lòng liên hệ với nhóm hỗ trợ của chúng tôi.",
"about": "Về chúng tôi",
"weAreOpenSource": "Chúng tôi mã nguồn mở!",
"enterYourRecoveryKey": "Nhập khôi phục của bạn",
"tempErrorContactSupportIfPersists": "Có vẻ đã xảy ra sự cố. Vui lòng thử lại sau ít phút. Nếu lỗi vẫn tiếp diễn, hãy liên hệ với đội ngũ hỗ trợ của chúng tôi.",
"networkHostLookUpErr": "Không thể kết nối với Ente, vui lòng kiểm tra cài đặt mạng của bạn và liên hệ với bộ phận hỗ trợ nếu lỗi vẫn tiếp diễn.",
"networkConnectionRefusedErr": "Không thể kết nối với Ente, vui lòng thử lại sau ít phút. Nếu lỗi vẫn tiếp diễn, hãy liên hệ với bộ phận hỗ trợ.",
"itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Có vẻ đã xảy ra sự cố. Vui lòng thử lại sau ít phút. Nếu lỗi vẫn tiếp diễn, hãy liên hệ với đội ngũ hỗ trợ của chúng tôi.",
"about": "Giới thiệu",
"weAreOpenSource": "Chúng tôi mã nguồn mở!",
"privacy": "Riêng tư",
"terms": "Điều khoản",
"checkForUpdates": "Kiểm tra cập nhật",
"checkStatus": "Kiểm tra trạng thái",
"downloadUpdate": "Tải xuống",
"criticalUpdateAvailable": "Đã có bản cập nhật quan trọng",
"updateAvailable": "Đã có bản cập nhật",
"criticalUpdateAvailable": "Cập nhật quan trọng có sẵn",
"updateAvailable": "Cập nhật có sẵn",
"update": "Cập nhật",
"checking": "Đang kiểm tra...",
"youAreOnTheLatestVersion": "Bạn đang sử dụng phiên bản mới nhất",
"warning": "Cánh báo",
"exportWarningDesc": "Tệp đã xuất chứa thông tin nhạy cảm. Hãy lưu trữ tệp này một cách an toàn.",
"warning": "Cnh báo",
"exportWarningDesc": "Tệp xuất chứa thông tin nhạy cảm. Hãy lưu trữ tệp này một cách an toàn.",
"iUnderStand": "Tôi hiểu",
"@iUnderStand": {
"description": "Text for the button to confirm the user understands the warning"
},
"authToExportCodes": "Vui lòng xác thực để xuất mã của bạn",
"importSuccessTitle": "Hoan hô!",
"authToExportCodes": "Vui lòng xác thực để xuất mã",
"importSuccessTitle": "Tuyệt!",
"importSuccessDesc": "Bạn đã nhập {count} mã!",
"@importSuccessDesc": {
"placeholders": {
@@ -316,14 +317,14 @@
}
}
},
"sorry": "Xin lỗi",
"importFailureDesc": "Không thể phân tích cú pháp tệp đã chọn.\nVui lòng viết thư cho support@ente.io nếu bạn cần trợ giúp!",
"pendingSyncs": "Cánh báo",
"sorry": "Rất tiếc",
"importFailureDesc": "Không thể phân tích tệp đã chọn.\nVui lòng liên hệ support@ente.io nếu bạn cần trợ giúp!",
"pendingSyncs": "Cnh báo",
"pendingSyncsWarningBody": "Một số mã của bạn chưa được sao lưu.\n\nVui lòng đảm bảo rằng bạn có bản sao lưu cho các mã này trước khi đăng xuất.",
"checkInboxAndSpamFolder": "Vui lòng kiểm tra hộp thư đến (và thư rác) của bạn để hoàn tất xác minh",
"tapToEnterCode": "Chạm để nhập mã",
"checkInboxAndSpamFolder": "Vui lòng kiểm tra hộp thư đến (và thư rác) để hoàn tất xác minh",
"tapToEnterCode": "Nhấn để nhập mã",
"resendEmail": "Gửi lại email",
"weHaveSendEmailTo": "Chúng tôi đã gửi thư đến <green>{email}</green>",
"weHaveSendEmailTo": "Chúng tôi đã gửi một email đến <green>{email}</green>",
"@weHaveSendEmailTo": {
"description": "Text to indicate that we have sent a mail to the user",
"placeholders": {
@@ -335,57 +336,57 @@
}
},
"manualSort": "Tùy chỉnh",
"editOrder": "Chỉnh sửa đơn hàng",
"editOrder": "Chỉnh sửa thứ tự",
"mostFrequentlyUsed": "Thường dùng",
"mostRecentlyUsed": "Dùng gần đây",
"activeSessions": "Các phiên làm việc hiện tại",
"somethingWentWrongPleaseTryAgain": "Phát hiện có lỗi, xin thử lại",
"thisWillLogYouOutOfThisDevice": "Thao tác này sẽ đăng xuất bạn khỏi thiết bị này!",
"thisWillLogYouOutOfTheFollowingDevice": "Thao tác này sẽ đăng xuất bạn khỏi thiết bị sau:",
"activeSessions": "Phiên hoạt động",
"somethingWentWrongPleaseTryAgain": "Có gì đó không ổn, vui lòng thử lại",
"thisWillLogYouOutOfThisDevice": "Bạn sẽ đăng xuất khỏi thiết bị này!",
"thisWillLogYouOutOfTheFollowingDevice": "Bạn cũng sẽ đăng xuất khỏi những thiết bị sau:",
"terminateSession": "Kết thúc phiên?",
"terminate": "Kết thúc",
"thisDevice": "Thiết bị này",
"toResetVerifyEmail": "Để đặt lại mật khẩu, vui lòng xác minh email của bạn trước.",
"thisEmailIsAlreadyInUse": "Email này đã được sử dụng",
"verificationFailedPleaseTryAgain": "Mã xác nhận thất bại. Vui lòng thử lại",
"verificationFailedPleaseTryAgain": "Xác minh không thành công, vui lòng thử lại",
"yourVerificationCodeHasExpired": "Mã xác minh của bạn đã hết hạn",
"incorrectCode": "Mã không chính xác",
"sorryTheCodeYouveEnteredIsIncorrect": "Xin lỗi, mã bạn đã nhập không chính xác",
"emailChangedTo": "Thay đổi email thành {newEmail}",
"authenticationFailedPleaseTryAgain": "Xác thực lỗi, vui lòng thử lại",
"sorryTheCodeYouveEnteredIsIncorrect": "Rất tiếc, mã bạn nhập không chính xác",
"emailChangedTo": "Email đã được đổi thành {newEmail}",
"authenticationFailedPleaseTryAgain": "Xác thực không thành công, vui lòng thử lại",
"authenticationSuccessful": "Xác thực thành công!",
"twofactorAuthenticationSuccessfullyReset": "Xác thực hai bước được khôi phục thành công",
"incorrectRecoveryKey": "Khóa khôi phục không chính xác",
"theRecoveryKeyYouEnteredIsIncorrect": "Khóa khôi phục bạn đã nhập không chính xác",
"twofactorAuthenticationSuccessfullyReset": "Xác thực 2 bước đã được đặt lại thành công",
"incorrectRecoveryKey": " khôi phục không chính xác",
"theRecoveryKeyYouEnteredIsIncorrect": " khôi phục bạn nhập không chính xác",
"enterPassword": "Nhập mật khẩu",
"selectExportFormat": "Chọn định dạng dữ liệu xuất",
"exportDialogDesc": "Xuất dữ liệu được mã hóa sẽ được bảo vệ bằng mật khẩu bạn chọn.",
"encrypted": "Đã mã hóa",
"exportDialogDesc": "Dữ liệu xuất được mã hóa sẽ được bảo vệ bằng mật khẩu bạn chọn.",
"encrypted": "Được mã hóa",
"plainText": "Văn bản thuần",
"passwordToEncryptExport": "Mật khẩu để giải mã dữ liệu xuất",
"export": "Xuất dữ liệu",
"export": "Xuất",
"useOffline": "Sử dụng mà không sao lưu",
"signInToBackup": "Đăng nhập để sao lưu mã của bạn",
"singIn": "Đăng nhập",
"sigInBackupReminder": "Vui lòng xuất mã của bạn để đảm bảo rằng bạn có bản sao lưu có thể khôi phục.",
"offlineModeWarning": "Bạn đã chọn tiếp tục mà không cần sao lưu. Vui lòng sao lưu thủ công để đảm bảo mã của bạn được an toàn.",
"showLargeIcons": "Hiển thị biểu tượng lớn",
"offlineModeWarning": "Bạn đã chọn tiếp tục mà không sao lưu. Vui lòng sao lưu thủ công để đảm bảo mã của bạn được an toàn.",
"showLargeIcons": "Hiện biểu tượng lớn",
"compactMode": "Chế độ thu gọn",
"shouldHideCode": "Ẩn mã",
"doubleTapToViewHiddenCode": "Bạn có thể nhấn đúp vào một mục để xem mã",
"focusOnSearchBar": "Mở tìm kiếm khi khởi động ứng dụng",
"confirmUpdatingkey": "Bạn có chắc chắn muốn cập nhật khóa bí mật không?",
"confirmUpdatingkey": "Bạn có chắc muốn cập nhật khóa bí mật không?",
"minimizeAppOnCopy": "Thu nhỏ khi sao chép",
"editCodeAuthMessage": "Xác minh để chỉnh sửa mã",
"deleteCodeAuthMessage": "Xác minh để xóa mã",
"showQRAuthMessage": "Xác minh để hiển thị mã QR",
"editCodeAuthMessage": "Xác thực để sửa mã",
"deleteCodeAuthMessage": "Xác thực để xóa mã",
"showQRAuthMessage": "Xác thực để hiện mã QR",
"confirmAccountDeleteTitle": "Xác nhận xóa tài khoản",
"confirmAccountDeleteMessage": "Tài khoản này được liên kết với các ứng dụng Ente trên các nền tảng khác, nếu bạn có sử dụng.\n\nDữ liệu đã tải lên của bạn, trên mọi nền tảng, sẽ bị lên lịch xóa và tài khoản của bạn sẽ bị xóa vĩnh viễn.",
"androidBiometricHint": "Xác định danh tính",
"confirmAccountDeleteMessage": "Tài khoản này được liên kết với các ứng dụng Ente khác, nếu bạn có ng.\n\nDữ liệu bạn đã tải lên, trên tất cả ứng dụng Ente, sẽ được lên lịch để xóa, và tài khoản của bạn sẽ bị xóa vĩnh viễn.",
"androidBiometricHint": "Xác minh danh tính",
"@androidBiometricHint": {
"description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters."
},
"androidBiometricNotRecognized": "Không nhận dạng được. Vui lòng thử lại.",
"androidBiometricNotRecognized": "Không nhận diện được. Thử lại.",
"@androidBiometricNotRecognized": {
"description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters."
},
@@ -413,19 +414,19 @@
"@androidDeviceCredentialsSetupDescription": {
"description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side."
},
"goToSettings": "Chuyển đến cài đặt",
"goToSettings": "Đi đến cài đặt",
"@goToSettings": {
"description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters."
},
"androidGoToSettingsDescription": "Xác thực sinh trắc học chưa được thiết lập trên thiết bị của bạn. Đi tới 'Cài đặt > Bảo mật' để thêm xác thực sinh trắc học.",
"androidGoToSettingsDescription": "Xác thực sinh trắc học chưa được thiết lập trên thiết bị của bạn. Đi đến 'Cài đặt > Bảo mật' để thêm xác thực sinh trắc học.",
"@androidGoToSettingsDescription": {
"description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side."
},
"iOSLockOut": "Xác thực sinh trắc học bị vô hiệu hóa. Vui lòng khóa và mở khóa màn hình của bạn để kích hoạt .",
"iOSLockOut": "Xác thực sinh trắc học đã bị vô hiệu hóa. Vui lòng khóa và mở khóa màn hình của bạn để kích hoạt lại.",
"@iOSLockOut": {
"description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side."
},
"iOSGoToSettingsDescription": "Xác thực sinh trắc học chưa được thiết lập trên thiết bị của bạn. Vui lòng bật Touch ID hoặc Face ID trên điện thoại của bạn.",
"iOSGoToSettingsDescription": "Xác thực sinh trắc học chưa được thiết lập trên thiết bị của bạn. Vui lòng kích hoạt Touch ID hoặc Face ID.",
"@iOSGoToSettingsDescription": {
"description": "Message advising the user to go to the settings and configure Biometrics for their device. It shows in a dialog on iOS side."
},
@@ -436,25 +437,25 @@
"noInternetConnection": "Không có kết nối Internet",
"pleaseCheckYourInternetConnectionAndTryAgain": "Vui lòng kiểm tra kết nối internet của bạn và thử lại.",
"signOutFromOtherDevices": "Đăng xuất khỏi các thiết bị khác",
"signOutOtherBody": "Nếu bạn cho rằng ai đó có thể biết mật khẩu của mình, bạn có thể buộc đăng xuất tất cả các thiết bị khác đang sử dụng tài khoản của mình.",
"signOutOtherBody": "Nếu bạn nghĩ rằng ai đó biết mật khẩu của bạn, hãy ép tài khoản của bạn đăng xuất khỏi tất cả thiết bị khác đang sử dụng.",
"signOutOtherDevices": "Đăng xuất khỏi các thiết bị khác",
"doNotSignOut": "Không được đăng xuất",
"hearUsWhereTitle": "Bạn biết đến Ente bằng cách nào? (không bắt buộc)",
"hearUsExplanation": "Chúng tôi không theo dõi lượt cài đặt ứng dụng. Sẽ rất hữu ích nếu bạn cho chúng tôi biết nơi bạn tìm thấy chúng tôi!",
"recoveryKeySaved": "Đã lưu khoá dự phòng vào thư mục Tải về!",
"doNotSignOut": "Không đăng xuất",
"hearUsWhereTitle": "Bạn biết Ente từ đâu? (tùy chọn)",
"hearUsExplanation": "Chúng tôi không theo dõi cài đặt ứng dụng, nên nếu bạn bật mí bạn tìm thấy chúng tôi từ đâu sẽ rất hữu ích!",
"recoveryKeySaved": "Đã lưu mã khôi phục vào thư mục Tải xuống!",
"waitingForBrowserRequest": "Đang chờ yêu cầu từ trình duyệt...",
"waitingForVerification": "Đang chờ xác thực",
"passkey": "Mã khoá",
"passKeyPendingVerification": "Đang chờ xác thực",
"loginSessionExpired": "Phiên làm việc hết hạn",
"loginSessionExpiredDetails": "Phiên làm việc hết hạn. Vui lòng đăng nhập lại.",
"developerSettingsWarning": "Bạn có chắc chắn muốn thay đổi Tuỳ chọn cho nhà phát triển không?",
"developerSettings": "Cài đặt cho nhà phát triển",
"waitingForVerification": "Đang chờ xác minh...",
"passkey": "Khóa truy cập",
"passKeyPendingVerification": "Xác minh vẫn đang chờ",
"loginSessionExpired": "Phiên đăng nhập đã hết hạn",
"loginSessionExpiredDetails": "Phiên đăng nhập của bạn đã hết hạn. Vui lòng đăng nhập lại.",
"developerSettingsWarning": "Bạn có chắc muốn thay đổi cài đặt Nhà phát triển không?",
"developerSettings": "Cài đặt Nhà phát triển",
"serverEndpoint": "Điểm cuối máy chủ",
"invalidEndpoint": "Điểm cuối không hợp lệ",
"invalidEndpointMessage": "Xin lỗi, điểm cuối bạn nhập không hợp lệ. Vui lòng nhập một điểm cuối hợp lệ và thử lại.",
"endpointUpdatedMessage": "Cập nhật điểm cuối thành công",
"customEndpoint": "Đã kết nối đến",
"endpointUpdatedMessage": "Điểm cuối đã được cập nhật thành công",
"customEndpoint": "Đã kết nối với {endpoint}",
"pinText": "Ghim",
"unpinText": "Bỏ ghim",
"pinnedCodeMessage": "{code} đã được ghim",
@@ -466,37 +467,37 @@
"create": "Tạo",
"editTag": "Sửa thẻ",
"deleteTagTitle": "Xóa thẻ?",
"deleteTagMessage": "Bạn có chắc chắn muốn xóa thẻ này không? Hành động này không thể đảo ngược.",
"deleteTagMessage": "Bạn có chắc muốn xóa thẻ này không? Hành động này không thể hoàn tác.",
"somethingWentWrongParsingCode": "Chúng tôi không thể phân tích {x} mã.",
"updateNotAvailable": "Cập nhật không khả dụng",
"viewRawCodes": "Xem mã nguồn",
"rawCodes": "Mã nguồn",
"rawCodeData": "Dữ liệu thô",
"viewRawCodes": "Xem mã thô",
"rawCodes": "Mã thô",
"rawCodeData": "Dữ liệu thô",
"appLock": "Khóa ứng dụng",
"noSystemLockFound": "Không thấy kh hệ thống",
"toEnableAppLockPleaseSetupDevicePasscodeOrScreen": "Để bật kh ứng dụng, vui lòng thiết lập mật khẩu thiết bị hoặc khóa màn hình trong cài đặt hệ thống của bạn.",
"noSystemLockFound": "Không tìm thấy khóa hệ thống",
"toEnableAppLockPleaseSetupDevicePasscodeOrScreen": "Để bật khóa ứng dụng, vui lòng thiết lập mã khóa thiết bị hoặc khóa màn hình trong cài đặt hệ thống của bạn.",
"autoLock": "Tự động khóa",
"immediately": "Tức thì",
"immediately": "Lập tức",
"reEnterPassword": "Nhập lại mật khẩu",
"reEnterPin": "Nhập lại PIN",
"next": "Tiếp theo",
"tooManyIncorrectAttempts": "Quá nhiều lần thử không chính xác",
"reEnterPin": "Nhập lại PIN",
"next": "Kế tiếp",
"tooManyIncorrectAttempts": "Thử sai nhiều lần",
"tapToUnlock": "Nhấn để mở khóa",
"setNewPassword": "Đặt lại mật khẩu",
"setNewPassword": "Đặt mật khẩu mới",
"deviceLock": "Khóa thiết bị",
"hideContent": "Ẩn nội dung",
"hideContentDescriptionAndroid": "Ẩn nội dung khi chuyển ứng dụng và chặn chụp màn hình",
"hideContentDescriptioniOS": "Ẩn nội dung khi chuyển ứng dụng",
"autoLockFeatureDescription": "Thời gian ứng dụng tự kh sau khi ở trạng thái nền",
"appLockDescription": "Chọn giữa màn hình kh mặc định của thiết bị và màn hình khoá tự chọn dùng mã PIN hoặc mật khẩu.",
"pinLock": " PIN",
"enterPin": "Nhập PIN",
"setNewPin": "Đổi mã PIN",
"importFailureDescNew": "Không thể phân tích file đã chọn.",
"hideContentDescriptioniOS": "Ẩn nội dung ứng dụng trong trình chuyển đổi ứng dụng",
"autoLockFeatureDescription": "Sau thời gian này, ứng dụng sẽ khóa sau khi được chạy ở chế độ nền",
"appLockDescription": "Chọn giữa màn hình khóa mặc định của thiết bị và màn hình khóa tùy chỉnh với PIN hoặc mật khẩu.",
"pinLock": "Khóa PIN",
"enterPin": "Nhập PIN",
"setNewPin": "Đặt PIN mới",
"importFailureDescNew": "Không thể phân tích tệp đã chọn.",
"appLockNotEnabled": "Khóa ứng dụng chưa được bật",
"appLockNotEnabledDescription": "Vui lòng bật khóa ứng dụng từ Bảo mật > Khóa ứng dụng",
"authToViewPasskey": "Vui lòng xác thực để xem mã khóa",
"appLockOfflineModeWarning": "Bạn đã chọn tiếp tục mà không có bản sao lưu. Nếu bạn quên khóa ứng dụng, bạn sẽ bị khóa khỏi việc truy cập dữ liệu của mình.",
"authToViewPasskey": "Vui lòng xác thực để xem mã truy cập",
"appLockOfflineModeWarning": "Bạn đã chọn tiếp tục mà không sao lưu. Nếu bạn quên khóa ứng dụng, bạn sẽ không thể truy cập dữ liệu của mình.",
"duplicateCodes": "Mã trùng lặp",
"noDuplicates": "✨ Không có trùng lặp",
"youveNoDuplicateCodesThatCanBeCleared": "Bạn không có mã nào bị trùng để xóa",
@@ -509,9 +510,14 @@
"dropReviewiOS": "Đánh giá ngay trên App Store",
"dropReviewAndroid": "Đánh giá ngay trên Play Store",
"supportEnte": "Hỗ trợ <bold-green>ente</bold-green>",
"giveUsAStarOnGithub": "Cho chúng tôi ngôi sao trên Github",
"free5GB": "Miễn phí 5GB cho <bold-green>ente</bold-green> Hình ảnh",
"giveUsAStarOnGithub": "Tặng sao trên GitHub",
"free5GB": "Miễn phí 5GB cho <bold-green>ente</bold-green> Photos",
"loginWithAuthAccount": "Đăng nhập bằng tài khoản Ente Auth",
"freeStorageOffer": "Giảm giá 10% cho <bold-green>ente</bold-green> Hình ảnh",
"freeStorageOfferDescription": "Sử dụng mã giảm giá \"AUTH\" để được giảm 10% trong năm đầu tiên"
"freeStorageOffer": "Giảm giá 10% cho <bold-green>ente</bold-green> Photos",
"freeStorageOfferDescription": "Dùng mã \"AUTH\" để được giảm 10% trong năm đầu tiên",
"advanced": "Nâng cao",
"algorithm": "Thuật toán",
"type": "Loại",
"period": "Thời hạn",
"digits": "Chữ số"
}

View File

@@ -5,4 +5,4 @@ export "package:flutter_gen/gen_l10n/app_localizations.dart";
extension AppLocalizationsX on BuildContext {
AppLocalizations get l10n => AppLocalizations.of(this);
}
}

View File

@@ -2,36 +2,34 @@ import 'dart:async';
import 'dart:io';
import 'package:adaptive_theme/adaptive_theme.dart';
import 'package:ente_accounts/services/user_service.dart';
import "package:ente_auth/app/view/app.dart";
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/core/constants.dart';
import 'package:ente_auth/core/logging/super_logging.dart';
import 'package:ente_auth/core/network.dart';
import 'package:ente_auth/ente_theme_data.dart';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/locale.dart';
import 'package:ente_auth/services/authenticator_service.dart';
import 'package:ente_auth/services/billing_service.dart';
import 'package:ente_auth/services/notification_service.dart';
import 'package:ente_auth/services/preference_service.dart';
import 'package:ente_auth/services/update_service.dart';
import 'package:ente_auth/services/user_service.dart';
import 'package:ente_auth/services/window_listener_service.dart';
import 'package:ente_auth/store/authenticator_db.dart';
import 'package:ente_auth/store/code_display_store.dart';
import 'package:ente_auth/store/code_store.dart';
import 'package:ente_auth/ui/home_page.dart';
import 'package:ente_auth/ui/tools/app_lock.dart';
import 'package:ente_auth/ui/tools/lock_screen.dart';
import 'package:ente_auth/ui/utils/icon_utils.dart';
import 'package:ente_auth/utils/directory_utils.dart';
import 'package:ente_auth/utils/lock_screen_settings.dart';
import 'package:ente_auth/utils/platform_util.dart';
import 'package:ente_auth/utils/window_protocol_handler.dart';
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
import 'package:ente_lock_screen/lock_screen_settings.dart';
import 'package:ente_lock_screen/ui/app_lock.dart';
import 'package:ente_lock_screen/ui/lock_screen.dart';
import 'package:ente_logging/logging.dart';
import 'package:ente_network/network.dart';
import 'package:flutter/foundation.dart';
import "package:flutter/material.dart";
import 'package:flutter_displaymode/flutter_displaymode.dart';
import 'package:logging/logging.dart';
import 'package:path_provider/path_provider.dart';
import 'package:tray_manager/tray_manager.dart';
import 'package:window_manager/window_manager.dart';
@@ -90,7 +88,6 @@ void main() async {
Future<void> _runInForeground() async {
final savedThemeMode = _themeMode(await AdaptiveTheme.getThemeMode());
final configuration = Configuration.instance;
return await _runWithLogs(() async {
_logger.info("Starting app in foreground");
try {
@@ -104,18 +101,12 @@ Future<void> _runInForeground() async {
runApp(
AppLock(
builder: (args) => App(locale: locale),
lockScreen: LockScreen(configuration),
lockScreen: const LockScreen(),
enabled: await LockScreenSettings.instance.shouldShowLockScreen(),
locale: locale,
lightTheme: lightThemeData,
darkTheme: darkThemeData,
savedThemeMode: savedThemeMode,
localeListResolutionCallback: localResolutionCallBack,
localizationsDelegates: const [
...AppLocalizations.localizationsDelegates,
],
supportedLocales: appSupportedLocales,
backgroundLockLatency: const Duration(seconds: 0),
),
);
});
@@ -161,13 +152,13 @@ Future<void> _init(bool bool, {String? via}) async {
await PreferenceService.instance.init();
await CodeStore.instance.init();
await CodeDisplayStore.instance.init();
await Configuration.instance.init([AuthenticatorDB.instance]);
await Network.instance.init(Configuration.instance);
await UserService.instance.init(Configuration.instance, const HomePage());
await Configuration.instance.init();
await Network.instance.init();
await UserService.instance.init();
await AuthenticatorService.instance.init();
await BillingService.instance.init();
await NotificationService.instance.init();
await UpdateService.instance.init();
await IconUtils.instance.init();
await LockScreenSettings.instance.init(Configuration.instance);
await LockScreenSettings.instance.init();
}

View File

@@ -81,7 +81,6 @@ class CompleteSRPSetupRequest {
);
}
}
class SrpAttributes {
final String srpUserID;
final String srpSalt;

View File

@@ -2,7 +2,7 @@ import 'dart:convert';
class BillingPlans {
final List<BillingPlan> plans;
final FreePlan? freePlan;
final FreePlan freePlan;
BillingPlans({
required this.plans,
@@ -12,12 +12,12 @@ class BillingPlans {
Map<String, dynamic> toMap() {
return {
'plans': plans.map((x) => x.toMap()).toList(),
'freePlan': freePlan?.toMap(),
'freePlan': freePlan.toMap(),
};
}
static BillingPlans fromMap(Map<String, dynamic>? map) {
if (map == null) return BillingPlans(plans: [], freePlan: null);
static fromMap(Map<String, dynamic>? map) {
if (map == null) return null;
return BillingPlans(
plans: List<BillingPlan>.from(
@@ -49,7 +49,7 @@ class FreePlan {
};
}
static FreePlan? fromMap(Map<String, dynamic>? map) {
static fromMap(Map<String, dynamic>? map) {
if (map == null) return null;
return FreePlan(
@@ -91,7 +91,7 @@ class BillingPlan {
};
}
static BillingPlan? fromMap(Map<String, dynamic>? map) {
static fromMap(Map<String, dynamic>? map) {
if (map == null) return null;
return BillingPlan(

View File

@@ -1,7 +1,7 @@
import "dart:typed_data";
import 'dart:typed_data';
import 'package:ente_base/models/key_attributes.dart';
import 'package:ente_base/models/private_key_attributes.dart';
import 'package:ente_auth/models/key_attributes.dart';
import 'package:ente_auth/models/private_key_attributes.dart';
class KeyGenResult {
final KeyAttributes keyAttributes;

View File

@@ -0,0 +1,65 @@
const freeProductID = "free";
const stripe = "stripe";
const appStore = "appstore";
const playStore = "playstore";
class Subscription {
final String productID;
final int storage;
final String originalTransactionID;
final String paymentProvider;
final int expiryTime;
final String price;
final String period;
final Attributes? attributes;
Subscription({
required this.productID,
required this.storage,
required this.originalTransactionID,
required this.paymentProvider,
required this.expiryTime,
required this.price,
required this.period,
this.attributes,
});
bool isValid() {
return expiryTime > DateTime.now().microsecondsSinceEpoch;
}
bool isYearlyPlan() {
return 'year' == period;
}
static fromMap(Map<String, dynamic>? map) {
if (map == null) return null;
return Subscription(
productID: map['productID'],
storage: map['storage'],
originalTransactionID: map['originalTransactionID'],
paymentProvider: map['paymentProvider'],
expiryTime: map['expiryTime'],
price: map['price'],
period: map['period'],
attributes: map["attributes"] != null
? Attributes.fromJson(map["attributes"])
: null,
);
}
}
class Attributes {
bool? isCancelled;
String? customerID;
Attributes({
this.isCancelled,
this.customerID,
});
Attributes.fromJson(dynamic json) {
isCancelled = json["isCancelled"];
customerID = json["customerID"];
}
}

View File

@@ -0,0 +1,146 @@
import 'dart:convert';
import 'dart:math';
import 'package:collection/collection.dart';
import 'package:ente_auth/models/subscription.dart';
class UserDetails {
final String email;
final int usage;
final int fileCount;
final int sharedCollectionsCount;
final Subscription subscription;
final FamilyData? familyData;
final ProfileData? profileData;
UserDetails(
this.email,
this.usage,
this.fileCount,
this.sharedCollectionsCount,
this.subscription,
this.familyData,
this.profileData,
);
bool isPartOfFamily() {
return familyData?.members?.isNotEmpty ?? false;
}
bool isFamilyAdmin() {
assert(isPartOfFamily(), "verify user is part of family before calling");
final FamilyMember currentUserMember = familyData!.members!
.firstWhere((element) => element.email.trim() == email.trim());
return currentUserMember.isAdmin;
}
// getFamilyOrPersonalUsage will return total usage for family if user
// belong to family group. Otherwise, it will return storage consumed by
// current user
int getFamilyOrPersonalUsage() {
return isPartOfFamily() ? familyData!.getTotalUsage() : usage;
}
int getFreeStorage() {
return max(
isPartOfFamily()
? (familyData!.storage - familyData!.getTotalUsage())
: (subscription.storage - (usage)),
0,
);
}
int getTotalStorage() {
return isPartOfFamily() ? familyData!.storage : subscription.storage;
}
factory UserDetails.fromMap(Map<String, dynamic> map) {
return UserDetails(
map['email'] as String,
map['usage'] as int,
(map['fileCount'] ?? 0) as int,
(map['sharedCollectionsCount'] ?? 0) as int,
Subscription.fromMap(map['subscription']),
FamilyData.fromMap(map['familyData']),
ProfileData.fromJson(map['profileData']),
);
}
}
class FamilyMember {
final String email;
final int usage;
final String id;
final bool isAdmin;
FamilyMember(this.email, this.usage, this.id, this.isAdmin);
factory FamilyMember.fromMap(Map<String, dynamic> map) {
return FamilyMember(
(map['email'] ?? '') as String,
map['usage'] as int,
map['id'] as String,
map['isAdmin'] as bool,
);
}
}
class ProfileData {
bool canDisableEmailMFA;
bool isEmailMFAEnabled;
bool isTwoFactorEnabled;
// Constructor with default values
ProfileData({
this.canDisableEmailMFA = false,
this.isEmailMFAEnabled = false,
this.isTwoFactorEnabled = false,
});
// Factory method to create ProfileData instance from JSON
factory ProfileData.fromJson(Map<String, dynamic>? json) {
if (json == null) null;
return ProfileData(
canDisableEmailMFA: json!['canDisableEmailMFA'] ?? false,
isEmailMFAEnabled: json['isEmailMFAEnabled'] ?? false,
isTwoFactorEnabled: json['isTwoFactorEnabled'] ?? false,
);
}
// Method to convert ProfileData instance to JSON
Map<String, dynamic> toJson() {
return {
'canDisableEmailMFA': canDisableEmailMFA,
'isEmailMFAEnabled': isEmailMFAEnabled,
'isTwoFactorEnabled': isTwoFactorEnabled,
};
}
String toJsonString() => json.encode(toJson());
}
class FamilyData {
final List<FamilyMember>? members;
// Storage available based on the family plan
final int storage;
final int expiryTime;
FamilyData(this.members, this.storage, this.expiryTime);
int getTotalUsage() {
return members!.map((e) => e.usage).toList().sum;
}
static fromMap(Map<String, dynamic>? map) {
if (map == null) return null;
assert(map['members'] != null && map['members'].length >= 0);
final members = List<FamilyMember>.from(
map['members'].map((x) => FamilyMember.fromMap(x)),
);
return FamilyData(
members,
map['storage'] as int,
map['expiryTime'] as int,
);
}
}

View File

@@ -1,18 +1,19 @@
import 'dart:async';
import 'dart:io';
import 'package:ente_accounts/pages/email_entry_page.dart';
import 'package:ente_accounts/pages/login_page.dart';
import 'package:ente_accounts/pages/password_entry_page.dart';
import 'package:ente_accounts/pages/password_reentry_page.dart';
import 'package:ente_auth/app/view/app.dart';
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/core/event_bus.dart';
import 'package:ente_auth/ente_theme_data.dart';
import 'package:ente_auth/events/trigger_logout_event.dart';
import "package:ente_auth/l10n/l10n.dart";
import 'package:ente_auth/locale.dart';
import 'package:ente_auth/theme/text_style.dart';
import 'package:ente_auth/ui/account/email_entry_page.dart';
import 'package:ente_auth/ui/account/login_page.dart';
import 'package:ente_auth/ui/account/logout_dialog.dart';
import 'package:ente_auth/ui/account/password_entry_page.dart';
import 'package:ente_auth/ui/account/password_reentry_page.dart';
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/models/button_result.dart';
@@ -23,7 +24,6 @@ import 'package:ente_auth/ui/settings/language_picker.dart';
import 'package:ente_auth/utils/dialog_util.dart';
import 'package:ente_auth/utils/navigation_util.dart';
import 'package:ente_auth/utils/toast_util.dart';
import 'package:ente_events/event_bus.dart';
import 'package:flutter/foundation.dart';
import "package:flutter/material.dart";
import 'package:local_auth/local_auth.dart';
@@ -260,22 +260,17 @@ class _OnboardingPageState extends State<OnboardingPage> {
void _navigateToSignUpPage() {
Widget page;
if (Configuration.instance.getEncryptedToken() == null) {
page = EmailEntryPage(Configuration.instance);
page = const EmailEntryPage();
} else {
// No key
if (Configuration.instance.getKeyAttributes() == null) {
// Never had a key
page = PasswordEntryPage(
Configuration.instance,
PasswordEntryMode.set,
const HomePage(),
page = const PasswordEntryPage(
mode: PasswordEntryMode.set,
);
} else if (Configuration.instance.getKey() == null) {
// Yet to decrypt the key
page = PasswordReentryPage(
Configuration.instance,
const HomePage(),
);
page = const PasswordReentryPage();
} else {
// All is well, user just has not subscribed
page = const HomePage();
@@ -293,22 +288,17 @@ class _OnboardingPageState extends State<OnboardingPage> {
void _navigateToSignInPage() {
Widget page;
if (Configuration.instance.getEncryptedToken() == null) {
page = LoginPage(Configuration.instance);
page = const LoginPage();
} else {
// No key
if (Configuration.instance.getKeyAttributes() == null) {
// Never had a key
page = PasswordEntryPage(
Configuration.instance,
PasswordEntryMode.set,
const HomePage(),
page = const PasswordEntryPage(
mode: PasswordEntryMode.set,
);
} else if (Configuration.instance.getKey() == null) {
// Yet to decrypt the key
page = PasswordReentryPage(
Configuration.instance,
const HomePage(),
);
page = const PasswordReentryPage();
} else {
// All is well, user just has not subscribed
// page = getSubscriptionPage(isOnBoarding: true);

View File

@@ -1,5 +1,6 @@
import 'dart:async';
import 'package:ente_auth/core/event_bus.dart';
import 'package:ente_auth/events/codes_updated_event.dart';
import "package:ente_auth/l10n/l10n.dart";
import 'package:ente_auth/models/all_icon_data.dart';
@@ -22,7 +23,6 @@ import 'package:ente_auth/ui/utils/icon_utils.dart';
import 'package:ente_auth/utils/dialog_util.dart';
import 'package:ente_auth/utils/toast_util.dart';
import 'package:ente_auth/utils/totp_util.dart';
import 'package:ente_events/event_bus.dart';
import "package:flutter/material.dart";
import 'package:logging/logging.dart';

View File

@@ -4,7 +4,9 @@ import 'dart:math';
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/core/errors.dart';
import 'package:ente_auth/core/event_bus.dart';
import 'package:ente_auth/events/codes_updated_event.dart';
import 'package:ente_auth/events/signed_in_event.dart';
import 'package:ente_auth/events/trigger_logout_event.dart';
import 'package:ente_auth/gateway/authenticator.dart';
import 'package:ente_auth/models/authenticator/auth_entity.dart';
@@ -15,8 +17,6 @@ import 'package:ente_auth/services/preference_service.dart';
import 'package:ente_auth/store/authenticator_db.dart';
import 'package:ente_auth/store/offline_authenticator_db.dart';
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
import 'package:ente_events/event_bus.dart';
import 'package:ente_events/models/signed_in_event.dart';
import 'package:flutter/foundation.dart';
import 'package:logging/logging.dart';
import 'package:shared_preferences/shared_preferences.dart';

View File

@@ -1,11 +1,11 @@
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:ente_accounts/ente_accounts.dart';
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/core/errors.dart';
import 'package:ente_auth/models/billing_plan.dart';
import 'package:ente_network/network.dart';
import 'package:ente_auth/core/network.dart';
import 'package:ente_auth/models/billing_plan.dart';
import 'package:ente_auth/models/subscription.dart';
import 'package:logging/logging.dart';
const kWebPaymentRedirectUrl = "https://payments.ente.io/frameRedirect";

View File

@@ -1,12 +1,12 @@
import 'dart:io';
import 'package:ente_lock_screen/auth_util.dart';
import 'package:ente_lock_screen/lock_screen_settings.dart';
import 'package:ente_lock_screen/ui/app_lock.dart';
import 'package:ente_lock_screen/ui/lock_screen_password.dart';
import 'package:ente_lock_screen/ui/lock_screen_pin.dart';
import 'package:ente_ui/utils/dialog_util.dart';
import 'package:ente_ui/utils/toast_util.dart';
import 'package:ente_auth/ui/settings/lock_screen/lock_screen_password.dart';
import 'package:ente_auth/ui/settings/lock_screen/lock_screen_pin.dart';
import 'package:ente_auth/ui/tools/app_lock.dart';
import 'package:ente_auth/utils/auth_util.dart';
import 'package:ente_auth/utils/dialog_util.dart';
import 'package:ente_auth/utils/lock_screen_settings.dart';
import 'package:ente_auth/utils/toast_util.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

View File

@@ -1,6 +1,6 @@
import 'package:ente_accounts/services/user_service.dart';
import 'package:ente_network/network.dart';
import 'package:ente_ui/utils/dialog_util.dart';
import 'package:ente_auth/core/constants.dart';
import 'package:ente_auth/core/network.dart';
import 'package:ente_auth/utils/dialog_util.dart';
import 'package:flutter/widgets.dart';
import 'package:logging/logging.dart';
import 'package:url_launcher/url_launcher_string.dart';

View File

@@ -1,5 +1,5 @@
import 'package:ente_auth/core/event_bus.dart';
import 'package:ente_auth/events/icons_changed_event.dart';
import 'package:ente_events/event_bus.dart';
import 'package:shared_preferences/shared_preferences.dart';
enum CodeSortKey {

View File

@@ -2,9 +2,9 @@ import 'dart:async';
import 'dart:io';
import 'package:ente_auth/core/constants.dart';
import 'package:ente_auth/core/network.dart';
import 'package:ente_auth/services/notification_service.dart';
import 'package:ente_auth/utils/platform_util.dart';
import 'package:ente_network/network.dart';
import 'package:logging/logging.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:shared_preferences/shared_preferences.dart';

View File

@@ -4,35 +4,35 @@ import "dart:math";
import 'package:bip39/bip39.dart' as bip39;
import 'package:dio/dio.dart';
import 'package:ente_accounts/models/delete_account.dart';
import 'package:ente_accounts/models/errors.dart';
import 'package:ente_accounts/models/sessions.dart';
import 'package:ente_accounts/models/set_keys_request.dart';
import 'package:ente_accounts/models/set_recovery_key_request.dart';
import 'package:ente_accounts/models/srp.dart';
import 'package:ente_accounts/models/two_factor.dart';
import 'package:ente_accounts/models/user_details.dart';
import 'package:ente_accounts/pages/login_page.dart';
import 'package:ente_accounts/pages/ott_verification_page.dart';
import 'package:ente_accounts/pages/passkey_page.dart';
import 'package:ente_accounts/pages/password_entry_page.dart';
import 'package:ente_accounts/pages/password_reentry_page.dart';
import 'package:ente_accounts/pages/recovery_page.dart';
import 'package:ente_accounts/pages/two_factor_authentication_page.dart';
import 'package:ente_accounts/pages/two_factor_recovery_page.dart';
import 'package:ente_base/models/key_attributes.dart';
import 'package:ente_base/models/key_gen_result.dart';
import 'package:ente_configuration/base_configuration.dart';
import 'package:ente_configuration/constants.dart';
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/core/constants.dart';
import 'package:ente_auth/core/errors.dart';
import 'package:ente_auth/core/event_bus.dart';
import 'package:ente_auth/core/network.dart';
import 'package:ente_auth/events/user_details_changed_event.dart';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/models/account/two_factor.dart';
import 'package:ente_auth/models/api/user/srp.dart';
import 'package:ente_auth/models/delete_account.dart';
import 'package:ente_auth/models/key_attributes.dart';
import 'package:ente_auth/models/key_gen_result.dart';
import 'package:ente_auth/models/sessions.dart';
import 'package:ente_auth/models/set_keys_request.dart';
import 'package:ente_auth/models/set_recovery_key_request.dart';
import 'package:ente_auth/models/user_details.dart';
import 'package:ente_auth/ui/account/login_page.dart';
import 'package:ente_auth/ui/account/ott_verification_page.dart';
import 'package:ente_auth/ui/account/password_entry_page.dart';
import 'package:ente_auth/ui/account/password_reentry_page.dart';
import 'package:ente_auth/ui/account/recovery_page.dart';
import 'package:ente_auth/ui/common/progress_dialog.dart';
import 'package:ente_auth/ui/home_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_recovery_page.dart';
import 'package:ente_auth/utils/dialog_util.dart';
import 'package:ente_auth/utils/toast_util.dart';
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
import 'package:ente_events/event_bus.dart';
import 'package:ente_events/models/user_details_changed_event.dart';
import 'package:ente_network/network.dart';
import 'package:ente_strings/ente_strings.dart';
import 'package:ente_ui/components/progress_dialog.dart';
import 'package:ente_ui/pages/base_home_page.dart';
import 'package:ente_ui/utils/dialog_util.dart';
import 'package:ente_ui/utils/toast_util.dart';
import "package:flutter/foundation.dart";
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
@@ -44,8 +44,6 @@ import "package:pointycastle/srp/srp6_verifier_generator.dart";
import 'package:shared_preferences/shared_preferences.dart';
import "package:uuid/uuid.dart";
const String kAccountsUrl = "https://accounts.ente.io";
class UserService {
static const keyHasEnabledTwoFactor = "has_enabled_two_factor";
static const keyUserDetails = "user_details";
@@ -56,19 +54,18 @@ class UserService {
final _dio = Network.instance.getDio();
final _enteDio = Network.instance.enteDio;
final _logger = Logger((UserService).toString());
final _config = Configuration.instance;
late SharedPreferences _preferences;
late ValueNotifier<String?> emailValueNotifier;
late BaseConfiguration _config;
late BaseHomePage _homePage;
UserService._privateConstructor();
static final UserService instance = UserService._privateConstructor();
Future<void> init(BaseConfiguration config, BaseHomePage homePage) async {
_config = config;
_homePage = homePage;
emailValueNotifier = ValueNotifier<String?>(config.getEmail());
Future<void> init() async {
emailValueNotifier =
ValueNotifier<String?>(Configuration.instance.getEmail());
_preferences = await SharedPreferences.getInstance();
}
@@ -80,7 +77,7 @@ class UserService {
bool isResetPasswordScreen = false,
String? purpose,
}) async {
final dialog = createProgressDialog(context, context.strings.pleaseWait);
final dialog = createProgressDialog(context, context.l10n.pleaseWait);
await dialog.show();
try {
final response = await _dio.post(
@@ -117,24 +114,24 @@ class UserService {
unawaited(
showErrorDialog(
context,
context.strings.oops,
context.strings.emailAlreadyRegistered,
context.l10n.oops,
context.l10n.emailAlreadyRegistered,
),
);
} else if (enteErrCode != null && enteErrCode == "USER_NOT_REGISTERED") {
unawaited(
showErrorDialog(
context,
context.strings.oops,
context.strings.emailNotRegistered,
context.l10n.oops,
context.l10n.emailNotRegistered,
),
);
} else if (e.response != null && e.response!.statusCode == 403) {
unawaited(
showErrorDialog(
context,
context.strings.oops,
context.strings.thisEmailIsAlreadyInUse,
context.l10n.oops,
context.l10n.thisEmailIsAlreadyInUse,
),
);
} else {
@@ -197,13 +194,6 @@ class UserService {
}
}
UserDetails? getCachedUserDetails() {
if (_preferences.containsKey(keyUserDetails)) {
return UserDetails.fromJson(_preferences.getString(keyUserDetails)!);
}
return null;
}
Future<Sessions> getActiveSessions() async {
try {
final response = await _enteDio.get("/users/sessions");
@@ -241,7 +231,7 @@ class UserService {
try {
final response = await _enteDio.post("/users/logout");
if (response.statusCode == 200) {
await _config.logout();
await Configuration.instance.logout();
Navigator.of(context).popUntil((route) => route.isFirst);
} else {
throw Exception("Log out action failed");
@@ -250,7 +240,7 @@ class UserService {
_logger.severe(e);
// check if token is already invalid
if (e is DioException && e.response?.statusCode == 401) {
await _config.logout();
await Configuration.instance.logout();
Navigator.of(context).popUntil((route) => route.isFirst);
return;
}
@@ -300,7 +290,7 @@ class UserService {
);
if (response.statusCode == 200) {
// clear data
await _config.logout();
await Configuration.instance.logout();
} else {
throw Exception("delete action failed");
}
@@ -337,7 +327,7 @@ class UserService {
Future<void> onPassKeyVerified(BuildContext context, Map response) async {
final ProgressDialog dialog =
createProgressDialog(context, context.strings.pleaseWait);
createProgressDialog(context, context.l10n.pleaseWait);
await dialog.show();
try {
final userPassword = _config.getVolatilePassword();
@@ -348,10 +338,7 @@ class UserService {
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (BuildContext context) {
return PasswordReentryPage(
_config,
_homePage,
);
return const PasswordReentryPage();
},
),
(route) => route.isFirst,
@@ -364,7 +351,7 @@ class UserService {
_config.getKeyAttributes()!,
);
_config.resetVolatilePassword();
page = _homePage;
page = const HomePage();
} else {
throw Exception("unexpected response during passkey verification");
}
@@ -392,7 +379,7 @@ class UserService {
String ott, {
bool isResettingPasswordScreen = false,
}) async {
final dialog = createProgressDialog(context, context.strings.pleaseWait);
final dialog = createProgressDialog(context, context.l10n.pleaseWait);
await dialog.show();
final verifyData = {
"email": _config.getEmail(),
@@ -418,7 +405,6 @@ class UserService {
}
if (passkeySessionID.isNotEmpty) {
page = PasskeyPage(
_config,
passkeySessionID,
totp2FASessionID: twoFASessionID,
accountsUrl: accountsUrl,
@@ -427,23 +413,15 @@ class UserService {
page = TwoFactorAuthenticationPage(twoFASessionID);
} else {
await _saveConfiguration(response);
if (_config.getEncryptedToken() != null) {
if (Configuration.instance.getEncryptedToken() != null) {
if (isResettingPasswordScreen) {
page = RecoveryPage(
_config,
_homePage,
);
page = const RecoveryPage();
} else {
page = PasswordReentryPage(
_config,
_homePage,
);
page = const PasswordReentryPage();
}
} else {
page = PasswordEntryPage(
_config,
PasswordEntryMode.set,
_homePage,
page = const PasswordEntryPage(
mode: PasswordEntryMode.set,
);
}
}
@@ -466,16 +444,16 @@ class UserService {
if (e.response != null && e.response!.statusCode == 410) {
await showErrorDialog(
context,
context.strings.oops,
context.strings.yourVerificationCodeHasExpired,
context.l10n.oops,
context.l10n.yourVerificationCodeHasExpired,
);
Navigator.of(context).pop();
} else {
// ignore: unawaited_futures
showErrorDialog(
context,
context.strings.incorrectCode,
context.strings.sorryTheCodeYouveEnteredIsIncorrect,
context.l10n.incorrectCode,
context.l10n.sorryTheCodeYouveEnteredIsIncorrect,
);
}
} catch (e) {
@@ -484,8 +462,8 @@ class UserService {
// ignore: unawaited_futures
showErrorDialog(
context,
context.strings.oops,
context.strings.verificationFailedPleaseTryAgain,
context.l10n.oops,
context.l10n.verificationFailedPleaseTryAgain,
);
}
}
@@ -500,7 +478,7 @@ class UserService {
String email,
String ott,
) async {
final dialog = createProgressDialog(context, context.strings.pleaseWait);
final dialog = createProgressDialog(context, context.l10n.pleaseWait);
await dialog.show();
try {
final response = await _enteDio.post(
@@ -512,7 +490,7 @@ class UserService {
);
await dialog.hide();
if (response.statusCode == 200) {
showShortToast(context, context.strings.emailChangedTo(email));
showShortToast(context, context.l10n.emailChangedTo(email));
await setEmail(email);
Navigator.of(context).popUntil((route) => route.isFirst);
Bus.instance.fire(UserDetailsChangedEvent());
@@ -521,8 +499,8 @@ class UserService {
// ignore: unawaited_futures
showErrorDialog(
context,
context.strings.oops,
context.strings.verificationFailedPleaseTryAgain,
context.l10n.oops,
context.l10n.verificationFailedPleaseTryAgain,
);
} on DioException catch (e) {
await dialog.hide();
@@ -530,15 +508,15 @@ class UserService {
// ignore: unawaited_futures
showErrorDialog(
context,
context.strings.oops,
context.strings.thisEmailIsAlreadyInUse,
context.l10n.oops,
context.l10n.thisEmailIsAlreadyInUse,
);
} else {
// ignore: unawaited_futures
showErrorDialog(
context,
context.strings.incorrectCode,
context.strings.authenticationFailedPleaseTryAgain,
context.l10n.incorrectCode,
context.l10n.authenticationFailedPleaseTryAgain,
);
}
} catch (e) {
@@ -547,8 +525,8 @@ class UserService {
// ignore: unawaited_futures
showErrorDialog(
context,
context.strings.oops,
context.strings.verificationFailedPleaseTryAgain,
context.l10n.oops,
context.l10n.verificationFailedPleaseTryAgain,
);
}
}
@@ -740,10 +718,9 @@ class UserService {
response.data["twoFactorSessionIDV2"] != null) {
twoFASessionID = response.data["twoFactorSessionIDV2"];
}
_config.setVolatilePassword(userPassword);
Configuration.instance.setVolatilePassword(userPassword);
if (passkeySessionID.isNotEmpty) {
page = PasskeyPage(
_config,
passkeySessionID,
totp2FASessionID: twoFASessionID,
accountsUrl: accountsUrl,
@@ -752,13 +729,13 @@ class UserService {
page = TwoFactorAuthenticationPage(twoFASessionID);
} else {
await _saveConfiguration(response);
if (_config.getEncryptedToken() != null) {
await _config.decryptSecretsAndGetKeyEncKey(
if (Configuration.instance.getEncryptedToken() != null) {
await Configuration.instance.decryptSecretsAndGetKeyEncKey(
userPassword,
_config.getKeyAttributes()!,
Configuration.instance.getKeyAttributes()!,
keyEncryptionKey: keyEncryptionKey,
);
page = _homePage;
page = const HomePage();
} else {
throw Exception("unexpected response during email verification");
}
@@ -828,7 +805,7 @@ class UserService {
String sessionID,
String code,
) async {
final dialog = createProgressDialog(context, context.strings.pleaseWait);
final dialog = createProgressDialog(context, context.l10n.pleaseWait);
await dialog.show();
try {
final response = await _dio.post(
@@ -840,16 +817,13 @@ class UserService {
);
await dialog.hide();
if (response.statusCode == 200) {
showShortToast(context, context.strings.authenticationSuccessful);
showShortToast(context, context.l10n.authenticationSuccessful);
await _saveConfiguration(response);
// ignore: unawaited_futures
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (BuildContext context) {
return PasswordReentryPage(
_config,
_homePage,
);
return const PasswordReentryPage();
},
),
(route) => route.isFirst,
@@ -864,7 +838,7 @@ class UserService {
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (BuildContext context) {
return LoginPage(_config);
return const LoginPage();
},
),
(route) => route.isFirst,
@@ -873,8 +847,8 @@ class UserService {
// ignore: unawaited_futures
showErrorDialog(
context,
context.strings.incorrectCode,
context.strings.authenticationFailedPleaseTryAgain,
context.l10n.incorrectCode,
context.l10n.authenticationFailedPleaseTryAgain,
);
}
} catch (e) {
@@ -883,8 +857,8 @@ class UserService {
// ignore: unawaited_futures
showErrorDialog(
context,
context.strings.oops,
context.strings.authenticationFailedPleaseTryAgain,
context.l10n.oops,
context.l10n.authenticationFailedPleaseTryAgain,
);
}
}
@@ -894,7 +868,7 @@ class UserService {
String sessionID,
TwoFactorType type,
) async {
final dialog = createProgressDialog(context, context.strings.pleaseWait);
final dialog = createProgressDialog(context, context.l10n.pleaseWait);
await dialog.show();
try {
final response = await _dio.get(
@@ -925,12 +899,12 @@ class UserService {
await dialog.hide();
_logger.severe(e);
if (e.response != null && e.response!.statusCode == 404) {
showToast(context, context.strings.sessionExpired);
showToast(context, context.l10n.sessionExpired);
// ignore: unawaited_futures
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (BuildContext context) {
return LoginPage(_config);
return const LoginPage();
},
),
(route) => route.isFirst,
@@ -939,8 +913,8 @@ class UserService {
// ignore: unawaited_futures
showErrorDialog(
context,
context.strings.oops,
context.strings.somethingWentWrongPleaseTryAgain,
context.l10n.oops,
context.l10n.somethingWentWrongPleaseTryAgain,
);
}
} catch (e) {
@@ -949,8 +923,8 @@ class UserService {
// ignore: unawaited_futures
showErrorDialog(
context,
context.strings.oops,
context.strings.somethingWentWrongPleaseTryAgain,
context.l10n.oops,
context.l10n.somethingWentWrongPleaseTryAgain,
);
} finally {
await dialog.hide();
@@ -965,7 +939,7 @@ class UserService {
String encryptedSecret,
String secretDecryptionNonce,
) async {
final dialog = createProgressDialog(context, context.strings.pleaseWait);
final dialog = createProgressDialog(context, context.l10n.pleaseWait);
await dialog.show();
String secret;
try {
@@ -988,8 +962,8 @@ class UserService {
await dialog.hide();
await showErrorDialog(
context,
context.strings.incorrectRecoveryKey,
context.strings.theRecoveryKeyYouEnteredIsIncorrect,
context.l10n.incorrectRecoveryKey,
context.l10n.theRecoveryKeyYouEnteredIsIncorrect,
);
return;
}
@@ -1006,17 +980,14 @@ class UserService {
if (response.statusCode == 200) {
showShortToast(
context,
context.strings.twofactorAuthenticationSuccessfullyReset,
context.l10n.twofactorAuthenticationSuccessfullyReset,
);
await _saveConfiguration(response);
// ignore: unawaited_futures
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (BuildContext context) {
return PasswordReentryPage(
_config,
_homePage,
);
return const PasswordReentryPage();
},
),
(route) => route.isFirst,
@@ -1031,7 +1002,7 @@ class UserService {
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (BuildContext context) {
return LoginPage(_config);
return const LoginPage();
},
),
(route) => route.isFirst,
@@ -1040,8 +1011,8 @@ class UserService {
// ignore: unawaited_futures
showErrorDialog(
context,
context.strings.oops,
context.strings.somethingWentWrongPleaseTryAgain,
context.l10n.oops,
context.l10n.somethingWentWrongPleaseTryAgain,
);
}
} catch (e) {
@@ -1050,8 +1021,8 @@ class UserService {
// ignore: unawaited_futures
showErrorDialog(
context,
context.strings.oops,
context.strings.somethingWentWrongPleaseTryAgain,
context.l10n.oops,
context.l10n.somethingWentWrongPleaseTryAgain,
);
} finally {
await dialog.hide();
@@ -1062,14 +1033,15 @@ class UserService {
final responseData = response is Map ? response : response.data as Map?;
if (responseData == null) return;
await _config.setUserID(responseData["id"]);
await Configuration.instance.setUserID(responseData["id"]);
if (responseData["encryptedToken"] != null) {
await _config.setEncryptedToken(responseData["encryptedToken"]);
await _config.setKeyAttributes(
await Configuration.instance
.setEncryptedToken(responseData["encryptedToken"]);
await Configuration.instance.setKeyAttributes(
KeyAttributes.fromMap(responseData["keyAttributes"]),
);
} else {
await _config.setToken(responseData["token"]);
await Configuration.instance.setToken(responseData["token"]);
}
}

View File

@@ -4,14 +4,13 @@ import 'dart:io';
import 'package:ente_auth/models/authenticator/auth_entity.dart';
import 'package:ente_auth/models/authenticator/local_auth_entity.dart';
import 'package:ente_auth/utils/directory_utils.dart';
import 'package:ente_base/ente_base.dart';
import 'package:flutter/foundation.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
class AuthenticatorDB extends EnteBaseDatabase {
class AuthenticatorDB {
static const _databaseName = "ente.authenticator.db";
static const _databaseVersion = 1;

View File

@@ -3,12 +3,12 @@ import 'dart:convert';
import 'package:collection/collection.dart';
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/core/event_bus.dart';
import 'package:ente_auth/events/codes_updated_event.dart';
import 'package:ente_auth/models/authenticator/entity_result.dart';
import 'package:ente_auth/models/code.dart';
import 'package:ente_auth/services/authenticator_service.dart';
import 'package:ente_auth/store/offline_authenticator_db.dart';
import 'package:ente_events/event_bus.dart';
import 'package:logging/logging.dart';
class CodeStore {

View File

@@ -1,7 +1,7 @@
import 'package:ente_accounts/ente_accounts.dart';
import 'package:ente_strings/ente_strings.dart';
import 'package:ente_ui/utils/dialog_util.dart';
import 'package:ente_utils/email_util.dart';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/services/user_service.dart';
import 'package:ente_auth/utils/dialog_util.dart';
import 'package:ente_auth/utils/email_util.dart';
import 'package:flutter/material.dart';
class ChangeEmailDialog extends StatefulWidget {
@@ -16,8 +16,9 @@ class _ChangeEmailDialogState extends State<ChangeEmailDialog> {
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
return AlertDialog(
title: Text(context.strings.enterNewEmailHint),
title: Text(l10n.enterNewEmailHint),
content: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
@@ -25,7 +26,7 @@ class _ChangeEmailDialogState extends State<ChangeEmailDialog> {
children: [
TextFormField(
decoration: InputDecoration(
hintText: context.strings.email,
hintText: l10n.email,
hintStyle: const TextStyle(
color: Colors.white30,
),
@@ -47,7 +48,7 @@ class _ChangeEmailDialogState extends State<ChangeEmailDialog> {
actions: [
TextButton(
child: Text(
context.strings.cancel,
l10n.cancel,
style: const TextStyle(
color: Colors.redAccent,
),
@@ -58,7 +59,7 @@ class _ChangeEmailDialogState extends State<ChangeEmailDialog> {
),
TextButton(
child: Text(
context.strings.verify,
l10n.verify,
style: const TextStyle(
color: Colors.purple,
),
@@ -67,8 +68,8 @@ class _ChangeEmailDialogState extends State<ChangeEmailDialog> {
if (!isValidEmail(_email)) {
showErrorDialog(
context,
context.strings.invalidEmailTitle,
context.strings.invalidEmailMessage,
l10n.invalidEmailTitle,
l10n.invalidEmailMessage,
);
return;
}

View File

@@ -1,31 +1,29 @@
import 'dart:convert';
import 'package:ente_accounts/ente_accounts.dart';
import 'package:ente_configuration/base_configuration.dart';
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/models/delete_account.dart';
import 'package:ente_auth/services/local_authentication_service.dart';
import 'package:ente_auth/services/user_service.dart';
import 'package:ente_auth/ui/common/dialogs.dart';
import 'package:ente_auth/ui/common/gradient_button.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:ente_lock_screen/local_authentication_service.dart';
import 'package:ente_strings/ente_strings.dart';
import 'package:ente_ui/components/buttons/gradient_button.dart';
import 'package:ente_ui/components/dialogs.dart';
import 'package:ente_ui/theme/ente_theme.dart';
import 'package:ente_utils/email_util.dart';
import 'package:ente_utils/platform_util.dart';
import 'package:flutter/material.dart';
class DeleteAccountPage extends StatelessWidget {
final BaseConfiguration config;
const DeleteAccountPage(
this.config, {
const DeleteAccountPage({
super.key,
});
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
return Scaffold(
appBar: AppBar(
elevation: 0,
title: Text(context.strings.deleteAccount),
title: Text(l10n.deleteAccount),
leading: IconButton(
icon: const Icon(Icons.arrow_back),
color: Theme.of(context).iconTheme.color,
@@ -49,7 +47,7 @@ class DeleteAccountPage extends StatelessWidget {
),
Center(
child: Text(
context.strings.deleteAccountQuery,
l10n.deleteAccountQuery,
style: Theme.of(context).textTheme.titleMedium,
),
),
@@ -76,7 +74,7 @@ class DeleteAccountPage extends StatelessWidget {
height: 24,
),
GradientButton(
text: context.strings.yesSendFeedbackAction,
text: l10n.yesSendFeedbackAction,
iconData: Icons.check,
onTap: () async {
await sendEmail(
@@ -107,7 +105,7 @@ class DeleteAccountPage extends StatelessWidget {
backgroundColor: Colors.white,
),
label: Text(
context.strings.noDeleteAccountAction,
l10n.noDeleteAccountAction,
style: const TextStyle(
color: Colors.redAccent, // same for both themes
),
@@ -145,10 +143,11 @@ class DeleteAccountPage extends StatelessWidget {
BuildContext context,
DeleteChallengeResponse response,
) async {
final l10n = context.l10n;
final hasAuthenticated =
await LocalAuthenticationService.instance.requestLocalAuthentication(
context,
context.strings.initiateAccountDeleteTitle,
l10n.initiateAccountDeleteTitle,
);
await PlatformUtil.refocusWindows();
@@ -156,11 +155,11 @@ class DeleteAccountPage extends StatelessWidget {
if (hasAuthenticated) {
final choice = await showChoiceDialogOld(
context,
context.strings.confirmAccountDeleteTitle,
context.strings.confirmAccountDeleteMessage,
firstAction: context.strings.cancel,
secondAction: context.strings.delete,
firstActionColor: getEnteColorScheme(context).surface,
l10n.confirmAccountDeleteTitle,
l10n.confirmAccountDeleteMessage,
firstAction: l10n.cancel,
secondAction: l10n.delete,
firstActionColor: Theme.of(context).colorScheme.onSurface,
secondActionColor: Colors.red,
);
if (choice != DialogUserChoice.secondChoice) {
@@ -169,9 +168,9 @@ class DeleteAccountPage extends StatelessWidget {
final decryptChallenge = CryptoUtil.openSealSync(
CryptoUtil.base642bin(response.encryptedChallenge),
CryptoUtil.base642bin(
config.getKeyAttributes()!.publicKey,
Configuration.instance.getKeyAttributes()!.publicKey,
),
config.getSecretKey()!,
Configuration.instance.getSecretKey()!,
);
final challengeResponseStr = utf8.decode(decryptChallenge);
await UserService.instance.deleteAccount(context, challengeResponseStr);
@@ -179,9 +178,10 @@ class DeleteAccountPage extends StatelessWidget {
}
Future<void> _requestEmailForDeletion(BuildContext context) async {
final l10n = context.l10n;
final AlertDialog alert = AlertDialog(
title: Text(
context.strings.deleteAccount,
l10n.deleteAccount,
style: const TextStyle(
color: Colors.red,
),
@@ -204,7 +204,7 @@ class DeleteAccountPage extends StatelessWidget {
),
],
style: TextStyle(
color: getEnteColorScheme(context).surface,
color: Theme.of(context).colorScheme.onSurface,
height: 1.5,
fontSize: 16,
),
@@ -213,7 +213,7 @@ class DeleteAccountPage extends StatelessWidget {
actions: [
TextButton(
child: Text(
context.strings.sendEmail,
l10n.sendEmail,
style: const TextStyle(
color: Colors.red,
),
@@ -229,9 +229,9 @@ class DeleteAccountPage extends StatelessWidget {
),
TextButton(
child: Text(
context.strings.ok,
l10n.ok,
style: TextStyle(
color: getEnteColorScheme(context).surface,
color: Theme.of(context).colorScheme.onSurface,
),
),
onPressed: () {

View File

@@ -1,20 +1,20 @@
import 'package:email_validator/email_validator.dart';
import 'package:ente_accounts/ente_accounts.dart';
import 'package:ente_configuration/base_configuration.dart';
import 'package:ente_strings/ente_strings.dart';
import 'package:ente_ui/components/buttons/dynamic_fab.dart';
import 'package:ente_ui/theme/ente_theme.dart';
import 'package:ente_utils/platform_util.dart';
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/ente_theme_data.dart';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/services/user_service.dart';
import 'package:ente_auth/theme/ente_theme.dart';
import 'package:ente_auth/ui/common/dynamic_fab.dart';
import 'package:ente_auth/utils/platform_util.dart';
import 'package:ente_auth/utils/toast_util.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:password_strength/password_strength.dart';
import "package:step_progress_indicator/step_progress_indicator.dart";
import 'package:step_progress_indicator/step_progress_indicator.dart';
import "package:styled_text/styled_text.dart";
class EmailEntryPage extends StatefulWidget {
final BaseConfiguration config;
const EmailEntryPage(this.config, {super.key});
const EmailEntryPage({super.key});
@override
State<EmailEntryPage> createState() => _EmailEntryPageState();
@@ -24,9 +24,10 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
static const kMildPasswordStrengthThreshold = 0.4;
static const kStrongPasswordStrengthThreshold = 0.7;
final _config = Configuration.instance;
final _passwordController1 = TextEditingController();
final _passwordController2 = TextEditingController();
Color? _validFieldValueColor;
final Color _validFieldValueColor = const Color.fromARGB(51, 157, 45, 194);
String? _email;
String? _password;
@@ -48,7 +49,7 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
@override
void initState() {
_email = widget.config.getEmail();
_email = _config.getEmail();
_password1FocusNode.addListener(() {
setState(() {
_password1InFocus = _password1FocusNode.hasFocus;
@@ -66,10 +67,6 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
Widget build(BuildContext context) {
final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100;
// Initialize theme-aware color
final colorScheme = getEnteColorScheme(context);
_validFieldValueColor = colorScheme.primary300.withOpacity(0.2);
FloatingActionButtonLocation? fabLocation() {
if (isKeypadOpen) {
return null;
@@ -92,10 +89,10 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
child: StepProgressIndicator(
totalSteps: 4,
currentStep: 1,
selectedColor: getEnteColorScheme(context).alternativeColor,
selectedColor: Theme.of(context).colorScheme.alternativeColor,
roundedEdges: const Radius.circular(10),
unselectedColor:
getEnteColorScheme(context).stepProgressUnselectedColor,
Theme.of(context).colorScheme.stepProgressUnselectedColor,
),
),
);
@@ -106,10 +103,10 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
floatingActionButton: DynamicFAB(
isKeypadOpen: isKeypadOpen,
isFormValid: _isFormValid(),
buttonText: context.strings.createAccount,
buttonText: context.l10n.createAccount,
onPressedFunction: () {
UserService.instance.setEmail(_email!);
widget.config.setVolatilePassword(_passwordController1.text);
_config.setVolatilePassword(_passwordController1.text);
UserService.instance.setRefSource(_referralSource);
UserService.instance.sendOtt(
context,
@@ -126,13 +123,13 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
}
Widget _getBody() {
var passwordStrengthText = context.strings.weakStrength;
var passwordStrengthText = context.l10n.weakStrength;
var passwordStrengthColor = Colors.redAccent;
if (_passwordStrength > kStrongPasswordStrengthThreshold) {
passwordStrengthText = context.strings.strongStrength;
passwordStrengthText = context.l10n.strongStrength;
passwordStrengthColor = Colors.greenAccent;
} else if (_passwordStrength > kMildPasswordStrengthThreshold) {
passwordStrengthText = context.strings.moderateStrength;
passwordStrengthText = context.l10n.moderateStrength;
passwordStrengthColor = Colors.orangeAccent;
}
return Column(
@@ -145,7 +142,7 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
padding:
const EdgeInsets.symmetric(vertical: 30, horizontal: 20),
child: Text(
context.strings.createNewAccount,
context.l10n.createNewAccount,
style: Theme.of(context).textTheme.headlineMedium,
),
),
@@ -157,7 +154,7 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
decoration: InputDecoration(
fillColor: _emailIsValid ? _validFieldValueColor : null,
filled: true,
hintText: context.strings.email,
hintText: context.l10n.email,
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 14,
@@ -170,7 +167,11 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
? Icon(
Icons.check,
size: 20,
color: getEnteColorScheme(context).primary300,
color: Theme.of(context)
.inputDecorationTheme
.focusedBorder!
.borderSide
.color,
)
: null,
),
@@ -202,7 +203,7 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
fillColor:
_passwordIsValid ? _validFieldValueColor : null,
filled: true,
hintText: context.strings.password,
hintText: context.l10n.password,
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 14,
@@ -225,7 +226,11 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
: _passwordIsValid
? Icon(
Icons.check,
color: getEnteColorScheme(context).primary300,
color: Theme.of(context)
.inputDecorationTheme
.focusedBorder!
.borderSide
.color,
)
: null,
border: UnderlineInputBorder(
@@ -267,7 +272,7 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
? _validFieldValueColor
: null,
filled: true,
hintText: context.strings.confirmPassword,
hintText: context.l10n.confirmPassword,
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 14,
@@ -290,7 +295,11 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
: _passwordsMatch
? Icon(
Icons.check,
color: getEnteColorScheme(context).primary300,
color: Theme.of(context)
.inputDecorationTheme
.focusedBorder!
.borderSide
.color,
)
: null,
border: UnderlineInputBorder(
@@ -315,7 +324,7 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
padding:
const EdgeInsets.symmetric(horizontal: 24, vertical: 8),
child: Text(
context.strings.passwordStrength(passwordStrengthText),
context.l10n.passwordStrength(passwordStrengthText),
style: TextStyle(
color: passwordStrengthColor,
fontWeight: FontWeight.w500,
@@ -329,7 +338,7 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
padding:
const EdgeInsets.symmetric(vertical: 0, horizontal: 20),
child: Text(
context.strings.hearUsWhereTitle,
context.l10n.hearUsWhereTitle,
style: getEnteTextTheme(context).smallFaint,
),
),
@@ -353,7 +362,7 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
onTap: () {
showToast(
context,
context.strings.hearUsExplanation,
context.l10n.hearUsExplanation,
);
},
child: Icon(
@@ -415,7 +424,7 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
),
Expanded(
child: StyledText(
text: context.strings.signUpTerms,
text: context.l10n.signUpTerms,
style: Theme.of(context)
.textTheme
.titleMedium!
@@ -425,7 +434,7 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
(String? text, Map<String?, String?> attrs) =>
PlatformUtil.openWebView(
context,
context.strings.termsOfServicesTitle,
context.l10n.termsOfServicesTitle,
"https://ente.io/terms",
),
style: const TextStyle(
@@ -436,7 +445,7 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
(String? text, Map<String?, String?> attrs) =>
PlatformUtil.openWebView(
context,
context.strings.privacyPolicyTitle,
context.l10n.privacyPolicyTitle,
"https://ente.io/privacy",
),
style: const TextStyle(
@@ -472,7 +481,7 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
),
Expanded(
child: StyledText(
text: context.strings.ackPasswordLostWarning,
text: context.l10n.ackPasswordLostWarning,
style: Theme.of(context)
.textTheme
.titleMedium!
@@ -482,7 +491,7 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
(String? text, Map<String?, String?> attrs) =>
PlatformUtil.openWebView(
context,
context.strings.encryption,
context.l10n.encryption,
"https://ente.io/architecture",
),
style: const TextStyle(
@@ -504,6 +513,4 @@ class _EmailEntryPageState extends State<EmailEntryPage> {
_hasAgreedToE2E &&
_passwordIsValid;
}
void showToast(BuildContext context, String hearUsExplanation) {}
}

View File

@@ -1,26 +1,25 @@
import "package:email_validator/email_validator.dart";
import 'package:ente_accounts/ente_accounts.dart';
import 'package:ente_accounts/models/errors.dart';
import 'package:ente_configuration/base_configuration.dart';
import "package:ente_strings/ente_strings.dart";
import 'package:ente_ui/components/buttons/dynamic_fab.dart';
import 'package:ente_ui/theme/ente_theme.dart';
import 'package:ente_utils/platform_util.dart';
import 'package:email_validator/email_validator.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/models/api/user/srp.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/common/dynamic_fab.dart';
import 'package:ente_auth/utils/platform_util.dart';
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import "package:styled_text/tags/styled_text_tag_action.dart";
import "package:styled_text/widgets/styled_text.dart";
import "package:styled_text/styled_text.dart";
class LoginPage extends StatefulWidget {
final BaseConfiguration config;
const LoginPage(this.config, {super.key});
const LoginPage({super.key});
@override
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final _config = Configuration.instance;
bool _emailIsValid = false;
String? _email;
Color? _emailInputFieldColor;
@@ -28,7 +27,7 @@ class _LoginPageState extends State<LoginPage> {
Future<void> onPressed() async {
await UserService.instance.setEmail(_email!);
widget.config.resetVolatilePassword();
Configuration.instance.resetVolatilePassword();
SrpAttributes? attr;
bool isEmailVerificationEnabled = true;
try {
@@ -44,8 +43,7 @@ class _LoginPageState extends State<LoginPage> {
MaterialPageRoute(
builder: (BuildContext context) {
return LoginPasswordVerificationPage(
widget.config,
attr!,
srpAttributes: attr!,
);
},
),
@@ -63,7 +61,7 @@ class _LoginPageState extends State<LoginPage> {
@override
void initState() {
_email = widget.config.getEmail();
_email = _config.getEmail();
super.initState();
}
@@ -95,7 +93,7 @@ class _LoginPageState extends State<LoginPage> {
floatingActionButton: DynamicFAB(
isKeypadOpen: isKeypadOpen,
isFormValid: _emailIsValid,
buttonText: context.strings.logInLabel,
buttonText: context.l10n.logInLabel,
onPressedFunction: onPressed,
),
floatingActionButtonLocation: fabLocation(),
@@ -104,6 +102,7 @@ class _LoginPageState extends State<LoginPage> {
}
Widget _getBody() {
final l10n = context.l10n;
return Column(
children: [
Expanded(
@@ -114,7 +113,7 @@ class _LoginPageState extends State<LoginPage> {
padding:
const EdgeInsets.symmetric(vertical: 30, horizontal: 20),
child: Text(
context.strings.welcomeBack,
l10n.welcomeBack,
style: Theme.of(context).textTheme.headlineMedium,
),
),
@@ -127,7 +126,7 @@ class _LoginPageState extends State<LoginPage> {
decoration: InputDecoration(
fillColor: _emailInputFieldColor,
filled: true,
hintText: context.strings.email,
hintText: l10n.email,
contentPadding: const EdgeInsets.symmetric(
horizontal: 15,
vertical: 15,
@@ -140,7 +139,11 @@ class _LoginPageState extends State<LoginPage> {
? Icon(
Icons.check,
size: 20,
color: getEnteColorScheme(context).primary300,
color: Theme.of(context)
.inputDecorationTheme
.focusedBorder!
.borderSide
.color,
)
: null,
),
@@ -149,9 +152,8 @@ class _LoginPageState extends State<LoginPage> {
_email = value.trim();
_emailIsValid = EmailValidator.validate(_email!);
if (_emailIsValid) {
_emailInputFieldColor = getEnteColorScheme(context)
.primary300
.withOpacity(0.2);
_emailInputFieldColor =
const Color.fromARGB(51, 157, 45, 194);
} else {
_emailInputFieldColor = null;
}
@@ -176,14 +178,17 @@ class _LoginPageState extends State<LoginPage> {
Expanded(
flex: 5,
child: StyledText(
text: context.strings.loginTerms,
style: getEnteTextTheme(context).small,
text: context.l10n.loginTerms,
style: Theme.of(context)
.textTheme
.titleMedium!
.copyWith(fontSize: 12),
tags: {
'u-terms': StyledTextActionTag(
(String? text, Map<String?, String?> attrs) =>
PlatformUtil.openWebView(
context,
context.strings.termsOfServicesTitle,
context.l10n.termsOfServicesTitle,
"https://ente.io/terms",
),
style: const TextStyle(
@@ -194,7 +199,7 @@ class _LoginPageState extends State<LoginPage> {
(String? text, Map<String?, String?> attrs) =>
PlatformUtil.openWebView(
context,
context.strings.privacyPolicyTitle,
context.l10n.privacyPolicyTitle,
"https://ente.io/privacy",
),
style: const TextStyle(

View File

@@ -1,13 +1,14 @@
import "package:dio/dio.dart";
import "package:ente_accounts/ente_accounts.dart";
import "package:ente_configuration/base_configuration.dart";
import 'package:ente_auth/core/configuration.dart';
import "package:ente_auth/l10n/l10n.dart";
import "package:ente_auth/models/api/user/srp.dart";
import "package:ente_auth/services/user_service.dart";
import "package:ente_auth/theme/ente_theme.dart";
import 'package:ente_auth/ui/common/dynamic_fab.dart';
import "package:ente_auth/ui/components/buttons/button_widget.dart";
import "package:ente_auth/utils/dialog_util.dart";
import "package:ente_auth/utils/email_util.dart";
import "package:ente_crypto_dart/ente_crypto_dart.dart";
import "package:ente_strings/ente_strings.dart";
import "package:ente_ui/components/buttons/button_widget.dart";
import "package:ente_ui/components/buttons/dynamic_fab.dart";
import "package:ente_ui/theme/ente_theme.dart";
import "package:ente_ui/utils/dialog_util.dart";
import "package:ente_utils/email_util.dart";
import 'package:flutter/material.dart';
import "package:logging/logging.dart";
@@ -17,13 +18,8 @@ import "package:logging/logging.dart";
// In the PasswordReentryPage, the password is auto-filled based on the
// volatile password.
class LoginPasswordVerificationPage extends StatefulWidget {
final BaseConfiguration config;
final SrpAttributes srpAttributes;
const LoginPasswordVerificationPage(
this.config,
this.srpAttributes, {
super.key,
});
const LoginPasswordVerificationPage({super.key, required this.srpAttributes});
@override
State<LoginPasswordVerificationPage> createState() =>
@@ -47,7 +43,7 @@ class _LoginPasswordVerificationPageState
@override
void initState() {
super.initState();
email = widget.config.getEmail();
email = Configuration.instance.getEmail();
_passwordFocusNode.addListener(() {
setState(() {
_passwordInFocus = _passwordFocusNode.hasFocus;
@@ -84,7 +80,7 @@ class _LoginPasswordVerificationPageState
key: const ValueKey("verifyPasswordButton"),
isKeypadOpen: isKeypadOpen,
isFormValid: _passwordController.text.isNotEmpty,
buttonText: context.strings.logInLabel,
buttonText: context.l10n.logInLabel,
onPressedFunction: onPressed,
),
floatingActionButtonLocation: fabLocation(),
@@ -95,7 +91,7 @@ class _LoginPasswordVerificationPageState
Future<void> verifyPassword(BuildContext context, String password) async {
final dialog = createProgressDialog(
context,
context.strings.pleaseWait,
context.l10n.pleaseWait,
isDismissible: true,
);
await dialog.show();
@@ -112,22 +108,22 @@ class _LoginPasswordVerificationPageState
_logger.severe('server reject, failed verify SRP login', e, s);
await _showContactSupportDialog(
context,
context.strings.incorrectPasswordTitle,
context.strings.pleaseTryAgain,
context.l10n.incorrectPasswordTitle,
context.l10n.pleaseTryAgain,
);
} else {
_logger.severe('API failure during SRP login', e, s);
if (e.type == DioExceptionType.connectionError) {
await _showContactSupportDialog(
context,
context.strings.noInternetConnection,
context.strings.pleaseCheckYourInternetConnectionAndTryAgain,
context.l10n.noInternetConnection,
context.l10n.pleaseCheckYourInternetConnectionAndTryAgain,
);
} else {
await _showContactSupportDialog(
context,
context.strings.oops,
context.strings.verificationFailedPleaseTryAgain,
context.l10n.oops,
context.l10n.verificationFailedPleaseTryAgain,
);
}
}
@@ -147,9 +143,9 @@ class _LoginPasswordVerificationPageState
// device is not powerful enough to perform derive key
final dialogChoice = await showChoiceDialog(
context,
title: context.strings.recreatePasswordTitle,
body: context.strings.recreatePasswordBody,
firstButtonLabel: context.strings.useRecoveryKey,
title: context.l10n.recreatePasswordTitle,
body: context.l10n.recreatePasswordBody,
firstButtonLabel: context.l10n.useRecoveryKey,
);
if (dialogChoice!.action == ButtonAction.first) {
await UserService.instance.sendOtt(
@@ -163,8 +159,8 @@ class _LoginPasswordVerificationPageState
_logger.severe('unexpected error while verifying password', e, s);
await _showContactSupportDialog(
context,
context.strings.oops,
context.strings.verificationFailedPleaseTryAgain,
context.l10n.oops,
context.l10n.verificationFailedPleaseTryAgain,
);
}
}
@@ -179,13 +175,13 @@ class _LoginPasswordVerificationPageState
context,
title: title,
body: message,
firstButtonLabel: context.strings.contactSupport,
secondButtonLabel: context.strings.ok,
firstButtonLabel: context.l10n.contactSupport,
secondButtonLabel: context.l10n.ok,
);
if (dialogChoice!.action == ButtonAction.first) {
await sendLogs(
context,
context.strings.contactSupport,
context.l10n.contactSupport,
postShare: () {},
);
}
@@ -201,7 +197,7 @@ class _LoginPasswordVerificationPageState
Padding(
padding: const EdgeInsets.only(top: 30, left: 20, right: 20),
child: Text(
context.strings.enterPassword,
context.l10n.enterPassword,
style: Theme.of(context).textTheme.headlineMedium,
),
),
@@ -239,7 +235,7 @@ class _LoginPasswordVerificationPageState
key: const ValueKey("passwordInputField"),
autofillHints: const [AutofillHints.password],
decoration: InputDecoration(
hintText: context.strings.enterYourPasswordHint,
hintText: context.l10n.enterYourPasswordHint,
filled: true,
contentPadding: const EdgeInsets.all(20),
border: UnderlineInputBorder(
@@ -299,7 +295,7 @@ class _LoginPasswordVerificationPageState
},
child: Center(
child: Text(
context.strings.forgotPassword,
context.l10n.forgotPassword,
style: Theme.of(context)
.textTheme
.titleMedium!
@@ -315,17 +311,17 @@ class _LoginPasswordVerificationPageState
onTap: () async {
final dialog = createProgressDialog(
context,
context.strings.pleaseWait,
context.l10n.pleaseWait,
);
await dialog.show();
await widget.config.logout();
await Configuration.instance.logout();
await dialog.hide();
Navigator.of(context)
.popUntil((route) => route.isFirst);
},
child: Center(
child: Text(
context.strings.changeEmail,
context.l10n.changeEmail,
style: Theme.of(context)
.textTheme
.titleMedium!

View File

@@ -1,7 +1,7 @@
import 'package:ente_accounts/ente_accounts.dart';
import 'package:ente_strings/ente_strings.dart';
import 'package:ente_ui/components/buttons/dynamic_fab.dart';
import 'package:ente_ui/theme/ente_theme.dart';
import 'package:ente_auth/ente_theme_data.dart';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/services/user_service.dart';
import 'package:ente_auth/ui/common/dynamic_fab.dart';
import 'package:flutter/material.dart';
import 'package:step_progress_indicator/step_progress_indicator.dart';
import 'package:styled_text/styled_text.dart';
@@ -46,6 +46,7 @@ class _OTTVerificationPageState extends State<OTTVerificationPage> {
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100;
FloatingActionButtonLocation? fabLocation() {
@@ -73,10 +74,10 @@ class _OTTVerificationPageState extends State<OTTVerificationPage> {
child: StepProgressIndicator(
totalSteps: 4,
currentStep: 2,
selectedColor: getEnteColorScheme(context).alternativeColor,
selectedColor: Theme.of(context).colorScheme.alternativeColor,
roundedEdges: const Radius.circular(10),
unselectedColor:
getEnteColorScheme(context).stepProgressUnselectedColor,
Theme.of(context).colorScheme.stepProgressUnselectedColor,
),
)
: null,
@@ -85,7 +86,7 @@ class _OTTVerificationPageState extends State<OTTVerificationPage> {
floatingActionButton: DynamicFAB(
isKeypadOpen: isKeypadOpen,
isFormValid: _verificationCodeController.text.isNotEmpty,
buttonText: context.strings.verify,
buttonText: l10n.verify,
onPressedFunction: onPressed,
),
floatingActionButtonLocation: fabLocation(),
@@ -94,6 +95,7 @@ class _OTTVerificationPageState extends State<OTTVerificationPage> {
}
Widget _getBody() {
final l10n = context.l10n;
return ListView(
children: [
Column(
@@ -102,7 +104,7 @@ class _OTTVerificationPageState extends State<OTTVerificationPage> {
Padding(
padding: const EdgeInsets.fromLTRB(20, 30, 20, 15),
child: Text(
context.strings.verifyEmail,
l10n.verifyEmail,
style: Theme.of(context).textTheme.headlineMedium,
),
),
@@ -117,8 +119,7 @@ class _OTTVerificationPageState extends State<OTTVerificationPage> {
Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 12),
child: StyledText(
text:
context.strings.weHaveSendEmailTo(widget.email),
text: l10n.weHaveSendEmailTo(widget.email),
style: Theme.of(context)
.textTheme
.titleMedium!
@@ -126,7 +127,8 @@ class _OTTVerificationPageState extends State<OTTVerificationPage> {
tags: {
'green': StyledTextTag(
style: TextStyle(
color: getEnteColorScheme(context)
color: Theme.of(context)
.colorScheme
.alternativeColor,
),
),
@@ -135,14 +137,14 @@ class _OTTVerificationPageState extends State<OTTVerificationPage> {
),
widget.isResetPasswordScreen
? Text(
context.strings.toResetVerifyEmail,
l10n.toResetVerifyEmail,
style: Theme.of(context)
.textTheme
.titleMedium!
.copyWith(fontSize: 14),
)
: Text(
context.strings.checkInboxAndSpamFolder,
l10n.checkInboxAndSpamFolder,
style: Theme.of(context)
.textTheme
.titleMedium!
@@ -167,7 +169,7 @@ class _OTTVerificationPageState extends State<OTTVerificationPage> {
: null,
decoration: InputDecoration(
filled: true,
hintText: context.strings.tapToEnterCode,
hintText: l10n.tapToEnterCode,
contentPadding: const EdgeInsets.all(15),
border: UnderlineInputBorder(
borderSide: BorderSide.none,
@@ -202,7 +204,7 @@ class _OTTVerificationPageState extends State<OTTVerificationPage> {
);
},
child: Text(
context.strings.resendEmail,
l10n.resendEmail,
style: Theme.of(context).textTheme.titleMedium!.copyWith(
fontSize: 14,
decoration: TextDecoration.underline,

View File

@@ -1,14 +1,15 @@
import 'package:ente_accounts/ente_accounts.dart';
import 'package:ente_configuration/base_configuration.dart';
import 'package:ente_strings/ente_strings.dart';
import 'package:ente_ui/components/buttons/dynamic_fab.dart';
import 'package:ente_ui/components/buttons/models/button_type.dart';
import 'package:ente_ui/pages/base_home_page.dart';
import 'package:ente_ui/theme/ente_theme.dart';
import 'package:ente_ui/utils/dialog_util.dart';
import 'package:ente_ui/utils/toast_util.dart';
import 'package:ente_utils/navigation_util.dart';
import 'package:ente_utils/platform_util.dart';
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/models/key_gen_result.dart';
import 'package:ente_auth/services/user_service.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/components/models/button_type.dart';
import 'package:ente_auth/ui/home_page.dart';
import 'package:ente_auth/utils/dialog_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:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:logging/logging.dart';
@@ -22,16 +23,9 @@ enum PasswordEntryMode {
}
class PasswordEntryPage extends StatefulWidget {
final BaseConfiguration config;
final PasswordEntryMode mode;
final BaseHomePage homePage;
const PasswordEntryPage(
this.config,
this.mode,
this.homePage, {
super.key,
});
const PasswordEntryPage({required this.mode, super.key});
@override
State<PasswordEntryPage> createState() => _PasswordEntryPageState();
@@ -44,7 +38,7 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
final _logger = Logger((_PasswordEntryPageState).toString());
final _passwordController1 = TextEditingController(),
_passwordController2 = TextEditingController();
Color? _validFieldValueColor;
final Color _validFieldValueColor = const Color.fromRGBO(45, 194, 98, 0.2);
String? _volatilePassword;
String _passwordInInputBox = '';
String _passwordInInputConfirmationBox = '';
@@ -62,7 +56,7 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
@override
void initState() {
super.initState();
_volatilePassword = widget.config.getVolatilePassword();
_volatilePassword = Configuration.instance.getVolatilePassword();
if (_volatilePassword != null) {
Future.delayed(
Duration.zero,
@@ -88,10 +82,6 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
Widget build(BuildContext context) {
final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100;
// Initialize theme-aware color
final colorScheme = getEnteColorScheme(context);
_validFieldValueColor = colorScheme.fillFaint.withOpacity(0.5);
FloatingActionButtonLocation? fabLocation() {
if (isKeypadOpen) {
return null;
@@ -100,13 +90,13 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
}
}
String title = context.strings.setPasswordTitle;
String title = context.l10n.setPasswordTitle;
if (widget.mode == PasswordEntryMode.update) {
title = context.strings.changePasswordTitle;
title = context.l10n.changePasswordTitle;
} else if (widget.mode == PasswordEntryMode.reset) {
title = context.strings.resetPasswordTitle;
title = context.l10n.resetPasswordTitle;
} else if (_volatilePassword != null) {
title = context.strings.encryptionKeys;
title = context.l10n.encryptionKeys;
}
return Scaffold(
resizeToAvoidBottomInset: isKeypadOpen,
@@ -142,14 +132,14 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
}
Widget _getBody(String buttonTextAndHeading) {
final email = widget.config.getEmail();
var passwordStrengthText = context.strings.weakStrength;
final email = Configuration.instance.getEmail();
var passwordStrengthText = context.l10n.weakStrength;
var passwordStrengthColor = Colors.redAccent;
if (_passwordStrength > kStrongPasswordStrengthThreshold) {
passwordStrengthText = context.strings.strongStrength;
passwordStrengthText = context.l10n.strongStrength;
passwordStrengthColor = Colors.greenAccent;
} else if (_passwordStrength > kMildPasswordStrengthThreshold) {
passwordStrengthText = context.strings.moderateStrength;
passwordStrengthText = context.l10n.moderateStrength;
passwordStrengthColor = Colors.orangeAccent;
}
if (_volatilePassword != null) {
@@ -177,8 +167,8 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Text(
widget.mode == PasswordEntryMode.set
? context.strings.enterPasswordToEncrypt
: context.strings.enterNewPasswordToEncrypt,
? context.l10n.enterPasswordToEncrypt
: context.l10n.enterNewPasswordToEncrypt,
textAlign: TextAlign.start,
style: Theme.of(context)
.textTheme
@@ -190,7 +180,7 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: StyledText(
text: context.strings.passwordWarning,
text: context.l10n.passwordWarning,
style: Theme.of(context)
.textTheme
.titleMedium!
@@ -235,7 +225,7 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
fillColor:
_isPasswordValid ? _validFieldValueColor : null,
filled: true,
hintText: context.strings.password,
hintText: context.l10n.password,
contentPadding: const EdgeInsets.all(20),
border: UnderlineInputBorder(
borderSide: BorderSide.none,
@@ -259,8 +249,11 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
: _isPasswordValid
? Icon(
Icons.check,
color:
getEnteColorScheme(context).primary300,
color: Theme.of(context)
.inputDecorationTheme
.focusedBorder!
.borderSide
.color,
)
: null,
),
@@ -298,7 +291,7 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
fillColor:
_passwordsMatch ? _validFieldValueColor : null,
filled: true,
hintText: context.strings.confirmPassword,
hintText: context.l10n.confirmPassword,
contentPadding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 20,
@@ -321,8 +314,11 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
: _passwordsMatch
? Icon(
Icons.check,
color:
getEnteColorScheme(context).primary300,
color: Theme.of(context)
.inputDecorationTheme
.focusedBorder!
.borderSide
.color,
)
: null,
border: UnderlineInputBorder(
@@ -352,7 +348,7 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
vertical: 8,
),
child: Text(
context.strings.passwordStrength(passwordStrengthText),
context.l10n.passwordStrength(passwordStrengthText),
style: TextStyle(
color: passwordStrengthColor,
),
@@ -365,7 +361,7 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
onTap: () {
PlatformUtil.openWebView(
context,
context.strings.howItWorks,
context.l10n.howItWorks,
"https://ente.io/architecture",
);
},
@@ -373,7 +369,7 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
padding: const EdgeInsets.symmetric(horizontal: 20),
child: RichText(
text: TextSpan(
text: context.strings.howItWorks,
text: context.l10n.howItWorks,
style:
Theme.of(context).textTheme.titleMedium!.copyWith(
fontSize: 14,
@@ -396,10 +392,10 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
void _updatePassword() async {
final logOutFromOthers = await logOutFromOtherDevices(context);
final dialog =
createProgressDialog(context, context.strings.generatingEncryptionKeys);
createProgressDialog(context, context.l10n.generatingEncryptionKeys);
await dialog.show();
try {
final result = await widget.config
final result = await Configuration.instance
.getAttributesForNewPassword(_passwordController1.text);
await UserService.instance.updateKeyAttributes(
result.item1,
@@ -407,7 +403,7 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
logoutOtherDevices: logOutFromOthers,
);
await dialog.hide();
showShortToast(context, context.strings.passwordChangedSuccessfully);
showShortToast(context, context.l10n.passwordChangedSuccessfully);
Navigator.of(context).pop();
if (widget.mode == PasswordEntryMode.reset) {
Navigator.of(context).popUntil((route) => route.isFirst);
@@ -427,15 +423,15 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
bool logOutFromOther = true;
await showChoiceDialog(
context,
title: context.strings.signOutFromOtherDevices,
body: context.strings.signOutOtherBody,
title: context.l10n.signOutFromOtherDevices,
body: context.l10n.signOutOtherBody,
isDismissible: false,
firstButtonLabel: context.strings.signOutOtherDevices,
firstButtonLabel: context.l10n.signOutOtherDevices,
firstButtonType: ButtonType.critical,
firstButtonOnTap: () async {
logOutFromOther = true;
},
secondButtonLabel: context.strings.doNotSignOut,
secondButtonLabel: context.l10n.doNotSignOut,
secondButtonOnTap: () async {
logOutFromOther = false;
},
@@ -447,31 +443,30 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
String password, {
bool usingVolatilePassword = false,
}) async {
final dialog = createProgressDialog(
context,
context.strings.generatingEncryptionKeysTitle,
);
final l10n = context.l10n;
final dialog =
createProgressDialog(context, l10n.generatingEncryptionKeysTitle);
await dialog.show();
try {
if (usingVolatilePassword) {
_logger.info('Using volatile password');
}
final result = await widget.config.generateKey(password);
widget.config.resetVolatilePassword();
final KeyGenResult result =
await Configuration.instance.generateKey(password);
Configuration.instance.resetVolatilePassword();
await dialog.hide();
onDone() async {
final dialog =
createProgressDialog(context, context.strings.pleaseWait);
final dialog = createProgressDialog(context, l10n.pleaseWait);
await dialog.show();
try {
await UserService.instance.setAttributes(result);
await dialog.hide();
widget.config.resetVolatilePassword();
Configuration.instance.resetVolatilePassword();
// ignore: unawaited_futures
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (BuildContext context) {
return widget.homePage;
return const HomePage();
},
),
(route) => route.isFirst,
@@ -491,9 +486,8 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
routeToPage(
context,
RecoveryKeyPage(
widget.config,
result.privateKeyAttributes.recoveryKey,
context.strings.continueLabel,
context.l10n.continueLabel,
showAppBar: false,
isDismissible: false,
onDone: onDone,
@@ -507,8 +501,8 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
// ignore: unawaited_futures
showErrorDialog(
context,
context.strings.insecureDevice,
context.strings.sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease,
context.l10n.insecureDevice,
context.l10n.sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease,
);
} else {
// ignore: unawaited_futures

View File

@@ -1,28 +1,22 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:ente_accounts/ente_accounts.dart';
import 'package:ente_accounts/models/errors.dart';
import 'package:ente_configuration/base_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/services/user_service.dart';
import 'package:ente_auth/ui/account/recovery_page.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/home_page.dart';
import 'package:ente_auth/utils/dialog_util.dart';
import 'package:ente_auth/utils/email_util.dart';
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
import 'package:ente_strings/ente_strings.dart';
import 'package:ente_ui/components/buttons/button_widget.dart';
import 'package:ente_ui/components/buttons/dynamic_fab.dart';
import 'package:ente_ui/pages/base_home_page.dart';
import 'package:ente_ui/utils/dialog_util.dart';
import 'package:ente_utils/email_util.dart';
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
class PasswordReentryPage extends StatefulWidget {
final BaseConfiguration config;
final BaseHomePage homePage;
const PasswordReentryPage(
this.config,
this.homePage, {
super.key,
});
const PasswordReentryPage({super.key});
@override
State<PasswordReentryPage> createState() => _PasswordReentryPageState();
@@ -40,8 +34,8 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
@override
void initState() {
super.initState();
email = widget.config.getEmail();
_volatilePassword = widget.config.getVolatilePassword();
email = Configuration.instance.getEmail();
_volatilePassword = Configuration.instance.getVolatilePassword();
if (_volatilePassword != null) {
_passwordController.text = _volatilePassword!;
Future.delayed(
@@ -85,7 +79,7 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
key: const ValueKey("verifyPasswordButton"),
isKeypadOpen: isKeypadOpen,
isFormValid: _passwordController.text.isNotEmpty,
buttonText: context.strings.verifyPassword,
buttonText: context.l10n.verifyPassword,
onPressedFunction: () async {
FocusScope.of(context).unfocus();
await verifyPassword(_passwordController.text);
@@ -101,15 +95,15 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
bool usingVolatilePassword = false,
}) async {
FocusScope.of(context).unfocus();
final dialog = createProgressDialog(context, context.strings.pleaseWait);
final dialog = createProgressDialog(context, context.l10n.pleaseWait);
await dialog.show();
if (usingVolatilePassword) {
_logger.info("Using volatile password");
}
try {
final kek = await widget.config.decryptSecretsAndGetKeyEncKey(
final kek = await Configuration.instance.decryptSecretsAndGetKeyEncKey(
password,
widget.config.getKeyAttributes()!,
Configuration.instance.getKeyAttributes()!,
);
_registerSRPForExistingUsers(kek).ignore();
} on KeyDerivationError catch (e, s) {
@@ -117,19 +111,16 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
await dialog.hide();
final dialogChoice = await showChoiceDialog(
context,
title: context.strings.recreatePasswordTitle,
body: context.strings.recreatePasswordBody,
firstButtonLabel: context.strings.useRecoveryKey,
title: context.l10n.recreatePasswordTitle,
body: context.l10n.recreatePasswordBody,
firstButtonLabel: context.l10n.useRecoveryKey,
);
if (dialogChoice!.action == ButtonAction.first) {
// ignore: unawaited_futures
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {
return RecoveryPage(
widget.config,
widget.homePage,
);
return const RecoveryPage();
},
),
);
@@ -140,27 +131,27 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
await dialog.hide();
final dialogChoice = await showChoiceDialog(
context,
title: context.strings.incorrectPasswordTitle,
body: context.strings.pleaseTryAgain,
firstButtonLabel: context.strings.contactSupport,
secondButtonLabel: context.strings.ok,
title: context.l10n.incorrectPasswordTitle,
body: context.l10n.pleaseTryAgain,
firstButtonLabel: context.l10n.contactSupport,
secondButtonLabel: context.l10n.ok,
);
if (dialogChoice!.action == ButtonAction.first) {
await sendLogs(
context,
context.strings.contactSupport,
context.l10n.contactSupport,
postShare: () {},
);
}
return;
}
widget.config.resetVolatilePassword();
Configuration.instance.resetVolatilePassword();
await dialog.hide();
unawaited(
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (BuildContext context) {
return widget.homePage;
return const HomePage();
},
),
(route) => false,
@@ -199,7 +190,7 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
padding:
const EdgeInsets.symmetric(vertical: 30, horizontal: 20),
child: Text(
context.strings.welcomeBack,
context.l10n.welcomeBack,
style: Theme.of(context).textTheme.headlineMedium,
),
),
@@ -223,7 +214,7 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
key: const ValueKey("passwordInputField"),
autofillHints: const [AutofillHints.password],
decoration: InputDecoration(
hintText: context.strings.enterYourPasswordHint,
hintText: context.l10n.enterYourPasswordHint,
filled: true,
contentPadding: const EdgeInsets.all(20),
border: UnderlineInputBorder(
@@ -278,17 +269,14 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {
return RecoveryPage(
widget.config,
widget.homePage,
);
return const RecoveryPage();
},
),
);
},
child: Center(
child: Text(
context.strings.forgotPassword,
context.l10n.forgotPassword,
style: Theme.of(context)
.textTheme
.titleMedium!
@@ -304,17 +292,17 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
onTap: () async {
final dialog = createProgressDialog(
context,
context.strings.pleaseWait,
context.l10n.pleaseWait,
);
await dialog.show();
await widget.config.logout();
await Configuration.instance.logout();
await dialog.hide();
Navigator.of(context)
.popUntil((route) => route.isFirst);
},
child: Center(
child: Text(
context.strings.changeEmail,
context.l10n.changeEmail,
style: Theme.of(context)
.textTheme
.titleMedium!

View File

@@ -3,14 +3,14 @@ import 'dart:io' as io;
import 'package:bip39/bip39.dart' as bip39;
import 'package:dotted_border/dotted_border.dart';
import 'package:ente_configuration/base_configuration.dart';
import 'package:ente_configuration/constants.dart';
import 'package:ente_strings/ente_strings.dart';
import 'package:ente_ui/components/buttons/gradient_button.dart';
import 'package:ente_ui/theme/ente_theme.dart';
import 'package:ente_ui/utils/toast_util.dart';
import 'package:ente_utils/platform_util.dart';
import 'package:ente_utils/share_utils.dart';
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/core/constants.dart';
import 'package:ente_auth/ente_theme_data.dart';
import 'package:ente_auth/l10n/l10n.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:file_saver/file_saver.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@@ -18,7 +18,6 @@ import 'package:share_plus/share_plus.dart';
import 'package:step_progress_indicator/step_progress_indicator.dart';
class RecoveryKeyPage extends StatefulWidget {
final BaseConfiguration config;
final bool? showAppBar;
final String recoveryKey;
final String doneText;
@@ -30,7 +29,6 @@ class RecoveryKeyPage extends StatefulWidget {
final bool showProgressBar;
const RecoveryKeyPage(
this.config,
this.recoveryKey,
this.doneText, {
super.key,
@@ -49,15 +47,9 @@ class RecoveryKeyPage extends StatefulWidget {
class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
bool _hasTriedToSave = false;
late final io.File _recoveryKeyFile;
@override
void initState() {
super.initState();
_recoveryKeyFile = io.File(
"${widget.config.getTempDirectory()}ente-recovery-key.txt",
);
}
final _recoveryKeyFile = io.File(
"${Configuration.instance.getTempDirectory()}ente-recovery-key.txt",
);
@override
Widget build(BuildContext context) {
@@ -81,7 +73,7 @@ class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
);
showShortToast(
context,
context.strings.recoveryKeyCopiedToClipboard,
context.l10n.recoveryKeyCopiedToClipboard,
);
setState(() {
_hasTriedToSave = true;
@@ -98,17 +90,17 @@ class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
child: StepProgressIndicator(
totalSteps: 4,
currentStep: 3,
selectedColor: getEnteColorScheme(context).alternativeColor,
selectedColor: Theme.of(context).colorScheme.alternativeColor,
roundedEdges: const Radius.circular(10),
unselectedColor:
getEnteColorScheme(context).stepProgressUnselectedColor,
Theme.of(context).colorScheme.stepProgressUnselectedColor,
),
),
)
: widget.showAppBar!
? AppBar(
elevation: 0,
title: Text(widget.title ?? context.strings.recoveryKey),
title: Text(widget.title ?? context.l10n.recoveryKey),
)
: null,
body: Padding(
@@ -129,15 +121,14 @@ class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
widget.showAppBar!
? const SizedBox.shrink()
: Text(
widget.title ?? context.strings.recoveryKey,
widget.title ?? context.l10n.recoveryKey,
style: Theme.of(context).textTheme.headlineMedium,
),
Padding(
padding: EdgeInsets.all(widget.showAppBar! ? 0 : 12),
),
Text(
widget.text ??
context.strings.recoveryKeyOnForgotPassword,
widget.text ?? context.l10n.recoveryKeyOnForgotPassword,
style: Theme.of(context).textTheme.titleMedium,
),
const Padding(padding: EdgeInsets.only(top: 24)),
@@ -145,22 +136,22 @@ class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
padding: const EdgeInsets.all(1),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
gradient: LinearGradient(
gradient: const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
getEnteColorScheme(context).primary500,
getEnteColorScheme(context).primary300,
Color(0x8E9610D6),
Color(0x8E9F4FC6),
],
stops: const [0.0, 0.9753],
stops: [0.0, 0.9753],
),
),
child: DottedBorder(
padding: EdgeInsets.zero,
borderType: BorderType.RRect,
strokeWidth: 1,
color: const Color(0xFF6B6B6B),
dashPattern: const [6, 6],
borderType: BorderType.RRect,
radius: const Radius.circular(8),
child: SizedBox(
width: double.infinity,
@@ -215,7 +206,7 @@ class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
padding: const EdgeInsets.symmetric(vertical: 20),
child: Text(
widget.subText ??
context.strings.recoveryKeySaveDescription,
context.l10n.recoveryKeySaveDescription,
style: Theme.of(context).textTheme.bodyLarge,
),
),
@@ -248,11 +239,12 @@ class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
childrens.add(
SizedBox(
height: 56,
child: OutlinedButton(
child: ElevatedButton(
style: Theme.of(context).colorScheme.optionalActionButtonStyle,
onPressed: () async {
await _saveKeys();
},
child: Text(context.strings.doThisLater),
child: Text(context.l10n.doThisLater),
),
),
);
@@ -264,7 +256,7 @@ class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
onTap: () async {
await shareDialog(
context,
context.strings.recoveryKey,
context.l10n.recoveryKey,
saveAction: () async {
await _saveRecoveryKey(recoveryKey);
},
@@ -273,7 +265,7 @@ class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
},
);
},
text: context.strings.saveKey,
text: context.l10n.saveKey,
),
);
@@ -309,7 +301,7 @@ class _RecoveryKeyPageState extends State<RecoveryKeyPage> {
if (mounted) {
showToast(
context,
context.strings.recoveryKeySaved,
context.l10n.recoveryKeySaved,
);
setState(() {
_hasTriedToSave = true;

View File

@@ -1,17 +1,13 @@
import 'package:ente_accounts/ente_accounts.dart';
import 'package:ente_configuration/base_configuration.dart';
import 'package:ente_strings/ente_strings.dart';
import 'package:ente_ui/components/buttons/dynamic_fab.dart';
import 'package:ente_ui/pages/base_home_page.dart';
import 'package:ente_ui/utils/dialog_util.dart';
import 'package:ente_ui/utils/toast_util.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/common/dynamic_fab.dart';
import 'package:ente_auth/utils/dialog_util.dart';
import 'package:ente_auth/utils/toast_util.dart';
import 'package:flutter/material.dart';
class RecoveryPage extends StatefulWidget {
final BaseConfiguration config;
final BaseHomePage homePage;
const RecoveryPage(this.config, this.homePage, {super.key});
const RecoveryPage({super.key});
@override
State<RecoveryPage> createState() => _RecoveryPageState();
@@ -25,18 +21,16 @@ class _RecoveryPageState extends State<RecoveryPage> {
final dialog = createProgressDialog(context, "Decrypting...");
await dialog.show();
try {
await widget.config.recover(_recoveryKey.text.trim());
await Configuration.instance.recover(_recoveryKey.text.trim());
await dialog.hide();
showToast(context, "Recovery successful!");
await Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (BuildContext context) {
return PopScope(
return const PopScope(
canPop: false,
child: PasswordEntryPage(
widget.config,
PasswordEntryMode.reset,
widget.homePage,
mode: PasswordEntryMode.reset,
),
);
},
@@ -92,7 +86,7 @@ class _RecoveryPageState extends State<RecoveryPage> {
padding:
const EdgeInsets.symmetric(vertical: 30, horizontal: 20),
child: Text(
context.strings.forgotPassword,
context.l10n.forgotPassword,
style: Theme.of(context).textTheme.headlineMedium,
),
),
@@ -143,7 +137,7 @@ class _RecoveryPageState extends State<RecoveryPage> {
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Center(
child: Text(
context.strings.noRecoveryKeyTitle,
context.l10n.noRecoveryKeyTitle,
style: Theme.of(context)
.textTheme
.titleMedium!

View File

@@ -1,24 +1,22 @@
import "dart:convert";
import "dart:typed_data";
import "package:ente_configuration/base_configuration.dart";
import 'package:ente_auth/core/configuration.dart';
import "package:ente_auth/l10n/l10n.dart";
import "package:ente_auth/theme/ente_theme.dart";
import 'package:ente_auth/ui/common/dynamic_fab.dart';
import "package:ente_auth/utils/dialog_util.dart";
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
import "package:ente_strings/ente_strings.dart";
import "package:ente_ui/components/buttons/dynamic_fab.dart";
import "package:ente_ui/theme/ente_theme.dart";
import "package:ente_ui/utils/dialog_util.dart";
import 'package:flutter/material.dart';
import "package:logging/logging.dart";
typedef OnPasswordVerifiedFn = Future<void> Function(Uint8List bytes);
class RequestPasswordVerificationPage extends StatefulWidget {
final BaseConfiguration config;
final OnPasswordVerifiedFn onPasswordVerified;
final Function? onPasswordError;
const RequestPasswordVerificationPage(
this.config, {
const RequestPasswordVerificationPage({
super.key,
required this.onPasswordVerified,
this.onPasswordError,
@@ -41,7 +39,7 @@ class _RequestPasswordVerificationPageState
@override
void initState() {
super.initState();
email = widget.config.getEmail();
email = Configuration.instance.getEmail();
_passwordFocusNode.addListener(() {
setState(() {
_passwordInFocus = _passwordFocusNode.hasFocus;
@@ -78,14 +76,13 @@ class _RequestPasswordVerificationPageState
key: const ValueKey("verifyPasswordButton"),
isKeypadOpen: isKeypadOpen,
isFormValid: _passwordController.text.isNotEmpty,
buttonText: context.strings.verifyPassword,
buttonText: context.l10n.verifyPassword,
onPressedFunction: () async {
FocusScope.of(context).unfocus();
final dialog =
createProgressDialog(context, context.strings.pleaseWait);
final dialog = createProgressDialog(context, context.l10n.pleaseWait);
await dialog.show();
try {
final attributes = widget.config.getKeyAttributes()!;
final attributes = Configuration.instance.getKeyAttributes()!;
final Uint8List keyEncryptionKey = await CryptoUtil.deriveKey(
utf8.encode(_passwordController.text),
CryptoUtil.base642bin(attributes.kekSalt),
@@ -111,8 +108,8 @@ class _RequestPasswordVerificationPageState
// ignore: unawaited_futures
showErrorDialog(
context,
context.strings.incorrectPasswordTitle,
context.strings.pleaseTryAgain,
context.l10n.incorrectPasswordTitle,
context.l10n.pleaseTryAgain,
);
}
}
@@ -133,7 +130,7 @@ class _RequestPasswordVerificationPageState
Padding(
padding: const EdgeInsets.only(top: 30, left: 20, right: 20),
child: Text(
context.strings.enterPassword,
context.l10n.enterPassword,
style: Theme.of(context).textTheme.headlineMedium,
),
),
@@ -168,7 +165,7 @@ class _RequestPasswordVerificationPageState
key: const ValueKey("passwordInputField"),
autofillHints: const [AutofillHints.password],
decoration: InputDecoration(
hintText: context.strings.enterYourPasswordHint,
hintText: context.l10n.enterYourPasswordHint,
filled: true,
contentPadding: const EdgeInsets.all(20),
border: UnderlineInputBorder(

View File

@@ -1,20 +1,17 @@
import 'package:ente_accounts/ente_accounts.dart';
import 'package:ente_configuration/base_configuration.dart';
import 'package:ente_strings/ente_strings.dart';
import 'package:ente_ui/components/loading_widget.dart';
import 'package:ente_ui/theme/ente_theme.dart';
import 'package:ente_ui/utils/dialog_util.dart';
import 'package:ente_ui/utils/toast_util.dart';
import 'package:ente_utils/date_time_util.dart';
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/ente_theme_data.dart';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/models/sessions.dart';
import 'package:ente_auth/services/user_service.dart';
import 'package:ente_auth/ui/common/loading_widget.dart';
import 'package:ente_auth/utils/date_time_util.dart';
import 'package:ente_auth/utils/dialog_util.dart';
import 'package:ente_auth/utils/toast_util.dart';
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
class SessionsPage extends StatefulWidget {
final BaseConfiguration config;
const SessionsPage(
this.config, {
super.key,
});
const SessionsPage({super.key});
@override
State<SessionsPage> createState() => _SessionsPageState();
@@ -37,7 +34,7 @@ class _SessionsPageState extends State<SessionsPage> {
return Scaffold(
appBar: AppBar(
elevation: 0,
title: Text(context.strings.activeSessions),
title: Text(context.l10n.activeSessions),
),
body: _getBody(),
);
@@ -115,7 +112,7 @@ class _SessionsPageState extends State<SessionsPage> {
}
Future<void> _terminateSession(Session session) async {
final dialog = createProgressDialog(context, context.strings.pleaseWait);
final dialog = createProgressDialog(context, context.l10n.pleaseWait);
await dialog.show();
try {
await UserService.instance.terminateSession(session.token);
@@ -127,8 +124,8 @@ class _SessionsPageState extends State<SessionsPage> {
// ignore: unawaited_futures
showErrorDialog(
context,
context.strings.oops,
context.strings.somethingWentWrongPleaseTryAgain,
context.l10n.oops,
context.l10n.somethingWentWrongPleaseTryAgain,
);
}
}
@@ -150,18 +147,18 @@ class _SessionsPageState extends State<SessionsPage> {
void _showSessionTerminationDialog(Session session) {
final isLoggingOutFromThisDevice =
session.token == widget.config.getToken();
session.token == Configuration.instance.getToken();
Widget text;
if (isLoggingOutFromThisDevice) {
text = Text(
context.strings.thisWillLogYouOutOfThisDevice,
context.l10n.thisWillLogYouOutOfThisDevice,
);
} else {
text = SingleChildScrollView(
child: Column(
children: [
Text(
context.strings.thisWillLogYouOutOfTheFollowingDevice,
context.l10n.thisWillLogYouOutOfTheFollowingDevice,
),
const Padding(padding: EdgeInsets.all(8)),
Text(
@@ -173,12 +170,12 @@ class _SessionsPageState extends State<SessionsPage> {
);
}
final AlertDialog alert = AlertDialog(
title: Text(context.strings.terminateSession),
title: Text(context.l10n.terminateSession),
content: text,
actions: [
TextButton(
child: Text(
context.strings.terminate,
context.l10n.terminate,
style: const TextStyle(
color: Colors.red,
),
@@ -194,11 +191,11 @@ class _SessionsPageState extends State<SessionsPage> {
),
TextButton(
child: Text(
context.strings.cancel,
context.l10n.cancel,
style: TextStyle(
color: isLoggingOutFromThisDevice
? getEnteColorScheme(context).alternativeColor
: getEnteColorScheme(context).textBase,
? Theme.of(context).colorScheme.alternativeColor
: Theme.of(context).colorScheme.defaultTextColor,
),
),
onPressed: () {
@@ -217,12 +214,12 @@ class _SessionsPageState extends State<SessionsPage> {
}
Widget _getUAWidget(Session session) {
if (session.token == widget.config.getToken()) {
if (session.token == Configuration.instance.getToken()) {
return Text(
context.strings.thisDevice,
context.l10n.thisDevice,
style: TextStyle(
fontWeight: FontWeight.bold,
color: getEnteColorScheme(context).alternativeColor,
color: Theme.of(context).colorScheme.alternativeColor,
),
);
}

View File

@@ -9,6 +9,7 @@ import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/models/code.dart';
import 'package:ente_auth/onboarding/view/setup_enter_secret_key_page.dart';
import 'package:ente_auth/onboarding/view/view_qr_page.dart';
import 'package:ente_auth/services/local_authentication_service.dart';
import 'package:ente_auth/services/preference_service.dart';
import 'package:ente_auth/store/code_store.dart';
import 'package:ente_auth/theme/ente_theme.dart';
@@ -21,7 +22,6 @@ 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/totp_util.dart';
import 'package:ente_lock_screen/local_authentication_service.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_context_menu/flutter_context_menu.dart';

View File

@@ -1,9 +1,9 @@
import 'package:ente_auth/ente_theme_data.dart';
import 'package:ente_auth/ente_theme_data.dart';
import 'package:ente_auth/models/typedefs.dart';
import 'package:ente_auth/ui/components/buttons/button_widget.dart';
import 'package:ente_auth/ui/components/dialog_widget.dart';
import 'package:ente_auth/ui/components/models/button_result.dart';
import 'package:ente_auth/ui/components/models/button_type.dart';
import 'package:ente_base/typedefs.dart';
import 'package:flutter/material.dart';
enum DialogUserChoice { firstChoice, secondChoice }

View File

@@ -1,4 +1,5 @@
import 'package:ente_auth/models/execution_states.dart';
import 'package:ente_auth/models/execution_states.dart';
import 'package:ente_auth/models/typedefs.dart';
import 'package:ente_auth/theme/colors.dart';
import 'package:ente_auth/theme/ente_theme.dart';
import 'package:ente_auth/theme/text_style.dart';
@@ -8,7 +9,6 @@ import 'package:ente_auth/ui/components/models/button_type.dart';
import 'package:ente_auth/ui/components/models/custom_button_style.dart';
import 'package:ente_auth/utils/debouncer.dart';
import "package:ente_auth/utils/dialog_util.dart";
import 'package:ente_base/typedefs.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';

View File

@@ -1,6 +1,7 @@
import 'dart:math';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/models/typedefs.dart';
import 'package:ente_auth/theme/colors.dart';
import 'package:ente_auth/theme/effects.dart';
import 'package:ente_auth/theme/ente_theme.dart';
@@ -10,7 +11,6 @@ import 'package:ente_auth/ui/components/models/button_result.dart';
import 'package:ente_auth/ui/components/models/button_type.dart';
import 'package:ente_auth/ui/components/separators.dart';
import 'package:ente_auth/ui/components/text_input_widget.dart';
import 'package:ente_base/typedefs.dart';
import 'package:flutter/material.dart';
///Will return null if dismissed by tapping outside

View File

@@ -1,8 +1,8 @@
import 'package:ente_auth/models/execution_states.dart';
import 'package:ente_auth/models/execution_states.dart';
import 'package:ente_auth/models/typedefs.dart';
import 'package:ente_auth/theme/ente_theme.dart';
import 'package:ente_auth/ui/components/menu_item_child_widgets.dart';
import 'package:ente_auth/utils/debouncer.dart';
import 'package:ente_base/typedefs.dart';
import 'package:expandable/expandable.dart';
import 'package:flutter/material.dart';

View File

@@ -1,9 +1,9 @@
import 'package:ente_auth/models/execution_states.dart';
import 'package:ente_auth/models/execution_states.dart';
import 'package:ente_auth/models/typedefs.dart';
import 'package:ente_auth/theme/ente_theme.dart';
import 'package:ente_auth/ui/common/loading_widget.dart';
import 'package:ente_auth/ui/components/separators.dart';
import 'package:ente_auth/utils/debouncer.dart';
import 'package:ente_base/typedefs.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:logging/logging.dart';

View File

@@ -123,7 +123,7 @@ class TitleBarWidget extends StatelessWidget {
);
}
List<Widget> _actionsWithPaddingInBetween() {
_actionsWithPaddingInBetween() {
if (actionIcons == null) {
return <Widget>[const SizedBox.shrink()];
}
@@ -135,7 +135,7 @@ class TitleBarWidget extends StatelessWidget {
return <Widget>[const SizedBox.shrink()];
}
if (length == 1) {
return actionIcons!;
return actionIcons;
}
while (index < length) {
if (!addWhiteSpace) {

View File

@@ -1,9 +1,8 @@
import 'package:ente_auth/ente_theme_data.dart';
import 'package:ente_auth/models/execution_states.dart';
import 'package:ente_auth/theme/colors.dart';
import 'package:ente_auth/models/execution_states.dart';
import 'package:ente_auth/models/typedefs.dart';
import 'package:ente_auth/ui/common/loading_widget.dart';
import 'package:ente_auth/utils/debouncer.dart';
import 'package:ente_base/typedefs.dart';
import 'package:flutter/material.dart';
typedef OnChangedCallBack = void Function(bool);
@@ -105,7 +104,7 @@ class _ToggleSwitchWidgetState extends State<ToggleSwitchWidget> {
);
}
Widget _stateIcon(EnteColorScheme enteColorScheme) {
Widget _stateIcon(enteColorScheme) {
if (executionState == ExecutionState.idle) {
return const SizedBox(width: 24);
} else if (executionState == ExecutionState.inProgress) {

View File

@@ -1,10 +1,10 @@
import 'dart:ui';
import 'package:ente_auth/core/event_bus.dart';
import 'package:ente_auth/events/codes_updated_event.dart';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/services/preference_service.dart';
import 'package:ente_auth/utils/platform_util.dart';
import 'package:ente_events/event_bus.dart';
import 'package:flutter/material.dart';
class CoachMarkWidget extends StatelessWidget {
@@ -23,8 +23,7 @@ class CoachMarkWidget extends StatelessWidget {
Expanded(
child: Container(
width: double.infinity,
color:
Theme.of(context).colorScheme.surface.withOpacity(0.1),
color: Theme.of(context).colorScheme.surface.withOpacity(0.1),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 8, sigmaY: 8),
child: Row(

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