Compare commits
1 Commits
famfix
...
streaming-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0982e01d7 |
2
.github/workflows/auth-internal-release.yml
vendored
2
.github/workflows/auth-internal-release.yml
vendored
@@ -40,7 +40,7 @@ jobs:
|
||||
|
||||
- name: Build PlayStore AAB
|
||||
run: |
|
||||
flutter build appbundle --dart-define=cronetHttpNoPlay=true --release --flavor playstore
|
||||
flutter build appbundle --release --flavor playstore --dart-define=app.flavor=playstore
|
||||
env:
|
||||
SIGNING_KEY_PATH: "/home/runner/work/_temp/keystore/ente_auth_key.jks"
|
||||
SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS }}
|
||||
|
||||
2
.github/workflows/auth-release.yml
vendored
2
.github/workflows/auth-release.yml
vendored
@@ -68,7 +68,7 @@ jobs:
|
||||
|
||||
- name: Build independent APK
|
||||
run: |
|
||||
flutter build apk --dart-define=cronetHttpNoPlay=true --release --flavor independent
|
||||
flutter build apk --release --flavor independent --dart-define=app.flavor=independent
|
||||
mv build/app/outputs/flutter-apk/app-independent-release.apk artifacts/ente-${{ github.ref_name }}.apk
|
||||
env:
|
||||
SIGNING_KEY_PATH: "/home/runner/work/_temp/keystore/ente_auth_key.jks"
|
||||
|
||||
@@ -208,10 +208,6 @@
|
||||
{
|
||||
"title": "Bugzilla"
|
||||
},
|
||||
{
|
||||
"title": "ButterflyMX",
|
||||
"slug": "butterflymx"
|
||||
},
|
||||
{
|
||||
"title": "Bybit"
|
||||
},
|
||||
@@ -310,9 +306,6 @@
|
||||
{
|
||||
"title": "Discourse"
|
||||
},
|
||||
{
|
||||
"title": "Deloitte"
|
||||
},
|
||||
{
|
||||
"title": "DMarket"
|
||||
},
|
||||
@@ -400,17 +393,9 @@
|
||||
{
|
||||
"title": "ForUsAll"
|
||||
},
|
||||
{
|
||||
"title": "FreeTaxUSA",
|
||||
"slug": "freetaxusa"
|
||||
},
|
||||
{
|
||||
"title": "G2A"
|
||||
},
|
||||
{
|
||||
"title": "Gate.io",
|
||||
"slug": "gateio.svg"
|
||||
},
|
||||
{
|
||||
"title": "GitHub"
|
||||
},
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="462px" height="404px" viewBox="0 0 462 404" version="1.1">
|
||||
<defs>
|
||||
<linearGradient id="linear0" gradientUnits="userSpaceOnUse" x1="464.529999" y1="-2595.189941" x2="1224.150024" y2="-2986.919922" gradientTransform="matrix(0.249968,0,0,-0.249968,150.980393,-561.725816)">
|
||||
<stop offset="0" style="stop-color:rgb(100%,92.156863%,18.039216%);stop-opacity:1;"/>
|
||||
<stop offset="0.92" style="stop-color:rgb(99.607843%,56.470591%,18.82353%);stop-opacity:1;"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="linear1" gradientUnits="userSpaceOnUse" x1="-580.880005" y1="-2987.179932" x2="121.110001" y2="-2623.179932" gradientTransform="matrix(0.249968,0,0,-0.249968,150.980393,-561.725816)">
|
||||
<stop offset="0.06" style="stop-color:rgb(0%,40.784314%,89.803922%);stop-opacity:1;"/>
|
||||
<stop offset="1" style="stop-color:rgb(3.921569%,85.09804%,100%);stop-opacity:1;"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="linear2" gradientUnits="userSpaceOnUse" x1="1063.689941" y1="-3644.949951" x2="436.269989" y2="-3207.530029" gradientTransform="matrix(0.249968,0,0,-0.249968,150.980393,-561.725816)">
|
||||
<stop offset="0" style="stop-color:rgb(93.725491%,3.921569%,21.176471%);stop-opacity:1;"/>
|
||||
<stop offset="1" style="stop-color:rgb(100%,59.215689%,54.11765%);stop-opacity:1;"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="linear3" gradientUnits="userSpaceOnUse" x1="-389.76001" y1="-3622.02002" x2="266.369995" y2="-3164.639893" gradientTransform="matrix(0.249968,0,0,-0.249968,150.980393,-561.725816)">
|
||||
<stop offset="0" style="stop-color:rgb(47.450981%,7.843138%,93.725491%);stop-opacity:1;"/>
|
||||
<stop offset="1" style="stop-color:rgb(85.882354%,41.176471%,100%);stop-opacity:1;"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="surface1">
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:url(#linear0);" d="M 437.566406 160.457031 C 437.566406 192.628906 411.488281 218.730469 379.324219 218.730469 L 239.71875 218.730469 C 239.71875 116.257812 317.625 31.976562 417.445312 21.886719 C 428.53125 20.769531 437.585938 29.886719 437.585938 41.027344 Z M 437.566406 160.457031 "/>
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:url(#linear1);" d="M 24.640625 160.457031 C 24.640625 192.628906 50.722656 218.730469 82.882812 218.730469 L 222.492188 218.730469 C 222.492188 116.257812 144.589844 31.976562 44.777344 21.886719 C 33.695312 20.769531 24.640625 29.886719 24.640625 41.027344 Z M 24.640625 160.457031 "/>
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:url(#linear2);" d="M 269.425781 338.792969 C 249.960938 308.039062 239.65625 272.375 239.71875 235.976562 L 381.667969 235.976562 C 412.507812 235.976562 437.574219 260.675781 437.574219 291.0625 C 437.566406 380.972656 317.730469 415.246094 269.425781 338.792969 Z M 269.425781 338.792969 "/>
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:url(#linear3);" d="M 192.78125 338.792969 C 212.25 308.039062 222.554688 272.375 222.492188 235.976562 L 80.546875 235.976562 C 49.707031 235.976562 24.640625 260.675781 24.640625 291.0625 C 24.640625 380.972656 144.480469 415.246094 192.78125 338.792969 Z M 192.78125 338.792969 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.1 KiB |
@@ -1,15 +0,0 @@
|
||||
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1545 1333" width="1545" height="1333">
|
||||
<title>Deloitte-svg</title>
|
||||
<style>
|
||||
.s0 { fill: #86bc24 }
|
||||
.s1 { fill: #0f0b0b }
|
||||
</style>
|
||||
<g id="layer1">
|
||||
<g id="g3359">
|
||||
<g id="g3371">
|
||||
<path id="path3356" class="s0" d="m1354.4 1332.5c-105.1 0-190-84.8-190-189.6 0-104.9 84.9-189.6 190-189.6 105 0 189.9 84.7 189.9 189.6 0 104.8-84.9 189.6-189.9 189.6z"/>
|
||||
<path id="path3360" fill-rule="evenodd" class="s1" d="m1089.4 628.2q0 328.2-176.7 505.8-176.8 177.6-497.1 177.6h-414.9v-1311.1h443.9q308.8 0 476.8 161.4c112 107.6 168 263 168 466.3zm-359.7 12.5q0-180.1-69.7-267.2c-46.6-58-117.1-87-211.9-87h-100.9v734.5h77.2c105.3 0 182.5-31.2 231.6-93.8 49.1-62.4 73.7-157.9 73.7-286.5z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 785 B |
@@ -1 +0,0 @@
|
||||
<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 560 400" xmlns="http://www.w3.org/2000/svg"><g transform="matrix(2.51518 0 0 2.51518 30 162.272)"><path d="m136.108 5.893 6.356 23.513h-4.7l-1.218-5.334h-6.587l-1.284 5.334h-4.545l6.52-23.513h5.468zm-2.833 4.107-2.436 10.242h4.806zm-65.8 1.7c2.02 0 3.508.522 4.463 1.563s1.432 2.674 1.432 4.9v11.263h-3.95v-1.9c-.505.746-1.114 1.323-1.828 1.73-.706.405-1.508.615-2.322.61-1.405 0-2.502-.45-3.294-1.35s-1.176-2.182-1.176-3.786c0-1.734.577-3.1 1.73-4.1s2.804-1.586 4.956-1.762l1.78-.132v-1.02c0-1.03-.153-1.788-.462-2.27s-.8-.725-1.482-.725c-.615 0-1.086.187-1.415.56s-.56.966-.7 1.78l-3.887-.33c.264-1.713.9-2.98 1.9-3.804s2.427-1.235 4.25-1.235zm1.78 9.78-1.317.132c-.988.088-1.752.373-2.3.856s-.808 1.13-.808 1.943c0 .724.153 1.28.463 1.662s.756.577 1.35.577c.748.022 1.459-.331 1.894-.939.472-.626.708-1.476.708-2.553v-1.68zm-25.26-9.78c2.064 0 3.617.714 4.66 2.14s1.563 3.535 1.563 6.323v1.68h-8.825c.044 3.338.9 5.005 2.536 5.005.66 0 1.152-.202 1.482-.608s.548-1.092.66-2.06h4.084c-.177 1.888-.797 3.322-1.86 4.298s-2.542 1.465-4.43 1.465c-2.282 0-3.972-.73-5.07-2.2s-1.647-3.716-1.647-6.768c0-3.03.58-5.334 1.745-6.915s2.869-2.36 5.107-2.36zm-.133 3.03c-.8 0-1.372.346-1.744 1.038s-.604 1.84-.7 3.442h4.676c0-1.56-.18-2.695-.544-3.4s-.927-1.07-1.696-1.07zm-14.29-3.03c2.064 0 3.617.714 4.66 2.14s1.563 3.535 1.563 6.323v1.68h-8.825c.044 3.338.9 5.005 2.536 5.005.66 0 1.152-.202 1.482-.608s.55-1.092.66-2.06h4.084c-.177 1.888-.797 3.322-1.86 4.298s-2.542 1.465-4.43 1.465c-2.282 0-3.972-.73-5.07-2.2s-1.647-3.716-1.647-6.768c0-3.03.58-5.334 1.745-6.915s2.867-2.37 5.105-2.37zm-.133 3.03c-.8 0-1.372.346-1.744 1.038s-.604 1.84-.7 3.442h4.676c0-1.56-.18-2.695-.544-3.4s-.927-1.07-1.696-1.07zm-29.439-8.8v23.513h4.645v-9.55h7.3v-4.15h-7.3v-5.665h7.837v-4.148zm13.7 23.477v-17.2h4.215v2.306c.46-.834 1-1.477 1.647-1.927s1.305-.675 2-.675c.352 0 .747.054 1.185.165l-.493 3.952c-.33-.087-.757-.132-1.285-.132-.792-.018-1.554.306-2.09.89-.56.594-.84 1.33-.84 2.206v10.406h-4.339zm39.813 0v-19.364h-5.335v-4.15h15.315v4.15h-5.336v19.365zm30.792 0-2.832-5.665-2.7 5.665h-4.28l4.84-8.727-4.545-8.464h4.6l2.47 5.204 2.536-5.204h4.282l-4.676 8.234 4.908 8.957zm17.652-23.514h4.644v15.083c0 2.92-.68 5.155-2.04 6.702s-3.328 2.322-5.897 2.322-4.533-.773-5.896-2.322-2.04-3.78-2.04-6.702v-15.083h4.644v15.413c0 1.537.27 2.68.807 3.424s1.366 1.12 2.486 1.12 1.948-.373 2.487-1.12.807-1.887.807-3.424v-15.413zm12.118 13.14c-1.865-.548-3.237-1.377-4.116-2.486s-1.318-2.564-1.318-4.364c0-2.085.682-3.754 2.042-5.005s3.163-1.878 5.4-1.878c2.13 0 3.788.538 4.973 1.614s1.93 2.733 2.24 4.972l-4.414.594c-.22-1.23-.548-2.103-.987-2.62s-1.087-.774-1.943-.774-1.515.247-1.977.74-.7 1.224-.7 2.2c0 .813.175 1.444.527 1.894s.955.806 1.812 1.07l2.503.8c1.338.418 2.415.94 3.227 1.564.783.594 1.397 1.384 1.78 2.29.376.9.56 1.977.56 3.228 0 2.196-.675 3.936-2.025 5.22s-3.179 1.908-5.507 1.908c-4.897 0-7.5-2.547-7.84-7.64h4.612c.1 1.34.422 2.316.938 2.93s1.3.922 2.355.922c.9 0 1.597-.27 2.1-.807s.74-1.312.74-2.322c0-.9-.203-1.614-.6-2.14s-1.048-.92-1.926-1.185l-2.438-.724z" fill="#212f63"/><path d="m140.638 5.893 1.207 4.25h49.07v-4.25zm2.7 9.463 1.213 4.25 46.373-.007v-4.25zm2.79 9.802 1.208 4.25h43.6v-4.25z" fill="#bf2032"/><path d="m195.992 2.462h.33c.364 0 .546-.143.546-.432.005-.113-.036-.223-.115-.304-.076-.076-.212-.115-.406-.115h-.354v.85zm-.52 1.295v-2.577h.964c.27 0 .502.06.698.183s.29.336.29.64c0 .153-.042.294-.126.425s-.2.223-.343.273l.584 1.053h-.584l-.444-.89h-.52v.89h-.52zm.913.774c.255.001.508-.049.743-.147.232-.097.433-.234.6-.412.174-.185.31-.402.4-.64.1-.265.15-.547.147-.831.002-.28-.048-.558-.147-.819-.098-.25-.23-.465-.4-.648-.169-.181-.374-.326-.601-.425-.234-.1-.48-.153-.743-.153-.257-.002-.512.05-.748.153-.224.099-.426.244-.591.425-.169.191-.302.41-.394.648-.097.25-.145.522-.145.82-.004.284.046.565.145.831.098.25.23.462.394.64s.363.316.59.412c.237.1.491.15.748.147zm0 .47c-.318.003-.634-.06-.927-.184-.288-.122-.538-.293-.754-.514-.221-.227-.396-.494-.515-.787-.127-.304-.19-.643-.19-1.015s.062-.71.19-1.016c.12-.293.295-.56.515-.787.215-.22.471-.394.754-.514.288-.123.596-.184.927-.184.32-.002.637.061.933.184.293.123.545.293.76.514s.388.483.515.787.2.643.2 1.016-.063.71-.2 1.015-.298.568-.515.787-.468.392-.76.514c-.295.124-.613.186-.933.184z" fill="#212f63"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 4.3 KiB |
@@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns:xodm="http://www.corel.com/coreldraw/odm/2003" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 2500 2500" style="enable-background:new 0 0 2500 2500;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:none;}
|
||||
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#2354E6;}
|
||||
.st2{fill-rule:evenodd;clip-rule:evenodd;fill:#17E6A1;}
|
||||
</style>
|
||||
<g id="Layer_x0020_1">
|
||||
<rect y="0" class="st0" width="2500" height="2500"></rect>
|
||||
<g id="_2500576017504">
|
||||
<path id="Fill-3" class="st1" d="M1250,1937.5c-379.7,0-687.5-307.8-687.5-687.5c0-379.7,307.8-687.5,687.5-687.5V0 C559.6,0,0,559.6,0,1250c0,690.3,559.6,1250,1250,1250c690.3,0,1250-559.6,1250-1250h-562.5 C1937.5,1629.7,1629.7,1937.5,1250,1937.5z"></path>
|
||||
<polygon id="Fill-4" class="st2" points="1250,1250 1937.5,1250 1937.5,562.5 1250,562.5 "></polygon>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 965 B |
1
auth/assets/custom-icons/icons/onshape.svg
Normal file
1
auth/assets/custom-icons/icons/onshape.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 33 KiB |
@@ -8,7 +8,6 @@ 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:uuid/uuid.dart';
|
||||
|
||||
int kConnectTimeout = 15000;
|
||||
@@ -51,10 +50,6 @@ class Network {
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
_dio.httpClientAdapter = NativeAdapter();
|
||||
_enteDio.httpClientAdapter = NativeAdapter();
|
||||
|
||||
_setupInterceptors(endpoint);
|
||||
|
||||
Bus.instance.on<EndpointUpdatedEvent>().listen((event) {
|
||||
|
||||
3
auth/lib/events/opened_settings_event.dart
Normal file
3
auth/lib/events/opened_settings_event.dart
Normal file
@@ -0,0 +1,3 @@
|
||||
import 'package:ente_auth/events/event.dart';
|
||||
|
||||
class OpenedSettingsEvent extends Event {}
|
||||
17
auth/lib/json/converter.dart
Normal file
17
auth/lib/json/converter.dart
Normal file
@@ -0,0 +1,17 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import "package:json_annotation/json_annotation.dart";
|
||||
|
||||
class Uint8ListConverter implements JsonConverter<Uint8List, List<int>> {
|
||||
const Uint8ListConverter();
|
||||
|
||||
@override
|
||||
Uint8List fromJson(List<int>? json) {
|
||||
return json == null ? Uint8List(0) : Uint8List.fromList(json);
|
||||
}
|
||||
|
||||
@override
|
||||
List<int> toJson(Uint8List object) {
|
||||
return object.toList();
|
||||
}
|
||||
}
|
||||
@@ -147,7 +147,6 @@
|
||||
"leaveFamily": "Familie verlassen",
|
||||
"leaveFamilyMessage": "Sind Sie sicher, dass Sie den Familien-Plan verlassen wollen?",
|
||||
"inFamilyPlanMessage": "Sie haben einen Familien-Plan!",
|
||||
"hintForDesktop": "Klicken Sie mit der rechten Maustaste auf einen Code zum Bearbeiten oder Entfernen.",
|
||||
"scan": "Scannen",
|
||||
"scanACode": "Scan einen Code",
|
||||
"verify": "Überprüfen Sie",
|
||||
@@ -157,7 +156,6 @@
|
||||
"twoFactorAuthTitle": "Zwei-Faktor-Authentifizierung",
|
||||
"passkeyAuthTitle": "Passkey Authentifizierung",
|
||||
"verifyPasskey": "Passkey verifizieren",
|
||||
"loginWithTOTP": "Mit TOTP anmelden",
|
||||
"recoverAccount": "Konto wiederherstellen",
|
||||
"enterRecoveryKeyHint": "Geben Sie Ihren Wiederherstellungsschlüssel ein",
|
||||
"recover": "Wiederherstellen",
|
||||
@@ -260,8 +258,6 @@
|
||||
"yesLogout": "Ja ausloggen",
|
||||
"exit": "Schließen",
|
||||
"theme": "Theme",
|
||||
"lightTheme": "Hell",
|
||||
"darkTheme": "Dunkel",
|
||||
"systemTheme": "System",
|
||||
"verifyingRecoveryKey": "Verifiziere Wiederherstellungsschlüssel...",
|
||||
"recoveryKeyVerified": "Wiederherstellungsschlüssel verifiziert",
|
||||
@@ -334,9 +330,6 @@
|
||||
}
|
||||
},
|
||||
"manualSort": "Benutzerdefiniert",
|
||||
"editOrder": "Reihenfolge bearbeiten",
|
||||
"mostFrequentlyUsed": "Häufig verwendet",
|
||||
"mostRecentlyUsed": "Zuletzt verwendet",
|
||||
"activeSessions": "Aktive Sitzungen",
|
||||
"somethingWentWrongPleaseTryAgain": "Ein Fehler ist aufgetreten, bitte versuche es erneut",
|
||||
"thisWillLogYouOutOfThisDevice": "Dadurch wirst du von diesem Gerät abgemeldet!",
|
||||
@@ -456,7 +449,6 @@
|
||||
"customEndpoint": "Mit {endpoint} verbunden",
|
||||
"pinText": "Anpinnen",
|
||||
"unpinText": "Lösen",
|
||||
"pinned": "Angeheftet",
|
||||
"tags": "Tags",
|
||||
"createNewTag": "Neuen Tag erstellen",
|
||||
"tag": "Tag",
|
||||
@@ -492,16 +484,8 @@
|
||||
"importFailureDescNew": "Die ausgewählte Datei konnte nicht verarbeitet werden.",
|
||||
"appLockNotEnabled": "App-Sperre nicht aktiviert",
|
||||
"appLockNotEnabledDescription": "Bitte aktivieren Sie die App-Sperre über Security > App-Sperre",
|
||||
"authToViewPasskey": "Bitte authentifizieren, um deinen Passkey zu sehen",
|
||||
"duplicateCodes": "Doppelte Codes",
|
||||
"noDuplicates": "✨ Keine Duplikate",
|
||||
"youveNoDuplicateCodesThatCanBeCleared": "Sie haben keine doppelten Codes, die gelöscht werden können",
|
||||
"deselectAll": "Alle abwählen",
|
||||
"selectAll": "Alles auswählen",
|
||||
"deleteDuplicates": "Duplikate löschen",
|
||||
"plainHTML": "Reines HTML",
|
||||
"tellUsWhatYouThink": "Sagen Sie uns, was Sie denken",
|
||||
"dropReview": "Eine Bewertung im App/Play Store ablegen",
|
||||
"giveUsAStarOnGithub": "Gib uns einen Stern auf Github",
|
||||
"loginWithAuthAccount": "Mit Ihrem Auth Account anmelden"
|
||||
"selectAll": "Alles auswählen"
|
||||
}
|
||||
@@ -505,10 +505,5 @@
|
||||
"selectAll": "Pasirinkti viską",
|
||||
"deleteDuplicates": "Ištrinti dublikatus",
|
||||
"plainHTML": "Grynasis HTML",
|
||||
"tellUsWhatYouThink": "Pasakykite mums, ką manote",
|
||||
"giveUsAStarOnGithub": "Suteikite mums žvaigždutę platformoje „Github“",
|
||||
"free5GB": "5 GB nemokami programai „<bold-green>ente</bold-green>“ nuotraukos",
|
||||
"loginWithAuthAccount": "Prisijungti su jūsų „Auth“ paskyra",
|
||||
"freeStorageOffer": "10 % nuolaida programai „<bold-green>ente</bold-green>“ nuotraukos",
|
||||
"freeStorageOfferDescription": "Naudokite kodą „AUTH“, kad gautumėte 10 % nuolaida pirmiesiems metams. "
|
||||
}
|
||||
73
auth/lib/models/magic_metadata.dart
Normal file
73
auth/lib/models/magic_metadata.dart
Normal file
@@ -0,0 +1,73 @@
|
||||
import 'dart:convert';
|
||||
|
||||
const visibilityVisible = 0;
|
||||
const visibilityArchive = 1;
|
||||
|
||||
const magicKeyVisibility = 'visibility';
|
||||
|
||||
const pubMagicKeyEditedTime = 'editedTime';
|
||||
const pubMagicKeyEditedName = 'editedName';
|
||||
|
||||
class MagicMetadata {
|
||||
// 0 -> visible
|
||||
// 1 -> archived
|
||||
// 2 -> hidden etc?
|
||||
int visibility;
|
||||
|
||||
MagicMetadata({required this.visibility});
|
||||
|
||||
factory MagicMetadata.fromEncodedJson(String encodedJson) =>
|
||||
MagicMetadata.fromJson(jsonDecode(encodedJson));
|
||||
|
||||
factory MagicMetadata.fromJson(dynamic json) => MagicMetadata.fromMap(json);
|
||||
|
||||
static fromMap(Map<String, dynamic>? map) {
|
||||
if (map == null) return null;
|
||||
return MagicMetadata(
|
||||
visibility: map[magicKeyVisibility] ?? visibilityVisible,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PubMagicMetadata {
|
||||
int? editedTime;
|
||||
String? editedName;
|
||||
|
||||
PubMagicMetadata({this.editedTime, this.editedName});
|
||||
|
||||
factory PubMagicMetadata.fromEncodedJson(String encodedJson) =>
|
||||
PubMagicMetadata.fromJson(jsonDecode(encodedJson));
|
||||
|
||||
factory PubMagicMetadata.fromJson(dynamic json) =>
|
||||
PubMagicMetadata.fromMap(json);
|
||||
|
||||
static fromMap(Map<String, dynamic>? map) {
|
||||
if (map == null) return null;
|
||||
return PubMagicMetadata(
|
||||
editedTime: map[pubMagicKeyEditedTime],
|
||||
editedName: map[pubMagicKeyEditedName],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CollectionMagicMetadata {
|
||||
// 0 -> visible
|
||||
// 1 -> archived
|
||||
// 2 -> hidden etc?
|
||||
int visibility;
|
||||
|
||||
CollectionMagicMetadata({required this.visibility});
|
||||
|
||||
factory CollectionMagicMetadata.fromEncodedJson(String encodedJson) =>
|
||||
CollectionMagicMetadata.fromJson(jsonDecode(encodedJson));
|
||||
|
||||
factory CollectionMagicMetadata.fromJson(dynamic json) =>
|
||||
CollectionMagicMetadata.fromMap(json);
|
||||
|
||||
static fromMap(Map<String, dynamic>? map) {
|
||||
if (map == null) return null;
|
||||
return CollectionMagicMetadata(
|
||||
visibility: map[magicKeyVisibility] ?? visibilityVisible,
|
||||
);
|
||||
}
|
||||
}
|
||||
6
auth/lib/models/public_key.dart
Normal file
6
auth/lib/models/public_key.dart
Normal file
@@ -0,0 +1,6 @@
|
||||
class PublicKey {
|
||||
final String email;
|
||||
final String publicKey;
|
||||
|
||||
PublicKey(this.email, this.publicKey);
|
||||
}
|
||||
23
auth/lib/services/auth_feature_flag.dart
Normal file
23
auth/lib/services/auth_feature_flag.dart
Normal file
@@ -0,0 +1,23 @@
|
||||
import 'package:ente_auth/core/configuration.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class FeatureFlagService {
|
||||
FeatureFlagService._privateConstructor();
|
||||
static final FeatureFlagService instance =
|
||||
FeatureFlagService._privateConstructor();
|
||||
|
||||
static final _internalUserIDs = const String.fromEnvironment(
|
||||
"internal_user_ids",
|
||||
defaultValue: "1,2,3,4,191,125,1580559962388044,1580559962392434,10000025",
|
||||
).split(",").map((element) {
|
||||
return int.parse(element);
|
||||
}).toSet();
|
||||
|
||||
bool isInternalUserOrDebugBuild() {
|
||||
final String? email = Configuration.instance.getEmail();
|
||||
final userID = Configuration.instance.getUserID();
|
||||
return (email != null && email.endsWith("@ente.io")) ||
|
||||
_internalUserIDs.contains(userID) ||
|
||||
kDebugMode;
|
||||
}
|
||||
}
|
||||
14
auth/lib/store/user_store.dart
Normal file
14
auth/lib/store/user_store.dart
Normal file
@@ -0,0 +1,14 @@
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class UserStore {
|
||||
UserStore._privateConstructor();
|
||||
|
||||
// ignore: unused_field
|
||||
late SharedPreferences _preferences;
|
||||
|
||||
static final UserStore instance = UserStore._privateConstructor();
|
||||
|
||||
Future<void> init() async {
|
||||
_preferences = await SharedPreferences.getInstance();
|
||||
}
|
||||
}
|
||||
@@ -113,7 +113,7 @@ class _LoginPasswordVerificationPageState
|
||||
);
|
||||
} else {
|
||||
_logger.severe('API failure during SRP login', e, s);
|
||||
if (e.type == DioExceptionType.connectionError) {
|
||||
if (e.type == DioExceptionType.unknown) {
|
||||
await _showContactSupportDialog(
|
||||
context,
|
||||
context.l10n.noInternetConnection,
|
||||
|
||||
25
auth/lib/ui/common/bottom_shadow.dart
Normal file
25
auth/lib/ui/common/bottom_shadow.dart
Normal file
@@ -0,0 +1,25 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class BottomShadowWidget extends StatelessWidget {
|
||||
final double offsetDy;
|
||||
final Color? shadowColor;
|
||||
const BottomShadowWidget({this.offsetDy = 28, this.shadowColor, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: 8,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: shadowColor ?? Theme.of(context).colorScheme.surface,
|
||||
spreadRadius: 42,
|
||||
blurRadius: 42,
|
||||
offset: Offset(0, offsetDy), // changes position of shadow
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
49
auth/lib/ui/common/linear_progress_dialog.dart
Normal file
49
auth/lib/ui/common/linear_progress_dialog.dart
Normal file
@@ -0,0 +1,49 @@
|
||||
import 'package:ente_auth/ente_theme_data.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class LinearProgressDialog extends StatefulWidget {
|
||||
final String message;
|
||||
|
||||
const LinearProgressDialog(this.message, {super.key});
|
||||
|
||||
@override
|
||||
LinearProgressDialogState createState() => LinearProgressDialogState();
|
||||
}
|
||||
|
||||
class LinearProgressDialogState extends State<LinearProgressDialog> {
|
||||
double? _progress;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_progress = 0;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void setProgress(double progress) {
|
||||
setState(() {
|
||||
_progress = progress;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
child: AlertDialog(
|
||||
title: Text(
|
||||
widget.message,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
content: LinearProgressIndicator(
|
||||
value: _progress,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
Theme.of(context).colorScheme.alternativeColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
98
auth/lib/ui/common/rename_dialog.dart
Normal file
98
auth/lib/ui/common/rename_dialog.dart
Normal file
@@ -0,0 +1,98 @@
|
||||
|
||||
|
||||
import 'package:ente_auth/utils/dialog_util.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class RenameDialog extends StatefulWidget {
|
||||
final String name;
|
||||
final String type;
|
||||
final int maxLength;
|
||||
|
||||
const RenameDialog(this.name, this.type, {super.key, this.maxLength = 100});
|
||||
|
||||
@override
|
||||
State<RenameDialog> createState() => _RenameDialogState();
|
||||
}
|
||||
|
||||
class _RenameDialogState extends State<RenameDialog> {
|
||||
String? _newName;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_newName = widget.name;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text("Enter a new name"),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TextFormField(
|
||||
decoration: InputDecoration(
|
||||
hintText: '${widget.type} name',
|
||||
hintStyle: const TextStyle(
|
||||
color: Colors.white30,
|
||||
),
|
||||
contentPadding: const EdgeInsets.all(12),
|
||||
),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_newName = value;
|
||||
});
|
||||
},
|
||||
autocorrect: false,
|
||||
keyboardType: TextInputType.text,
|
||||
initialValue: _newName,
|
||||
autofocus: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: const Text(
|
||||
"Cancel",
|
||||
style: TextStyle(
|
||||
color: Colors.redAccent,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(null);
|
||||
},
|
||||
),
|
||||
TextButton(
|
||||
child: Text(
|
||||
"Rename",
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
if (_newName!.trim().isEmpty) {
|
||||
showErrorDialog(
|
||||
context,
|
||||
"Empty name",
|
||||
"${widget.type} name cannot be empty",
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (_newName!.trim().length > widget.maxLength) {
|
||||
showErrorDialog(
|
||||
context,
|
||||
"Name too large",
|
||||
"${widget.type} name should be less than ${widget.maxLength} characters",
|
||||
);
|
||||
return;
|
||||
}
|
||||
Navigator.of(context).pop(_newName!.trim());
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
41
auth/lib/ui/components/home_header_widget.dart
Normal file
41
auth/lib/ui/components/home_header_widget.dart
Normal file
@@ -0,0 +1,41 @@
|
||||
import 'package:ente_auth/core/event_bus.dart';
|
||||
import 'package:ente_auth/events/opened_settings_event.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class HomeHeaderWidget extends StatefulWidget {
|
||||
final Widget centerWidget;
|
||||
const HomeHeaderWidget({required this.centerWidget, super.key});
|
||||
|
||||
@override
|
||||
State<HomeHeaderWidget> createState() => _HomeHeaderWidgetState();
|
||||
}
|
||||
|
||||
class _HomeHeaderWidgetState extends State<HomeHeaderWidget> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final hasNotch = View.of(context).viewPadding.top > 65;
|
||||
return Padding(
|
||||
padding: EdgeInsets.fromLTRB(4, hasNotch ? 4 : 8, 4, 4),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
IconButton(
|
||||
visualDensity: const VisualDensity(horizontal: -2, vertical: -2),
|
||||
onPressed: () {
|
||||
Scaffold.of(context).openDrawer();
|
||||
Bus.instance.fire(OpenedSettingsEvent());
|
||||
},
|
||||
splashColor: Colors.transparent,
|
||||
icon: const Icon(
|
||||
Icons.menu_outlined,
|
||||
),
|
||||
),
|
||||
AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 250),
|
||||
child: widget.centerWidget,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
69
auth/lib/ui/settings/danger_section_widget.dart
Normal file
69
auth/lib/ui/settings/danger_section_widget.dart
Normal file
@@ -0,0 +1,69 @@
|
||||
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/account/delete_account_page.dart';
|
||||
import 'package:ente_auth/ui/components/captioned_text_widget.dart';
|
||||
import 'package:ente_auth/ui/components/expandable_menu_item_widget.dart';
|
||||
import 'package:ente_auth/ui/components/menu_item_widget.dart';
|
||||
import 'package:ente_auth/ui/settings/common_settings.dart';
|
||||
import 'package:ente_auth/utils/dialog_util.dart';
|
||||
import 'package:ente_auth/utils/navigation_util.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class DangerSectionWidget extends StatelessWidget {
|
||||
const DangerSectionWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ExpandableMenuItemWidget(
|
||||
title: context.l10n.exit,
|
||||
selectionOptionsWidget: _getSectionOptions(context),
|
||||
leadingIcon: Icons.logout_outlined,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _getSectionOptions(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
sectionOptionSpacing,
|
||||
MenuItemWidget(
|
||||
captionedTextWidget: CaptionedTextWidget(
|
||||
title: context.l10n.logout,
|
||||
),
|
||||
pressedColor: getEnteColorScheme(context).fillFaint,
|
||||
trailingIcon: Icons.chevron_right_outlined,
|
||||
trailingIconIsMuted: true,
|
||||
onTap: () async {
|
||||
_onLogoutTapped(context);
|
||||
},
|
||||
),
|
||||
sectionOptionSpacing,
|
||||
MenuItemWidget(
|
||||
captionedTextWidget: CaptionedTextWidget(
|
||||
title: context.l10n.deleteAccount,
|
||||
),
|
||||
pressedColor: getEnteColorScheme(context).fillFaint,
|
||||
trailingIcon: Icons.chevron_right_outlined,
|
||||
trailingIconIsMuted: true,
|
||||
onTap: () async {
|
||||
// ignore: unawaited_futures
|
||||
routeToPage(context, const DeleteAccountPage());
|
||||
},
|
||||
),
|
||||
sectionOptionSpacing,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _onLogoutTapped(BuildContext context) {
|
||||
showChoiceActionSheet(
|
||||
context,
|
||||
title: context.l10n.areYouSureYouWantToLogout,
|
||||
firstButtonLabel: context.l10n.yesLogout,
|
||||
isCritical: true,
|
||||
firstButtonOnTap: () async {
|
||||
await UserService.instance.logout(context);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
94
auth/lib/ui/settings/debug_section_widget.dart
Normal file
94
auth/lib/ui/settings/debug_section_widget.dart
Normal file
@@ -0,0 +1,94 @@
|
||||
import 'package:ente_auth/core/configuration.dart';
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/ui/settings/common_settings.dart';
|
||||
import 'package:ente_auth/ui/settings/settings_section_title.dart';
|
||||
import 'package:ente_auth/ui/settings/settings_text_item.dart';
|
||||
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
|
||||
import 'package:expandable/expandable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class DebugSectionWidget extends StatelessWidget {
|
||||
const DebugSectionWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// This is a debug only section not shown to end users, so these strings are
|
||||
// not translated.
|
||||
return ExpandablePanel(
|
||||
header: const SettingsSectionTitle("Debug"),
|
||||
collapsed: Container(),
|
||||
expanded: _getSectionOptions(context),
|
||||
theme: getExpandableTheme(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _getSectionOptions(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onTap: () async {
|
||||
_showKeyAttributesDialog(context);
|
||||
},
|
||||
child: const SettingsTextItem(
|
||||
text: "Key attributes",
|
||||
icon: Icons.navigate_next,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _showKeyAttributesDialog(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
final keyAttributes = Configuration.instance.getKeyAttributes()!;
|
||||
final AlertDialog alert = AlertDialog(
|
||||
title: const Text("key attributes"),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
const Text(
|
||||
"Key",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
Text(CryptoUtil.bin2base64(Configuration.instance.getKey()!)),
|
||||
const Padding(padding: EdgeInsets.all(12)),
|
||||
const Text(
|
||||
"Encrypted Key",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
Text(keyAttributes.encryptedKey),
|
||||
const Padding(padding: EdgeInsets.all(12)),
|
||||
const Text(
|
||||
"Key Decryption Nonce",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
Text(keyAttributes.keyDecryptionNonce),
|
||||
const Padding(padding: EdgeInsets.all(12)),
|
||||
const Text(
|
||||
"KEK Salt",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
Text(keyAttributes.kekSalt),
|
||||
const Padding(padding: EdgeInsets.all(12)),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text(l10n.ok),
|
||||
onPressed: () {
|
||||
Navigator.of(context, rootNavigator: true).pop('dialog');
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return alert;
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
34
auth/lib/ui/settings/made_with_love_widget.dart
Normal file
34
auth/lib/ui/settings/made_with_love_widget.dart
Normal file
@@ -0,0 +1,34 @@
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class MadeWithLoveWidget extends StatelessWidget {
|
||||
const MadeWithLoveWidget({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
launchUrl(Uri.parse("https://ente.io"));
|
||||
},
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
text: l10n.madeWithLoveAtPrefix,
|
||||
style: DefaultTextStyle.of(context).style,
|
||||
children: const <TextSpan>[
|
||||
TextSpan(
|
||||
text: 'ente.io',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.green,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
35
auth/lib/ui/settings/settings_text_item.dart
Normal file
35
auth/lib/ui/settings/settings_text_item.dart
Normal file
@@ -0,0 +1,35 @@
|
||||
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SettingsTextItem extends StatelessWidget {
|
||||
final String text;
|
||||
final IconData icon;
|
||||
const SettingsTextItem({
|
||||
super.key,
|
||||
required this.text,
|
||||
required this.icon,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
Padding(padding: EdgeInsets.all(Platform.isIOS ? 4 : 6)),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(text, style: Theme.of(context).textTheme.titleMedium),
|
||||
),
|
||||
Icon(icon),
|
||||
],
|
||||
),
|
||||
Padding(padding: EdgeInsets.all(Platform.isIOS ? 4 : 6)),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
83
auth/lib/ui/settings/support_dev_widget.dart
Normal file
83
auth/lib/ui/settings/support_dev_widget.dart
Normal file
@@ -0,0 +1,83 @@
|
||||
import 'package:dotted_border/dotted_border.dart';
|
||||
import 'package:ente_auth/core/configuration.dart';
|
||||
import 'package:ente_auth/l10n/l10n.dart';
|
||||
import 'package:ente_auth/models/subscription.dart';
|
||||
import 'package:ente_auth/services/billing_service.dart';
|
||||
import 'package:ente_auth/theme/ente_theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:styled_text/styled_text.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class SupportDevWidget extends StatelessWidget {
|
||||
const SupportDevWidget({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
|
||||
// fetch
|
||||
if (Configuration.instance.hasConfiguredAccount()) {
|
||||
return FutureBuilder<Subscription>(
|
||||
future: BillingService.instance.getSubscription(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
final subscription = snapshot.data;
|
||||
if (subscription != null && subscription.productID == "free") {
|
||||
return buildWidget(l10n, context);
|
||||
}
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return buildWidget(l10n, context);
|
||||
}
|
||||
}
|
||||
|
||||
Widget buildWidget(AppLocalizations l10n, BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
launchUrl(Uri.parse("https://ente.io"));
|
||||
},
|
||||
child: DottedBorder(
|
||||
borderType: BorderType.RRect,
|
||||
radius: const Radius.circular(12),
|
||||
padding: const EdgeInsets.all(6),
|
||||
dashPattern: const <double>[3, 3],
|
||||
color: getEnteColorScheme(context).primaryGreen,
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 6),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
StyledText(
|
||||
text: l10n.supportDevs,
|
||||
style: getEnteTextTheme(context).large,
|
||||
tags: {
|
||||
'bold-green': StyledTextTag(
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: getEnteColorScheme(context).primaryGreen,
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
const Padding(padding: EdgeInsets.all(6)),
|
||||
Text(
|
||||
l10n.supportDiscount,
|
||||
style: const TextStyle(
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
1
auth/lib/ui/settings_section_title.dart
Normal file
1
auth/lib/ui/settings_section_title.dart
Normal file
@@ -0,0 +1 @@
|
||||
// TODO Implement this library.
|
||||
@@ -113,12 +113,12 @@ String parseErrorForUI(
|
||||
if (dioError.response?.data["code"] != null) {
|
||||
errorInfo = "Reason: ${dioError.response!.data["code"]}";
|
||||
} else {
|
||||
errorInfo = "Reason: ${dioError.response!.data.toString()}";
|
||||
errorInfo = "Reason: ${dioError.response!.data}";
|
||||
}
|
||||
} else if (dioError.type == DioExceptionType.badCertificate) {
|
||||
errorInfo = "Reason: ${dioError.error.toString()}";
|
||||
} else if (dioError.type == DioExceptionType.unknown) {
|
||||
errorInfo = "Reason: $dioError.error";
|
||||
} else {
|
||||
errorInfo = "Reason: ${dioError.type.toString()}";
|
||||
errorInfo = "Reason: $dioError.type";
|
||||
}
|
||||
} else {
|
||||
if (kDebugMode) {
|
||||
|
||||
@@ -250,10 +250,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: collection
|
||||
sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf
|
||||
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.0"
|
||||
version: "1.18.0"
|
||||
confetti:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -286,14 +286,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.1"
|
||||
cronet_http:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cronet_http
|
||||
sha256: "3af9c4d57bf07ef4b307e77b22be4ad61bea19ee6ff65e62184863f3a09f1415"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.2"
|
||||
cross_file:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -318,14 +310,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
cupertino_http:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cupertino_http
|
||||
sha256: "6fcf79586ad872ddcd6004d55c8c2aab3cdf0337436e8f99837b1b6c30665d0c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
dart_style:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -362,10 +346,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dio
|
||||
sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9"
|
||||
sha256: "5598aa796bbf4699afd5c67c0f5f6e2ed542afc956884b9cd58c306966efc260"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.8.0+1"
|
||||
version: "5.7.0"
|
||||
dio_web_adapter:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -877,14 +861,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
http_profile:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_profile
|
||||
sha256: "7e679e355b09aaee2ab5010915c932cce3f2d1c11c3b2dc177891687014ffa78"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.0"
|
||||
image:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -909,14 +885,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
jni:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: jni
|
||||
sha256: f377c585ea9c08d48b427dc2e03780af2889d1bb094440da853c6883c1acba4b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.10.1"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1093,14 +1061,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
native_dio_adapter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: native_dio_adapter
|
||||
sha256: "7420bc9517b2abe09810199a19924617b45690a44ecfb0616ac9babc11875c03"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
nested:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1117,14 +1077,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.0"
|
||||
objective_c:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: objective_c
|
||||
sha256: "62e79ab8c3ed6f6a340ea50dd48d65898f5d70425d404f0d99411f6e56e04584"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.0"
|
||||
otp:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
@@ -21,7 +21,7 @@ dependencies:
|
||||
connectivity_plus: ^6.0.5
|
||||
convert: ^3.1.1
|
||||
device_info_plus: ^9.1.1
|
||||
dio: ^5.8.0+1
|
||||
dio: ^5.4.0
|
||||
dotted_border: ^2.0.0+2
|
||||
dropdown_button2: ^2.3.9
|
||||
email_validator: ^3.0.0
|
||||
@@ -72,7 +72,6 @@ dependencies:
|
||||
logging: ^1.0.1
|
||||
modal_bottom_sheet: ^3.0.0
|
||||
move_to_background: ^1.0.2
|
||||
native_dio_adapter: ^1.4.0
|
||||
otp: ^3.1.1
|
||||
package_info_plus: ^8.0.2
|
||||
password_strength: ^0.2.0
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
## v1.7.10 (Unreleased)
|
||||
|
||||
- Speed up selection for large libraries.
|
||||
- Support Japanese translations.
|
||||
- Fix video thumbnail generation on drag and drop.
|
||||
- .
|
||||
|
||||
## v1.7.9
|
||||
|
||||
@@ -135,11 +135,11 @@ export const sidebar = [
|
||||
link: "/photos/faq/hidden-and-archive",
|
||||
},
|
||||
{
|
||||
text: "Face recognition",
|
||||
link: "/photos/faq/face-recognition",
|
||||
text: "Machine Learning",
|
||||
link: "/photos/faq/machine-learning",
|
||||
},
|
||||
{
|
||||
text: "Video streaming",
|
||||
text: "Video Streaming",
|
||||
link: "/photos/faq/video-streaming",
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Face recognition
|
||||
title: Machine Learning FAQ
|
||||
description:
|
||||
Frequently asked questions about Ente's face recognition
|
||||
Frequently asked questions about several features of Ente's ML suite
|
||||
---
|
||||
|
||||
# Face recognition
|
||||
# Machine Learning
|
||||
|
||||
## Can I merge or de-merge persons recognized by the app?
|
||||
|
||||
@@ -19,7 +19,7 @@ instead of typing the name again, tap on the already given name that should now
|
||||
be listed.
|
||||
|
||||
De-merging a certain grouping can be done by going to the person, pressing
|
||||
`Review suggestions` and then the top right `History icon`. Now press on the
|
||||
`review suggestions` and then the top right `history icon`. Now press on the
|
||||
`minus icon` beside the group you want to de-merge.
|
||||
|
||||
### Desktop
|
||||
@@ -29,16 +29,6 @@ selecting an existing person, and use the "Review suggestions" sheet to de-merge
|
||||
previously merged persons (click the top right history icon on the suggestion
|
||||
sheet to see the previous merges, and if necessary, undo them).
|
||||
|
||||
## How can I remove an incorrectly grouped face from a person?
|
||||
|
||||
On our mobile app, open up the person from the People section, click on the
|
||||
three dots to open up overflow menu, and click on Edit. Now you will be
|
||||
presented with the list of all photos that were merged to create this person.
|
||||
|
||||
You can click on the merged photos and select the photos you think are
|
||||
incorrectly grouped (by long-pressing on them) and select "Remove" from the
|
||||
action bar that pops up to remove any incorrect faces.
|
||||
|
||||
## How do I change the cover for a recognized person?
|
||||
|
||||
### Mobile
|
||||
@@ -1,11 +1,12 @@
|
||||
---
|
||||
title: Metadata
|
||||
description: Handling of metadata in Ente Photos
|
||||
description: Handling of metadata, in particular creation dates, in Ente Photos
|
||||
---
|
||||
|
||||
# Metadata
|
||||
|
||||
This document describes Ente's handling of metadata
|
||||
This document describes Ente's handling of metadata, in particular photo
|
||||
creation dates.
|
||||
|
||||
## Import
|
||||
|
||||
@@ -45,7 +46,7 @@ importing that folder into Ente**. This way, we will be able to always correctly
|
||||
map, for example, `flower.jpeg` and `flower.json` and show the same date for
|
||||
`flower.jpeg` that you would've seen within Google Photos.
|
||||
|
||||
### File name
|
||||
### Screenshots
|
||||
|
||||
In case the photo does not have a date in the Exif data (and it is not a Google
|
||||
takeout), for example, for screenshots or Whatsapp forwards, Ente will still try
|
||||
@@ -56,28 +57,6 @@ and deduce the correct date for the file from the name of the file.
|
||||
> This process works great most of the time, but it is inherently based on
|
||||
> heuristics and is not exact.
|
||||
|
||||
If we are unable to decipher the creation time from these 3 sources, we will set
|
||||
the upload time as the photo's creation time.
|
||||
|
||||
## Modifications
|
||||
|
||||
Ente supports modifications to the following metadata:
|
||||
- File name
|
||||
- Date & time
|
||||
- Location
|
||||
|
||||
The first two options are available on both mobile and desktop, while the
|
||||
ability to update location is only available within our mobile apps.
|
||||
|
||||
### Bulk modifications
|
||||
|
||||
You can bulk-edit creation time of photos from our desktop app, by
|
||||
multi-selecting items and selecting the "Fix time" option from the action bar.
|
||||
|
||||
You can bulk-edit location coordinates of photos from our mobile app, by
|
||||
multi-selecting items and selecting the "Edit location" option from the action
|
||||
bar.
|
||||
|
||||
## Export
|
||||
|
||||
Ente guarantees that you will get back the _exact_ same original photos and
|
||||
|
||||
@@ -47,9 +47,6 @@ availability and durability. Our
|
||||
[reliability document](https://ente.io/reliability) provides in-depth
|
||||
information about our storage infrastructure and data replication strategies.
|
||||
|
||||
In short, we store 3 copies of your data, across 3 different providers, in 3
|
||||
different countries. One of them is in an underground fall-out shelter in Paris.
|
||||
|
||||
### How does Ente's encryption compare to industry standards?
|
||||
|
||||
Our encryption model goes beyond industry standards. While many services use
|
||||
|
||||
@@ -157,21 +157,6 @@ The same applies to monthly plans.
|
||||
If you prefer to have this credit refunded to your original payment method,
|
||||
please contact support@ente.io, and we'll assist you.
|
||||
|
||||
## How can I update my payment method?
|
||||
|
||||
You can view and manage your payment method by clicking on the green
|
||||
subscription card within the Ente app, and selecting the "Manage payment method"
|
||||
button.
|
||||
|
||||
You will be able to see all of your previous invoices, with details regarding
|
||||
their payment status. In case of failed payments, you will also have an option
|
||||
to retry those charges.
|
||||
|
||||
## How can I cancel my subscription?
|
||||
|
||||
You can cancel your subscription by clicking on the green subscription card
|
||||
within the Ente app, and selecting the "Cancel subscription" button.
|
||||
|
||||
## Is there an x GB plan?
|
||||
|
||||
We have experimented quite a bit and have found it hard to design a single
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: Video streaming FAQ
|
||||
title: Video Streaming FAQ
|
||||
description:
|
||||
Frequently asked questions about Ente's video streaming feature
|
||||
Frequently asked questions about Ente's Video Streaming feature
|
||||
---
|
||||
|
||||
# Video streaming
|
||||
# Video Streaming
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
@@ -31,8 +31,8 @@ You can open these videos by tapping on them.
|
||||
Processed videos will show a `Play stream` button, clicking which will load and
|
||||
play the stream.
|
||||
|
||||
Clicking on the `Info` icon within the original video will show details about
|
||||
the generated stream.
|
||||
Clicking on the `Info` icon within the original video will show details
|
||||
about the generated stream.
|
||||
|
||||
### What is a stream?
|
||||
|
||||
|
||||
@@ -50,5 +50,5 @@ end-to-end encrypted security that we use for syncing your photos.
|
||||
Note that the desktop app does not currently support modifying the face
|
||||
groupings, that is only supported by the mobile app.
|
||||
|
||||
For more information on how to use Machine Learning for face recognition please
|
||||
check out [the FAQ](../faq/face-recognition).
|
||||
For more information on how to use Machine Learning please check out
|
||||
[the FAQ](../faq/machine-learning).
|
||||
|
||||
@@ -10,9 +10,3 @@ automatically deleted from Trash after 30 days. You can manaully select photos
|
||||
to permanently delete or completely empty the trash if you wish.
|
||||
|
||||
Items in trash are included in your used storage calculation.
|
||||
|
||||
## Recovery
|
||||
|
||||
If you have deleted items accidentally, you can recover them from Trash by
|
||||
selecting these items, and clicking the "Restore" button on the action bar that
|
||||
pops up.
|
||||
|
||||
@@ -144,6 +144,8 @@ PODS:
|
||||
- Flutter
|
||||
- media_kit_libs_ios_video (1.0.4):
|
||||
- Flutter
|
||||
- media_kit_native_event_loop (1.0.0):
|
||||
- Flutter
|
||||
- media_kit_video (0.0.1):
|
||||
- Flutter
|
||||
- motion_sensors (0.0.1):
|
||||
@@ -187,6 +189,8 @@ PODS:
|
||||
- PromisesObjC (2.4.0)
|
||||
- receive_sharing_intent (1.8.0):
|
||||
- Flutter
|
||||
- screen_brightness_ios (0.1.0):
|
||||
- Flutter
|
||||
- SDWebImage (5.20.0):
|
||||
- SDWebImage/Core (= 5.20.0)
|
||||
- SDWebImage/Core (5.20.0)
|
||||
@@ -274,6 +278,7 @@ DEPENDENCIES:
|
||||
- maps_launcher (from `.symlinks/plugins/maps_launcher/ios`)
|
||||
- media_extension (from `.symlinks/plugins/media_extension/ios`)
|
||||
- media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`)
|
||||
- media_kit_native_event_loop (from `.symlinks/plugins/media_kit_native_event_loop/ios`)
|
||||
- media_kit_video (from `.symlinks/plugins/media_kit_video/ios`)
|
||||
- motion_sensors (from `.symlinks/plugins/motion_sensors/ios`)
|
||||
- motionphoto (from `.symlinks/plugins/motionphoto/ios`)
|
||||
@@ -288,6 +293,7 @@ DEPENDENCIES:
|
||||
- photo_manager (from `.symlinks/plugins/photo_manager/ios`)
|
||||
- privacy_screen (from `.symlinks/plugins/privacy_screen/ios`)
|
||||
- receive_sharing_intent (from `.symlinks/plugins/receive_sharing_intent/ios`)
|
||||
- screen_brightness_ios (from `.symlinks/plugins/screen_brightness_ios/ios`)
|
||||
- sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`)
|
||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
@@ -384,6 +390,8 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/media_extension/ios"
|
||||
media_kit_libs_ios_video:
|
||||
:path: ".symlinks/plugins/media_kit_libs_ios_video/ios"
|
||||
media_kit_native_event_loop:
|
||||
:path: ".symlinks/plugins/media_kit_native_event_loop/ios"
|
||||
media_kit_video:
|
||||
:path: ".symlinks/plugins/media_kit_video/ios"
|
||||
motion_sensors:
|
||||
@@ -412,6 +420,8 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/privacy_screen/ios"
|
||||
receive_sharing_intent:
|
||||
:path: ".symlinks/plugins/receive_sharing_intent/ios"
|
||||
screen_brightness_ios:
|
||||
:path: ".symlinks/plugins/screen_brightness_ios/ios"
|
||||
sentry_flutter:
|
||||
:path: ".symlinks/plugins/sentry_flutter/ios"
|
||||
share_plus:
|
||||
@@ -440,82 +450,84 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/wakelock_plus/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
background_fetch: 39f11371c0dce04b001c4bfd5e782bcccb0a85e2
|
||||
battery_info: 09f5c9ee65394f2291c8c6227bedff345b8a730c
|
||||
connectivity_plus: ddd7f30999e1faaef5967c23d5b6d503d10434db
|
||||
cupertino_http: 947a233f40cfea55167a49f2facc18434ea117ba
|
||||
dart_ui_isolate: d5bcda83ca4b04f129d70eb90110b7a567aece14
|
||||
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
|
||||
background_fetch: 94b36ee293e82972852dba8ede1fbcd3bd3d9d57
|
||||
battery_info: a06b00c06a39bc94c92beebf600f1810cb6c8c87
|
||||
connectivity_plus: 3f6c9057f4cd64198dc826edfb0542892f825343
|
||||
cupertino_http: 94ac07f5ff090b8effa6c5e2c47871d48ab7c86c
|
||||
dart_ui_isolate: 46f6714abe6891313267153ef6f9748d8ecfcab1
|
||||
device_info_plus: 335f3ce08d2e174b9fdc3db3db0f4e3b1f66bd89
|
||||
ffmpeg-kit-ios-full-gpl: 80adc341962e55ef709e36baa8ed9a70cf4ea62b
|
||||
ffmpeg_kit_flutter_full_gpl: 8d15c14c0c3aba616fac04fe44b3d27d02e3c330
|
||||
file_saver: 503e386464dbe118f630e17b4c2e1190fa0cf808
|
||||
ffmpeg_kit_flutter_full_gpl: ce18b888487c05c46ed252cd2e7956812f2e3bd1
|
||||
file_saver: 6cdbcddd690cb02b0c1a0c225b37cd805c2bf8b6
|
||||
Firebase: 98e6bf5278170668a7983e12971a66b2cd57fc8c
|
||||
firebase_core: 2bedc3136ec7c7b8561c6123ed0239387b53f2af
|
||||
firebase_messaging: 15d114e1a41fc31e4fbabcd48d765a19eec94a38
|
||||
firebase_core: 085320ddfaacb80d1a96eac3a87857afcc150db1
|
||||
firebase_messaging: d398edc15fe825f832836e74f6ac61e8cd2f3ad3
|
||||
FirebaseCore: a282032ae9295c795714ded2ec9c522fc237f8da
|
||||
FirebaseCoreInternal: d98ab91e2d80a56d7b246856a8885443b302c0c2
|
||||
FirebaseInstallations: 6ef4a1c7eb2a61ee1f74727d7f6ce2e72acf1414
|
||||
FirebaseMessaging: c9ec7b90c399c7a6100297e9d16f8a27fc7f7152
|
||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||
flutter_email_sender: 02d7443217d8c41483223627972bfdc09f74276b
|
||||
flutter_image_compress: 5a5e9aee05b6553048b8df1c3bc456d0afaac433
|
||||
flutter_inappwebview_ios: 6f63631e2c62a7c350263b13fa5427aedefe81d4
|
||||
flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086
|
||||
flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778
|
||||
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
|
||||
flutter_sodium: c84426b4de738514b5b66cfdeb8a06634e72fe0b
|
||||
fluttertoast: e9a18c7be5413da53898f660530c56f35edfba9c
|
||||
flutter_email_sender: cd533cdc7ea5eda6fabb2c7f78521c71207778a4
|
||||
flutter_image_compress: 4b058288a81f76e5e80340af37c709abafff34c4
|
||||
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
|
||||
flutter_local_notifications: ad39620c743ea4c15127860f4b5641649a988100
|
||||
flutter_native_splash: 35ddbc7228eafcb3969dcc5f1fbbe27c1145a4f0
|
||||
flutter_secure_storage: 2c2ff13db9e0a5647389bff88b0ecac56e3f3418
|
||||
flutter_sodium: 152647449ba89a157fd48d7e293dcd6d29c6ab0e
|
||||
fluttertoast: 76fea30fcf04176325f6864c87306927bd7d2038
|
||||
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
||||
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
|
||||
home_widget: 0434835a4c9a75704264feff6be17ea40e0f0d57
|
||||
image_editor_common: d6f6644ae4a6de80481e89fe6d0a8c49e30b4b43
|
||||
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
|
||||
in_app_purchase_storekit: 8c3b0b3eb1b0f04efbff401c3de6266d4258d433
|
||||
integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573
|
||||
home_widget: f169fc41fd807b4d46ab6615dc44d62adbf9f64f
|
||||
image_editor_common: 3de87e7c4804f4ae24c8f8a998362b98c105cac1
|
||||
image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
|
||||
in_app_purchase_storekit: e126ef1b89e4a9fdf07e28f005f82632b4609437
|
||||
integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e
|
||||
libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009
|
||||
local_auth_darwin: 66e40372f1c29f383a314c738c7446e2f7fdadc3
|
||||
local_auth_ios: 5046a18c018dd973247a0564496c8898dbb5adf9
|
||||
local_auth_darwin: 553ce4f9b16d3fdfeafce9cf042e7c9f77c1c391
|
||||
local_auth_ios: f7a1841beef3151d140a967c2e46f30637cdf451
|
||||
Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
|
||||
maps_launcher: 2e5b6a2d664ec6c27f82ffa81b74228d770ab203
|
||||
media_extension: 6d30dc1431ebaa63f43c397c37917b1a0a597a4c
|
||||
media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1
|
||||
media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e
|
||||
motion_sensors: 03f55b7c637a7e365a0b5f9697a449f9059d5d91
|
||||
motionphoto: d4a432b8c8f22fb3ad966258597c0103c9c5ff16
|
||||
move_to_background: 39a5b79b26d577b0372cbe8a8c55e7aa9fcd3a2d
|
||||
maps_launcher: edf829809ba9e894d70e569bab11c16352dedb45
|
||||
media_extension: a1fec16ee9c8241a6aef9613578ebf097d6c5e64
|
||||
media_kit_libs_ios_video: 5a18affdb97d1f5d466dc79988b13eff6c5e2854
|
||||
media_kit_native_event_loop: 5fba1a849a6c87a34985f1e178a0de5bd444a0cf
|
||||
media_kit_video: 1746e198cb697d1ffb734b1d05ec429d1fcd1474
|
||||
motion_sensors: 741e702c17467b9569a92165dda8d4d88c6167f1
|
||||
motionphoto: 584b43031ead3060225cdff08fa49818879801d2
|
||||
move_to_background: 155f7bfbd34d43ad847cb630d2d2d87c17199710
|
||||
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
|
||||
native_video_player: d12af78a1a4a8cf09775a5177d5b392def6fd23c
|
||||
objective_c: 77e887b5ba1827970907e10e832eec1683f3431d
|
||||
onnxruntime: e7c2ae44385191eaad5ae64c935a72debaddc997
|
||||
native_video_player: b65c58951ede2f93d103a25366bdebca95081265
|
||||
objective_c: 89e720c30d716b036faf9c9684022048eee1eee2
|
||||
onnxruntime: f9b296392c96c42882be020a59dbeac6310d81b2
|
||||
onnxruntime-c: a909204639a1f035f575127ac406f781ac797c9c
|
||||
onnxruntime-objc: b6fab0f1787aa6f7190c2013f03037df4718bd8b
|
||||
open_mail_app: 794172f6a22cd16319d3ddaf45e945b2f74952b0
|
||||
open_mail_app: 06d5a4162866388a92b1df3deb96e56be20cf45c
|
||||
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
|
||||
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
|
||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
|
||||
photo_manager: ff695c7a1dd5bc379974953a2b5c0a293f7c4c8a
|
||||
privacy_screen: 1a131c052ceb3c3659934b003b0d397c2381a24e
|
||||
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
||||
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
|
||||
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
|
||||
photo_manager: d2fbcc0f2d82458700ee6256a15018210a81d413
|
||||
privacy_screen: 3159a541f5d3a31bea916cfd4e58f9dc722b3fd4
|
||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
||||
receive_sharing_intent: df9c334dc9feadcbd3266e5cb49c8443405e1c9f
|
||||
receive_sharing_intent: f6a12b7e8f7ed745f61c982de8a65de88db44a44
|
||||
screen_brightness_ios: 5ed898fa50fa82a26171c086ca5e28228f932576
|
||||
SDWebImage: 73c6079366fea25fa4bb9640d5fb58f0893facd8
|
||||
SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380
|
||||
Sentry: f8374b5415bc38dfb5645941b3ae31230fbeae57
|
||||
sentry_flutter: 0eb93e5279eb41e2392212afe1ccd2fecb4f8cbe
|
||||
share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad
|
||||
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
||||
sqflite_darwin: a553b1fd6fe66f53bbb0fe5b4f5bab93f08d7a13
|
||||
sentry_flutter: 0a211008f52553ba5dd81ceb71f48d78f0f1f6ab
|
||||
share_plus: 011d6fb4f9d2576b83179a3a5c5e323202cdabcf
|
||||
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
|
||||
sqflite_darwin: 44bb54cc302bff1fbe5752293aba1820b157cf1c
|
||||
sqlite3: 0bb0e6389d824e40296f531b858a2a0b71c0d2fb
|
||||
sqlite3_flutter_libs: c00457ebd31e59fa6bb830380ddba24d44fbcd3b
|
||||
system_info_plus: 5393c8da281d899950d751713575fbf91c7709aa
|
||||
sqlite3_flutter_libs: 9379996d65aa23dcda7585a5b58766cebe0aa042
|
||||
system_info_plus: 555ce7047fbbf29154726db942ae785c29211740
|
||||
Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e
|
||||
ua_client_hints: 46bb5817a868f9e397c0ba7e3f2f5c5d90c35156
|
||||
uni_links: d97da20c7701486ba192624d99bffaaffcfc298a
|
||||
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
||||
video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3
|
||||
video_thumbnail: c4e2a3c539e247d4de13cd545344fd2d26ffafd1
|
||||
volume_controller: 2e3de73d6e7e81a0067310d17fb70f2f86d71ac7
|
||||
wakelock_plus: 373cfe59b235a6dd5837d0fb88791d2f13a90d56
|
||||
ua_client_hints: 0b48eae1134283f5b131ee0871fa878377f07a01
|
||||
uni_links: ed8c961e47ed9ce42b6d91e1de8049e38a4b3152
|
||||
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
|
||||
video_player_avfoundation: 2cef49524dd1f16c5300b9cd6efd9611ce03639b
|
||||
video_thumbnail: b637e0ad5f588ca9945f6e2c927f73a69a661140
|
||||
volume_controller: ca1cde542ee70fad77d388f82e9616488110942b
|
||||
wakelock_plus: 04623e3f525556020ebd4034310f20fe7fda8b49
|
||||
|
||||
PODFILE CHECKSUM: 20e086e6008977d43a3d40260f3f9bffcac748dd
|
||||
|
||||
|
||||
@@ -315,6 +315,7 @@
|
||||
"${BUILT_PRODUCTS_DIR}/maps_launcher/maps_launcher.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/media_extension/media_extension.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/media_kit_libs_ios_video/media_kit_libs_ios_video.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/media_kit_native_event_loop/media_kit_native_event_loop.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/media_kit_video/media_kit_video.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/motion_sensors/motion_sensors.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/motionphoto/motionphoto.framework",
|
||||
@@ -328,6 +329,7 @@
|
||||
"${BUILT_PRODUCTS_DIR}/photo_manager/photo_manager.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/privacy_screen/privacy_screen.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/receive_sharing_intent/receive_sharing_intent.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/screen_brightness_ios/screen_brightness_ios.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/sentry_flutter/sentry_flutter.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/share_plus/share_plus.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/shared_preferences_foundation/shared_preferences_foundation.framework",
|
||||
@@ -410,6 +412,7 @@
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/maps_launcher.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/media_extension.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/media_kit_libs_ios_video.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/media_kit_native_event_loop.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/media_kit_video.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/motion_sensors.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/motionphoto.framework",
|
||||
@@ -423,6 +426,7 @@
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/photo_manager.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/privacy_screen.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/receive_sharing_intent.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/screen_brightness_ios.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sentry_flutter.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/share_plus.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences_foundation.framework",
|
||||
|
||||
@@ -3,7 +3,6 @@ import 'dart:convert';
|
||||
import "dart:io";
|
||||
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
import "package:ente_crypto/ente_crypto.dart";
|
||||
import "package:flutter/services.dart";
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
@@ -20,9 +19,9 @@ import 'package:photos/db/upload_locks_db.dart';
|
||||
import "package:photos/events/endpoint_updated_event.dart";
|
||||
import 'package:photos/events/signed_in_event.dart';
|
||||
import 'package:photos/events/user_logged_out_event.dart';
|
||||
import 'package:photos/models/api/user/key_attributes.dart';
|
||||
import 'package:photos/models/api/user/key_gen_result.dart';
|
||||
import 'package:photos/models/api/user/private_key_attributes.dart';
|
||||
import 'package:photos/models/key_attributes.dart';
|
||||
import 'package:photos/models/key_gen_result.dart';
|
||||
import 'package:photos/models/private_key_attributes.dart';
|
||||
import 'package:photos/services/collections_service.dart';
|
||||
import 'package:photos/services/favorites_service.dart';
|
||||
import "package:photos/services/home_widget_service.dart";
|
||||
@@ -31,6 +30,7 @@ import "package:photos/services/machine_learning/face_ml/person/person_service.d
|
||||
import 'package:photos/services/memories_service.dart';
|
||||
import 'package:photos/services/search_service.dart';
|
||||
import 'package:photos/services/sync_service.dart';
|
||||
import 'package:photos/utils/crypto_util.dart';
|
||||
import 'package:photos/utils/file_uploader.dart';
|
||||
import "package:photos/utils/lock_screen_settings.dart";
|
||||
import 'package:photos/utils/validator_util.dart';
|
||||
@@ -248,7 +248,7 @@ class Configuration {
|
||||
// decrypt the master key
|
||||
final kekSalt = CryptoUtil.getSaltToDeriveKey();
|
||||
final derivedKeyResult = await CryptoUtil.deriveSensitiveKey(
|
||||
utf8.encode(password),
|
||||
utf8.encode(password) as Uint8List,
|
||||
kekSalt,
|
||||
);
|
||||
final loginKey = await CryptoUtil.deriveLoginKey(derivedKeyResult.key);
|
||||
@@ -294,7 +294,7 @@ class Configuration {
|
||||
// decrypt the master key
|
||||
final kekSalt = CryptoUtil.getSaltToDeriveKey();
|
||||
final derivedKeyResult = await CryptoUtil.deriveSensitiveKey(
|
||||
utf8.encode(password),
|
||||
utf8.encode(password) as Uint8List,
|
||||
kekSalt,
|
||||
);
|
||||
final loginKey = await CryptoUtil.deriveLoginKey(derivedKeyResult.key);
|
||||
@@ -332,7 +332,7 @@ class Configuration {
|
||||
// Derive key-encryption-key from the entered password and existing
|
||||
// mem and ops limits
|
||||
keyEncryptionKey ??= await CryptoUtil.deriveKey(
|
||||
utf8.encode(password),
|
||||
utf8.encode(password) as Uint8List,
|
||||
CryptoUtil.base642bin(attributes.kekSalt),
|
||||
attributes.memLimit!,
|
||||
attributes.opsLimit!,
|
||||
|
||||
@@ -20,7 +20,7 @@ extension InvalidReasonExn on InvalidReason {
|
||||
class InvalidFileError extends ArgumentError {
|
||||
final InvalidReason reason;
|
||||
|
||||
InvalidFileError(String super.message, this.reason);
|
||||
InvalidFileError(String message, this.reason) : super(message);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
@@ -63,15 +63,19 @@ class UnauthorizedError extends Error {}
|
||||
class RequestCancelledError extends Error {}
|
||||
|
||||
class InvalidSyncStatusError extends AssertionError {
|
||||
InvalidSyncStatusError(String super.message);
|
||||
InvalidSyncStatusError(String message) : super(message);
|
||||
}
|
||||
|
||||
class UnauthorizedEditError extends AssertionError {}
|
||||
|
||||
class InvalidStateError extends AssertionError {
|
||||
InvalidStateError(String super.message);
|
||||
InvalidStateError(String message) : super(message);
|
||||
}
|
||||
|
||||
class KeyDerivationError extends Error {}
|
||||
|
||||
class LoginKeyDerivationError extends Error {}
|
||||
|
||||
class SrpSetupNotCompleteError extends Error {}
|
||||
|
||||
class SharingNotPermittedForFreeAccountsError extends Error {}
|
||||
|
||||
@@ -3,18 +3,18 @@ import "dart:math";
|
||||
import "dart:typed_data";
|
||||
|
||||
import "package:dio/dio.dart";
|
||||
import "package:ente_crypto/ente_crypto.dart";
|
||||
import "package:flutter/cupertino.dart";
|
||||
import "package:logging/logging.dart";
|
||||
import "package:photos/core/configuration.dart";
|
||||
import "package:photos/core/network/network.dart";
|
||||
import "package:photos/emergency/model.dart";
|
||||
import "package:photos/generated/l10n.dart";
|
||||
import "package:photos/models/api/user/key_attributes.dart";
|
||||
import "package:photos/models/api/user/set_keys_request.dart";
|
||||
import "package:photos/models/api/user/srp.dart";
|
||||
import "package:photos/models/key_attributes.dart";
|
||||
import "package:photos/models/set_keys_request.dart";
|
||||
import "package:photos/services/user_service.dart";
|
||||
import "package:photos/ui/common/user_dialogs.dart";
|
||||
import "package:photos/utils/crypto_util.dart";
|
||||
import "package:photos/utils/dialog_util.dart";
|
||||
import "package:photos/utils/email_util.dart";
|
||||
import "package:pointycastle/pointycastle.dart";
|
||||
|
||||
@@ -7,7 +7,7 @@ import "package:photos/emergency/model.dart";
|
||||
import "package:photos/emergency/recover_others_account.dart";
|
||||
import "package:photos/generated/l10n.dart";
|
||||
import "package:photos/l10n/l10n.dart";
|
||||
import "package:photos/models/api/user/key_attributes.dart";
|
||||
import "package:photos/models/key_attributes.dart";
|
||||
import "package:photos/theme/colors.dart";
|
||||
import "package:photos/theme/ente_theme.dart";
|
||||
import "package:photos/ui/components/action_sheet_widget.dart";
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import "dart:convert";
|
||||
|
||||
import "package:ente_crypto/ente_crypto.dart";
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
@@ -8,9 +7,10 @@ import 'package:password_strength/password_strength.dart';
|
||||
import "package:photos/emergency/emergency_service.dart";
|
||||
import "package:photos/emergency/model.dart";
|
||||
import "package:photos/generated/l10n.dart";
|
||||
import "package:photos/models/api/user/key_attributes.dart";
|
||||
import "package:photos/models/api/user/set_keys_request.dart";
|
||||
import "package:photos/models/key_attributes.dart";
|
||||
import "package:photos/models/set_keys_request.dart";
|
||||
import 'package:photos/ui/common/dynamic_fab.dart';
|
||||
import "package:photos/utils/crypto_util.dart";
|
||||
import 'package:photos/utils/dialog_util.dart';
|
||||
import 'package:photos/utils/toast_util.dart';
|
||||
|
||||
@@ -325,7 +325,7 @@ class _RecoverOthersAccountState extends State<RecoverOthersAccount> {
|
||||
// decrypt the master key
|
||||
final kekSalt = CryptoUtil.getSaltToDeriveKey();
|
||||
final derivedKeyResult = await CryptoUtil.deriveSensitiveKey(
|
||||
utf8.encode(password),
|
||||
utf8.encode(password) as Uint8List,
|
||||
kekSalt,
|
||||
);
|
||||
final loginKey = await CryptoUtil.deriveLoginKey(derivedKeyResult.key);
|
||||
|
||||
@@ -15,7 +15,7 @@ final lightThemeData = ThemeData(
|
||||
colorScheme: const ColorScheme.light(
|
||||
primary: Colors.black,
|
||||
secondary: Color.fromARGB(255, 163, 163, 163),
|
||||
surface: Colors.white,
|
||||
background: Colors.white,
|
||||
surfaceTint: Colors.transparent,
|
||||
),
|
||||
outlinedButtonTheme: buildOutlinedButtonThemeData(
|
||||
@@ -70,13 +70,13 @@ final lightThemeData = ThemeData(
|
||||
color: Colors.black,
|
||||
width: 2,
|
||||
),
|
||||
fillColor: WidgetStateProperty.resolveWith((states) {
|
||||
return states.contains(WidgetState.selected)
|
||||
fillColor: MaterialStateProperty.resolveWith((states) {
|
||||
return states.contains(MaterialState.selected)
|
||||
? const Color.fromRGBO(0, 0, 0, 1)
|
||||
: const Color.fromRGBO(255, 255, 255, 1);
|
||||
}),
|
||||
checkColor: WidgetStateProperty.resolveWith((states) {
|
||||
return states.contains(WidgetState.selected)
|
||||
checkColor: MaterialStateProperty.resolveWith((states) {
|
||||
return states.contains(MaterialState.selected)
|
||||
? const Color.fromRGBO(255, 255, 255, 1)
|
||||
: const Color.fromRGBO(0, 0, 0, 1);
|
||||
}),
|
||||
@@ -93,7 +93,7 @@ final darkThemeData = ThemeData(
|
||||
hintColor: const Color.fromRGBO(158, 158, 158, 1),
|
||||
colorScheme: const ColorScheme.dark(
|
||||
primary: Colors.white,
|
||||
surface: Color.fromRGBO(0, 0, 0, 1),
|
||||
background: Color.fromRGBO(0, 0, 0, 1),
|
||||
secondary: Color.fromARGB(255, 163, 163, 163),
|
||||
surfaceTint: Colors.transparent,
|
||||
),
|
||||
@@ -145,15 +145,15 @@ final darkThemeData = ThemeData(
|
||||
color: Colors.grey,
|
||||
width: 2,
|
||||
),
|
||||
fillColor: WidgetStateProperty.resolveWith((states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
fillColor: MaterialStateProperty.resolveWith((states) {
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
return const Color.fromRGBO(158, 158, 158, 1);
|
||||
} else {
|
||||
return const Color.fromRGBO(0, 0, 0, 1);
|
||||
}
|
||||
}),
|
||||
checkColor: WidgetStateProperty.resolveWith((states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
checkColor: MaterialStateProperty.resolveWith((states) {
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
return const Color.fromRGBO(0, 0, 0, 1);
|
||||
} else {
|
||||
return const Color.fromRGBO(158, 158, 158, 1);
|
||||
@@ -378,17 +378,17 @@ OutlinedButtonThemeData buildOutlinedButtonThemeData({
|
||||
fontSize: 18,
|
||||
),
|
||||
).copyWith(
|
||||
backgroundColor: WidgetStateProperty.resolveWith<Color>(
|
||||
(Set<WidgetState> states) {
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
backgroundColor: MaterialStateProperty.resolveWith<Color>(
|
||||
(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return bgDisabled;
|
||||
}
|
||||
return bgEnabled;
|
||||
},
|
||||
),
|
||||
foregroundColor: WidgetStateProperty.resolveWith<Color>(
|
||||
(Set<WidgetState> states) {
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
foregroundColor: MaterialStateProperty.resolveWith<Color>(
|
||||
(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return fgDisabled;
|
||||
}
|
||||
return fgEnabled;
|
||||
@@ -426,21 +426,21 @@ ElevatedButtonThemeData buildElevatedButtonThemeData({
|
||||
SwitchThemeData getSwitchThemeData(Color activeColor) {
|
||||
return SwitchThemeData(
|
||||
thumbColor:
|
||||
WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return null;
|
||||
}
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
return activeColor;
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
trackColor:
|
||||
WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return null;
|
||||
}
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
return activeColor;
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -4,7 +4,6 @@ import 'dart:io';
|
||||
import "package:adaptive_theme/adaptive_theme.dart";
|
||||
import 'package:background_fetch/background_fetch.dart';
|
||||
import "package:computer/computer.dart";
|
||||
import 'package:ente_crypto/ente_crypto.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -48,10 +47,12 @@ import "package:photos/services/sync_service.dart";
|
||||
import "package:photos/services/user_service.dart";
|
||||
import 'package:photos/ui/tools/app_lock.dart';
|
||||
import 'package:photos/ui/tools/lock_screen.dart';
|
||||
import 'package:photos/utils/crypto_util.dart';
|
||||
import "package:photos/utils/email_util.dart";
|
||||
import 'package:photos/utils/file_uploader.dart';
|
||||
import "package:photos/utils/lock_screen_settings.dart";
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import "package:video_player_media_kit/video_player_media_kit.dart";
|
||||
|
||||
final _logger = Logger("main");
|
||||
|
||||
@@ -237,6 +238,10 @@ Future<void> _init(bool isBackground, {String via = ''}) async {
|
||||
ServiceLocator.instance
|
||||
.init(preferences, NetworkClient.instance.enteDio, packageInfo);
|
||||
|
||||
if (!isBackground && flagService.internalUser) {
|
||||
VideoPlayerMediaKit.ensureInitialized(iOS: true);
|
||||
}
|
||||
|
||||
_logger.info("UserService init $tlog");
|
||||
await UserService.instance.init();
|
||||
_logger.info("UserService init done $tlog");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import "package:freezed_annotation/freezed_annotation.dart";
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@immutable
|
||||
class EntityData {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import "dart:typed_data";
|
||||
|
||||
import 'package:photos/models/api/user/key_attributes.dart';
|
||||
import 'package:photos/models/api/user/private_key_attributes.dart';
|
||||
import 'package:photos/models/key_attributes.dart';
|
||||
import 'package:photos/models/private_key_attributes.dart';
|
||||
|
||||
class KeyGenResult {
|
||||
final KeyAttributes keyAttributes;
|
||||
@@ -2,8 +2,9 @@ import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import "package:photos/models/api/billing/subscription.dart";
|
||||
import "package:photos/models/api/storage_bonus/bonus.dart";
|
||||
import 'package:photos/models/file/file_type.dart';
|
||||
import 'package:photos/models/subscription.dart';
|
||||
|
||||
class UserDetails {
|
||||
final String email;
|
||||
@@ -51,10 +52,6 @@ class UserDetails {
|
||||
}
|
||||
|
||||
int getFreeStorage() {
|
||||
final int? memberLimit = familyMemberStorageLimit();
|
||||
if (memberLimit != null) {
|
||||
return max(memberLimit - usage, 0);
|
||||
}
|
||||
return max(getTotalStorage() - getFamilyOrPersonalUsage(), 0);
|
||||
}
|
||||
|
||||
@@ -65,17 +62,6 @@ class UserDetails {
|
||||
storageBonus;
|
||||
}
|
||||
|
||||
// return the member storage limit if user is part of family and the admin
|
||||
// has set the storage limit for the user.
|
||||
int? familyMemberStorageLimit() {
|
||||
if (isPartOfFamily()) {
|
||||
final FamilyMember? currentUserMember = familyData!.members!
|
||||
.firstWhereOrNull((element) => element.email.trim() == email.trim());
|
||||
return currentUserMember?.storageLimit;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// This is the total storage for which user has paid for.
|
||||
int getPlanPlusAddonStorage() {
|
||||
return (isPartOfFamily() ? familyData!.storage : subscription.storage) +
|
||||
@@ -121,14 +107,12 @@ class FamilyMember {
|
||||
final int usage;
|
||||
final String id;
|
||||
final bool isAdmin;
|
||||
final int? storageLimit;
|
||||
|
||||
FamilyMember(
|
||||
this.email,
|
||||
this.usage,
|
||||
this.id,
|
||||
this.isAdmin,
|
||||
this.storageLimit,
|
||||
);
|
||||
|
||||
factory FamilyMember.fromMap(Map<String, dynamic> map) {
|
||||
@@ -137,7 +121,6 @@ class FamilyMember {
|
||||
map['usage'] as int,
|
||||
map['id'] as String,
|
||||
map['isAdmin'] as bool,
|
||||
map['storageLimit'] as int?,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -147,7 +130,6 @@ class FamilyMember {
|
||||
'usage': usage,
|
||||
'id': id,
|
||||
'isAdmin': isAdmin,
|
||||
'storageLimit': storageLimit,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -207,10 +189,6 @@ class FamilyData {
|
||||
return members!.map((e) => e.usage).toList().sum;
|
||||
}
|
||||
|
||||
FamilyMember? getMemberByID(String id) {
|
||||
return members!.firstWhereOrNull((element) => element.id == id);
|
||||
}
|
||||
|
||||
static fromMap(Map<String, dynamic>? map) {
|
||||
if (map == null) return null;
|
||||
assert(map['members'] != null && map['members'].length >= 0);
|
||||
@@ -237,3 +215,19 @@ class FamilyData {
|
||||
factory FamilyData.fromJson(String source) =>
|
||||
FamilyData.fromMap(json.decode(source));
|
||||
}
|
||||
|
||||
class FilesCount {
|
||||
final Map<FileType, int> filesCount;
|
||||
FilesCount(this.filesCount);
|
||||
|
||||
int get total =>
|
||||
images + videos + livePhotos + (filesCount[getInt(FileType.other)] ?? 0);
|
||||
|
||||
int get photos => images + livePhotos;
|
||||
|
||||
int get images => filesCount[FileType.image] ?? 0;
|
||||
|
||||
int get videos => filesCount[FileType.video] ?? 0;
|
||||
|
||||
int get livePhotos => filesCount[FileType.livePhoto] ?? 0;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import "dart:io";
|
||||
|
||||
import "package:dio/dio.dart";
|
||||
import "package:ente_crypto/ente_crypto.dart";
|
||||
import "package:ente_feature_flag/ente_feature_flag.dart";
|
||||
import "package:flutter/foundation.dart";
|
||||
import "package:logging/logging.dart";
|
||||
import "package:photos/core/constants.dart";
|
||||
import "package:photos/db/upload_locks_db.dart";
|
||||
import "package:photos/models/encryption_result.dart";
|
||||
import "package:photos/module/upload/model/multipart.dart";
|
||||
import "package:photos/module/upload/model/xml.dart";
|
||||
import "package:photos/service_locator.dart";
|
||||
import "package:photos/services/collections_service.dart";
|
||||
import "package:photos/utils/crypto_util.dart";
|
||||
|
||||
class MultiPartUploader {
|
||||
final Dio _enteDio;
|
||||
|
||||
@@ -8,8 +8,8 @@ import 'package:in_app_purchase/in_app_purchase.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:photos/core/errors.dart';
|
||||
import "package:photos/generated/l10n.dart";
|
||||
import 'package:photos/models/api/billing/billing_plan.dart';
|
||||
import 'package:photos/models/api/billing/subscription.dart';
|
||||
import 'package:photos/models/billing_plan.dart';
|
||||
import 'package:photos/models/subscription.dart';
|
||||
import 'package:photos/models/user_details.dart';
|
||||
import 'package:photos/services/user_service.dart';
|
||||
import 'package:photos/ui/common/web_page.dart';
|
||||
|
||||
@@ -4,7 +4,6 @@ import 'dart:math';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:ente_crypto/ente_crypto.dart';
|
||||
import "package:fast_base58/fast_base58.dart";
|
||||
import 'package:flutter/foundation.dart';
|
||||
import "package:flutter/material.dart";
|
||||
@@ -25,11 +24,11 @@ import 'package:photos/events/local_photos_updated_event.dart';
|
||||
import 'package:photos/extensions/list.dart';
|
||||
import 'package:photos/extensions/stop_watch.dart';
|
||||
import "package:photos/generated/l10n.dart";
|
||||
import 'package:photos/models/api/collection/collection_file_item.dart';
|
||||
import 'package:photos/models/api/collection/create_request.dart';
|
||||
import "package:photos/models/api/collection/public_url.dart";
|
||||
import "package:photos/models/api/collection/user.dart";
|
||||
import 'package:photos/models/collection/collection.dart';
|
||||
import 'package:photos/models/collection/collection_file_item.dart';
|
||||
import 'package:photos/models/collection/collection_items.dart';
|
||||
import 'package:photos/models/file/file.dart';
|
||||
import "package:photos/models/files_split.dart";
|
||||
@@ -40,6 +39,7 @@ import "package:photos/services/favorites_service.dart";
|
||||
import 'package:photos/services/file_magic_service.dart';
|
||||
import 'package:photos/services/local_sync_service.dart';
|
||||
import 'package:photos/services/remote_sync_service.dart';
|
||||
import 'package:photos/utils/crypto_util.dart';
|
||||
import "package:photos/utils/dialog_util.dart";
|
||||
import "package:photos/utils/file_key.dart";
|
||||
import "package:photos/utils/local_settings.dart";
|
||||
@@ -730,7 +730,7 @@ class CollectionsService {
|
||||
await updateMagicMetadata(collection, {"subType": 0});
|
||||
}
|
||||
final encryptedName = CryptoUtil.encryptSync(
|
||||
utf8.encode(newName),
|
||||
utf8.encode(newName) as Uint8List,
|
||||
getCollectionKey(collection.id),
|
||||
);
|
||||
await _enteDio.post(
|
||||
@@ -781,7 +781,7 @@ class CollectionsService {
|
||||
|
||||
final key = getCollectionKey(collection.id);
|
||||
final encryptedMMd = await CryptoUtil.encryptChaCha(
|
||||
utf8.encode(jsonEncode(jsonToUpdate)),
|
||||
utf8.encode(jsonEncode(jsonToUpdate)) as Uint8List,
|
||||
key,
|
||||
);
|
||||
// for required field, the json validator on golang doesn't treat 0 as valid
|
||||
@@ -840,7 +840,7 @@ class CollectionsService {
|
||||
|
||||
final key = getCollectionKey(collection.id);
|
||||
final encryptedMMd = await CryptoUtil.encryptChaCha(
|
||||
utf8.encode(jsonEncode(jsonToUpdate)),
|
||||
utf8.encode(jsonEncode(jsonToUpdate)) as Uint8List,
|
||||
key,
|
||||
);
|
||||
// for required field, the json validator on golang doesn't treat 0 as valid
|
||||
@@ -900,7 +900,7 @@ class CollectionsService {
|
||||
|
||||
final key = getCollectionKey(collection.id);
|
||||
final encryptedMMd = await CryptoUtil.encryptChaCha(
|
||||
utf8.encode(jsonEncode(jsonToUpdate)),
|
||||
utf8.encode(jsonEncode(jsonToUpdate)) as Uint8List,
|
||||
key,
|
||||
);
|
||||
// for required field, the json validator on golang doesn't treat 0 as valid
|
||||
@@ -1271,7 +1271,7 @@ class CollectionsService {
|
||||
final encryptedKeyData =
|
||||
CryptoUtil.encryptSync(collectionKey, _config.getKey()!);
|
||||
final encryptedName = CryptoUtil.encryptSync(
|
||||
utf8.encode(albumName),
|
||||
utf8.encode(albumName) as Uint8List,
|
||||
collectionKey,
|
||||
);
|
||||
final collection = await createAndCacheCollection(
|
||||
@@ -1321,7 +1321,7 @@ class CollectionsService {
|
||||
final encryptedKeyData =
|
||||
CryptoUtil.encryptSync(collectionKey, _config.getKey()!);
|
||||
final encryptedPath =
|
||||
CryptoUtil.encryptSync(utf8.encode(path), collectionKey);
|
||||
CryptoUtil.encryptSync(utf8.encode(path) as Uint8List, collectionKey);
|
||||
final collection = await createAndCacheCollection(
|
||||
CreateRequest(
|
||||
encryptedKey: CryptoUtil.bin2base64(encryptedKeyData.encryptedData!),
|
||||
|
||||
@@ -2,7 +2,6 @@ import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import "package:ente_crypto/ente_crypto.dart";
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import "package:photos/core/configuration.dart";
|
||||
@@ -13,6 +12,7 @@ import "package:photos/models/api/entity/data.dart";
|
||||
import "package:photos/models/api/entity/key.dart";
|
||||
import "package:photos/models/api/entity/type.dart";
|
||||
import "package:photos/models/local_entity_data.dart";
|
||||
import "package:photos/utils/crypto_util.dart";
|
||||
import "package:photos/utils/gzip.dart";
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:ente_crypto/ente_crypto.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/core/configuration.dart';
|
||||
import 'package:photos/core/event_bus.dart';
|
||||
@@ -15,6 +15,7 @@ import 'package:photos/models/file/file.dart';
|
||||
import 'package:photos/services/collections_service.dart';
|
||||
import 'package:photos/services/remote_sync_service.dart';
|
||||
import 'package:photos/ui/actions/collection/collection_sharing_actions.dart';
|
||||
import 'package:photos/utils/crypto_util.dart';
|
||||
|
||||
class FavoritesService {
|
||||
late Configuration _config;
|
||||
@@ -253,7 +254,7 @@ class FavoritesService {
|
||||
final encryptedKeyResult =
|
||||
CryptoUtil.encryptSync(favoriteCollectionKey, _config.getKey()!);
|
||||
final encName = CryptoUtil.encryptSync(
|
||||
utf8.encode("Favorites"),
|
||||
utf8.encode("Favorites") as Uint8List,
|
||||
favoriteCollectionKey,
|
||||
);
|
||||
final collection = await _collectionsService.createAndCacheCollection(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:ente_crypto/ente_crypto.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:photos/core/configuration.dart';
|
||||
import 'package:photos/core/constants.dart';
|
||||
@@ -16,6 +16,7 @@ import 'package:photos/models/file/file.dart';
|
||||
import "package:photos/models/metadata/common_keys.dart";
|
||||
import "package:photos/models/metadata/file_magic.dart";
|
||||
import 'package:photos/services/remote_sync_service.dart';
|
||||
import 'package:photos/utils/crypto_util.dart';
|
||||
import "package:photos/utils/file_key.dart";
|
||||
|
||||
class FileMagicService {
|
||||
@@ -94,7 +95,7 @@ class FileMagicService {
|
||||
|
||||
final fileKey = getFileKey(file);
|
||||
final encryptedMMd = await CryptoUtil.encryptChaCha(
|
||||
utf8.encode(jsonEncode(jsonToUpdate)),
|
||||
utf8.encode(jsonEncode(jsonToUpdate)) as Uint8List,
|
||||
fileKey,
|
||||
);
|
||||
params['metadataList'].add(
|
||||
@@ -160,7 +161,7 @@ class FileMagicService {
|
||||
|
||||
final fileKey = getFileKey(file);
|
||||
final encryptedMMd = await CryptoUtil.encryptChaCha(
|
||||
utf8.encode(jsonEncode(jsonToUpdate)),
|
||||
utf8.encode(jsonEncode(jsonToUpdate)) as Uint8List,
|
||||
fileKey,
|
||||
);
|
||||
params['metadataList'].add(
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import "dart:async";
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:ente_crypto/ente_crypto.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import "package:photos/core/constants.dart";
|
||||
@@ -18,6 +18,7 @@ import "package:photos/models/metadata/collection_magic.dart";
|
||||
import "package:photos/models/metadata/common_keys.dart";
|
||||
import 'package:photos/services/collections_service.dart';
|
||||
import 'package:photos/services/file_magic_service.dart';
|
||||
import 'package:photos/utils/crypto_util.dart';
|
||||
import 'package:photos/utils/dialog_util.dart';
|
||||
|
||||
extension HiddenService on CollectionsService {
|
||||
@@ -215,7 +216,7 @@ extension HiddenService on CollectionsService {
|
||||
final encKey =
|
||||
CryptoUtil.encryptSync(uncategorizedCollectionKey, config.getKey()!);
|
||||
final encName = CryptoUtil.encryptSync(
|
||||
utf8.encode("Uncategorized"),
|
||||
utf8.encode("Uncategorized") as Uint8List,
|
||||
uncategorizedCollectionKey,
|
||||
);
|
||||
final collection = await createAndCacheCollection(
|
||||
@@ -241,7 +242,7 @@ extension HiddenService on CollectionsService {
|
||||
final encryptedKeyData =
|
||||
CryptoUtil.encryptSync(collectionKey, config.getKey()!);
|
||||
final encryptedName = CryptoUtil.encryptSync(
|
||||
utf8.encode(name),
|
||||
utf8.encode(name) as Uint8List,
|
||||
collectionKey,
|
||||
);
|
||||
final jsonToUpdate = CollectionMagicMetadata(
|
||||
@@ -250,7 +251,7 @@ extension HiddenService on CollectionsService {
|
||||
).toJson();
|
||||
assert(jsonToUpdate.length == 2, "metadata should have two keys");
|
||||
final encryptedMMd = await CryptoUtil.encryptChaCha(
|
||||
utf8.encode(jsonEncode(jsonToUpdate)),
|
||||
utf8.encode(jsonEncode(jsonToUpdate)) as Uint8List,
|
||||
collectionKey,
|
||||
);
|
||||
final MetadataRequest metadataRequest = MetadataRequest(
|
||||
|
||||
@@ -57,7 +57,7 @@ class FaceDetectionRelative extends Detection {
|
||||
List<double> get rightMouth => allKeypoints[4];
|
||||
|
||||
FaceDetectionRelative({
|
||||
required super.score,
|
||||
required double score,
|
||||
required List<double> box,
|
||||
required List<List<double>> allKeypoints,
|
||||
}) : assert(
|
||||
@@ -75,7 +75,8 @@ class FaceDetectionRelative extends Detection {
|
||||
(sublist) =>
|
||||
List<double>.from(sublist.map((e) => e.clamp(0.0, 1.0))),
|
||||
)
|
||||
.toList();
|
||||
.toList(),
|
||||
super(score: score);
|
||||
|
||||
void correctForMaintainedAspectRatio(
|
||||
Dimensions originalSize,
|
||||
@@ -251,10 +252,10 @@ class FaceDetectionAbsolute extends Detection {
|
||||
List<double> get rightMouth => allKeypoints[4];
|
||||
|
||||
FaceDetectionAbsolute({
|
||||
required super.score,
|
||||
required double score,
|
||||
required this.box,
|
||||
required this.allKeypoints,
|
||||
});
|
||||
}) : super(score: score);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
|
||||
@@ -50,7 +50,7 @@ class PreviewVideoStore {
|
||||
final cacheManager = DefaultCacheManager();
|
||||
final videoCacheManager = VideoCacheManager.instance;
|
||||
|
||||
LinkedHashSet<EnteFile> fileQueue = LinkedHashSet();
|
||||
LinkedHashSet<EnteFile> files = LinkedHashSet();
|
||||
int uploadingFileId = -1;
|
||||
|
||||
final _dio = NetworkClient.instance.enteDio;
|
||||
@@ -58,9 +58,10 @@ class PreviewVideoStore {
|
||||
void init(SharedPreferences prefs) {
|
||||
_prefs = prefs;
|
||||
|
||||
FileDataService.instance.syncFDStatus().then(
|
||||
(_) => _putFilesForPreviewCreation(),
|
||||
);
|
||||
Future.delayed(
|
||||
const Duration(seconds: 10),
|
||||
PreviewVideoStore.instance.putFilesForPreviewCreation,
|
||||
);
|
||||
}
|
||||
|
||||
late final SharedPreferences _prefs;
|
||||
@@ -73,27 +74,24 @@ class PreviewVideoStore {
|
||||
|
||||
Future<void> setIsVideoStreamingEnabled(bool value) async {
|
||||
final oneMonthBack = DateTime.now().subtract(const Duration(days: 30));
|
||||
_prefs.setBool(_videoStreamingEnabled, value).ignore();
|
||||
_prefs
|
||||
.setInt(
|
||||
_videoStreamingCutoff,
|
||||
oneMonthBack.millisecondsSinceEpoch,
|
||||
)
|
||||
.ignore();
|
||||
await _prefs.setBool(_videoStreamingEnabled, value);
|
||||
await _prefs.setInt(
|
||||
_videoStreamingCutoff,
|
||||
oneMonthBack.millisecondsSinceEpoch,
|
||||
);
|
||||
Bus.instance.fire(VideoStreamingChanged());
|
||||
|
||||
if (isVideoStreamingEnabled) {
|
||||
await FileDataService.instance.syncFDStatus();
|
||||
_putFilesForPreviewCreation().ignore();
|
||||
putFilesForPreviewCreation().ignore();
|
||||
} else {
|
||||
clearQueue();
|
||||
}
|
||||
}
|
||||
|
||||
void clearQueue() {
|
||||
fileQueue.clear();
|
||||
clearQueue() {
|
||||
_items.clear();
|
||||
Bus.instance.fire(PreviewUpdatedEvent(_items));
|
||||
files.clear();
|
||||
}
|
||||
|
||||
DateTime? get videoStreamingCutoff {
|
||||
@@ -113,42 +111,31 @@ class PreviewVideoStore {
|
||||
}
|
||||
|
||||
try {
|
||||
if (!enteFile.isUploaded) {
|
||||
_removeFile(enteFile);
|
||||
return;
|
||||
}
|
||||
if (!enteFile.isUploaded) return;
|
||||
final file = await getFile(enteFile, isOrigin: true);
|
||||
if (file == null) return;
|
||||
|
||||
try {
|
||||
// check if playlist already exist
|
||||
await getPlaylist(enteFile);
|
||||
final _ = await getPreviewUrl(enteFile);
|
||||
|
||||
final resultUrl = await getPreviewUrl(enteFile);
|
||||
if (ctx != null && ctx.mounted) {
|
||||
showShortToast(ctx, 'Video preview already exists');
|
||||
}
|
||||
_removeFile(enteFile);
|
||||
debugPrint("previewUrl $resultUrl");
|
||||
_items.removeWhere((key, value) => value.file == enteFile);
|
||||
Bus.instance.fire(PreviewUpdatedEvent(_items));
|
||||
return;
|
||||
} catch (e, s) {
|
||||
if (e is DioException && e.response?.statusCode == 404) {
|
||||
if (e is DioError && e.response?.statusCode == 404) {
|
||||
_logger.info("No preview found for $enteFile");
|
||||
} else {
|
||||
_logger.warning("Failed to get playlist for $enteFile", e, s);
|
||||
_retryFile(enteFile, e);
|
||||
return;
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// elimination case for <=10 MB with H.264
|
||||
var (props, result, file) = await _checkFileForPreviewCreation(enteFile);
|
||||
if (result) {
|
||||
_removeFile(enteFile);
|
||||
return;
|
||||
}
|
||||
|
||||
// check if there is already a preview in processing
|
||||
if (uploadingFileId >= 0) {
|
||||
if (uploadingFileId == enteFile.uploadedFileID) return;
|
||||
|
||||
_items[enteFile.uploadedFileID!] = PreviewItem(
|
||||
status: PreviewItemStatus.inQueue,
|
||||
file: enteFile,
|
||||
@@ -158,11 +145,9 @@ class PreviewVideoStore {
|
||||
collectionID: enteFile.collectionID ?? 0,
|
||||
);
|
||||
Bus.instance.fire(PreviewUpdatedEvent(_items));
|
||||
fileQueue.add(enteFile);
|
||||
files.add(enteFile);
|
||||
return;
|
||||
}
|
||||
|
||||
// everything is fine, let's process
|
||||
uploadingFileId = enteFile.uploadedFileID!;
|
||||
_items[enteFile.uploadedFileID!] = PreviewItem(
|
||||
status: PreviewItemStatus.compressing,
|
||||
@@ -173,31 +158,17 @@ class PreviewVideoStore {
|
||||
);
|
||||
Bus.instance.fire(PreviewUpdatedEvent(_items));
|
||||
|
||||
// get file
|
||||
file ??= await getFile(enteFile, isOrigin: true);
|
||||
if (file == null) {
|
||||
_retryFile(enteFile, "Unable to fetch file");
|
||||
return;
|
||||
}
|
||||
|
||||
// check metadata for bitrate, codec, color space
|
||||
props ??= await getVideoPropsAsync(file);
|
||||
final props = await getVideoPropsAsync(file);
|
||||
final fileSize = enteFile.fileSize ?? file.lengthSync();
|
||||
|
||||
final videoData = List.from(props?.propData?["streams"] ?? [])
|
||||
.firstWhereOrNull((e) => e["type"] == "video");
|
||||
|
||||
final codec = videoData["codec_name"]?.toString().toLowerCase();
|
||||
final codecIsH264 = codec?.contains("h264") ?? false;
|
||||
|
||||
final bitrate = props?.duration?.inSeconds != null
|
||||
? (fileSize * 8) / props!.duration!.inSeconds
|
||||
: null;
|
||||
|
||||
final colorSpace = videoData["color_space"]?.toString().toLowerCase();
|
||||
final isColorGood = colorSpace == "bt709";
|
||||
|
||||
// create temp file & directory for preview generation
|
||||
final String tempDir = Configuration.instance.getTempDirectory();
|
||||
final String prefix =
|
||||
"${tempDir}_${enteFile.uploadedFileID}_${newID("pv")}";
|
||||
@@ -211,14 +182,15 @@ class PreviewVideoStore {
|
||||
final keyinfo = File('$prefix/mykey.keyinfo');
|
||||
keyinfo.writeAsStringSync("data:text/plain;base64,${key.base64}\n"
|
||||
"${keyfile.path}\n");
|
||||
|
||||
_logger.info(
|
||||
'Generating HLS Playlist ${enteFile.displayName} at $prefix/output.m3u8}',
|
||||
);
|
||||
|
||||
FFmpegSession? session;
|
||||
final colorSpace = videoData["color_space"]?.toString().toLowerCase();
|
||||
final isColorGood = colorSpace == "bt709";
|
||||
final codecIsH264 = codec?.contains("h264") ?? false;
|
||||
|
||||
// case 1, if it's already a good stream
|
||||
if (bitrate != null && bitrate <= 4000 * 1000 && codecIsH264) {
|
||||
session = await FFmpegKit.execute(
|
||||
'-i "${file.path}" '
|
||||
@@ -228,8 +200,7 @@ class PreviewVideoStore {
|
||||
'-hls_list_size 0 -hls_key_info_file ${keyinfo.path} '
|
||||
'$prefix/output.m3u8',
|
||||
);
|
||||
} // case 2, if it's bitrate is good, but codec is not
|
||||
else if (bitrate != null &&
|
||||
} else if (bitrate != null &&
|
||||
codec != null &&
|
||||
bitrate <= 2000 * 1000 &&
|
||||
!codecIsH264) {
|
||||
@@ -244,9 +215,10 @@ class PreviewVideoStore {
|
||||
'-hls_list_size 0 -hls_key_info_file ${keyinfo.path} '
|
||||
'$prefix/output.m3u8',
|
||||
);
|
||||
} // case 3, if it's color space is good
|
||||
else if (colorSpace != null && isColorGood) {
|
||||
session = await FFmpegKit.execute(
|
||||
}
|
||||
|
||||
if (colorSpace != null && isColorGood) {
|
||||
session ??= await FFmpegKit.execute(
|
||||
'-i "${file.path}" '
|
||||
'-metadata:s:v:0 rotate=0 '
|
||||
'-vf "scale=-2:720,fps=30" '
|
||||
@@ -255,21 +227,20 @@ class PreviewVideoStore {
|
||||
'-hls_list_size 0 -hls_key_info_file ${keyinfo.path} '
|
||||
'$prefix/output.m3u8',
|
||||
);
|
||||
} // case 4, make it compatible
|
||||
else {
|
||||
session = await FFmpegKit.execute(
|
||||
'-i "${file.path}" '
|
||||
'-metadata:s:v:0 rotate=0 '
|
||||
'-vf "scale=-2:720,fps=30,format=yuv420p10le,zscale=transfer=linear,tonemap=tonemap=hable:desat=0:peak=10,zscale=transfer=bt709:matrix=bt709:primaries=bt709,format=yuv420p" '
|
||||
'-color_primaries bt709 -color_trc bt709 -colorspace bt709 '
|
||||
'-x264-params "colorprim=bt709:transfer=bt709:colormatrix=bt709" '
|
||||
'-c:v libx264 -b:v 2000k -crf 23 -preset medium '
|
||||
'-c:a aac -b:a 128k -f hls -hls_time 2 -hls_flags single_file '
|
||||
'-hls_list_size 0 -hls_key_info_file ${keyinfo.path} '
|
||||
'$prefix/output.m3u8',
|
||||
);
|
||||
}
|
||||
|
||||
session ??= await FFmpegKit.execute(
|
||||
'-i "${file.path}" '
|
||||
'-metadata:s:v:0 rotate=0 '
|
||||
'-vf "scale=-2:720,fps=30,format=yuv420p10le,zscale=transfer=linear,tonemap=tonemap=hable:desat=0:peak=10,zscale=transfer=bt709:matrix=bt709:primaries=bt709,format=yuv420p" '
|
||||
'-color_primaries bt709 -color_trc bt709 -colorspace bt709 '
|
||||
'-x264-params "colorprim=bt709:transfer=bt709:colormatrix=bt709" '
|
||||
'-c:v libx264 -b:v 2000k -crf 23 -preset medium '
|
||||
'-c:a aac -b:a 128k -f hls -hls_time 2 -hls_flags single_file '
|
||||
'-hls_list_size 0 -hls_key_info_file ${keyinfo.path} '
|
||||
'$prefix/output.m3u8',
|
||||
);
|
||||
|
||||
final returnCode = await session.getReturnCode();
|
||||
|
||||
String? error;
|
||||
@@ -285,15 +256,14 @@ class PreviewVideoStore {
|
||||
Bus.instance.fire(PreviewUpdatedEvent(_items));
|
||||
|
||||
_logger.info('Playlist Generated ${enteFile.displayName}');
|
||||
|
||||
final playlistFile = File("$prefix/output.m3u8");
|
||||
final previewFile = File("$prefix/output.ts");
|
||||
final result = await _uploadPreviewVideo(enteFile, previewFile);
|
||||
|
||||
final String objectID = result.$1;
|
||||
final objectSize = result.$2;
|
||||
|
||||
// Fetch resolution of generated stream by decrypting a single frame
|
||||
// Logic to fetch width & height of preview
|
||||
//-allowed_extensions ALL -i "https://example.com/stream.m3u8" -frames:v 1 -c copy frame.ts
|
||||
final FFmpegSession session2 = await FFmpegKit.execute(
|
||||
'-allowed_extensions ALL -i "$prefix/output.m3u8" -frames:v 1 -c copy "$prefix/frame.ts"',
|
||||
);
|
||||
@@ -308,8 +278,8 @@ class PreviewVideoStore {
|
||||
width = props2?.width;
|
||||
height = props2?.height;
|
||||
}
|
||||
} catch (err, sT) {
|
||||
_logger.warning("Failed to fetch resolution of stream", err, sT);
|
||||
} catch (_) {
|
||||
_logger.warning("Failed to get width and height", _);
|
||||
}
|
||||
|
||||
await _reportVideoPreview(
|
||||
@@ -324,7 +294,7 @@ class PreviewVideoStore {
|
||||
_logger.info("Video preview uploaded for $enteFile");
|
||||
} catch (err, sT) {
|
||||
error = "Failed to upload video preview\nError: $err";
|
||||
_logger.shout("Something went wrong with preview upload", err, sT);
|
||||
_logger.shout("Video preview uploaded for $enteFile", err, sT);
|
||||
}
|
||||
} else if (ReturnCode.isCancel(returnCode)) {
|
||||
_logger.warning("FFmpeg command cancelled");
|
||||
@@ -335,13 +305,14 @@ class PreviewVideoStore {
|
||||
"FFmpeg command failed with return code $returnCode",
|
||||
output ?? "Error not found",
|
||||
);
|
||||
if (kDebugMode) {
|
||||
_logger.severe(output);
|
||||
}
|
||||
error = "Failed to generate video preview\nError: $output";
|
||||
}
|
||||
|
||||
if (error == null) {
|
||||
// update previewIds
|
||||
FileDataService.instance.syncFDStatus().ignore();
|
||||
|
||||
_items[enteFile.uploadedFileID!] = PreviewItem(
|
||||
status: PreviewItemStatus.uploaded,
|
||||
file: enteFile,
|
||||
@@ -349,49 +320,37 @@ class PreviewVideoStore {
|
||||
collectionID: enteFile.collectionID ?? 0,
|
||||
);
|
||||
} else {
|
||||
_retryFile(enteFile, error);
|
||||
if (_items[enteFile.uploadedFileID!]!.retryCount < 3) {
|
||||
_items[enteFile.uploadedFileID!] = PreviewItem(
|
||||
status: PreviewItemStatus.retry,
|
||||
file: enteFile,
|
||||
retryCount: _items[enteFile.uploadedFileID!]!.retryCount + 1,
|
||||
collectionID: enteFile.collectionID ?? 0,
|
||||
);
|
||||
files.add(enteFile);
|
||||
} else {
|
||||
_items[enteFile.uploadedFileID!] = PreviewItem(
|
||||
status: PreviewItemStatus.failed,
|
||||
file: enteFile,
|
||||
retryCount: _items[enteFile.uploadedFileID!]!.retryCount,
|
||||
collectionID: enteFile.collectionID ?? 0,
|
||||
error: error,
|
||||
);
|
||||
}
|
||||
}
|
||||
Bus.instance.fire(PreviewUpdatedEvent(_items));
|
||||
} finally {
|
||||
// reset uploading status if this was getting processed
|
||||
if (uploadingFileId == enteFile.uploadedFileID!) {
|
||||
uploadingFileId = -1;
|
||||
}
|
||||
_logger.info("[chunk] Processing ${_items.length} items for streaming");
|
||||
// process next file
|
||||
if (fileQueue.isNotEmpty) {
|
||||
final file = fileQueue.first;
|
||||
fileQueue.remove(file);
|
||||
if (files.isNotEmpty) {
|
||||
final file = files.first;
|
||||
files.remove(file);
|
||||
await chunkAndUploadVideo(ctx, file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _removeFile(EnteFile enteFile) {
|
||||
_items.remove(enteFile.uploadedFileID!);
|
||||
Bus.instance.fire(PreviewUpdatedEvent(_items));
|
||||
}
|
||||
|
||||
void _retryFile(EnteFile enteFile, Object error) {
|
||||
if (_items[enteFile.uploadedFileID!]!.retryCount < 3) {
|
||||
_items[enteFile.uploadedFileID!] = PreviewItem(
|
||||
status: PreviewItemStatus.retry,
|
||||
file: enteFile,
|
||||
retryCount: _items[enteFile.uploadedFileID!]!.retryCount + 1,
|
||||
collectionID: enteFile.collectionID ?? 0,
|
||||
);
|
||||
fileQueue.add(enteFile);
|
||||
} else {
|
||||
_items[enteFile.uploadedFileID!] = PreviewItem(
|
||||
status: PreviewItemStatus.failed,
|
||||
file: enteFile,
|
||||
retryCount: _items[enteFile.uploadedFileID!]!.retryCount,
|
||||
collectionID: enteFile.collectionID ?? 0,
|
||||
error: error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _reportVideoPreview(
|
||||
EnteFile file,
|
||||
File playlist, {
|
||||
@@ -561,7 +520,7 @@ class PreviewVideoStore {
|
||||
final previewURL = response2.data["url"];
|
||||
if (objectKey != null) {
|
||||
unawaited(
|
||||
_downloadAndCacheVideo(
|
||||
downloadAndCacheVideo(
|
||||
previewURL,
|
||||
_getVideoPreviewKey(objectKey),
|
||||
),
|
||||
@@ -590,7 +549,7 @@ class PreviewVideoStore {
|
||||
}
|
||||
}
|
||||
|
||||
Future _downloadAndCacheVideo(String url, String key) async {
|
||||
Future downloadAndCacheVideo(String url, String key) async {
|
||||
final file = await videoCacheManager.downloadFile(url, key: key);
|
||||
return file;
|
||||
}
|
||||
@@ -612,35 +571,9 @@ class PreviewVideoStore {
|
||||
}
|
||||
}
|
||||
|
||||
Future<(FFProbeProps?, bool, File?)> _checkFileForPreviewCreation(
|
||||
EnteFile enteFile,
|
||||
) async {
|
||||
final fileSize = enteFile.fileSize;
|
||||
FFProbeProps? props;
|
||||
File? file;
|
||||
bool result = false;
|
||||
|
||||
try {
|
||||
final isFileUnder10MB = fileSize != null && fileSize <= 10 * 1024 * 1024;
|
||||
if (isFileUnder10MB) {
|
||||
file = await getFile(enteFile, isOrigin: true);
|
||||
if (file != null) {
|
||||
props = await getVideoPropsAsync(file);
|
||||
final videoData = List.from(props?.propData?["streams"] ?? [])
|
||||
.firstWhereOrNull((e) => e["type"] == "video");
|
||||
|
||||
final codec = videoData["codec_name"]?.toString().toLowerCase();
|
||||
result = codec?.contains("h264") ?? false;
|
||||
}
|
||||
}
|
||||
} catch (e, sT) {
|
||||
_logger.warning("Failed to check props", e, sT);
|
||||
}
|
||||
return (props, result, file);
|
||||
}
|
||||
|
||||
// generate stream for all files after cutoff date
|
||||
Future<void> _putFilesForPreviewCreation() async {
|
||||
// get all files after cutoff date and add it to queue for preview creation
|
||||
// only run when video streaming is enabled
|
||||
Future<void> putFilesForPreviewCreation() async {
|
||||
if (!isVideoStreamingEnabled) return;
|
||||
|
||||
final cutoff = videoStreamingCutoff;
|
||||
@@ -656,37 +589,49 @@ class PreviewVideoStore {
|
||||
final allFiles = files
|
||||
.where((file) => previewIds?[file.uploadedFileID] == null)
|
||||
.sorted((a, b) {
|
||||
// put higher duration videos last along with remote files
|
||||
final first = (a.localID == null ? 2 : 0) +
|
||||
(a.duration == null || a.duration! >= 10 * 60 ? 1 : 0);
|
||||
final second = (b.localID == null ? 2 : 0) +
|
||||
(b.duration == null || b.duration! >= 10 * 60 ? 1 : 0);
|
||||
// put higher duration videos last
|
||||
final first = a.duration == null || a.duration! >= 10 * 60 ? 1 : 0;
|
||||
final second = b.duration == null || b.duration! >= 10 * 60 ? 1 : 0;
|
||||
return first.compareTo(second);
|
||||
}).toList();
|
||||
|
||||
// set all video status to in queue
|
||||
final n = allFiles.length;
|
||||
for (int i = 0; i < n; i++) {
|
||||
final enteFile = allFiles[i];
|
||||
// elimination case for <=10 MB with H.264
|
||||
final (_, result, _) = await _checkFileForPreviewCreation(enteFile);
|
||||
if (result) {
|
||||
allFiles.removeAt(i);
|
||||
} else {
|
||||
_items[enteFile.uploadedFileID!] = PreviewItem(
|
||||
status: PreviewItemStatus.inQueue,
|
||||
file: enteFile,
|
||||
collectionID: enteFile.collectionID ?? 0,
|
||||
);
|
||||
// set all video status to be in queue
|
||||
for (final enteFile in allFiles) {
|
||||
final fileSize = enteFile.fileSize;
|
||||
FFProbeProps? props;
|
||||
|
||||
if (fileSize != null && fileSize <= 10 * 1024 * 1024) {
|
||||
final file = await getFile(enteFile, isOrigin: true);
|
||||
if (file != null) {
|
||||
props = await getVideoPropsAsync(file);
|
||||
final videoData = List.from(props?.propData?["streams"] ?? [])
|
||||
.firstWhereOrNull((e) => e["type"] == "video");
|
||||
|
||||
final codec = videoData["codec_name"]?.toString().toLowerCase();
|
||||
final codecIsH264 = codec?.contains("h264") ?? false;
|
||||
|
||||
if (codecIsH264) {
|
||||
_items.removeWhere((key, value) => value.file == enteFile);
|
||||
Bus.instance.fire(PreviewUpdatedEvent(_items));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_items[enteFile.uploadedFileID!] = PreviewItem(
|
||||
status: PreviewItemStatus.inQueue,
|
||||
file: enteFile,
|
||||
collectionID: enteFile.collectionID ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
Bus.instance.fire(PreviewUpdatedEvent(_items));
|
||||
|
||||
_logger.info("[init] Processing ${allFiles.length} items for streaming");
|
||||
final file = allFiles.first;
|
||||
allFiles.remove(file);
|
||||
|
||||
this.files.addAll(allFiles);
|
||||
|
||||
// take first file and put it for stream generation
|
||||
final file = allFiles.removeAt(0);
|
||||
fileQueue.addAll(allFiles);
|
||||
await chunkAndUploadVideo(null, file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:photos/core/configuration.dart';
|
||||
|
||||
@@ -9,10 +9,10 @@ import 'package:photos/events/collection_updated_event.dart';
|
||||
import 'package:photos/events/force_reload_trash_page_event.dart';
|
||||
import 'package:photos/events/trash_updated_event.dart';
|
||||
import 'package:photos/extensions/list.dart';
|
||||
import 'package:photos/models/api/collection/trash_item_request.dart';
|
||||
import 'package:photos/models/file/file.dart';
|
||||
import 'package:photos/models/file/trash_file.dart';
|
||||
import 'package:photos/models/ignored_file.dart';
|
||||
import 'package:photos/models/trash_item_request.dart';
|
||||
import 'package:photos/services/ignored_files_service.dart';
|
||||
import 'package:photos/utils/trash_diff_fetcher.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
@@ -4,7 +4,6 @@ import "dart:math";
|
||||
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:ente_crypto/ente_crypto.dart';
|
||||
import "package:flutter/foundation.dart";
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
@@ -20,13 +19,13 @@ import "package:photos/generated/l10n.dart";
|
||||
import "package:photos/l10n/l10n.dart";
|
||||
import "package:photos/models/account/two_factor.dart";
|
||||
import "package:photos/models/api/collection/user.dart";
|
||||
import 'package:photos/models/api/user/delete_account.dart';
|
||||
import 'package:photos/models/api/user/key_attributes.dart';
|
||||
import 'package:photos/models/api/user/key_gen_result.dart';
|
||||
import 'package:photos/models/api/user/sessions.dart';
|
||||
import 'package:photos/models/api/user/set_keys_request.dart';
|
||||
import 'package:photos/models/api/user/set_recovery_key_request.dart';
|
||||
import "package:photos/models/api/user/srp.dart";
|
||||
import 'package:photos/models/delete_account.dart';
|
||||
import 'package:photos/models/key_attributes.dart';
|
||||
import 'package:photos/models/key_gen_result.dart';
|
||||
import 'package:photos/models/sessions.dart';
|
||||
import 'package:photos/models/set_keys_request.dart';
|
||||
import 'package:photos/models/set_recovery_key_request.dart';
|
||||
import 'package:photos/models/user_details.dart';
|
||||
import "package:photos/services/collections_service.dart";
|
||||
import "package:photos/services/machine_learning/face_ml/person/person_service.dart";
|
||||
@@ -41,6 +40,7 @@ import 'package:photos/ui/account/two_factor_recovery_page.dart';
|
||||
import 'package:photos/ui/account/two_factor_setup_page.dart';
|
||||
import "package:photos/ui/common/progress_dialog.dart";
|
||||
import "package:photos/ui/tabs/home_widget.dart";
|
||||
import 'package:photos/utils/crypto_util.dart';
|
||||
import 'package:photos/utils/dialog_util.dart';
|
||||
import 'package:photos/utils/navigation_util.dart';
|
||||
import 'package:photos/utils/toast_util.dart';
|
||||
@@ -695,7 +695,7 @@ class UserService {
|
||||
late Uint8List keyEncryptionKey;
|
||||
_logger.finest('Start deriving key');
|
||||
keyEncryptionKey = await CryptoUtil.deriveKey(
|
||||
utf8.encode(userPassword),
|
||||
utf8.encode(userPassword) as Uint8List,
|
||||
CryptoUtil.base642bin(srpAttributes.kekSalt),
|
||||
srpAttributes.memLimit,
|
||||
srpAttributes.opsLimit,
|
||||
|
||||
@@ -11,8 +11,8 @@ class UserDetailsStateWidget extends StatefulWidget {
|
||||
|
||||
const UserDetailsStateWidget({
|
||||
required this.child,
|
||||
super.key,
|
||||
});
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<UserDetailsStateWidget> createState() => UserDetailsStateWidgetState();
|
||||
@@ -65,12 +65,12 @@ class InheritedUserDetails extends InheritedWidget {
|
||||
final bool isCached;
|
||||
|
||||
const InheritedUserDetails({
|
||||
super.key,
|
||||
required super.child,
|
||||
Key? key,
|
||||
required Widget child,
|
||||
required this.userDetails,
|
||||
required this.isCached,
|
||||
required this.userDetailsState,
|
||||
});
|
||||
}) : super(key: key, child: child);
|
||||
|
||||
static InheritedUserDetails? of(BuildContext context) =>
|
||||
context.dependOnInheritedWidgetOfExactType<InheritedUserDetails>();
|
||||
|
||||
@@ -5,7 +5,7 @@ import 'package:photos/utils/dialog_util.dart';
|
||||
import 'package:photos/utils/email_util.dart';
|
||||
|
||||
class ChangeEmailDialog extends StatefulWidget {
|
||||
const ChangeEmailDialog({super.key});
|
||||
const ChangeEmailDialog({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<ChangeEmailDialog> createState() => _ChangeEmailDialogState();
|
||||
|
||||
@@ -2,16 +2,16 @@ import "dart:async";
|
||||
import 'dart:convert';
|
||||
|
||||
import "package:dropdown_button2/dropdown_button2.dart";
|
||||
import 'package:ente_crypto/ente_crypto.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import "package:logging/logging.dart";
|
||||
import 'package:photos/core/configuration.dart';
|
||||
import "package:photos/generated/l10n.dart";
|
||||
import 'package:photos/models/api/user/delete_account.dart';
|
||||
import 'package:photos/models/delete_account.dart';
|
||||
import 'package:photos/services/user_service.dart';
|
||||
import 'package:photos/theme/ente_theme.dart';
|
||||
import 'package:photos/ui/components/buttons/button_widget.dart';
|
||||
import 'package:photos/ui/components/models/button_type.dart';
|
||||
import 'package:photos/utils/crypto_util.dart';
|
||||
import 'package:photos/utils/dialog_util.dart';
|
||||
import "package:photos/utils/toast_util.dart";
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import 'package:photos/ui/common/web_page.dart';
|
||||
import "package:styled_text/styled_text.dart";
|
||||
|
||||
class LoginPage extends StatefulWidget {
|
||||
const LoginPage({super.key});
|
||||
const LoginPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<LoginPage> createState() => _LoginPageState();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import "package:dio/dio.dart";
|
||||
import "package:ente_crypto/ente_crypto.dart";
|
||||
import "package:flutter/foundation.dart";
|
||||
import 'package:flutter/material.dart';
|
||||
import "package:logging/logging.dart";
|
||||
import 'package:photos/core/configuration.dart';
|
||||
import "package:photos/core/errors.dart";
|
||||
import "package:photos/generated/l10n.dart";
|
||||
import "package:photos/models/api/user/srp.dart";
|
||||
import "package:photos/services/user_service.dart";
|
||||
@@ -21,7 +21,8 @@ import "package:photos/utils/email_util.dart";
|
||||
class LoginPasswordVerificationPage extends StatefulWidget {
|
||||
final SrpAttributes srpAttributes;
|
||||
|
||||
const LoginPasswordVerificationPage({super.key, required this.srpAttributes});
|
||||
const LoginPasswordVerificationPage({Key? key, required this.srpAttributes})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<LoginPasswordVerificationPage> createState() =>
|
||||
|
||||
@@ -18,8 +18,8 @@ class OTTVerificationPage extends StatefulWidget {
|
||||
this.isChangeEmail = false,
|
||||
this.isCreateAccountScreen = false,
|
||||
this.isResetPasswordScreen = false,
|
||||
super.key,
|
||||
});
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<OTTVerificationPage> createState() => _OTTVerificationPageState();
|
||||
|
||||
@@ -10,7 +10,7 @@ import 'package:photos/events/account_configured_event.dart';
|
||||
import 'package:photos/events/subscription_purchased_event.dart';
|
||||
import "package:photos/generated/l10n.dart";
|
||||
import "package:photos/l10n/l10n.dart";
|
||||
import "package:photos/models/api/user/key_gen_result.dart";
|
||||
import "package:photos/models/key_gen_result.dart";
|
||||
import 'package:photos/services/user_service.dart';
|
||||
import "package:photos/theme/ente_theme.dart";
|
||||
import 'package:photos/ui/account/recovery_key_page.dart';
|
||||
@@ -34,8 +34,8 @@ class PasswordEntryPage extends StatefulWidget {
|
||||
|
||||
const PasswordEntryPage({
|
||||
required this.mode,
|
||||
super.key,
|
||||
});
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<PasswordEntryPage> createState() => _PasswordEntryPageState();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'dart:async';
|
||||
import "dart:typed_data";
|
||||
|
||||
import "package:ente_crypto/ente_crypto.dart";
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:photos/core/configuration.dart';
|
||||
@@ -15,11 +14,12 @@ import 'package:photos/ui/account/recovery_page.dart';
|
||||
import 'package:photos/ui/common/dynamic_fab.dart';
|
||||
import 'package:photos/ui/components/buttons/button_widget.dart';
|
||||
import 'package:photos/ui/tabs/home_widget.dart';
|
||||
import "package:photos/utils/crypto_util.dart";
|
||||
import 'package:photos/utils/dialog_util.dart';
|
||||
import 'package:photos/utils/email_util.dart';
|
||||
|
||||
class PasswordReentryPage extends StatefulWidget {
|
||||
const PasswordReentryPage({super.key});
|
||||
const PasswordReentryPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<PasswordReentryPage> createState() => _PasswordReentryPageState();
|
||||
|
||||
@@ -28,7 +28,7 @@ class RecoveryKeyPage extends StatefulWidget {
|
||||
const RecoveryKeyPage(
|
||||
this.recoveryKey,
|
||||
this.doneText, {
|
||||
super.key,
|
||||
Key? key,
|
||||
this.showAppBar,
|
||||
this.onDone,
|
||||
this.isDismissible,
|
||||
@@ -36,7 +36,7 @@ class RecoveryKeyPage extends StatefulWidget {
|
||||
this.text,
|
||||
this.subText,
|
||||
this.showProgressBar = false,
|
||||
});
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<RecoveryKeyPage> createState() => _RecoveryKeyPageState();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/core/configuration.dart';
|
||||
@@ -9,7 +10,7 @@ import 'package:photos/utils/dialog_util.dart';
|
||||
import 'package:photos/utils/toast_util.dart';
|
||||
|
||||
class RecoveryPage extends StatefulWidget {
|
||||
const RecoveryPage({super.key});
|
||||
const RecoveryPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<RecoveryPage> createState() => _RecoveryPageState();
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import "dart:convert";
|
||||
import "dart:typed_data";
|
||||
|
||||
import "package:ente_crypto/ente_crypto.dart";
|
||||
import 'package:flutter/material.dart';
|
||||
import "package:logging/logging.dart";
|
||||
import 'package:photos/core/configuration.dart';
|
||||
import "package:photos/l10n/l10n.dart";
|
||||
import "package:photos/theme/ente_theme.dart";
|
||||
import 'package:photos/ui/common/dynamic_fab.dart';
|
||||
import "package:photos/utils/crypto_util.dart";
|
||||
import "package:photos/utils/dialog_util.dart";
|
||||
|
||||
typedef OnPasswordVerifiedFn = Future<void> Function(Uint8List bytes);
|
||||
@@ -91,7 +91,7 @@ class _RequestPasswordVerificationPageState
|
||||
try {
|
||||
final attributes = Configuration.instance.getKeyAttributes()!;
|
||||
final Uint8List keyEncryptionKey = await CryptoUtil.deriveKey(
|
||||
utf8.encode(_passwordController.text),
|
||||
utf8.encode(_passwordController.text) as Uint8List,
|
||||
CryptoUtil.base642bin(attributes.kekSalt),
|
||||
attributes.memLimit!,
|
||||
attributes.opsLimit!,
|
||||
|
||||
@@ -3,7 +3,7 @@ import 'package:logging/logging.dart';
|
||||
import 'package:photos/core/configuration.dart';
|
||||
import 'package:photos/ente_theme_data.dart';
|
||||
import "package:photos/generated/l10n.dart";
|
||||
import 'package:photos/models/api/user/sessions.dart';
|
||||
import 'package:photos/models/sessions.dart';
|
||||
import 'package:photos/services/user_service.dart';
|
||||
import "package:photos/theme/ente_theme.dart";
|
||||
import 'package:photos/ui/common/loading_widget.dart';
|
||||
@@ -12,7 +12,7 @@ import 'package:photos/utils/dialog_util.dart';
|
||||
import 'package:photos/utils/toast_util.dart';
|
||||
|
||||
class SessionsPage extends StatefulWidget {
|
||||
const SessionsPage({super.key});
|
||||
const SessionsPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<SessionsPage> createState() => _SessionsPageState();
|
||||
|
||||
@@ -16,8 +16,8 @@ class TwoFactorRecoveryPage extends StatefulWidget {
|
||||
this.sessionID,
|
||||
this.encryptedSecret,
|
||||
this.secretDecryptionNonce, {
|
||||
super.key,
|
||||
});
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<TwoFactorRecoveryPage> createState() => _TwoFactorRecoveryPageState();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:ente_crypto/ente_crypto.dart';
|
||||
import "package:flutter/material.dart";
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:photos/core/configuration.dart';
|
||||
@@ -9,6 +8,7 @@ import "package:photos/generated/l10n.dart";
|
||||
import 'package:photos/services/user_service.dart';
|
||||
import 'package:photos/ui/account/recovery_key_page.dart';
|
||||
import 'package:photos/ui/lifecycle_event_handler.dart';
|
||||
import 'package:photos/utils/crypto_util.dart';
|
||||
import 'package:photos/utils/navigation_util.dart';
|
||||
import 'package:photos/utils/toast_util.dart';
|
||||
import "package:pinput/pinput.dart";
|
||||
@@ -22,8 +22,8 @@ class TwoFactorSetupPage extends StatefulWidget {
|
||||
this.secretCode,
|
||||
this.qrCode,
|
||||
this.completer, {
|
||||
super.key,
|
||||
});
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<TwoFactorSetupPage> createState() => _TwoFactorSetupPageState();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:ente_crypto/ente_crypto.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:photos/core/event_bus.dart';
|
||||
@@ -14,11 +13,12 @@ import "package:photos/theme/ente_theme.dart";
|
||||
import 'package:photos/ui/account/recovery_key_page.dart';
|
||||
import 'package:photos/ui/common/gradient_button.dart';
|
||||
import 'package:photos/ui/components/buttons/button_widget.dart';
|
||||
import 'package:photos/utils/crypto_util.dart';
|
||||
import 'package:photos/utils/dialog_util.dart';
|
||||
import 'package:photos/utils/navigation_util.dart';
|
||||
|
||||
class VerifyRecoveryPage extends StatefulWidget {
|
||||
const VerifyRecoveryPage({super.key});
|
||||
const VerifyRecoveryPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<VerifyRecoveryPage> createState() => _VerifyRecoveryPageState();
|
||||
|
||||
@@ -14,8 +14,8 @@ class AutoCastDialog extends StatefulWidget {
|
||||
final void Function(String) onConnect;
|
||||
AutoCastDialog(
|
||||
this.onConnect, {
|
||||
super.key,
|
||||
}) {}
|
||||
Key? key,
|
||||
}) : super(key: key) {}
|
||||
|
||||
@override
|
||||
State<AutoCastDialog> createState() => _AutoCastDialogState();
|
||||
|
||||
@@ -7,8 +7,8 @@ import "package:photos/ui/components/models/button_type.dart";
|
||||
|
||||
class CastChooseDialog extends StatefulWidget {
|
||||
const CastChooseDialog({
|
||||
super.key,
|
||||
});
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<CastChooseDialog> createState() => _CastChooseDialogState();
|
||||
|
||||
@@ -17,8 +17,8 @@ class AlbumHorizontalList extends StatefulWidget {
|
||||
const AlbumHorizontalList(
|
||||
this.collectionsFuture, {
|
||||
this.hasVerifiedLock,
|
||||
super.key,
|
||||
});
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<AlbumHorizontalList> createState() => _AlbumHorizontalListState();
|
||||
|
||||
@@ -12,8 +12,8 @@ class ArchivedCollectionsButton extends StatelessWidget {
|
||||
|
||||
const ArchivedCollectionsButton(
|
||||
this.textStyle, {
|
||||
super.key,
|
||||
});
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -21,7 +21,7 @@ class ArchivedCollectionsButton extends StatelessWidget {
|
||||
CollectionsService.instance.getHiddenCollectionIds();
|
||||
return OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
backgroundColor: Theme.of(context).colorScheme.background,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
|
||||
@@ -16,7 +16,7 @@ class HiddenCollectionsButtonWidget extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
backgroundColor: Theme.of(context).colorScheme.background,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
|
||||
@@ -45,7 +45,7 @@ class _TrashSectionButtonState extends State<TrashSectionButton> {
|
||||
Widget build(BuildContext context) {
|
||||
return OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
backgroundColor: Theme.of(context).colorScheme.background,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
|
||||
@@ -12,8 +12,8 @@ class UnCategorizedCollections extends StatelessWidget {
|
||||
|
||||
const UnCategorizedCollections(
|
||||
this.textStyle, {
|
||||
super.key,
|
||||
});
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -26,7 +26,7 @@ class UnCategorizedCollections extends StatelessWidget {
|
||||
}
|
||||
return OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
backgroundColor: Theme.of(context).colorScheme.background,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
|
||||
@@ -27,8 +27,8 @@ class CollectionListPage extends StatefulWidget {
|
||||
this.appTitle,
|
||||
this.initialScrollOffset,
|
||||
this.tag = "",
|
||||
super.key,
|
||||
});
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<CollectionListPage> createState() => _CollectionListPageState();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user