Compare commits

...

140 Commits

Author SHA1 Message Date
mngshm
0591a22eeb don't show edit icon if user in list is admin 2025-02-21 13:29:13 +05:30
mngshm
b2d9305f7f allow admin to see storage Limit on dashboard 2025-02-21 09:32:58 +05:30
Neeraj
7c679cd38b [auth] Add Custom Icons (ButterflyMX / FreeTaxUSA) (#5116)
## Description
New Icons: ButterFlyMX / FreeTaxUSA
2025-02-21 09:13:03 +05:30
Francis Geronimo
3669b6be09 Rename Butterflymx.svg to butterflymx.svg 2025-02-20 09:45:01 -07:00
Manav Rathi
88b0ecf472 [web] Trim the gallery context (#5124)
Code restructuring, no functional changes
2025-02-20 19:04:46 +05:30
Manav Rathi
ee2f03adca Fix 2025-02-20 18:56:48 +05:30
Manav Rathi
3e6c253a24 Uncontext 2025-02-20 18:45:17 +05:30
Manav Rathi
776b7488d3 Fin 2025-02-20 18:27:03 +05:30
Manav Rathi
371377d4d1 R 2025-02-20 18:16:26 +05:30
Manav Rathi
aad42b3c00 del 2 2025-02-20 18:10:00 +05:30
Manav Rathi
9ce0b43bfe del 1 2025-02-20 18:05:19 +05:30
Manav Rathi
f5ea565aa8 Combine visually single section 2025-02-20 17:57:25 +05:30
Manav Rathi
d41e177b3c ex 2025-02-20 17:53:00 +05:30
Manav Rathi
bc742f20e1 Use 3 2025-02-20 17:42:00 +05:30
Manav Rathi
35601956d2 Use 2025-02-20 17:30:28 +05:30
Manav Rathi
bc699b8f37 sys 2025-02-20 17:16:39 +05:30
Neeraj
d20615002c [sever] Minor fixes in modify-storage endpoint (#5121)
## Description

## Tests
2025-02-20 14:39:05 +05:30
Neeraj Gupta
b44f844513 [server] Remove null resp 2025-02-20 14:27:18 +05:30
Neeraj Gupta
09d390bd38 [server] Return on err 2025-02-20 14:26:10 +05:30
Manav Rathi
e439e4a5f7 [web] Show an activity indicator when the user is selecting files/folders/drag-drop (#5119)
When selecting 100k+ files, the browser doesn't get back to us for
seconds, so let the user know something's happening.

> During this I found that the dd library is also doing some synchronous
processing of its own the further delays the callback, but there is
still a base delay anyways even when directly using the HTML inputs
elements.
2025-02-20 14:20:43 +05:30
Manav Rathi
3147800486 Fin 2025-02-20 14:11:40 +05:30
Natoshi
0a7984a0d2 [auth] Add Gate.io icon (#5107)
Add new icon to ente auth (gateio.svg)
and Add Gate.io icon to custom-icons.json
2025-02-20 14:01:21 +05:30
Manav Rathi
189a3ebc40 Take 1 2025-02-20 13:58:29 +05:30
Manav Rathi
c0eeb7dd2f Maybe 2025-02-20 13:53:43 +05:30
Manav Rathi
4f271887fc dd 2025-02-20 13:50:42 +05:30
Manav Rathi
f46f063beb Disable backdrop clicks 2025-02-20 13:17:04 +05:30
Manav Rathi
270a628478 Use 2025-02-20 13:09:53 +05:30
Manav Rathi
b5f850b3be Activity indicator 2025-02-20 13:06:37 +05:30
Manav Rathi
77d16e275d Also mark selection 2025-02-20 12:46:54 +05:30
Neeraj
098a4526ad [mob] Share only image for live photos (#5118)
## Description

## Tests
2025-02-20 12:08:49 +05:30
Manav Rathi
957c333cf3 Use the same enum throughout 2025-02-20 12:08:44 +05:30
Neeraj Gupta
101a9d4b5d [mob] Share only image for live photos 2025-02-20 12:00:27 +05:30
Manav Rathi
e3ef1e4628 Move 2025-02-20 11:51:53 +05:30
Manav Rathi
fd133d4023 Attach cancel callback 2025-02-20 11:42:30 +05:30
Neeraj
cdfdc83083 [mob] streaming feedbacks resolved (#5112)
## Description

This PR deals with following:

- [x] Android Artifacts fixes
- [x] Queuing Fixes
- [x] Document functions better
- [x] Make UX similar to native video player
- [x] Check for seekbar changes


## Tests
2025-02-20 11:36:39 +05:30
Manav Rathi
8618babc11 Attempt to intercept cancellation 2025-02-20 11:20:54 +05:30
Manav Rathi
ca28a3c595 Also 2025-02-20 11:05:57 +05:30
Manav Rathi
5eba06a269 Sublimate 2025-02-20 11:02:12 +05:30
Manav Rathi
91017969b3 Transplant 2025-02-20 10:49:42 +05:30
Francis Geronimo
d25e37e2ad New Icons
ButterFlyMX
FreeTaxUSA
2025-02-19 17:33:13 -07:00
Prateek Sunal
7b902a607a chore: bump version 2025-02-19 20:37:11 +05:30
Prateek Sunal
68bc6fac38 chore: bump locks 2025-02-19 20:36:38 +05:30
Prateek Sunal
82fdae9253 fix: seek bar & buffering 2025-02-19 20:36:30 +05:30
Manav Rathi
d78ffced78 [desktop] Improve error bifurcation during ML indexing (#5113) 2025-02-19 19:24:52 +05:30
Manav Rathi
7035d3ca90 Use 2025-02-19 19:02:42 +05:30
Manav Rathi
8920462b54 wrap 2025-02-19 18:59:59 +05:30
Manav Rathi
56f9f2a028 wrap 2025-02-19 18:58:00 +05:30
Prateek Sunal
94a77b7df1 chore: update lock files 2025-02-19 18:35:41 +05:30
Prateek Sunal
7fc42bed64 fix: add playbackCallback to hideStuff properly 2025-02-19 18:32:47 +05:30
Manav Rathi
8eb34503ac net 1 2025-02-19 18:32:25 +05:30
Prateek Sunal
2fe6df5d21 fix: sync previewIds after enabling streaming 2025-02-19 18:31:57 +05:30
Manav Rathi
d59d3c3b07 grandfather 2025-02-19 17:40:43 +05:30
Prateek Sunal
503c2506aa fix: put remote files at last, get file after updating status as compressing, 2025-02-19 15:21:38 +05:30
Prateek Sunal
e44405b46f feat: document more, better code 2025-02-19 14:54:18 +05:30
Prateek Sunal
cdaeec0e8e fix: skip errors in checking file for preview creation 2025-02-19 14:17:09 +05:30
Prateek Sunal
29671aa154 fix: use media kit in android too 2025-02-19 12:28:44 +05:30
Manav Rathi
370299d433 [desktop] Improve ML worker logging (#5111) 2025-02-19 12:14:33 +05:30
Manav Rathi
38d42c67fb Log in other workers too 2025-02-19 12:08:40 +05:30
Manav Rathi
802ad184d2 Log at top level to cover unknown scenarios too 2025-02-19 12:06:02 +05:30
Manav Rathi
55cff6f174 log ww unhandled 2025-02-19 11:41:31 +05:30
Manav Rathi
e5448685ca Tweak 2025-02-19 10:52:32 +05:30
Manav Rathi
bde8a17cb4 Tweak 2025-02-19 10:39:24 +05:30
Manav Rathi
fc3200af73 [desktop] Fix NaN% ML status for empty account (#5110) 2025-02-19 10:35:51 +05:30
Manav Rathi
d21ea0a5a6 Fix NaN% for empty account 2025-02-19 10:31:34 +05:30
Manav Rathi
a1fda786f5 Fix look (paper now has a default shadow) 2025-02-19 10:30:33 +05:30
Manav Rathi
bc0980eb8d [web] Code reorg (#5109)
Intermezzo between the photoswipe changes.
2025-02-19 10:08:46 +05:30
Manav Rathi
2cfc5d9c59 Unused 2025-02-19 09:58:25 +05:30
Manav Rathi
682710a8a8 Remove unneeded ignore
We no longer have any source in public
2025-02-19 09:56:42 +05:30
Manav Rathi
ed50e5a36c pkg json cleanup 2025-02-19 09:51:58 +05:30
Manav Rathi
f31a6f2401 Move 2025-02-19 09:51:58 +05:30
Manav Rathi
239f08b516 Fixes 2025-02-19 09:32:23 +05:30
Manav Rathi
8544f5e109 Fixes 2025-02-19 09:30:40 +05:30
Manav Rathi
1eaaafb8df Ditto 2025-02-19 08:58:25 +05:30
Manav Rathi
149196e7dd Rename 2025-02-19 08:45:38 +05:30
Manav Rathi
38a31b7492 5s 2025-02-19 08:41:56 +05:30
Manav Rathi
b14ad92b91 Improvements 2025-02-19 08:41:56 +05:30
Manav Rathi
872455cce2 [web] Use upstream PhotoSwipe - Part x/x (#5108)
- Error handling 
- Exif handling (partial)
2025-02-18 20:25:05 +05:30
Manav Rathi
65a5248338 Revert "Revert "Mainlineable""
This reverts commit d398838742.
2025-02-18 20:17:50 +05:30
Manav Rathi
fcf06cff57 Cleanup 2025-02-18 20:17:03 +05:30
Manav Rathi
13752654cd Retain original image URL 2025-02-18 19:59:56 +05:30
Manav Rathi
d5e8777e0d Take 1 2025-02-18 19:47:06 +05:30
Manav Rathi
7de2a47c51 Sketch 2025-02-18 19:23:13 +05:30
Manav Rathi
20bc84ca96 Doc 2025-02-18 18:24:47 +05:30
Manav Rathi
fd7c25029e Restructure 2025-02-18 16:52:39 +05:30
Manav Rathi
764add95c8 EP 1 2025-02-18 16:30:15 +05:30
Prateek Sunal
8c3fc0a879 [mob] remove internal user flag for media kit (#5106)
## Description

Remove internal user flag for media kit

## Tests
2025-02-18 15:11:55 +05:30
Prateek Sunal
37c467eb86 fix: remove internal user flag 2025-02-18 15:09:52 +05:30
Manav Rathi
931d7c8513 Close 2025-02-18 15:03:58 +05:30
Manav Rathi
132962b92f Exclusive 2025-02-18 14:48:51 +05:30
Manav Rathi
a5c4d9cc18 ff 2025-02-18 14:40:25 +05:30
Neeraj
39c31779a4 [mob] Lint fixes (#5103)
## Description

* Modified the analysis file and disabled all rules except
use_super_key.

Ran automatic fix command
❯ dart fix --apply

This also removed redundant cast warning. As the changes seemed
harmless, decided to keep them.

## Tests
2025-02-18 14:19:35 +05:30
Neeraj
d09613a946 [mob] video editor fixes (#5091)
## Description

Previously video editor was giving subpar quality videos, this fixes it
and also re-encodes videos using libx264 mp4 format.

This will also increase the time to process the edited video.

## Tests
2025-02-18 14:17:15 +05:30
Prateek Sunal
88e50982b2 [mob] streaming queue fixes (#5105)
## Description

## Tests
2025-02-18 14:16:57 +05:30
Prateek Sunal
ce5a8f0457 fix: logic for queuing and item removal 2025-02-18 14:09:39 +05:30
Neeraj Gupta
8c81a377c0 Lint fixes 2025-02-18 14:08:10 +05:30
Neeraj Gupta
a0025ab09b [mob] Lint fixes 2025-02-18 13:48:28 +05:30
Manav Rathi
324c156ea1 Use last best instead of thumbnail 2025-02-18 13:43:23 +05:30
Manav Rathi
4b87c9f3ac Thumb errs 2025-02-18 13:36:19 +05:30
Neeraj
dfda0c2c32 [mob] Extract ente_crypto plugin (#5102)
## Description

## Tests
2025-02-18 13:27:29 +05:30
Neeraj Gupta
c1a53bdfce Fix lint issues 2025-02-18 13:24:15 +05:30
Neeraj Gupta
0c8dc3af95 [mob] Extract ente_crypto plugin 2025-02-18 13:19:09 +05:30
Manav Rathi
b3100f098b Reset failures 2025-02-18 12:50:09 +05:30
Prateek Sunal
0e157a4e33 fix: still check 10mb preview limit 2025-02-18 12:19:16 +05:30
Prateek Sunal
04a5372f6e Merge branch 'main' into bumpversion 2025-02-18 11:47:52 +05:30
Manav Rathi
f47837f550 fin visually 2025-02-18 11:21:34 +05:30
Manav Rathi
1af8d7481d err 4 2025-02-18 11:09:39 +05:30
Manav Rathi
af91adeb72 err 3 2025-02-18 10:53:16 +05:30
Neeraj
8fd90651b1 [auth] Delete unused code (#5094)
## Description

## Tests
2025-02-18 10:31:47 +05:30
Edoardo Mileto
c3da41eee2 Create a custom icon for Deloitte (#5096)
## Description
followed [this
guide](https://github.com/ente-io/ente/blob/main/auth/docs/adding-icons.md)
2025-02-18 10:31:04 +05:30
Neeraj Gupta
ac94dccb90 remove oversize icon 2025-02-18 10:26:17 +05:30
Manav Rathi
89881975f2 err 2 2025-02-18 10:22:48 +05:30
Manav Rathi
e715d582ac err 1 2025-02-18 09:54:07 +05:30
Manav Rathi
808c611a92 Docs 2025-02-18 09:25:53 +05:30
Manav Rathi
fe5146ead8 Singleton => module 2025-02-18 09:15:25 +05:30
Manav Rathi
d398838742 Revert "Mainlineable"
This reverts commit 6ccca2114e.
2025-02-18 09:02:11 +05:30
Manav Rathi
717dc0996f [web] Use upstream PhotoSwipe (WIP) (#5097)
Continue https://github.com/ente-io/ente/pull/5066
2025-02-17 19:47:00 +05:30
Manav Rathi
00db3c0335 sp 2025-02-17 19:42:40 +05:30
Manav Rathi
6ccca2114e Mainlineable 2025-02-17 19:38:03 +05:30
Manav Rathi
0e3708ffdc LP resume 2025-02-17 19:38:03 +05:30
Manav Rathi
36e7dae2ee Simplify 2025-02-17 19:30:48 +05:30
Manav Rathi
9d76d93254 Integrate 2025-02-17 19:16:03 +05:30
Prateek Sunal
aab4bff6ff bump version 2025-02-17 18:54:20 +05:30
Manav Rathi
12a96b68ba wip ds 2025-02-17 18:39:27 +05:30
Manav Rathi
a851caf78f Another ARIA workaround 2025-02-17 17:11:14 +05:30
Neeraj Gupta
4bbac0ca66 [auth] Delete unused code 2025-02-17 17:08:22 +05:30
Manav Rathi
bb2bbb5655 Workaround for an aria issue...
...somewhere (In something we're doing? In PS? In Chrome? In ARIA?)
2025-02-17 16:32:13 +05:30
Neeraj Gupta
d36934ec0d remove 2025-02-17 16:27:50 +05:30
Neeraj Gupta
f922df304e [mob] Refactor 2025-02-17 16:27:50 +05:30
Manav Rathi
f6d949db38 wrap 2025-02-17 16:09:18 +05:30
Manav Rathi
e937027667 Title gets used as ariaLabel 2025-02-17 16:04:51 +05:30
Manav Rathi
84aeb79412 Move out icons 2025-02-17 15:44:05 +05:30
Manav Rathi
6140f35e69 Move 2025-02-17 15:27:39 +05:30
Manav Rathi
0eba6b9c98 Move 2025-02-17 15:07:30 +05:30
Manav Rathi
fe86075868 Namespace 2025-02-17 14:54:11 +05:30
Manav Rathi
e9d63dfea9 zi 2025-02-17 14:39:49 +05:30
Manav Rathi
1c322a9c62 Modal 2025-02-17 14:11:16 +05:30
Manav Rathi
dc0450b155 Modal 2025-02-17 14:05:55 +05:30
Manav Rathi
8e90541d87 Fix path for lps 2025-02-17 13:57:25 +05:30
Prateek Sunal
8df04b2363 fix: convert the edited video to mp4 with x264 2025-02-17 13:50:03 +05:30
Manav Rathi
43c0d8a6ad Revert "Revert "Workbench""
This reverts commit 20fea517ce.
2025-02-17 13:42:38 +05:30
246 changed files with 3206 additions and 2907 deletions

View File

@@ -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"
},

View 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

View 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

View 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

View 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

View File

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

View File

@@ -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();
}
}

View File

@@ -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,
);
}
}

View File

@@ -1,6 +0,0 @@
class PublicKey {
final String email;
final String publicKey;
PublicKey(this.email, this.publicKey);
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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
),
],
),
);
}
}

View File

@@ -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,
),
),
),
);
}
}

View File

@@ -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());
},
),
],
);
}
}

View File

@@ -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,
),
],
),
);
}
}

View File

@@ -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);
},
);
}
}

View File

@@ -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;
},
);
}
}

View File

@@ -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,
),
),
],
),
),
);
}
}

View File

@@ -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)),
],
);
}
}

View File

@@ -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,
),
),
],
),
),
),
),
);
}
}

View File

@@ -1 +0,0 @@
// TODO Implement this library.

View File

@@ -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>

View File

@@ -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

View File

@@ -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!,

View File

@@ -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 {}

View File

@@ -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";

View File

@@ -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";

View File

@@ -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);

View File

@@ -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;

View File

@@ -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");

View File

@@ -1,6 +1,6 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import "package:freezed_annotation/freezed_annotation.dart";
@immutable
class EntityData {

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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';

View File

@@ -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!),

View File

@@ -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';

View File

@@ -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(

View File

@@ -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(

View File

@@ -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(

View File

@@ -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() {

View File

@@ -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);
}
}

View 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';

View File

@@ -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';

View File

@@ -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,

View File

@@ -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>();

View File

@@ -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();

View File

@@ -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";

View File

@@ -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();

View File

@@ -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() =>

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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!,

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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),
),

View File

@@ -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),
),

View File

@@ -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),
),

View File

@@ -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),
),

View File

@@ -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();

View File

@@ -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) {

View File

@@ -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() =>

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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

View File

@@ -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),

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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();

View File

@@ -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();

View File

@@ -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) {

View File

@@ -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();

View File

@@ -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();

View File

@@ -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,

View File

@@ -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.
///

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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>();

View File

@@ -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