Compare commits
140 Commits
streaming-
...
family
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0591a22eeb | ||
|
|
b2d9305f7f | ||
|
|
7c679cd38b | ||
|
|
3669b6be09 | ||
|
|
88b0ecf472 | ||
|
|
ee2f03adca | ||
|
|
3e6c253a24 | ||
|
|
776b7488d3 | ||
|
|
371377d4d1 | ||
|
|
aad42b3c00 | ||
|
|
9ce0b43bfe | ||
|
|
f5ea565aa8 | ||
|
|
d41e177b3c | ||
|
|
bc742f20e1 | ||
|
|
35601956d2 | ||
|
|
bc699b8f37 | ||
|
|
d20615002c | ||
|
|
b44f844513 | ||
|
|
09d390bd38 | ||
|
|
e439e4a5f7 | ||
|
|
3147800486 | ||
|
|
0a7984a0d2 | ||
|
|
189a3ebc40 | ||
|
|
c0eeb7dd2f | ||
|
|
4f271887fc | ||
|
|
f46f063beb | ||
|
|
270a628478 | ||
|
|
b5f850b3be | ||
|
|
77d16e275d | ||
|
|
098a4526ad | ||
|
|
957c333cf3 | ||
|
|
101a9d4b5d | ||
|
|
e3ef1e4628 | ||
|
|
fd133d4023 | ||
|
|
cdfdc83083 | ||
|
|
8618babc11 | ||
|
|
ca28a3c595 | ||
|
|
5eba06a269 | ||
|
|
91017969b3 | ||
|
|
d25e37e2ad | ||
|
|
7b902a607a | ||
|
|
68bc6fac38 | ||
|
|
82fdae9253 | ||
|
|
d78ffced78 | ||
|
|
7035d3ca90 | ||
|
|
8920462b54 | ||
|
|
56f9f2a028 | ||
|
|
94a77b7df1 | ||
|
|
7fc42bed64 | ||
|
|
8eb34503ac | ||
|
|
2fe6df5d21 | ||
|
|
d59d3c3b07 | ||
|
|
503c2506aa | ||
|
|
e44405b46f | ||
|
|
cdaeec0e8e | ||
|
|
29671aa154 | ||
|
|
370299d433 | ||
|
|
38d42c67fb | ||
|
|
802ad184d2 | ||
|
|
55cff6f174 | ||
|
|
e5448685ca | ||
|
|
bde8a17cb4 | ||
|
|
fc3200af73 | ||
|
|
d21ea0a5a6 | ||
|
|
a1fda786f5 | ||
|
|
bc0980eb8d | ||
|
|
2cfc5d9c59 | ||
|
|
682710a8a8 | ||
|
|
ed50e5a36c | ||
|
|
f31a6f2401 | ||
|
|
239f08b516 | ||
|
|
8544f5e109 | ||
|
|
1eaaafb8df | ||
|
|
149196e7dd | ||
|
|
38a31b7492 | ||
|
|
b14ad92b91 | ||
|
|
872455cce2 | ||
|
|
65a5248338 | ||
|
|
fcf06cff57 | ||
|
|
13752654cd | ||
|
|
d5e8777e0d | ||
|
|
7de2a47c51 | ||
|
|
20bc84ca96 | ||
|
|
fd7c25029e | ||
|
|
764add95c8 | ||
|
|
8c3fc0a879 | ||
|
|
37c467eb86 | ||
|
|
931d7c8513 | ||
|
|
132962b92f | ||
|
|
a5c4d9cc18 | ||
|
|
39c31779a4 | ||
|
|
d09613a946 | ||
|
|
88e50982b2 | ||
|
|
ce5a8f0457 | ||
|
|
8c81a377c0 | ||
|
|
a0025ab09b | ||
|
|
324c156ea1 | ||
|
|
4b87c9f3ac | ||
|
|
dfda0c2c32 | ||
|
|
c1a53bdfce | ||
|
|
0c8dc3af95 | ||
|
|
b3100f098b | ||
|
|
0e157a4e33 | ||
|
|
04a5372f6e | ||
|
|
f47837f550 | ||
|
|
1af8d7481d | ||
|
|
af91adeb72 | ||
|
|
8fd90651b1 | ||
|
|
c3da41eee2 | ||
|
|
ac94dccb90 | ||
|
|
89881975f2 | ||
|
|
e715d582ac | ||
|
|
808c611a92 | ||
|
|
fe5146ead8 | ||
|
|
d398838742 | ||
|
|
717dc0996f | ||
|
|
00db3c0335 | ||
|
|
6ccca2114e | ||
|
|
0e3708ffdc | ||
|
|
36e7dae2ee | ||
|
|
9d76d93254 | ||
|
|
aab4bff6ff | ||
|
|
12a96b68ba | ||
|
|
a851caf78f | ||
|
|
4bbac0ca66 | ||
|
|
bb2bbb5655 | ||
|
|
d36934ec0d | ||
|
|
f922df304e | ||
|
|
f6d949db38 | ||
|
|
e937027667 | ||
|
|
84aeb79412 | ||
|
|
6140f35e69 | ||
|
|
0eba6b9c98 | ||
|
|
fe86075868 | ||
|
|
e9d63dfea9 | ||
|
|
1c322a9c62 | ||
|
|
dc0450b155 | ||
|
|
8e90541d87 | ||
|
|
8df04b2363 | ||
|
|
43c0d8a6ad |
@@ -208,6 +208,10 @@
|
||||
{
|
||||
"title": "Bugzilla"
|
||||
},
|
||||
{
|
||||
"title": "ButterflyMX",
|
||||
"slug": "butterflymx"
|
||||
},
|
||||
{
|
||||
"title": "Bybit"
|
||||
},
|
||||
@@ -306,6 +310,9 @@
|
||||
{
|
||||
"title": "Discourse"
|
||||
},
|
||||
{
|
||||
"title": "Deloitte"
|
||||
},
|
||||
{
|
||||
"title": "DMarket"
|
||||
},
|
||||
@@ -393,9 +400,17 @@
|
||||
{
|
||||
"title": "ForUsAll"
|
||||
},
|
||||
{
|
||||
"title": "FreeTaxUSA",
|
||||
"slug": "freetaxusa"
|
||||
},
|
||||
{
|
||||
"title": "G2A"
|
||||
},
|
||||
{
|
||||
"title": "Gate.io",
|
||||
"slug": "gateio.svg"
|
||||
},
|
||||
{
|
||||
"title": "GitHub"
|
||||
},
|
||||
|
||||
27
auth/assets/custom-icons/icons/butterflymx.svg
Normal file
27
auth/assets/custom-icons/icons/butterflymx.svg
Normal file
@@ -0,0 +1,27 @@
|
||||
<?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>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
15
auth/assets/custom-icons/icons/deloitte.svg
Normal file
15
auth/assets/custom-icons/icons/deloitte.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 785 B |
1
auth/assets/custom-icons/icons/freetaxusa.svg
Normal file
1
auth/assets/custom-icons/icons/freetaxusa.svg
Normal file
@@ -0,0 +1 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
15
auth/assets/custom-icons/icons/gateio.svg
Normal file
15
auth/assets/custom-icons/icons/gateio.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<?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>
|
||||
|
After Width: | Height: | Size: 965 B |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 33 KiB |
@@ -1,3 +0,0 @@
|
||||
import 'package:ente_auth/events/event.dart';
|
||||
|
||||
class OpenedSettingsEvent extends Event {}
|
||||
@@ -1,17 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
class PublicKey {
|
||||
final String email;
|
||||
final String publicKey;
|
||||
|
||||
PublicKey(this.email, this.publicKey);
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
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
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
|
||||
|
||||
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());
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
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,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
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);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
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;
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
|
||||
|
||||
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)),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
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 +0,0 @@
|
||||
// TODO Implement this library.
|
||||
@@ -1,3 +1,4 @@
|
||||
import ModeIcon from "@mui/icons-material/Mode";
|
||||
import {
|
||||
Button,
|
||||
CircularProgress,
|
||||
@@ -14,12 +15,14 @@ import { useEffect, useState } from "react";
|
||||
import { getEmail, getToken } from "../App";
|
||||
import { apiOrigin } from "../services/support";
|
||||
import CloseFamily from "./CloseFamily";
|
||||
import EditDialog from "./EditStorage";
|
||||
|
||||
interface FamilyMember {
|
||||
id: string;
|
||||
email: string;
|
||||
status: string;
|
||||
usage: number;
|
||||
storageLimit: number;
|
||||
}
|
||||
|
||||
interface UserData {
|
||||
@@ -33,8 +36,11 @@ interface UserData {
|
||||
const FamilyTableComponent: React.FC = () => {
|
||||
const [familyMembers, setFamilyMembers] = useState<FamilyMember[]>([]);
|
||||
const [closeFamilyOpen, setCloseFamilyOpen] = useState(false);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [editDialog, setEditDialog] = useState(false);
|
||||
const [memID, selectedMemID] = useState<string>("");
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
@@ -53,9 +59,7 @@ const FamilyTableComponent: React.FC = () => {
|
||||
throw new Error("Network response was not ok");
|
||||
}
|
||||
const userData = (await response.json()) as UserData; // Typecast to UserData interface
|
||||
const members: FamilyMember[] =
|
||||
userData.details.familyData.members;
|
||||
setFamilyMembers(members);
|
||||
setFamilyMembers(userData.details.familyData.members);
|
||||
} catch (error) {
|
||||
console.error("Error fetching family data:", error);
|
||||
setError("No family data");
|
||||
@@ -69,9 +73,9 @@ const FamilyTableComponent: React.FC = () => {
|
||||
);
|
||||
}, []);
|
||||
|
||||
const formatUsageToGB = (usage: number): string => {
|
||||
const usageInGB = (usage / (1024 * 1024 * 1024)).toFixed(2);
|
||||
return `${usageInGB} GB`;
|
||||
const formatBytesToGB = (bytesValue: number): string => {
|
||||
const valueInGB = (bytesValue / (1024 * 1024 * 1024)).toFixed(2);
|
||||
return `${valueInGB} GB`;
|
||||
};
|
||||
|
||||
const handleOpenCloseFamily = () => {
|
||||
@@ -87,6 +91,13 @@ const FamilyTableComponent: React.FC = () => {
|
||||
handleOpenCloseFamily();
|
||||
};
|
||||
|
||||
const handleEditDialog = () => {
|
||||
familyMembers.forEach((member) => {
|
||||
selectedMemID(member.id);
|
||||
});
|
||||
setOpen(true);
|
||||
setEditDialog(true);
|
||||
};
|
||||
if (loading) {
|
||||
return <CircularProgress />;
|
||||
}
|
||||
@@ -111,6 +122,9 @@ const FamilyTableComponent: React.FC = () => {
|
||||
<Table aria-label="family-table">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<b>ID</b>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<b>User</b>
|
||||
</TableCell>
|
||||
@@ -121,13 +135,17 @@ const FamilyTableComponent: React.FC = () => {
|
||||
<b>Usage</b>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<b>ID</b>
|
||||
<b>Storage Limit</b>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<b> </b>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{familyMembers.map((member) => (
|
||||
<TableRow key={member.id}>
|
||||
<TableCell>{member.id}</TableCell>
|
||||
<TableCell>{member.email}</TableCell>
|
||||
<TableCell>
|
||||
<span
|
||||
@@ -150,9 +168,29 @@ const FamilyTableComponent: React.FC = () => {
|
||||
</span>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{formatUsageToGB(member.usage)}
|
||||
{formatBytesToGB(member.usage)}
|
||||
</TableCell>
|
||||
<TableCell>{member.id}</TableCell>
|
||||
<TableCell>
|
||||
{formatBytesToGB(member.storageLimit)}
|
||||
</TableCell>
|
||||
{(member.status === "ADMIN" || member.status === "SELF" ) ? (
|
||||
<span></span>
|
||||
) : <TableCell>
|
||||
<div
|
||||
onClick={handleEditDialog}
|
||||
style={{
|
||||
marginLeft: "8px",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
<ModeIcon />
|
||||
<EditDialog
|
||||
open={editDialog}
|
||||
setOpen={setEditDialog}
|
||||
memberID={member.id}
|
||||
></EditDialog>
|
||||
</div>
|
||||
</TableCell>}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
|
||||
@@ -450,12 +450,12 @@ 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: ce18b888487c05c46ed252cd2e7956812f2e3bd1
|
||||
file_saver: 6cdbcddd690cb02b0c1a0c225b37cd805c2bf8b6
|
||||
@@ -495,14 +495,14 @@ SPEC CHECKSUMS:
|
||||
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: 06d5a4162866388a92b1df3deb96e56be20cf45c
|
||||
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
|
||||
package_info_plus: 566e1b7a2f3900e4b0020914ad3fc051dcc95596
|
||||
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
||||
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
|
||||
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
|
||||
photo_manager: d2fbcc0f2d82458700ee6256a15018210a81d413
|
||||
@@ -527,7 +527,7 @@ SPEC CHECKSUMS:
|
||||
video_player_avfoundation: 2cef49524dd1f16c5300b9cd6efd9611ce03639b
|
||||
video_thumbnail: b637e0ad5f588ca9945f6e2c927f73a69a661140
|
||||
volume_controller: ca1cde542ee70fad77d388f82e9616488110942b
|
||||
wakelock_plus: 8c239121a007daa1d6759c6acdc507860273dd2f
|
||||
wakelock_plus: 04623e3f525556020ebd4034310f20fe7fda8b49
|
||||
|
||||
PODFILE CHECKSUM: 20e086e6008977d43a3d40260f3f9bffcac748dd
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ 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';
|
||||
@@ -19,9 +20,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/key_attributes.dart';
|
||||
import 'package:photos/models/key_gen_result.dart';
|
||||
import 'package:photos/models/private_key_attributes.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/services/collections_service.dart';
|
||||
import 'package:photos/services/favorites_service.dart';
|
||||
import "package:photos/services/home_widget_service.dart";
|
||||
@@ -30,7 +31,6 @@ 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) as Uint8List,
|
||||
utf8.encode(password),
|
||||
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) as Uint8List,
|
||||
utf8.encode(password),
|
||||
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) as Uint8List,
|
||||
utf8.encode(password),
|
||||
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 message, this.reason) : super(message);
|
||||
InvalidFileError(String super.message, this.reason);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
@@ -63,19 +63,15 @@ class UnauthorizedError extends Error {}
|
||||
class RequestCancelledError extends Error {}
|
||||
|
||||
class InvalidSyncStatusError extends AssertionError {
|
||||
InvalidSyncStatusError(String message) : super(message);
|
||||
InvalidSyncStatusError(String super.message);
|
||||
}
|
||||
|
||||
class UnauthorizedEditError extends AssertionError {}
|
||||
|
||||
class InvalidStateError extends AssertionError {
|
||||
InvalidStateError(String message) : super(message);
|
||||
InvalidStateError(String 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/key_attributes.dart";
|
||||
import "package:photos/models/api/user/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,5 +1,6 @@
|
||||
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';
|
||||
@@ -7,10 +8,9 @@ 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/key_attributes.dart";
|
||||
import "package:photos/models/set_keys_request.dart";
|
||||
import "package:photos/models/api/user/key_attributes.dart";
|
||||
import "package:photos/models/api/user/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) as Uint8List,
|
||||
utf8.encode(password),
|
||||
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),
|
||||
background: Colors.white,
|
||||
surface: Colors.white,
|
||||
surfaceTint: Colors.transparent,
|
||||
),
|
||||
outlinedButtonTheme: buildOutlinedButtonThemeData(
|
||||
@@ -70,13 +70,13 @@ final lightThemeData = ThemeData(
|
||||
color: Colors.black,
|
||||
width: 2,
|
||||
),
|
||||
fillColor: MaterialStateProperty.resolveWith((states) {
|
||||
return states.contains(MaterialState.selected)
|
||||
fillColor: WidgetStateProperty.resolveWith((states) {
|
||||
return states.contains(WidgetState.selected)
|
||||
? const Color.fromRGBO(0, 0, 0, 1)
|
||||
: const Color.fromRGBO(255, 255, 255, 1);
|
||||
}),
|
||||
checkColor: MaterialStateProperty.resolveWith((states) {
|
||||
return states.contains(MaterialState.selected)
|
||||
checkColor: WidgetStateProperty.resolveWith((states) {
|
||||
return states.contains(WidgetState.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,
|
||||
background: Color.fromRGBO(0, 0, 0, 1),
|
||||
surface: 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: MaterialStateProperty.resolveWith((states) {
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
fillColor: WidgetStateProperty.resolveWith((states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return const Color.fromRGBO(158, 158, 158, 1);
|
||||
} else {
|
||||
return const Color.fromRGBO(0, 0, 0, 1);
|
||||
}
|
||||
}),
|
||||
checkColor: MaterialStateProperty.resolveWith((states) {
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
checkColor: WidgetStateProperty.resolveWith((states) {
|
||||
if (states.contains(WidgetState.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: MaterialStateProperty.resolveWith<Color>(
|
||||
(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
backgroundColor: WidgetStateProperty.resolveWith<Color>(
|
||||
(Set<WidgetState> states) {
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
return bgDisabled;
|
||||
}
|
||||
return bgEnabled;
|
||||
},
|
||||
),
|
||||
foregroundColor: MaterialStateProperty.resolveWith<Color>(
|
||||
(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
foregroundColor: WidgetStateProperty.resolveWith<Color>(
|
||||
(Set<WidgetState> states) {
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
return fgDisabled;
|
||||
}
|
||||
return fgEnabled;
|
||||
@@ -426,21 +426,21 @@ ElevatedButtonThemeData buildElevatedButtonThemeData({
|
||||
SwitchThemeData getSwitchThemeData(Color activeColor) {
|
||||
return SwitchThemeData(
|
||||
thumbColor:
|
||||
MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
return null;
|
||||
}
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return activeColor;
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
trackColor:
|
||||
MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
|
||||
if (states.contains(WidgetState.disabled)) {
|
||||
return null;
|
||||
}
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return activeColor;
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -4,6 +4,7 @@ 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';
|
||||
@@ -47,7 +48,6 @@ 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";
|
||||
@@ -238,8 +238,8 @@ Future<void> _init(bool isBackground, {String via = ''}) async {
|
||||
ServiceLocator.instance
|
||||
.init(preferences, NetworkClient.instance.enteDio, packageInfo);
|
||||
|
||||
if (!isBackground && flagService.internalUser) {
|
||||
VideoPlayerMediaKit.ensureInitialized(iOS: true);
|
||||
if (!isBackground) {
|
||||
VideoPlayerMediaKit.ensureInitialized(iOS: true, android: true);
|
||||
}
|
||||
|
||||
_logger.info("UserService init $tlog");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import "package:freezed_annotation/freezed_annotation.dart";
|
||||
|
||||
@immutable
|
||||
class EntityData {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import "dart:typed_data";
|
||||
|
||||
import 'package:photos/models/key_attributes.dart';
|
||||
import 'package:photos/models/private_key_attributes.dart';
|
||||
import 'package:photos/models/api/user/key_attributes.dart';
|
||||
import 'package:photos/models/api/user/private_key_attributes.dart';
|
||||
|
||||
class KeyGenResult {
|
||||
final KeyAttributes keyAttributes;
|
||||
@@ -2,9 +2,8 @@ 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;
|
||||
@@ -215,19 +214,3 @@ 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,17 +1,16 @@
|
||||
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/billing_plan.dart';
|
||||
import 'package:photos/models/subscription.dart';
|
||||
import 'package:photos/models/api/billing/billing_plan.dart';
|
||||
import 'package:photos/models/api/billing/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,6 +4,7 @@ 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";
|
||||
@@ -24,11 +25,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";
|
||||
@@ -39,7 +40,6 @@ 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) as Uint8List,
|
||||
utf8.encode(newName),
|
||||
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)) as Uint8List,
|
||||
utf8.encode(jsonEncode(jsonToUpdate)),
|
||||
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)) as Uint8List,
|
||||
utf8.encode(jsonEncode(jsonToUpdate)),
|
||||
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)) as Uint8List,
|
||||
utf8.encode(jsonEncode(jsonToUpdate)),
|
||||
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) as Uint8List,
|
||||
utf8.encode(albumName),
|
||||
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) as Uint8List, collectionKey);
|
||||
CryptoUtil.encryptSync(utf8.encode(path), collectionKey);
|
||||
final collection = await createAndCacheCollection(
|
||||
CreateRequest(
|
||||
encryptedKey: CryptoUtil.bin2base64(encryptedKeyData.encryptedData!),
|
||||
|
||||
@@ -2,6 +2,7 @@ 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";
|
||||
@@ -12,7 +13,6 @@ 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,7 +15,6 @@ 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;
|
||||
@@ -254,7 +253,7 @@ class FavoritesService {
|
||||
final encryptedKeyResult =
|
||||
CryptoUtil.encryptSync(favoriteCollectionKey, _config.getKey()!);
|
||||
final encName = CryptoUtil.encryptSync(
|
||||
utf8.encode("Favorites") as Uint8List,
|
||||
utf8.encode("Favorites"),
|
||||
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,7 +16,6 @@ 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 {
|
||||
@@ -95,7 +94,7 @@ class FileMagicService {
|
||||
|
||||
final fileKey = getFileKey(file);
|
||||
final encryptedMMd = await CryptoUtil.encryptChaCha(
|
||||
utf8.encode(jsonEncode(jsonToUpdate)) as Uint8List,
|
||||
utf8.encode(jsonEncode(jsonToUpdate)),
|
||||
fileKey,
|
||||
);
|
||||
params['metadataList'].add(
|
||||
@@ -161,7 +160,7 @@ class FileMagicService {
|
||||
|
||||
final fileKey = getFileKey(file);
|
||||
final encryptedMMd = await CryptoUtil.encryptChaCha(
|
||||
utf8.encode(jsonEncode(jsonToUpdate)) as Uint8List,
|
||||
utf8.encode(jsonEncode(jsonToUpdate)),
|
||||
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,7 +18,6 @@ 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 {
|
||||
@@ -216,7 +215,7 @@ extension HiddenService on CollectionsService {
|
||||
final encKey =
|
||||
CryptoUtil.encryptSync(uncategorizedCollectionKey, config.getKey()!);
|
||||
final encName = CryptoUtil.encryptSync(
|
||||
utf8.encode("Uncategorized") as Uint8List,
|
||||
utf8.encode("Uncategorized"),
|
||||
uncategorizedCollectionKey,
|
||||
);
|
||||
final collection = await createAndCacheCollection(
|
||||
@@ -242,7 +241,7 @@ extension HiddenService on CollectionsService {
|
||||
final encryptedKeyData =
|
||||
CryptoUtil.encryptSync(collectionKey, config.getKey()!);
|
||||
final encryptedName = CryptoUtil.encryptSync(
|
||||
utf8.encode(name) as Uint8List,
|
||||
utf8.encode(name),
|
||||
collectionKey,
|
||||
);
|
||||
final jsonToUpdate = CollectionMagicMetadata(
|
||||
@@ -251,7 +250,7 @@ extension HiddenService on CollectionsService {
|
||||
).toJson();
|
||||
assert(jsonToUpdate.length == 2, "metadata should have two keys");
|
||||
final encryptedMMd = await CryptoUtil.encryptChaCha(
|
||||
utf8.encode(jsonEncode(jsonToUpdate)) as Uint8List,
|
||||
utf8.encode(jsonEncode(jsonToUpdate)),
|
||||
collectionKey,
|
||||
);
|
||||
final MetadataRequest metadataRequest = MetadataRequest(
|
||||
|
||||
@@ -57,7 +57,7 @@ class FaceDetectionRelative extends Detection {
|
||||
List<double> get rightMouth => allKeypoints[4];
|
||||
|
||||
FaceDetectionRelative({
|
||||
required double score,
|
||||
required super.score,
|
||||
required List<double> box,
|
||||
required List<List<double>> allKeypoints,
|
||||
}) : assert(
|
||||
@@ -75,8 +75,7 @@ class FaceDetectionRelative extends Detection {
|
||||
(sublist) =>
|
||||
List<double>.from(sublist.map((e) => e.clamp(0.0, 1.0))),
|
||||
)
|
||||
.toList(),
|
||||
super(score: score);
|
||||
.toList();
|
||||
|
||||
void correctForMaintainedAspectRatio(
|
||||
Dimensions originalSize,
|
||||
@@ -252,10 +251,10 @@ class FaceDetectionAbsolute extends Detection {
|
||||
List<double> get rightMouth => allKeypoints[4];
|
||||
|
||||
FaceDetectionAbsolute({
|
||||
required double score,
|
||||
required super.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> files = LinkedHashSet();
|
||||
LinkedHashSet<EnteFile> fileQueue = LinkedHashSet();
|
||||
int uploadingFileId = -1;
|
||||
|
||||
final _dio = NetworkClient.instance.enteDio;
|
||||
@@ -60,7 +60,7 @@ class PreviewVideoStore {
|
||||
|
||||
Future.delayed(
|
||||
const Duration(seconds: 10),
|
||||
PreviewVideoStore.instance.putFilesForPreviewCreation,
|
||||
_putFilesForPreviewCreation,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -82,16 +82,17 @@ class PreviewVideoStore {
|
||||
Bus.instance.fire(VideoStreamingChanged());
|
||||
|
||||
if (isVideoStreamingEnabled) {
|
||||
putFilesForPreviewCreation().ignore();
|
||||
await FileDataService.instance.syncFDStatus();
|
||||
_putFilesForPreviewCreation().ignore();
|
||||
} else {
|
||||
clearQueue();
|
||||
}
|
||||
}
|
||||
|
||||
clearQueue() {
|
||||
void clearQueue() {
|
||||
fileQueue.clear();
|
||||
_items.clear();
|
||||
Bus.instance.fire(PreviewUpdatedEvent(_items));
|
||||
files.clear();
|
||||
}
|
||||
|
||||
DateTime? get videoStreamingCutoff {
|
||||
@@ -111,31 +112,42 @@ class PreviewVideoStore {
|
||||
}
|
||||
|
||||
try {
|
||||
if (!enteFile.isUploaded) return;
|
||||
final file = await getFile(enteFile, isOrigin: true);
|
||||
if (file == null) return;
|
||||
if (!enteFile.isUploaded) {
|
||||
_removeFile(enteFile);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// check if playlist already exist
|
||||
await getPlaylist(enteFile);
|
||||
final resultUrl = await getPreviewUrl(enteFile);
|
||||
final _ = await getPreviewUrl(enteFile);
|
||||
|
||||
if (ctx != null && ctx.mounted) {
|
||||
showShortToast(ctx, 'Video preview already exists');
|
||||
}
|
||||
debugPrint("previewUrl $resultUrl");
|
||||
_items.removeWhere((key, value) => value.file == enteFile);
|
||||
Bus.instance.fire(PreviewUpdatedEvent(_items));
|
||||
_removeFile(enteFile);
|
||||
return;
|
||||
} catch (e, s) {
|
||||
if (e is DioError && e.response?.statusCode == 404) {
|
||||
if (e is DioException && e.response?.statusCode == 404) {
|
||||
_logger.info("No preview found for $enteFile");
|
||||
} else {
|
||||
_logger.warning("Failed to get playlist for $enteFile", e, s);
|
||||
rethrow;
|
||||
_retryFile(enteFile, e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 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,
|
||||
@@ -145,9 +157,11 @@ class PreviewVideoStore {
|
||||
collectionID: enteFile.collectionID ?? 0,
|
||||
);
|
||||
Bus.instance.fire(PreviewUpdatedEvent(_items));
|
||||
files.add(enteFile);
|
||||
fileQueue.add(enteFile);
|
||||
return;
|
||||
}
|
||||
|
||||
// everything is fine, let's process
|
||||
uploadingFileId = enteFile.uploadedFileID!;
|
||||
_items[enteFile.uploadedFileID!] = PreviewItem(
|
||||
status: PreviewItemStatus.compressing,
|
||||
@@ -158,17 +172,31 @@ class PreviewVideoStore {
|
||||
);
|
||||
Bus.instance.fire(PreviewUpdatedEvent(_items));
|
||||
|
||||
final props = await getVideoPropsAsync(file);
|
||||
// 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 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")}";
|
||||
@@ -182,15 +210,14 @@ 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}" '
|
||||
@@ -200,7 +227,8 @@ class PreviewVideoStore {
|
||||
'-hls_list_size 0 -hls_key_info_file ${keyinfo.path} '
|
||||
'$prefix/output.m3u8',
|
||||
);
|
||||
} else if (bitrate != null &&
|
||||
} // case 2, if it's bitrate is good, but codec is not
|
||||
else if (bitrate != null &&
|
||||
codec != null &&
|
||||
bitrate <= 2000 * 1000 &&
|
||||
!codecIsH264) {
|
||||
@@ -215,10 +243,9 @@ class PreviewVideoStore {
|
||||
'-hls_list_size 0 -hls_key_info_file ${keyinfo.path} '
|
||||
'$prefix/output.m3u8',
|
||||
);
|
||||
}
|
||||
|
||||
if (colorSpace != null && isColorGood) {
|
||||
session ??= await FFmpegKit.execute(
|
||||
} // case 3, if it's color space is good
|
||||
else if (colorSpace != null && isColorGood) {
|
||||
session = await FFmpegKit.execute(
|
||||
'-i "${file.path}" '
|
||||
'-metadata:s:v:0 rotate=0 '
|
||||
'-vf "scale=-2:720,fps=30" '
|
||||
@@ -227,20 +254,21 @@ 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;
|
||||
@@ -256,14 +284,15 @@ 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;
|
||||
|
||||
// Logic to fetch width & height of preview
|
||||
//-allowed_extensions ALL -i "https://example.com/stream.m3u8" -frames:v 1 -c copy frame.ts
|
||||
// Fetch resolution of generated stream by decrypting a single frame
|
||||
final FFmpegSession session2 = await FFmpegKit.execute(
|
||||
'-allowed_extensions ALL -i "$prefix/output.m3u8" -frames:v 1 -c copy "$prefix/frame.ts"',
|
||||
);
|
||||
@@ -278,8 +307,8 @@ class PreviewVideoStore {
|
||||
width = props2?.width;
|
||||
height = props2?.height;
|
||||
}
|
||||
} catch (_) {
|
||||
_logger.warning("Failed to get width and height", _);
|
||||
} catch (err, sT) {
|
||||
_logger.warning("Failed to fetch resolution of stream", err, sT);
|
||||
}
|
||||
|
||||
await _reportVideoPreview(
|
||||
@@ -294,7 +323,7 @@ class PreviewVideoStore {
|
||||
_logger.info("Video preview uploaded for $enteFile");
|
||||
} catch (err, sT) {
|
||||
error = "Failed to upload video preview\nError: $err";
|
||||
_logger.shout("Video preview uploaded for $enteFile", err, sT);
|
||||
_logger.shout("Something went wrong with preview upload", err, sT);
|
||||
}
|
||||
} else if (ReturnCode.isCancel(returnCode)) {
|
||||
_logger.warning("FFmpeg command cancelled");
|
||||
@@ -305,14 +334,13 @@ 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,
|
||||
@@ -320,37 +348,49 @@ class PreviewVideoStore {
|
||||
collectionID: enteFile.collectionID ?? 0,
|
||||
);
|
||||
} else {
|
||||
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,
|
||||
);
|
||||
}
|
||||
_retryFile(enteFile, error);
|
||||
}
|
||||
Bus.instance.fire(PreviewUpdatedEvent(_items));
|
||||
} finally {
|
||||
// reset uploading status if this was getting processed
|
||||
if (uploadingFileId == enteFile.uploadedFileID!) {
|
||||
uploadingFileId = -1;
|
||||
}
|
||||
if (files.isNotEmpty) {
|
||||
final file = files.first;
|
||||
files.remove(file);
|
||||
_logger.info("[chunk] Processing ${_items.length} items for streaming");
|
||||
// process next file
|
||||
if (fileQueue.isNotEmpty) {
|
||||
final file = fileQueue.first;
|
||||
fileQueue.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, {
|
||||
@@ -520,7 +560,7 @@ class PreviewVideoStore {
|
||||
final previewURL = response2.data["url"];
|
||||
if (objectKey != null) {
|
||||
unawaited(
|
||||
downloadAndCacheVideo(
|
||||
_downloadAndCacheVideo(
|
||||
previewURL,
|
||||
_getVideoPreviewKey(objectKey),
|
||||
),
|
||||
@@ -549,7 +589,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;
|
||||
}
|
||||
@@ -571,9 +611,35 @@ class PreviewVideoStore {
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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 {
|
||||
if (!isVideoStreamingEnabled) return;
|
||||
|
||||
final cutoff = videoStreamingCutoff;
|
||||
@@ -589,33 +655,21 @@ class PreviewVideoStore {
|
||||
final allFiles = files
|
||||
.where((file) => previewIds?[file.uploadedFileID] == null)
|
||||
.sorted((a, b) {
|
||||
// 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;
|
||||
// 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);
|
||||
return first.compareTo(second);
|
||||
}).toList();
|
||||
|
||||
// set all video status to be in queue
|
||||
// set all video status to 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;
|
||||
}
|
||||
}
|
||||
// elimination case for <=10 MB with H.264
|
||||
final (_, result, _) = await _checkFileForPreviewCreation(enteFile);
|
||||
if (result) {
|
||||
allFiles.remove(enteFile);
|
||||
continue;
|
||||
}
|
||||
|
||||
_items[enteFile.uploadedFileID!] = PreviewItem(
|
||||
@@ -624,14 +678,13 @@ class PreviewVideoStore {
|
||||
collectionID: enteFile.collectionID ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
Bus.instance.fire(PreviewUpdatedEvent(_items));
|
||||
|
||||
final file = allFiles.first;
|
||||
allFiles.remove(file);
|
||||
|
||||
this.files.addAll(allFiles);
|
||||
_logger.info("[init] Processing ${_items.length} items for streaming");
|
||||
|
||||
// take first file and put it for stream generation
|
||||
final file = allFiles.removeAt(0);
|
||||
fileQueue.addAll(allFiles);
|
||||
await chunkAndUploadVideo(null, file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ 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,6 +4,7 @@ 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';
|
||||
@@ -19,13 +20,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";
|
||||
@@ -40,7 +41,6 @@ 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) as Uint8List,
|
||||
utf8.encode(userPassword),
|
||||
CryptoUtil.base642bin(srpAttributes.kekSalt),
|
||||
srpAttributes.memLimit,
|
||||
srpAttributes.opsLimit,
|
||||
|
||||
@@ -11,8 +11,8 @@ class UserDetailsStateWidget extends StatefulWidget {
|
||||
|
||||
const UserDetailsStateWidget({
|
||||
required this.child,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<UserDetailsStateWidget> createState() => UserDetailsStateWidgetState();
|
||||
@@ -65,12 +65,12 @@ class InheritedUserDetails extends InheritedWidget {
|
||||
final bool isCached;
|
||||
|
||||
const InheritedUserDetails({
|
||||
Key? key,
|
||||
required Widget child,
|
||||
super.key,
|
||||
required super.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({Key? key}) : super(key: key);
|
||||
const ChangeEmailDialog({super.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/delete_account.dart';
|
||||
import 'package:photos/models/api/user/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({Key? key}) : super(key: key);
|
||||
const LoginPage({super.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,8 +21,7 @@ import "package:photos/utils/email_util.dart";
|
||||
class LoginPasswordVerificationPage extends StatefulWidget {
|
||||
final SrpAttributes srpAttributes;
|
||||
|
||||
const LoginPasswordVerificationPage({Key? key, required this.srpAttributes})
|
||||
: super(key: key);
|
||||
const LoginPasswordVerificationPage({super.key, required this.srpAttributes});
|
||||
|
||||
@override
|
||||
State<LoginPasswordVerificationPage> createState() =>
|
||||
|
||||
@@ -18,8 +18,8 @@ class OTTVerificationPage extends StatefulWidget {
|
||||
this.isChangeEmail = false,
|
||||
this.isCreateAccountScreen = false,
|
||||
this.isResetPasswordScreen = false,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.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/key_gen_result.dart";
|
||||
import "package:photos/models/api/user/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,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<PasswordEntryPage> createState() => _PasswordEntryPageState();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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';
|
||||
@@ -14,12 +15,11 @@ 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({Key? key}) : super(key: key);
|
||||
const PasswordReentryPage({super.key});
|
||||
|
||||
@override
|
||||
State<PasswordReentryPage> createState() => _PasswordReentryPageState();
|
||||
|
||||
@@ -28,7 +28,7 @@ class RecoveryKeyPage extends StatefulWidget {
|
||||
const RecoveryKeyPage(
|
||||
this.recoveryKey,
|
||||
this.doneText, {
|
||||
Key? key,
|
||||
super.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,4 +1,3 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:photos/core/configuration.dart';
|
||||
@@ -10,7 +9,7 @@ import 'package:photos/utils/dialog_util.dart';
|
||||
import 'package:photos/utils/toast_util.dart';
|
||||
|
||||
class RecoveryPage extends StatefulWidget {
|
||||
const RecoveryPage({Key? key}) : super(key: key);
|
||||
const RecoveryPage({super.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) as Uint8List,
|
||||
utf8.encode(_passwordController.text),
|
||||
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/sessions.dart';
|
||||
import 'package:photos/models/api/user/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({Key? key}) : super(key: key);
|
||||
const SessionsPage({super.key});
|
||||
|
||||
@override
|
||||
State<SessionsPage> createState() => _SessionsPageState();
|
||||
|
||||
@@ -16,8 +16,8 @@ class TwoFactorRecoveryPage extends StatefulWidget {
|
||||
this.sessionID,
|
||||
this.encryptedSecret,
|
||||
this.secretDecryptionNonce, {
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<TwoFactorRecoveryPage> createState() => _TwoFactorRecoveryPageState();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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';
|
||||
@@ -8,7 +9,6 @@ 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, {
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<TwoFactorSetupPage> createState() => _TwoFactorSetupPageState();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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';
|
||||
@@ -13,12 +14,11 @@ 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({Key? key}) : super(key: key);
|
||||
const VerifyRecoveryPage({super.key});
|
||||
|
||||
@override
|
||||
State<VerifyRecoveryPage> createState() => _VerifyRecoveryPageState();
|
||||
|
||||
@@ -14,8 +14,8 @@ class AutoCastDialog extends StatefulWidget {
|
||||
final void Function(String) onConnect;
|
||||
AutoCastDialog(
|
||||
this.onConnect, {
|
||||
Key? key,
|
||||
}) : super(key: key) {}
|
||||
super.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({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<CastChooseDialog> createState() => _CastChooseDialogState();
|
||||
|
||||
@@ -17,8 +17,8 @@ class AlbumHorizontalList extends StatefulWidget {
|
||||
const AlbumHorizontalList(
|
||||
this.collectionsFuture, {
|
||||
this.hasVerifiedLock,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<AlbumHorizontalList> createState() => _AlbumHorizontalListState();
|
||||
|
||||
@@ -12,8 +12,8 @@ class ArchivedCollectionsButton extends StatelessWidget {
|
||||
|
||||
const ArchivedCollectionsButton(
|
||||
this.textStyle, {
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.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.background,
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
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.background,
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
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.background,
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
|
||||
@@ -12,8 +12,8 @@ class UnCategorizedCollections extends StatelessWidget {
|
||||
|
||||
const UnCategorizedCollections(
|
||||
this.textStyle, {
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -26,7 +26,7 @@ class UnCategorizedCollections extends StatelessWidget {
|
||||
}
|
||||
return OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.background,
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
|
||||
@@ -27,8 +27,8 @@ class CollectionListPage extends StatefulWidget {
|
||||
this.appTitle,
|
||||
this.initialScrollOffset,
|
||||
this.tag = "",
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<CollectionListPage> createState() => _CollectionListPageState();
|
||||
|
||||
@@ -13,8 +13,8 @@ class DeviceFolderItem extends StatelessWidget {
|
||||
this.deviceCollection, {
|
||||
///120 is default for the 'on device' scrollview in albums section
|
||||
this.sideOfThumbnail = 120,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@@ -44,9 +44,7 @@ class DeviceFolderVerticalGridView extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _DeviceFolderVerticalGridViewBody extends StatefulWidget {
|
||||
const _DeviceFolderVerticalGridViewBody({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
const _DeviceFolderVerticalGridViewBody();
|
||||
|
||||
@override
|
||||
State<_DeviceFolderVerticalGridViewBody> createState() =>
|
||||
|
||||
@@ -28,8 +28,8 @@ class CollectionsFlexiGridViewWidget extends StatelessWidget {
|
||||
this.displayLimitCount = 10,
|
||||
this.shrinkWrap = false,
|
||||
this.tag = "",
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@@ -17,8 +17,8 @@ class NewAlbumIcon extends StatelessWidget {
|
||||
required this.icon,
|
||||
required this.iconButtonType,
|
||||
this.color,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@@ -13,7 +13,7 @@ class BottomShadowWidget extends StatelessWidget {
|
||||
color: Colors.transparent,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: shadowColor ?? Theme.of(context).colorScheme.background,
|
||||
color: shadowColor ?? Theme.of(context).colorScheme.surface,
|
||||
spreadRadius: 42,
|
||||
blurRadius: 42,
|
||||
offset: Offset(0, offsetDy), // changes position of shadow
|
||||
|
||||
@@ -10,12 +10,12 @@ class DynamicFAB extends StatelessWidget {
|
||||
final Function? onPressedFunction;
|
||||
|
||||
const DynamicFAB({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.isKeypadOpen,
|
||||
this.buttonText,
|
||||
this.isFormValid,
|
||||
this.onPressedFunction,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -24,7 +24,7 @@ class DynamicFAB extends StatelessWidget {
|
||||
decoration: BoxDecoration(
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
spreadRadius: 200,
|
||||
blurRadius: 100,
|
||||
offset: const Offset(0, 230),
|
||||
|
||||
@@ -3,8 +3,7 @@ import 'package:flutter/material.dart';
|
||||
class FastScrollPhysics extends PageScrollPhysics {
|
||||
final double speedFactor;
|
||||
|
||||
const FastScrollPhysics({this.speedFactor = 2.0, ScrollPhysics? parent})
|
||||
: super(parent: parent);
|
||||
const FastScrollPhysics({this.speedFactor = 2.0, super.parent});
|
||||
|
||||
@override
|
||||
FastScrollPhysics applyTo(ScrollPhysics? ancestor) {
|
||||
|
||||
@@ -15,7 +15,7 @@ class GradientButton extends StatelessWidget {
|
||||
final double paddingValue;
|
||||
|
||||
const GradientButton({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.linearGradientColors = const [
|
||||
Color(0xFF2CD267),
|
||||
Color(0xFF1DB954),
|
||||
@@ -24,7 +24,7 @@ class GradientButton extends StatelessWidget {
|
||||
this.text = '',
|
||||
this.iconData,
|
||||
this.paddingValue = 0.0,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import 'package:photos/ente_theme_data.dart';
|
||||
class LinearProgressDialog extends StatefulWidget {
|
||||
final String message;
|
||||
|
||||
const LinearProgressDialog(this.message, {Key? key}) : super(key: key);
|
||||
const LinearProgressDialog(this.message, {super.key});
|
||||
|
||||
@override
|
||||
LinearProgressDialogState createState() => LinearProgressDialogState();
|
||||
|
||||
@@ -14,9 +14,9 @@ class WebPage extends StatefulWidget {
|
||||
const WebPage(
|
||||
this.title,
|
||||
this.url, {
|
||||
Key? key,
|
||||
super.key,
|
||||
this.canOpenInBrowser = false,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
State<WebPage> createState() => _WebPageState();
|
||||
|
||||
@@ -15,8 +15,8 @@ class CaptionedTextWidget extends StatelessWidget {
|
||||
this.makeTextBold = false,
|
||||
this.textColor,
|
||||
this.subTitleColor,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@@ -14,8 +14,7 @@ import "package:photos/utils/photo_manager_util.dart";
|
||||
|
||||
class HomeHeaderWidget extends StatefulWidget {
|
||||
final Widget centerWidget;
|
||||
const HomeHeaderWidget({required this.centerWidget, Key? key})
|
||||
: super(key: key);
|
||||
const HomeHeaderWidget({required this.centerWidget, super.key});
|
||||
|
||||
@override
|
||||
State<HomeHeaderWidget> createState() => _HomeHeaderWidgetState();
|
||||
|
||||
@@ -88,8 +88,8 @@ class MenuItemWidget extends StatefulWidget {
|
||||
this.showOnlyLoadingState = false,
|
||||
this.surfaceExecutionStates = true,
|
||||
this.alwaysShowSuccessState = false,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<MenuItemWidget> createState() => _MenuItemWidgetState();
|
||||
|
||||
@@ -14,8 +14,8 @@ class ToggleSwitchWidget extends StatefulWidget {
|
||||
const ToggleSwitchWidget({
|
||||
required this.value,
|
||||
required this.onChanged,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ToggleSwitchWidget> createState() => _ToggleSwitchWidgetState();
|
||||
@@ -58,7 +58,7 @@ class _ToggleSwitchWidgetState extends State<ToggleSwitchWidget> {
|
||||
activeTrackColor: enteColorScheme.primary500,
|
||||
activeColor: Colors.white,
|
||||
inactiveThumbColor: enteColorScheme.primary500,
|
||||
trackOutlineColor: MaterialStateColor.resolveWith(
|
||||
trackOutlineColor: WidgetStateColor.resolveWith(
|
||||
(states) => enteColorScheme.primary500,
|
||||
),
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
|
||||
@@ -42,7 +42,7 @@ class ExtentsPageView extends StatefulWidget {
|
||||
/// child that could possibly be displayed in the page view, instead of just
|
||||
/// those children that are actually visible.
|
||||
ExtentsPageView({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.scrollDirection = Axis.horizontal,
|
||||
this.reverse = false,
|
||||
required this.controller,
|
||||
@@ -53,8 +53,7 @@ class ExtentsPageView extends StatefulWidget {
|
||||
this.dragStartBehavior = DragStartBehavior.start,
|
||||
this.openDrawer,
|
||||
}) : childrenDelegate = SliverChildListDelegate(children),
|
||||
extents = children.length,
|
||||
super(key: key);
|
||||
extents = children.length;
|
||||
|
||||
/// Creates a scrollable list that works page by page using widgets that are
|
||||
/// created on demand.
|
||||
@@ -73,7 +72,7 @@ class ExtentsPageView extends StatefulWidget {
|
||||
/// you are planning to change child order at a later time, consider using
|
||||
/// [PageView] or [PageView.custom].
|
||||
ExtentsPageView.builder({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.scrollDirection = Axis.horizontal,
|
||||
this.reverse = false,
|
||||
required this.controller,
|
||||
@@ -86,11 +85,10 @@ class ExtentsPageView extends StatefulWidget {
|
||||
this.openDrawer,
|
||||
}) : childrenDelegate =
|
||||
SliverChildBuilderDelegate(itemBuilder, childCount: itemCount),
|
||||
extents = 0,
|
||||
super(key: key);
|
||||
extents = 0;
|
||||
|
||||
ExtentsPageView.extents({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.extents = 1,
|
||||
this.scrollDirection = Axis.horizontal,
|
||||
this.reverse = false,
|
||||
@@ -107,8 +105,7 @@ class ExtentsPageView extends StatefulWidget {
|
||||
childCount: itemCount,
|
||||
addAutomaticKeepAlives: false,
|
||||
addRepaintBoundaries: false,
|
||||
),
|
||||
super(key: key);
|
||||
);
|
||||
|
||||
/// Creates a scrollable list that works page by page with a custom child
|
||||
/// model.
|
||||
@@ -191,7 +188,7 @@ class ExtentsPageView extends StatefulWidget {
|
||||
/// ```
|
||||
/// {@end-tool}
|
||||
const ExtentsPageView.custom({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.scrollDirection = Axis.horizontal,
|
||||
this.reverse = false,
|
||||
required this.controller,
|
||||
@@ -201,8 +198,7 @@ class ExtentsPageView extends StatefulWidget {
|
||||
required this.childrenDelegate,
|
||||
this.dragStartBehavior = DragStartBehavior.start,
|
||||
this.openDrawer,
|
||||
}) : extents = 0,
|
||||
super(key: key);
|
||||
}) : extents = 0;
|
||||
|
||||
/// The number of pages to build off screen.
|
||||
///
|
||||
|
||||
@@ -9,9 +9,8 @@ import "package:photos/utils/navigation_util.dart";
|
||||
class HeaderErrorWidget extends StatelessWidget {
|
||||
final Error? _error;
|
||||
|
||||
const HeaderErrorWidget({Key? key, required Error? error})
|
||||
: _error = error,
|
||||
super(key: key);
|
||||
const HeaderErrorWidget({super.key, required Error? error})
|
||||
: _error = error;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@@ -12,8 +12,8 @@ class HomeBottomNavigationBar extends StatefulWidget {
|
||||
const HomeBottomNavigationBar(
|
||||
this.selectedFiles, {
|
||||
required this.selectedTabIndex,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
final SelectedFiles selectedFiles;
|
||||
final int selectedTabIndex;
|
||||
|
||||
@@ -26,7 +26,7 @@ import "package:photos/utils/dialog_util.dart";
|
||||
import "package:photos/utils/navigation_util.dart";
|
||||
|
||||
class LandingPageWidget extends StatefulWidget {
|
||||
const LandingPageWidget({Key? key}) : super(key: key);
|
||||
const LandingPageWidget({super.key});
|
||||
|
||||
@override
|
||||
State<LandingPageWidget> createState() => _LandingPageWidgetState();
|
||||
@@ -317,8 +317,8 @@ class FeatureItemWidget extends StatelessWidget {
|
||||
this.featureTitleFirstLine,
|
||||
this.featureTitleSecondLine,
|
||||
this.subText, {
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@@ -96,9 +96,9 @@ class FullScreenMemoryData extends InheritedWidget {
|
||||
required this.memories,
|
||||
required this.indexNotifier,
|
||||
required this.removeCurrentMemory,
|
||||
required Widget child,
|
||||
Key? key,
|
||||
}) : super(child: child, key: key);
|
||||
required super.child,
|
||||
super.key,
|
||||
});
|
||||
|
||||
static FullScreenMemoryData? of(BuildContext context) {
|
||||
return context.dependOnInheritedWidgetOfExactType<FullScreenMemoryData>();
|
||||
|
||||
@@ -19,7 +19,7 @@ class DraggableScrollbar extends StatefulWidget {
|
||||
final bool isEnabled;
|
||||
|
||||
const DraggableScrollbar({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.child,
|
||||
this.backgroundColor = Colors.white,
|
||||
this.drawColor = Colors.grey,
|
||||
@@ -32,7 +32,7 @@ class DraggableScrollbar extends StatefulWidget {
|
||||
required this.labelTextBuilder,
|
||||
this.onChange,
|
||||
this.isEnabled = true,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
DraggableScrollbarState createState() => DraggableScrollbarState();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user