Compare commits

..

230 Commits

Author SHA1 Message Date
mngshm
daa09136ef [server] remove option to set storageLimit from Member Invite workflow 2025-02-24 17:14:56 +05:30
mngshm
82ebcf66a5 fix: allow null values in storage_limit column 2025-02-24 17:12:13 +05:30
Vishnu Mohandas
490a7221e7 [docs] Update (#5157) 2025-02-24 16:20:50 +05:30
vishnukvmd
1b98f782f1 Update 2025-02-24 16:19:54 +05:30
Vishnu Mohandas
a46a64e386 [docs] Update (#5156) 2025-02-24 16:03:54 +05:30
vishnukvmd
492ea61bb7 Update 2025-02-24 16:03:28 +05:30
Neeraj
9447f1c767 [server] Remove embeddings handler (#5154)
## Description

## Tests
2025-02-24 15:11:47 +05:30
Neeraj Gupta
76b2a73f9a clean up 2025-02-24 15:00:33 +05:30
Neeraj Gupta
4bbe1ae0d2 [server] Remove embeddings handler 2025-02-24 14:49:21 +05:30
Manav Rathi
b15b707600 [server] Limit support while fetching collections (#5148)
## Description
This endpoint let's us add limit which fetching collections.

## Tests
2025-02-24 14:18:44 +05:30
Neeraj
454363d772 [server] Minor refactor (#5152)
## Description
Moving few methods related to collection_files in diff file.
## Tests
2025-02-24 14:08:28 +05:30
Vishnu Mohandas
196fa2c8a4 [docs] Update (#5153)
## Description

## Tests
2025-02-24 14:05:34 +05:30
vishnukvmd
4c3ca8a565 Update 2025-02-24 14:05:02 +05:30
Neeraj Gupta
15aea42b96 [server] Minor refactor 2025-02-24 14:04:39 +05:30
Vishnu Mohandas
29c7f587f6 [docs] Update (#5151) 2025-02-24 13:55:36 +05:30
vishnukvmd
5f0bb21491 Update 2025-02-24 13:54:59 +05:30
Vishnu Mohandas
c882ce0f98 [docs] Update (#5149) 2025-02-24 13:07:25 +05:30
vishnukvmd
02dde7f6a2 Update 2025-02-24 13:07:04 +05:30
Neeraj Gupta
d268e1f309 Fix 2025-02-24 12:39:55 +05:30
Neeraj Gupta
245e78ac42 [server] Limit support while fetching collections 2025-02-24 12:29:20 +05:30
Neeraj
fa55bd88a2 [auth] New translations (#5144)
New translations from
[Crowdin](https://crowdin.com/project/ente-authenticator-app)
2025-02-24 11:13:56 +05:30
Neeraj
11538236c0 [auth] Upgrade dio (#5137)
## Description
Upgrade dio 5.4.0 -> 5.8.0+1
2025-02-24 11:12:56 +05:30
Manav Rathi
09996f77ea [web] [desktop] Enable Japanese translations (#5146) 2025-02-24 10:26:38 +05:30
Manav Rathi
3f512bc959 lf 2025-02-24 10:08:16 +05:30
Manav Rathi
8b0990bd6c [web] [desktop] Enable Japanese translations 2025-02-24 09:58:52 +05:30
Manav Rathi
70ff886252 [web] New translations (#5143)
New translations from
[Crowdin](https://crowdin.com/project/ente-photos-web)
2025-02-24 09:46:57 +05:30
Crowdin Bot
4314d42ab4 New Crowdin translations by GitHub Action 2025-02-24 01:17:26 +00:00
Crowdin Bot
8abe6957d7 New Crowdin translations by GitHub Action 2025-02-24 00:35:43 +00:00
Aman Raj Singh Mourya
d6ec6809c2 [auth] Use native dio adapter 2025-02-22 00:53:16 +05:30
Aman Raj Singh Mourya
8fe9b9571a [auth] Upgrade dio 2025-02-22 00:49:22 +05:30
Aman Raj Singh Mourya
d667cc4f98 [auth] Set cronetHttpNoPlay=true while building apk for droid 2025-02-22 00:48:53 +05:30
Manav Rathi
5806eb6e60 [web] Update storage card when family member's storage limit is configured (#5135)
Sibling of https://github.com/ente-io/ente/pull/5123
2025-02-21 17:42:21 +05:30
Manav Rathi
8830deb619 [web] Update storage card when family member's storage limit is configured
Sibling of https://github.com/ente-io/ente/pull/5123
2025-02-21 17:22:54 +05:30
Manav Rathi
5e32e975df [web] New translations (#5134)
New translations from
[Crowdin](https://crowdin.com/project/ente-photos-web)
2025-02-21 16:54:41 +05:30
Crowdin Bot
8633dabd92 New Crowdin translations by GitHub Action 2025-02-21 11:22:42 +00:00
Manav Rathi
c256f0a8c4 [web] Misc cleanup (#5133) 2025-02-21 16:52:01 +05:30
Manav Rathi
b150bbe15e Rename 2025-02-21 16:40:51 +05:30
Manav Rathi
7f69fa5d65 Reorder 2025-02-21 16:36:50 +05:30
Manav Rathi
3968dd93e9 R 2025-02-21 16:34:09 +05:30
Manav Rathi
557bdd142b R 2025-02-21 16:32:29 +05:30
Manav Rathi
b89da99c96 Rename 2025-02-21 16:29:54 +05:30
Manav Rathi
e79050a3b6 ann2 2025-02-21 16:28:42 +05:30
Manav Rathi
5e4707b695 ann 2025-02-21 16:28:42 +05:30
Manav Rathi
fd0c2866a2 Doc 2025-02-21 16:28:42 +05:30
Manav Rathi
69eee661d1 Prop 2025-02-21 16:28:42 +05:30
Manav Rathi
c78578fce5 [web] Speed up file selection for libraries with 100k+ files (#5132)
(Drastically!)
2025-02-21 16:15:51 +05:30
Manav Rathi
a0f103be9b Fix 2025-02-21 16:05:15 +05:30
Manav Rathi
04ede4326a Cleanup scaffolding 2025-02-21 15:59:57 +05:30
Manav Rathi
7cb9bc3eb7 Reduce some duplication 2025-02-21 15:53:29 +05:30
Manav Rathi
488402156f take 1 2025-02-21 15:45:46 +05:30
Manav Rathi
e80e602786 bespoke 1 2025-02-21 15:38:59 +05:30
Manav Rathi
5d553afea7 Rearrange 2025-02-21 15:28:29 +05:30
Neeraj
9a25356abf [mob] Update storage card when family member's storage limit is configured (#5123)
## Description

## Tests
2025-02-21 15:15:35 +05:30
Neeraj Gupta
5625733429 Merge remote-tracking branch 'origin/main' into family_limits 2025-02-21 14:59:18 +05:30
Manav Rathi
d2dd08391a Investigate speeding up the checkbox action too 2025-02-21 14:47:37 +05:30
Neeraj Gupta
9d06db2b6b [mob] Fix free storage calculation for mob upload 2025-02-21 14:44:50 +05:30
Neeraj Gupta
80049b11ba [mob] Handle null result 2025-02-21 14:42:59 +05:30
Manav Rathi
28160b04b9 Clean 2025-02-21 14:22:47 +05:30
Manav Rathi
02441239d5 fast 4 2025-02-21 14:11:11 +05:30
Manav Rathi
760b50b417 fast 3 2025-02-21 14:08:20 +05:30
Manav Rathi
d118e0e63e fast 1 2025-02-21 14:07:07 +05:30
Manav Rathi
49f9caac90 wip speed up 2025-02-21 14:05:38 +05:30
Prateek Sunal
ea875730dd [mob] streaming patches (#5122)
## Description

Quality of Life fixes:
- [x] Queue fixes
- [x] Android Impeller fix
- [x] No video_player_media_kit proxy, just using media_kit directory

Quality of Dev fixes:
- [x] Use master branch of media_kit
- [x] extract common functions from native player and media kit for
seconds to duration.
2025-02-21 13:34:01 +05:30
Neeraj
e3b03db06f [server] Clean up old logic for collection diff (#5130)
## Description
We still have some traffic from old mobile client v0.7.xx that is making
call to the v1 endpoint.
## Tests
2025-02-21 13:25:14 +05:30
Neeraj Gupta
b030c4e182 [server] Clean up old logic for collection diff 2025-02-21 13:22:45 +05:30
Manav Rathi
1a39846d25 Swap 2025-02-21 13:05:16 +05:30
Manav Rathi
e44020f93a Swap 2025-02-21 13:02:58 +05:30
Neeraj
435621496c [server] Move refactor (#5129)
## Description
Just grouping various actions inside different file to improve
readability
## Tests
2025-02-21 12:52:18 +05:30
Manav Rathi
8379162716 Fix 2025-02-21 12:51:21 +05:30
Manav Rathi
e8d9f4f6cf Conv 2025-02-21 12:45:12 +05:30
Manav Rathi
6724527c27 Another 2025-02-21 12:35:11 +05:30
Neeraj Gupta
6b65a974b5 [server] Refactor 2025-02-21 12:28:11 +05:30
Manav Rathi
1b90fa93ee Use 2025-02-21 12:20:12 +05:30
Neeraj Gupta
f907303c8b [server] move files 2025-02-21 12:08:16 +05:30
Manav Rathi
4317f819d8 mod 2025-02-21 12:07:31 +05:30
Manav Rathi
587da41f53 Dup 2025-02-21 11:56:23 +05:30
Manav Rathi
3214031a0c Reuse 2025-02-21 11:56:23 +05:30
Manav Rathi
0b1eee6c8e select all by date is the culprit
for 100k
t0: 291.615234375 ms
t1: 4.695068359375 ms
t2: 5.75 ms
t3: 5.9921875 ms
t4: 0.014892578125 ms
t5: 0.794921875 ms
t6: 0.0029296875 ms
2025-02-21 10:54:25 +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
Neeraj Gupta
898658f0ef [mob] Show memberLimit and usage when familyMember limit is set 2025-02-20 17:39:21 +05:30
Prateek Sunal
7743a4af98 chore: move date related utils to date_time_util.dart 2025-02-20 17:35:07 +05:30
Prateek Sunal
d2764fe7e1 chore: delete redundant files 2025-02-20 17:33:13 +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
Prateek Sunal
82df23a3b2 fix: bump build number 2025-02-20 15:24:09 +05:30
Prateek Sunal
00028e3a10 fix: queue addition logic 2025-02-20 15:23:53 +05:30
Prateek Sunal
c5dab37dfa feat: use media kit directly for preview, instead of video_player proxy 2025-02-20 15:23:31 +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
Prateek Sunal
c37deecb96 chore: extract out some functions 2025-02-20 13:55:37 +05:30
Prateek Sunal
d2a4634f02 fix: callbacks 2025-02-20 13:55:22 +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
Prateek Sunal
2275a47438 chore: bump deps changes 2025-02-20 13:14:47 +05:30
Prateek Sunal
80ab0a308f chore: bump locks 2025-02-20 13:14:32 +05:30
Prateek Sunal
ddb867d21f fix: correct sources 2025-02-20 13:14:21 +05:30
Prateek Sunal
c63cf362b6 Merge remote-tracking branch 'origin/main' into streaming-patched 2025-02-20 13:12:57 +05:30
Manav Rathi
270a628478 Use 2025-02-20 13:09:53 +05:30
Prateek Sunal
3f358b9511 fix: run preview creation after sync fd status 2025-02-20 13:08:56 +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
c480dd71f1 [mob] Parse family member storage limit 2025-02-20 12:06:22 +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
347 changed files with 6326 additions and 7016 deletions

View File

@@ -40,7 +40,7 @@ jobs:
- name: Build PlayStore AAB
run: |
flutter build appbundle --release --flavor playstore --dart-define=app.flavor=playstore
flutter build appbundle --dart-define=cronetHttpNoPlay=true --release --flavor playstore
env:
SIGNING_KEY_PATH: "/home/runner/work/_temp/keystore/ente_auth_key.jks"
SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS }}

View File

@@ -68,7 +68,7 @@ jobs:
- name: Build independent APK
run: |
flutter build apk --release --flavor independent --dart-define=app.flavor=independent
flutter build apk --dart-define=cronetHttpNoPlay=true --release --flavor independent
mv build/app/outputs/flutter-apk/app-independent-release.apk artifacts/ente-${{ github.ref_name }}.apk
env:
SIGNING_KEY_PATH: "/home/runner/work/_temp/keystore/ente_auth_key.jks"

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

@@ -8,6 +8,7 @@ import 'package:ente_auth/utils/package_info_util.dart';
import 'package:ente_auth/utils/platform_util.dart';
import 'package:fk_user_agent/fk_user_agent.dart';
import 'package:flutter/foundation.dart';
import 'package:native_dio_adapter/native_dio_adapter.dart';
import 'package:uuid/uuid.dart';
int kConnectTimeout = 15000;
@@ -50,6 +51,10 @@ class Network {
},
),
);
_dio.httpClientAdapter = NativeAdapter();
_enteDio.httpClientAdapter = NativeAdapter();
_setupInterceptors(endpoint);
Bus.instance.on<EndpointUpdatedEvent>().listen((event) {

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

@@ -147,6 +147,7 @@
"leaveFamily": "Familie verlassen",
"leaveFamilyMessage": "Sind Sie sicher, dass Sie den Familien-Plan verlassen wollen?",
"inFamilyPlanMessage": "Sie haben einen Familien-Plan!",
"hintForDesktop": "Klicken Sie mit der rechten Maustaste auf einen Code zum Bearbeiten oder Entfernen.",
"scan": "Scannen",
"scanACode": "Scan einen Code",
"verify": "Überprüfen Sie",
@@ -156,6 +157,7 @@
"twoFactorAuthTitle": "Zwei-Faktor-Authentifizierung",
"passkeyAuthTitle": "Passkey Authentifizierung",
"verifyPasskey": "Passkey verifizieren",
"loginWithTOTP": "Mit TOTP anmelden",
"recoverAccount": "Konto wiederherstellen",
"enterRecoveryKeyHint": "Geben Sie Ihren Wiederherstellungsschlüssel ein",
"recover": "Wiederherstellen",
@@ -258,6 +260,8 @@
"yesLogout": "Ja ausloggen",
"exit": "Schließen",
"theme": "Theme",
"lightTheme": "Hell",
"darkTheme": "Dunkel",
"systemTheme": "System",
"verifyingRecoveryKey": "Verifiziere Wiederherstellungsschlüssel...",
"recoveryKeyVerified": "Wiederherstellungsschlüssel verifiziert",
@@ -330,6 +334,9 @@
}
},
"manualSort": "Benutzerdefiniert",
"editOrder": "Reihenfolge bearbeiten",
"mostFrequentlyUsed": "Häufig verwendet",
"mostRecentlyUsed": "Zuletzt verwendet",
"activeSessions": "Aktive Sitzungen",
"somethingWentWrongPleaseTryAgain": "Ein Fehler ist aufgetreten, bitte versuche es erneut",
"thisWillLogYouOutOfThisDevice": "Dadurch wirst du von diesem Gerät abgemeldet!",
@@ -449,6 +456,7 @@
"customEndpoint": "Mit {endpoint} verbunden",
"pinText": "Anpinnen",
"unpinText": "Lösen",
"pinned": "Angeheftet",
"tags": "Tags",
"createNewTag": "Neuen Tag erstellen",
"tag": "Tag",
@@ -484,8 +492,16 @@
"importFailureDescNew": "Die ausgewählte Datei konnte nicht verarbeitet werden.",
"appLockNotEnabled": "App-Sperre nicht aktiviert",
"appLockNotEnabledDescription": "Bitte aktivieren Sie die App-Sperre über Security > App-Sperre",
"authToViewPasskey": "Bitte authentifizieren, um deinen Passkey zu sehen",
"duplicateCodes": "Doppelte Codes",
"noDuplicates": "✨ Keine Duplikate",
"youveNoDuplicateCodesThatCanBeCleared": "Sie haben keine doppelten Codes, die gelöscht werden können",
"deselectAll": "Alle abwählen",
"selectAll": "Alles auswählen"
"selectAll": "Alles auswählen",
"deleteDuplicates": "Duplikate löschen",
"plainHTML": "Reines HTML",
"tellUsWhatYouThink": "Sagen Sie uns, was Sie denken",
"dropReview": "Eine Bewertung im App/Play Store ablegen",
"giveUsAStarOnGithub": "Gib uns einen Stern auf Github",
"loginWithAuthAccount": "Mit Ihrem Auth Account anmelden"
}

View File

@@ -505,5 +505,10 @@
"selectAll": "Pasirinkti viską",
"deleteDuplicates": "Ištrinti dublikatus",
"plainHTML": "Grynasis HTML",
"tellUsWhatYouThink": "Pasakykite mums, ką manote",
"giveUsAStarOnGithub": "Suteikite mums žvaigždutę platformoje „Github“",
"free5GB": "5 GB nemokami programai „<bold-green>ente</bold-green>“ nuotraukos",
"loginWithAuthAccount": "Prisijungti su jūsų „Auth“ paskyra",
"freeStorageOffer": "10 % nuolaida programai „<bold-green>ente</bold-green>“ nuotraukos",
"freeStorageOfferDescription": "Naudokite kodą „AUTH“, kad gautumėte 10 % nuolaida pirmiesiems metams. "
}

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

@@ -113,7 +113,7 @@ class _LoginPasswordVerificationPageState
);
} else {
_logger.severe('API failure during SRP login', e, s);
if (e.type == DioExceptionType.unknown) {
if (e.type == DioExceptionType.connectionError) {
await _showContactSupportDialog(
context,
context.l10n.noInternetConnection,

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

@@ -113,12 +113,12 @@ String parseErrorForUI(
if (dioError.response?.data["code"] != null) {
errorInfo = "Reason: ${dioError.response!.data["code"]}";
} else {
errorInfo = "Reason: ${dioError.response!.data}";
errorInfo = "Reason: ${dioError.response!.data.toString()}";
}
} else if (dioError.type == DioExceptionType.unknown) {
errorInfo = "Reason: $dioError.error";
} else if (dioError.type == DioExceptionType.badCertificate) {
errorInfo = "Reason: ${dioError.error.toString()}";
} else {
errorInfo = "Reason: $dioError.type";
errorInfo = "Reason: ${dioError.type.toString()}";
}
} else {
if (kDebugMode) {

View File

@@ -250,10 +250,10 @@ packages:
dependency: "direct main"
description:
name: collection
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf
url: "https://pub.dev"
source: hosted
version: "1.18.0"
version: "1.19.0"
confetti:
dependency: "direct main"
description:
@@ -286,6 +286,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.1"
cronet_http:
dependency: transitive
description:
name: cronet_http
sha256: "3af9c4d57bf07ef4b307e77b22be4ad61bea19ee6ff65e62184863f3a09f1415"
url: "https://pub.dev"
source: hosted
version: "1.3.2"
cross_file:
dependency: transitive
description:
@@ -310,6 +318,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.0"
cupertino_http:
dependency: transitive
description:
name: cupertino_http
sha256: "6fcf79586ad872ddcd6004d55c8c2aab3cdf0337436e8f99837b1b6c30665d0c"
url: "https://pub.dev"
source: hosted
version: "2.0.2"
dart_style:
dependency: transitive
description:
@@ -346,10 +362,10 @@ packages:
dependency: "direct main"
description:
name: dio
sha256: "5598aa796bbf4699afd5c67c0f5f6e2ed542afc956884b9cd58c306966efc260"
sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9"
url: "https://pub.dev"
source: hosted
version: "5.7.0"
version: "5.8.0+1"
dio_web_adapter:
dependency: transitive
description:
@@ -861,6 +877,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.0.2"
http_profile:
dependency: transitive
description:
name: http_profile
sha256: "7e679e355b09aaee2ab5010915c932cce3f2d1c11c3b2dc177891687014ffa78"
url: "https://pub.dev"
source: hosted
version: "0.1.0"
image:
dependency: transitive
description:
@@ -885,6 +909,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.4"
jni:
dependency: transitive
description:
name: jni
sha256: f377c585ea9c08d48b427dc2e03780af2889d1bb094440da853c6883c1acba4b
url: "https://pub.dev"
source: hosted
version: "0.10.1"
js:
dependency: transitive
description:
@@ -1061,6 +1093,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.2"
native_dio_adapter:
dependency: "direct main"
description:
name: native_dio_adapter
sha256: "7420bc9517b2abe09810199a19924617b45690a44ecfb0616ac9babc11875c03"
url: "https://pub.dev"
source: hosted
version: "1.4.0"
nested:
dependency: transitive
description:
@@ -1077,6 +1117,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.5.0"
objective_c:
dependency: transitive
description:
name: objective_c
sha256: "62e79ab8c3ed6f6a340ea50dd48d65898f5d70425d404f0d99411f6e56e04584"
url: "https://pub.dev"
source: hosted
version: "4.1.0"
otp:
dependency: "direct main"
description:

View File

@@ -21,7 +21,7 @@ dependencies:
connectivity_plus: ^6.0.5
convert: ^3.1.1
device_info_plus: ^9.1.1
dio: ^5.4.0
dio: ^5.8.0+1
dotted_border: ^2.0.0+2
dropdown_button2: ^2.3.9
email_validator: ^3.0.0
@@ -72,6 +72,7 @@ dependencies:
logging: ^1.0.1
modal_bottom_sheet: ^3.0.0
move_to_background: ^1.0.2
native_dio_adapter: ^1.4.0
otp: ^3.1.1
package_info_plus: ^8.0.2
password_strength: ^0.2.0

View File

@@ -2,6 +2,9 @@
## v1.7.10 (Unreleased)
- Speed up selection for large libraries.
- Support Japanese translations.
- Fix video thumbnail generation on drag and drop.
- .
## v1.7.9

View File

@@ -135,11 +135,11 @@ export const sidebar = [
link: "/photos/faq/hidden-and-archive",
},
{
text: "Machine Learning",
link: "/photos/faq/machine-learning",
text: "Face recognition",
link: "/photos/faq/face-recognition",
},
{
text: "Video Streaming",
text: "Video streaming",
link: "/photos/faq/video-streaming",
},
],

View File

@@ -1,10 +1,10 @@
---
title: Machine Learning FAQ
title: Face recognition
description:
Frequently asked questions about several features of Ente's ML suite
Frequently asked questions about Ente's face recognition
---
# Machine Learning
# Face recognition
## Can I merge or de-merge persons recognized by the app?
@@ -19,7 +19,7 @@ instead of typing the name again, tap on the already given name that should now
be listed.
De-merging a certain grouping can be done by going to the person, pressing
`review suggestions` and then the top right `history icon`. Now press on the
`Review suggestions` and then the top right `History icon`. Now press on the
`minus icon` beside the group you want to de-merge.
### Desktop
@@ -29,6 +29,16 @@ selecting an existing person, and use the "Review suggestions" sheet to de-merge
previously merged persons (click the top right history icon on the suggestion
sheet to see the previous merges, and if necessary, undo them).
## How can I remove an incorrectly grouped face from a person?
On our mobile app, open up the person from the People section, click on the
three dots to open up overflow menu, and click on Edit. Now you will be
presented with the list of all photos that were merged to create this person.
You can click on the merged photos and select the photos you think are
incorrectly grouped (by long-pressing on them) and select "Remove" from the
action bar that pops up to remove any incorrect faces.
## How do I change the cover for a recognized person?
### Mobile

View File

@@ -1,12 +1,11 @@
---
title: Metadata
description: Handling of metadata, in particular creation dates, in Ente Photos
description: Handling of metadata in Ente Photos
---
# Metadata
This document describes Ente's handling of metadata, in particular photo
creation dates.
This document describes Ente's handling of metadata
## Import
@@ -46,7 +45,7 @@ importing that folder into Ente**. This way, we will be able to always correctly
map, for example, `flower.jpeg` and `flower.json` and show the same date for
`flower.jpeg` that you would've seen within Google Photos.
### Screenshots
### File name
In case the photo does not have a date in the Exif data (and it is not a Google
takeout), for example, for screenshots or Whatsapp forwards, Ente will still try
@@ -57,6 +56,28 @@ and deduce the correct date for the file from the name of the file.
> This process works great most of the time, but it is inherently based on
> heuristics and is not exact.
If we are unable to decipher the creation time from these 3 sources, we will set
the upload time as the photo's creation time.
## Modifications
Ente supports modifications to the following metadata:
- File name
- Date & time
- Location
The first two options are available on both mobile and desktop, while the
ability to update location is only available within our mobile apps.
### Bulk modifications
You can bulk-edit creation time of photos from our desktop app, by
multi-selecting items and selecting the "Fix time" option from the action bar.
You can bulk-edit location coordinates of photos from our mobile app, by
multi-selecting items and selecting the "Edit location" option from the action
bar.
## Export
Ente guarantees that you will get back the _exact_ same original photos and

View File

@@ -47,6 +47,9 @@ availability and durability. Our
[reliability document](https://ente.io/reliability) provides in-depth
information about our storage infrastructure and data replication strategies.
In short, we store 3 copies of your data, across 3 different providers, in 3
different countries. One of them is in an underground fall-out shelter in Paris.
### How does Ente's encryption compare to industry standards?
Our encryption model goes beyond industry standards. While many services use

View File

@@ -157,6 +157,21 @@ The same applies to monthly plans.
If you prefer to have this credit refunded to your original payment method,
please contact support@ente.io, and we'll assist you.
## How can I update my payment method?
You can view and manage your payment method by clicking on the green
subscription card within the Ente app, and selecting the "Manage payment method"
button.
You will be able to see all of your previous invoices, with details regarding
their payment status. In case of failed payments, you will also have an option
to retry those charges.
## How can I cancel my subscription?
You can cancel your subscription by clicking on the green subscription card
within the Ente app, and selecting the "Cancel subscription" button.
## Is there an x GB plan?
We have experimented quite a bit and have found it hard to design a single

View File

@@ -1,10 +1,10 @@
---
title: Video Streaming FAQ
title: Video streaming FAQ
description:
Frequently asked questions about Ente's Video Streaming feature
Frequently asked questions about Ente's video streaming feature
---
# Video Streaming
# Video streaming
> [!NOTE]
>
@@ -31,8 +31,8 @@ You can open these videos by tapping on them.
Processed videos will show a `Play stream` button, clicking which will load and
play the stream.
Clicking on the `Info` icon within the original video will show details
about the generated stream.
Clicking on the `Info` icon within the original video will show details about
the generated stream.
### What is a stream?

View File

@@ -50,5 +50,5 @@ end-to-end encrypted security that we use for syncing your photos.
Note that the desktop app does not currently support modifying the face
groupings, that is only supported by the mobile app.
For more information on how to use Machine Learning please check out
[the FAQ](../faq/machine-learning).
For more information on how to use Machine Learning for face recognition please
check out [the FAQ](../faq/face-recognition).

View File

@@ -10,3 +10,9 @@ automatically deleted from Trash after 30 days. You can manaully select photos
to permanently delete or completely empty the trash if you wish.
Items in trash are included in your used storage calculation.
## Recovery
If you have deleted items accidentally, you can recover them from Trash by
selecting these items, and clicking the "Restore" button on the action bar that
pops up.

View File

@@ -144,8 +144,6 @@ PODS:
- Flutter
- media_kit_libs_ios_video (1.0.4):
- Flutter
- media_kit_native_event_loop (1.0.0):
- Flutter
- media_kit_video (0.0.1):
- Flutter
- motion_sensors (0.0.1):
@@ -189,8 +187,6 @@ PODS:
- PromisesObjC (2.4.0)
- receive_sharing_intent (1.8.0):
- Flutter
- screen_brightness_ios (0.1.0):
- Flutter
- SDWebImage (5.20.0):
- SDWebImage/Core (= 5.20.0)
- SDWebImage/Core (5.20.0)
@@ -278,7 +274,6 @@ DEPENDENCIES:
- maps_launcher (from `.symlinks/plugins/maps_launcher/ios`)
- media_extension (from `.symlinks/plugins/media_extension/ios`)
- media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`)
- media_kit_native_event_loop (from `.symlinks/plugins/media_kit_native_event_loop/ios`)
- media_kit_video (from `.symlinks/plugins/media_kit_video/ios`)
- motion_sensors (from `.symlinks/plugins/motion_sensors/ios`)
- motionphoto (from `.symlinks/plugins/motionphoto/ios`)
@@ -293,7 +288,6 @@ DEPENDENCIES:
- photo_manager (from `.symlinks/plugins/photo_manager/ios`)
- privacy_screen (from `.symlinks/plugins/privacy_screen/ios`)
- receive_sharing_intent (from `.symlinks/plugins/receive_sharing_intent/ios`)
- screen_brightness_ios (from `.symlinks/plugins/screen_brightness_ios/ios`)
- sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
@@ -390,8 +384,6 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/media_extension/ios"
media_kit_libs_ios_video:
:path: ".symlinks/plugins/media_kit_libs_ios_video/ios"
media_kit_native_event_loop:
:path: ".symlinks/plugins/media_kit_native_event_loop/ios"
media_kit_video:
:path: ".symlinks/plugins/media_kit_video/ios"
motion_sensors:
@@ -420,8 +412,6 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/privacy_screen/ios"
receive_sharing_intent:
:path: ".symlinks/plugins/receive_sharing_intent/ios"
screen_brightness_ios:
:path: ".symlinks/plugins/screen_brightness_ios/ios"
sentry_flutter:
:path: ".symlinks/plugins/sentry_flutter/ios"
share_plus:
@@ -450,84 +440,82 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/wakelock_plus/ios"
SPEC CHECKSUMS:
background_fetch: 94b36ee293e82972852dba8ede1fbcd3bd3d9d57
battery_info: a06b00c06a39bc94c92beebf600f1810cb6c8c87
connectivity_plus: 3f6c9057f4cd64198dc826edfb0542892f825343
cupertino_http: 94ac07f5ff090b8effa6c5e2c47871d48ab7c86c
dart_ui_isolate: 46f6714abe6891313267153ef6f9748d8ecfcab1
device_info_plus: 335f3ce08d2e174b9fdc3db3db0f4e3b1f66bd89
background_fetch: 39f11371c0dce04b001c4bfd5e782bcccb0a85e2
battery_info: 09f5c9ee65394f2291c8c6227bedff345b8a730c
connectivity_plus: ddd7f30999e1faaef5967c23d5b6d503d10434db
cupertino_http: 947a233f40cfea55167a49f2facc18434ea117ba
dart_ui_isolate: d5bcda83ca4b04f129d70eb90110b7a567aece14
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
ffmpeg-kit-ios-full-gpl: 80adc341962e55ef709e36baa8ed9a70cf4ea62b
ffmpeg_kit_flutter_full_gpl: ce18b888487c05c46ed252cd2e7956812f2e3bd1
file_saver: 6cdbcddd690cb02b0c1a0c225b37cd805c2bf8b6
ffmpeg_kit_flutter_full_gpl: 8d15c14c0c3aba616fac04fe44b3d27d02e3c330
file_saver: 503e386464dbe118f630e17b4c2e1190fa0cf808
Firebase: 98e6bf5278170668a7983e12971a66b2cd57fc8c
firebase_core: 085320ddfaacb80d1a96eac3a87857afcc150db1
firebase_messaging: d398edc15fe825f832836e74f6ac61e8cd2f3ad3
firebase_core: 2bedc3136ec7c7b8561c6123ed0239387b53f2af
firebase_messaging: 15d114e1a41fc31e4fbabcd48d765a19eec94a38
FirebaseCore: a282032ae9295c795714ded2ec9c522fc237f8da
FirebaseCoreInternal: d98ab91e2d80a56d7b246856a8885443b302c0c2
FirebaseInstallations: 6ef4a1c7eb2a61ee1f74727d7f6ce2e72acf1414
FirebaseMessaging: c9ec7b90c399c7a6100297e9d16f8a27fc7f7152
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_email_sender: cd533cdc7ea5eda6fabb2c7f78521c71207778a4
flutter_image_compress: 4b058288a81f76e5e80340af37c709abafff34c4
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
flutter_local_notifications: ad39620c743ea4c15127860f4b5641649a988100
flutter_native_splash: 35ddbc7228eafcb3969dcc5f1fbbe27c1145a4f0
flutter_secure_storage: 2c2ff13db9e0a5647389bff88b0ecac56e3f3418
flutter_sodium: 152647449ba89a157fd48d7e293dcd6d29c6ab0e
fluttertoast: 76fea30fcf04176325f6864c87306927bd7d2038
flutter_email_sender: 02d7443217d8c41483223627972bfdc09f74276b
flutter_image_compress: 5a5e9aee05b6553048b8df1c3bc456d0afaac433
flutter_inappwebview_ios: 6f63631e2c62a7c350263b13fa5427aedefe81d4
flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086
flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
flutter_sodium: c84426b4de738514b5b66cfdeb8a06634e72fe0b
fluttertoast: e9a18c7be5413da53898f660530c56f35edfba9c
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
home_widget: f169fc41fd807b4d46ab6615dc44d62adbf9f64f
image_editor_common: 3de87e7c4804f4ae24c8f8a998362b98c105cac1
image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
in_app_purchase_storekit: e126ef1b89e4a9fdf07e28f005f82632b4609437
integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e
home_widget: 0434835a4c9a75704264feff6be17ea40e0f0d57
image_editor_common: d6f6644ae4a6de80481e89fe6d0a8c49e30b4b43
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
in_app_purchase_storekit: 8c3b0b3eb1b0f04efbff401c3de6266d4258d433
integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573
libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009
local_auth_darwin: 553ce4f9b16d3fdfeafce9cf042e7c9f77c1c391
local_auth_ios: f7a1841beef3151d140a967c2e46f30637cdf451
local_auth_darwin: 66e40372f1c29f383a314c738c7446e2f7fdadc3
local_auth_ios: 5046a18c018dd973247a0564496c8898dbb5adf9
Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
maps_launcher: edf829809ba9e894d70e569bab11c16352dedb45
media_extension: a1fec16ee9c8241a6aef9613578ebf097d6c5e64
media_kit_libs_ios_video: 5a18affdb97d1f5d466dc79988b13eff6c5e2854
media_kit_native_event_loop: 5fba1a849a6c87a34985f1e178a0de5bd444a0cf
media_kit_video: 1746e198cb697d1ffb734b1d05ec429d1fcd1474
motion_sensors: 741e702c17467b9569a92165dda8d4d88c6167f1
motionphoto: 584b43031ead3060225cdff08fa49818879801d2
move_to_background: 155f7bfbd34d43ad847cb630d2d2d87c17199710
maps_launcher: 2e5b6a2d664ec6c27f82ffa81b74228d770ab203
media_extension: 6d30dc1431ebaa63f43c397c37917b1a0a597a4c
media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1
media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e
motion_sensors: 03f55b7c637a7e365a0b5f9697a449f9059d5d91
motionphoto: d4a432b8c8f22fb3ad966258597c0103c9c5ff16
move_to_background: 39a5b79b26d577b0372cbe8a8c55e7aa9fcd3a2d
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
native_video_player: b65c58951ede2f93d103a25366bdebca95081265
objective_c: 89e720c30d716b036faf9c9684022048eee1eee2
onnxruntime: f9b296392c96c42882be020a59dbeac6310d81b2
native_video_player: d12af78a1a4a8cf09775a5177d5b392def6fd23c
objective_c: 77e887b5ba1827970907e10e832eec1683f3431d
onnxruntime: e7c2ae44385191eaad5ae64c935a72debaddc997
onnxruntime-c: a909204639a1f035f575127ac406f781ac797c9c
onnxruntime-objc: b6fab0f1787aa6f7190c2013f03037df4718bd8b
open_mail_app: 06d5a4162866388a92b1df3deb96e56be20cf45c
open_mail_app: 794172f6a22cd16319d3ddaf45e945b2f74952b0
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
photo_manager: d2fbcc0f2d82458700ee6256a15018210a81d413
privacy_screen: 3159a541f5d3a31bea916cfd4e58f9dc722b3fd4
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
photo_manager: ff695c7a1dd5bc379974953a2b5c0a293f7c4c8a
privacy_screen: 1a131c052ceb3c3659934b003b0d397c2381a24e
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
receive_sharing_intent: f6a12b7e8f7ed745f61c982de8a65de88db44a44
screen_brightness_ios: 5ed898fa50fa82a26171c086ca5e28228f932576
receive_sharing_intent: df9c334dc9feadcbd3266e5cb49c8443405e1c9f
SDWebImage: 73c6079366fea25fa4bb9640d5fb58f0893facd8
SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380
Sentry: f8374b5415bc38dfb5645941b3ae31230fbeae57
sentry_flutter: 0a211008f52553ba5dd81ceb71f48d78f0f1f6ab
share_plus: 011d6fb4f9d2576b83179a3a5c5e323202cdabcf
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
sqflite_darwin: 44bb54cc302bff1fbe5752293aba1820b157cf1c
sentry_flutter: 0eb93e5279eb41e2392212afe1ccd2fecb4f8cbe
share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
sqflite_darwin: a553b1fd6fe66f53bbb0fe5b4f5bab93f08d7a13
sqlite3: 0bb0e6389d824e40296f531b858a2a0b71c0d2fb
sqlite3_flutter_libs: 9379996d65aa23dcda7585a5b58766cebe0aa042
system_info_plus: 555ce7047fbbf29154726db942ae785c29211740
sqlite3_flutter_libs: c00457ebd31e59fa6bb830380ddba24d44fbcd3b
system_info_plus: 5393c8da281d899950d751713575fbf91c7709aa
Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e
ua_client_hints: 0b48eae1134283f5b131ee0871fa878377f07a01
uni_links: ed8c961e47ed9ce42b6d91e1de8049e38a4b3152
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
video_player_avfoundation: 2cef49524dd1f16c5300b9cd6efd9611ce03639b
video_thumbnail: b637e0ad5f588ca9945f6e2c927f73a69a661140
volume_controller: ca1cde542ee70fad77d388f82e9616488110942b
wakelock_plus: 04623e3f525556020ebd4034310f20fe7fda8b49
ua_client_hints: 46bb5817a868f9e397c0ba7e3f2f5c5d90c35156
uni_links: d97da20c7701486ba192624d99bffaaffcfc298a
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3
video_thumbnail: c4e2a3c539e247d4de13cd545344fd2d26ffafd1
volume_controller: 2e3de73d6e7e81a0067310d17fb70f2f86d71ac7
wakelock_plus: 373cfe59b235a6dd5837d0fb88791d2f13a90d56
PODFILE CHECKSUM: 20e086e6008977d43a3d40260f3f9bffcac748dd

View File

@@ -315,7 +315,6 @@
"${BUILT_PRODUCTS_DIR}/maps_launcher/maps_launcher.framework",
"${BUILT_PRODUCTS_DIR}/media_extension/media_extension.framework",
"${BUILT_PRODUCTS_DIR}/media_kit_libs_ios_video/media_kit_libs_ios_video.framework",
"${BUILT_PRODUCTS_DIR}/media_kit_native_event_loop/media_kit_native_event_loop.framework",
"${BUILT_PRODUCTS_DIR}/media_kit_video/media_kit_video.framework",
"${BUILT_PRODUCTS_DIR}/motion_sensors/motion_sensors.framework",
"${BUILT_PRODUCTS_DIR}/motionphoto/motionphoto.framework",
@@ -329,7 +328,6 @@
"${BUILT_PRODUCTS_DIR}/photo_manager/photo_manager.framework",
"${BUILT_PRODUCTS_DIR}/privacy_screen/privacy_screen.framework",
"${BUILT_PRODUCTS_DIR}/receive_sharing_intent/receive_sharing_intent.framework",
"${BUILT_PRODUCTS_DIR}/screen_brightness_ios/screen_brightness_ios.framework",
"${BUILT_PRODUCTS_DIR}/sentry_flutter/sentry_flutter.framework",
"${BUILT_PRODUCTS_DIR}/share_plus/share_plus.framework",
"${BUILT_PRODUCTS_DIR}/shared_preferences_foundation/shared_preferences_foundation.framework",
@@ -412,7 +410,6 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/maps_launcher.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/media_extension.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/media_kit_libs_ios_video.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/media_kit_native_event_loop.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/media_kit_video.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/motion_sensors.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/motionphoto.framework",
@@ -426,7 +423,6 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/photo_manager.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/privacy_screen.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/receive_sharing_intent.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/screen_brightness_ios.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sentry_flutter.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/share_plus.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences_foundation.framework",

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,12 +48,10 @@ import "package:photos/services/sync_service.dart";
import "package:photos/services/user_service.dart";
import 'package:photos/ui/tools/app_lock.dart';
import 'package:photos/ui/tools/lock_screen.dart';
import 'package:photos/utils/crypto_util.dart';
import "package:photos/utils/email_util.dart";
import 'package:photos/utils/file_uploader.dart';
import "package:photos/utils/lock_screen_settings.dart";
import 'package:shared_preferences/shared_preferences.dart';
import "package:video_player_media_kit/video_player_media_kit.dart";
final _logger = Logger("main");
@@ -238,10 +237,6 @@ Future<void> _init(bool isBackground, {String via = ''}) async {
ServiceLocator.instance
.init(preferences, NetworkClient.instance.enteDio, packageInfo);
if (!isBackground && flagService.internalUser) {
VideoPlayerMediaKit.ensureInitialized(iOS: true);
}
_logger.info("UserService init $tlog");
await UserService.instance.init();
_logger.info("UserService init done $tlog");

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;
@@ -52,6 +51,10 @@ class UserDetails {
}
int getFreeStorage() {
final int? memberLimit = familyMemberStorageLimit();
if (memberLimit != null) {
return max(memberLimit - usage, 0);
}
return max(getTotalStorage() - getFamilyOrPersonalUsage(), 0);
}
@@ -62,6 +65,17 @@ class UserDetails {
storageBonus;
}
// return the member storage limit if user is part of family and the admin
// has set the storage limit for the user.
int? familyMemberStorageLimit() {
if (isPartOfFamily()) {
final FamilyMember? currentUserMember = familyData!.members!
.firstWhereOrNull((element) => element.email.trim() == email.trim());
return currentUserMember?.storageLimit;
}
return null;
}
// This is the total storage for which user has paid for.
int getPlanPlusAddonStorage() {
return (isPartOfFamily() ? familyData!.storage : subscription.storage) +
@@ -107,12 +121,14 @@ class FamilyMember {
final int usage;
final String id;
final bool isAdmin;
final int? storageLimit;
FamilyMember(
this.email,
this.usage,
this.id,
this.isAdmin,
this.storageLimit,
);
factory FamilyMember.fromMap(Map<String, dynamic> map) {
@@ -121,6 +137,7 @@ class FamilyMember {
map['usage'] as int,
map['id'] as String,
map['isAdmin'] as bool,
map['storageLimit'] as int?,
);
}
@@ -130,6 +147,7 @@ class FamilyMember {
'usage': usage,
'id': id,
'isAdmin': isAdmin,
'storageLimit': storageLimit,
};
}
@@ -189,6 +207,10 @@ class FamilyData {
return members!.map((e) => e.usage).toList().sum;
}
FamilyMember? getMemberByID(String id) {
return members!.firstWhereOrNull((element) => element.id == id);
}
static fromMap(Map<String, dynamic>? map) {
if (map == null) return null;
assert(map['members'] != null && map['members'].length >= 0);
@@ -215,19 +237,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;
@@ -58,10 +58,9 @@ class PreviewVideoStore {
void init(SharedPreferences prefs) {
_prefs = prefs;
Future.delayed(
const Duration(seconds: 10),
PreviewVideoStore.instance.putFilesForPreviewCreation,
);
FileDataService.instance.syncFDStatus().then(
(_) => _putFilesForPreviewCreation(),
);
}
late final SharedPreferences _prefs;
@@ -74,24 +73,27 @@ class PreviewVideoStore {
Future<void> setIsVideoStreamingEnabled(bool value) async {
final oneMonthBack = DateTime.now().subtract(const Duration(days: 30));
await _prefs.setBool(_videoStreamingEnabled, value);
await _prefs.setInt(
_videoStreamingCutoff,
oneMonthBack.millisecondsSinceEpoch,
);
_prefs.setBool(_videoStreamingEnabled, value).ignore();
_prefs
.setInt(
_videoStreamingCutoff,
oneMonthBack.millisecondsSinceEpoch,
)
.ignore();
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 +113,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 +158,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 +173,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 +211,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 +228,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 +244,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 +255,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 +285,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 +308,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 +324,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 +335,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 +349,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 +561,7 @@ class PreviewVideoStore {
final previewURL = response2.data["url"];
if (objectKey != null) {
unawaited(
downloadAndCacheVideo(
_downloadAndCacheVideo(
previewURL,
_getVideoPreviewKey(objectKey),
),
@@ -549,7 +590,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 +612,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,49 +656,37 @@ 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
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;
}
}
// set all video status to in queue
final n = allFiles.length;
for (int i = 0; i < n; i++) {
final enteFile = allFiles[i];
// elimination case for <=10 MB with H.264
final (_, result, _) = await _checkFileForPreviewCreation(enteFile);
if (result) {
allFiles.removeAt(i);
} else {
_items[enteFile.uploadedFileID!] = PreviewItem(
status: PreviewItemStatus.inQueue,
file: enteFile,
collectionID: enteFile.collectionID ?? 0,
);
}
_items[enteFile.uploadedFileID!] = PreviewItem(
status: PreviewItemStatus.inQueue,
file: enteFile,
collectionID: enteFile.collectionID ?? 0,
);
}
Bus.instance.fire(PreviewUpdatedEvent(_items));
final file = allFiles.first;
allFiles.remove(file);
this.files.addAll(allFiles);
_logger.info("[init] Processing ${allFiles.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() =>

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