Compare commits

...

359 Commits

Author SHA1 Message Date
Neeraj Gupta
6146350aae [docs] Update custom server section for cli 2024-03-13 12:02:36 +05:30
Vishnu Mohandas
f8d8550b10 v0.8.68 (#1076) 2024-03-13 11:47:47 +05:30
vishnukvmd
bc4fa44edd v0.8.68 2024-03-13 11:47:25 +05:30
Vishnu Mohandas
92de88e778 [photos] Update flow for Independent APK (#1074) 2024-03-13 11:46:04 +05:30
Vishnu Mohandas
7814cbcc91 Remove unused code for app-updates (#1072) 2024-03-13 11:45:53 +05:30
Ashil
518b947808 [mobile][photos] Make scrollbar in log file viewer interactive (#1075) 2024-03-13 11:43:31 +05:30
Neeraj Gupta
077ba04664 [cli-release.yml] Pass release version in build flag 2024-03-13 11:43:19 +05:30
Neeraj Gupta
e42422407c [cli] Pick version from the github tag 2024-03-13 11:43:19 +05:30
Neeraj Gupta
2711a227fc [cli] Update gen docs + add docs/selfhost.md 2024-03-13 11:43:19 +05:30
Neeraj Gupta
4325de6fde [mob] Remove .env.example 2024-03-13 11:43:19 +05:30
Neeraj Gupta
c7d7d436c3 [docs] Add guide to connect CLI to self-hosted instance 2024-03-13 11:43:19 +05:30
Neeraj Gupta
f7077c2b11 [docs] Update guide to build mobile app 2024-03-13 11:43:19 +05:30
vishnukvmd
8f525cb88d Update Github Action for Photos 2024-03-13 11:27:24 +05:30
vishnukvmd
be3b4dc7ba Open the link to Github APK instead of downloading it in-app 2024-03-13 11:21:36 +05:30
Ashil
0c1c0ad400 [mobile][photos] Home widget UI tweaks (#1060) 2024-03-13 11:20:50 +05:30
Ashil
773f4cdca2 [mobile][photos] Fix missing hero animation (#1064)
## Description

Sometimes, when opening an image from gallery, the hero animation fails
to happen. This PR fixes this issue.



https://github.com/ente-io/ente/assets/77285023/2ee40ec8-58d0-4ca1-82fb-1be96581137b



## Tests

- [x] Didn't break hero animations of videos, it almost stays the same.
2024-03-13 11:20:20 +05:30
Manav Rathi
96bb79b9e9 [web] Allow running the build outside of a git repository (#1073)
This was found useful by @Bramas when building a Dockerfile of the web
app itself. See https://github.com/ente-io/ente/pull/1065.

Now, the GIT_SHA environment variable can just be undefined if we're not
in a git repository, and the code using it deals with that case
explicitly.

**Tested by**

Temporarily inverted the isDevBuild flag, then

1. Ran the build normally and verified that the SHA continued to appear
in the logs.

2. Ran the build after copying to a standalone folder without an
associated git repository and verified that the SHA was skipped without
causing the build to fail.
2024-03-13 11:19:44 +05:30
Manav Rathi
d5164693ff Fix vitepress warning
Change the syntax highlighting of the `env` code block from `env` to `sh`
because currently vite press doesn't support the env language and instead
complains

> The language 'env' is not loaded, falling back to 'txt' for syntax highlighting.
2024-03-13 11:17:53 +05:30
Manav Rathi
26b162c8dc [web] Allow running the build outside of a git repository
This was found useful by @Bramas when building a Dockerfile of the web app
itself. See https://github.com/ente-io/ente/pull/1065.

Now, the GIT_SHA environment variable can just be undefined if we're not in a
git repository, and the code using it deals with that case explicitly.

**Tested by**

Temporarily inverted the isDevBuild flag, tehn

1. Ran the build normally and verified that the SHA continued to appear in the logs.

2. Ran the build after copying to a standalone folder without an associated git
   repository and verified that the SHA was skipped without causing the build to
   fail.
2024-03-13 11:15:12 +05:30
Neeraj Gupta
297148dc67 [auth][mob] Add recovery support for passkey (#1013)
## Description

## Tests
  Verified that reset flow is working fine on both auth and photos app.
2024-03-13 11:12:30 +05:30
vishnukvmd
46522c329c Remove unused code for app-updates 2024-03-13 11:12:06 +05:30
Manav Rathi
8358eef34e [docs] Move the self hosting using external S3 buckets guide to independent page (#1070)
See: https://github.com/ente-io/ente/pull/1066
2024-03-13 10:44:12 +05:30
Manav Rathi
4326409046 [docs] Move the self hosting using external S3 buckets guide to independent page
See: https://github.com/ente-io/ente/pull/1066
2024-03-13 10:32:35 +05:30
Manav Rathi
687d575bf4 add guide for self-hosting server + webapp with external S3 storage (#1066)
## Description

I wrote a small guide to run the server and the web app using docker
compose (locally or on a server), with an external S3 storage (which I
assume will be a common use-case).

It requires #1065 , or at least this line change:
https://github.com/ente-io/ente/pull/1065/files#diff-5a9426639947f5afb92612a4583a5b7d496f9cb6a791db9c29f28ff298282aebR16
2024-03-13 09:55:24 +05:30
Neeraj Gupta
0678e3129a [cli] generated docs 2024-03-13 02:03:44 +05:30
Neeraj Gupta
b164b0df21 [cli] By default, update sub to high storage & expiry 2024-03-13 02:03:44 +05:30
Neeraj Gupta
0d38346722 [cli] Add admin API to bump up storage for free users 2024-03-13 02:03:44 +05:30
Neeraj Gupta
51d3238a52 [cli] Log query params in debug mode 2024-03-13 02:03:44 +05:30
Neeraj Gupta
ddd89aa1d1 [cli] Log query params in debug mode 2024-03-13 02:03:44 +05:30
Neeraj Gupta
f21a627a71 Add cli generated docs inside docs/generated 2024-03-13 02:03:44 +05:30
Neeraj Gupta
063e980280 [cli] Add command to get-token 2024-03-13 02:03:44 +05:30
Neeraj Gupta
d7d42b6854 [cli] Add example config file 2024-03-13 02:03:44 +05:30
Neeraj Gupta
260a7fbcaa [cli] Allow switching API host based on config 2024-03-13 02:03:44 +05:30
Quentin Bramas
55e0ec39ed add guide for self-hosting server + webapp with external S3 storage 2024-03-12 15:14:35 +01:00
Manav Rathi
9c04a7102b [desktop] Fix desktop dev builds - Part 1 (#1063)
- Main change here is removing a submodule and moving to the upstream
dependency
- Also updated to Prettier 3

The original issue, about yarn dev not working because of context
isolation, still remains. This PR prepares the ground, will have a go at
it in a subsequent PR.
2024-03-12 18:03:10 +05:30
Manav Rathi
a5e6f0cc30 Let Prettier 3 have a go at it 2024-03-12 18:01:09 +05:30
Manav Rathi
2322b41f67 Watch during dev 2024-03-12 17:56:46 +05:30
Manav Rathi
685e75d97d Switch to upstream 2024-03-12 17:38:39 +05:30
Manav Rathi
cde87716a1 Remove the custom next-electron-server
The only change this has in addition to next-electron-server seems to be
https://github.com/ente-io/next-electron-server/pull/1/files, will try to test
along the way to determine its impact.
2024-03-12 17:02:28 +05:30
Manav Rathi
dff0af3397 Fix path to main process entrypoint 2024-03-12 16:47:41 +05:30
Manav Rathi
ca771993ee [desktop] Fix desktop build (#1062)
`yarn dev` is still not working, but that is a previous issue unrelated
to the monorepo migration. Will fix that in a subsequent PR.
2024-03-12 16:38:06 +05:30
Manav Rathi
c8b9b4cd8f Document better 2024-03-12 16:33:43 +05:30
Manav Rathi
d7cd2cecbc Default buildResources is build 2024-03-12 16:12:53 +05:30
Manav Rathi
e219197e2f Fix import 2024-03-12 15:07:06 +05:30
Manav Rathi
3eb84ceba8 [web] New translations (#1058)
New translations from
[Crowdin](https://crowdin.com/project/ente-photos-web)
2024-03-12 14:22:28 +05:30
Manav Rathi
e358738c35 [workflows] Prevent duplicate runs of lint workflows after merging a PR (#1059) 2024-03-12 14:21:46 +05:30
Manav Rathi
b15901df67 [workflows] Prevent duplicate runs of lint workflows after merging a PR 2024-03-12 14:14:18 +05:30
Crowdin Bot
ee7d90e55b New Crowdin translations by GitHub Action 2024-03-12 08:38:32 +00:00
Manav Rathi
22b744aa96 [web] [desktop] Remove Sentry (#1057)
Sentry has a measurable impact on page load, a metric that I'm keen to
improve. Apparently by default it loses us 8-9 page speed points, though
that can be reduced to 3-4
(https://github.com/getsentry/sentry-javascript/issues/9179).

All of this is doable, but there are bigger tasks to deal with. This is
not to say that Sentry won't be useful again at some point, when we have
time to deal with it better. But right now, we discussed that it's just
better to remove Sentry instead of piling on to the sunk cost.
2024-03-12 14:07:29 +05:30
Manav Rathi
5d01931402 Retain -web suffix to allow disambiguating between main and renderer process 2024-03-12 14:06:42 +05:30
Manav Rathi
206ad46950 Remove CRASH_REPORTING key 2024-03-12 13:59:45 +05:30
Manav Rathi
9b6e47d291 [desktop] Remove sentry
See 70cddfdf0b
2024-03-12 13:54:17 +05:30
Manav Rathi
70cddfdf0b [web] Remove Sentry
Sentry has a measurable impact on page load, a metric that I'm keen to
improve. Apparently by default it loses us 8-9 page speed points, though that
can be reduced to 3-4
(https://github.com/getsentry/sentry-javascript/issues/9179).

All of this is doable, but there are bigger tasks to deal with. This is not to
say that Sentry won't be useful again at some point, when we have time to deal
with it better. But right now, we discussed that it's just better to remove
Sentry instead of piling on to the sunk cost.
2024-03-12 13:24:33 +05:30
Neeraj Gupta
c0a2347b80 [server] Refactor /user/plans 2024-03-12 12:17:04 +05:30
Manav Rathi
dd556f8f72 [server] Fix uploads on self-hosted Docker when running on Windows (#1056)
On our Discord @Degos ran into an issue where their uploads to the self
hosted docker compose cluster were failing. On debugging, it was found
that the line-endings in `server/scripts/compose/minio-provision.sh`
were set to CRLF, causing the script to fail when run inside Docker. The
minIO buckets never got provisioned, and so the uploads would fail with

    <Code>NoSuchBucket</Code>
    <Message>The specified bucket does not exist</Message>


![image](https://github.com/ente-io/ente/assets/24503581/37945ed3-3c52-4796-8092-47be6e4a85da)

To fix this, we add a new .gitattributes that enforces the LF for the
scripts that run inside Docker. To (perhaps too preemptively) guard
against similar issues in other scenarios, turn this on for all shell
scripts.

Refs:
-
https://stackoverflow.com/questions/29603737/bash-seamlessly-run-scripts-with-crlf-line-endings
-
https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings
2024-03-12 12:00:13 +05:30
Manav Rathi
2fc1a96c8b [server] Fix uploads on self-hosted Docker when running on Windows
On our Discord @Degos ran into an issue where their uploads to the self hosted
docker compose cluster were failing. On debugging, it was found that the
line-endings in `server/scripts/compose/minio-provision.sh` were set to CRLF,
causing the script to fail when run inside Docker. The minIO buckets never got
provisioned, and so the uploads would fail with

    <Code>NoSuchBucket</Code>
    <Message>The specified bucket does not exist</Message>

To fix this, we add a new .gitattributes that enforces the LF for the scripts
that run inside Docker. To (perhaps too preemptively) guard against similar
issues in other scenarios, turn this on for all shell scripts.

Refs:
- https://stackoverflow.com/questions/29603737/bash-seamlessly-run-scripts-with-crlf-line-endings
- https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings
2024-03-12 11:57:41 +05:30
Manav Rathi
8a7f64b889 [web] New translations (#1049)
New translations from
[Crowdin](https://crowdin.com/project/ente-photos-web)
2024-03-12 11:28:39 +05:30
Manav Rathi
c153b28ed6 [web] Support preview deployments (#1052)
Preview deployments can be made by manually triggering the "Preview
(web)" workflow. You can select the app you want to build
(accounts/auth/cast/photos) and the branch to take the code from. The
deployment will be available at https://preview.ente.sh.
2024-03-12 10:11:56 +05:30
Manav Rathi
a9557df240 Add preview deployment docs 2024-03-12 10:08:52 +05:30
Manav Rathi
8c23090abd Remove placeholders 2024-03-12 09:56:07 +05:30
Manav Rathi
6efe2cd5fd Add a push trigger to register the workflow
See https://stackoverflow.com/questions/63362126/github-actions-how-to-run-a-workflow-created-on-a-non-master-branch-from-the-wo
2024-03-12 09:53:58 +05:30
Manav Rathi
4feea01879 [web] Support preview deployments 2024-03-12 09:52:34 +05:30
Neeraj Gupta
51f19cf2fd [web] Fix lint error 2024-03-12 09:33:14 +05:30
Neeraj Gupta
dbc50760af [web][passkey] Whitelist *.ente.sh redirect urls 2024-03-12 09:33:14 +05:30
Neeraj Gupta
638de0a769 [authw] Handle passkey finish redirect 2024-03-12 09:33:14 +05:30
Neeraj Gupta
0ab1c0ee89 [mob] gitignore android/.settings 2024-03-12 09:33:14 +05:30
Neeraj Gupta
cb8738287a [photosw] Enable passkey for internal users 2024-03-12 09:33:14 +05:30
ashilkn
ddedaf2d0e bump up version to v0.8.67 2024-03-12 07:51:57 +05:30
Crowdin Bot
cfa4077b5c New Crowdin translations by GitHub Action 2024-03-12 01:36:05 +00:00
Prateek Sunal
1d2de8d9b8 [mobile][photos] Home Widget fixes (#992)
## Description

- Upscale default placeholder
- Keep image ready to be rendered in home widget before adding the home
widget.
- Change the photo in home widget every time it's clicked.
- Open favourites when home widget is clicked.
- Fix multiple issues

## Tests

Did a good amount of testing.

---------

Co-authored-by: ashilkn <ashilkn99@gmail.com>
Co-authored-by: Ashil <77285023+ashilkn@users.noreply.github.com>
2024-03-12 01:39:30 +05:30
Neeraj Gupta
e843ea6669 [server] Change param type from uuid to string 2024-03-11 22:45:53 +05:30
Neeraj Gupta
9f2a66e0ef Fix lint warnings 2024-03-11 22:45:22 +05:30
Neeraj Gupta
944ef2e564 [mob]Generate randomkey using crypto library 2024-03-11 22:34:00 +05:30
Manav Rathi
00f45ef39d [photosd] Fix desktop build - part 2 (#1044)
The build is still not fixed, but this is a step closer to where we want
to be.
2024-03-11 18:53:09 +05:30
Manav Rathi
84926cbee1 build take 2 2024-03-11 18:52:28 +05:30
Neeraj Gupta
27c1b66c08 [auth][mob] Add recovery support for passkey 2024-03-11 17:54:24 +05:30
Manav Rathi
027ae1cfb9 build => resources 2024-03-11 17:37:43 +05:30
Manav Rathi
621f81355b Work towards fixing build 2024-03-11 17:37:10 +05:30
Manav Rathi
849b61c5cf Document some and fix the path 2024-03-11 17:29:39 +05:30
Manav Rathi
267ad0d11f [desktop] Fix yarn build 2024-03-11 17:04:17 +05:30
Neeraj Gupta
e272722d6e [mob][photos] Add dialog to describe clean uncat option 2024-03-11 16:52:27 +05:30
Neeraj Gupta
a73f3cc52b [mob] Fixed sorting bug in uncategorized section 2024-03-11 16:52:27 +05:30
Manav Rathi
748d18cc2a [photosd] Format using Prettier (#1010)
This is part 1 of general fixes to get the Photos desktop app back up
and building after moving to this monorepo. The main change in this PR
is to reformat the source to match the prettier config for the other
JS/TS (web/, docs/) code. And a few other fixes.

It still doesn't build, and there are a few eslint errors too. Those
will come in a subsequent PR.
2024-03-11 16:49:20 +05:30
Manav Rathi
88741083fe Prettier 3 + reformat (same as web) 2024-03-11 16:46:52 +05:30
Manav Rathi
829406fa62 Tweak .gitignore 2024-03-11 16:38:32 +05:30
Manav Rathi
df13eac6ef Remove unused files 2024-03-11 16:35:00 +05:30
Manav Rathi
25dda3598c Remove husky and lint-staged 2024-03-11 16:31:48 +05:30
Neeraj Gupta
9a8e76b494 [docs] Fix the location for auth faq 2024-03-11 16:14:38 +05:30
Neeraj Gupta
c6120f33de [docs] Copy Auth FAQ from mobile app 2024-03-11 16:05:56 +05:30
Manav Rathi
200504dc01 [web] New translations (#1007)
New translations from
[Crowdin](https://crowdin.com/project/ente-photos-web)
2024-03-11 16:03:57 +05:30
Crowdin Bot
2a33707db2 New Crowdin translations by GitHub Action 2024-03-11 10:29:16 +00:00
Manav Rathi
3fd5af8134 [web] Various small internal improvements (#1006)
- to the web alias for shared albums during nightly deployments
- Remove duplicate favicon link
- Capitalize Ente
- Document translation
- Remove the unnecessary console warning
2024-03-11 15:58:16 +05:30
Neeraj Gupta
bb68b22adb [server] mark queue as deprecated 2024-03-11 15:41:18 +05:30
Neeraj Gupta
5accf4c6a8 [server] Remove dead code related to collecton trashV2 2024-03-11 15:41:18 +05:30
Neeraj Gupta
28335700e3 [server] Remove deprecated API 2024-03-11 15:41:18 +05:30
Manav Rathi
e3826695c5 Remove the unnecessary console warning 2024-03-11 15:31:46 +05:30
Manav Rathi
6fdfa24e89 Document translation 2024-03-11 15:30:24 +05:30
Vishnu Mohandas
fb2abd8afc [mobile][photos]Rediscovery tab fixes (#1003)
## Description

Changed,
1. Section title in the discovery page
2. Page title in the page that pops up on tapping the > next to the
header on the discovery page

for Location and Description section.
2024-03-11 15:24:33 +05:30
Manav Rathi
bd84b54c5a Capitalize Ente 2024-03-11 13:27:17 +05:30
Manav Rathi
af4eaac158 Remove duplicate favicon link 2024-03-11 13:26:15 +05:30
Manav Rathi
de166645b0 Point to the web alias for shared albums during nightly deployments 2024-03-11 13:20:58 +05:30
ashilkn
ae67f0d67b nit: change description section's heading from 'Photo descriptions' to 'Descriptions' 2024-03-11 13:06:07 +05:30
ashilkn
fab16a7947 nit: change location section's heading from 'Location' to 'Locations' 2024-03-11 12:59:59 +05:30
Neeraj Gupta
9711e0e29e Improve log 2024-03-11 12:34:03 +05:30
Neeraj Gupta
d7292dc670 [server] Release locks before initiating any other controller 2024-03-11 12:34:03 +05:30
Neeraj Gupta
6930aaf220 [server]Update freq for metadatacron 2024-03-11 12:34:03 +05:30
Neeraj Gupta
12903a3748 [server] Clean up collecitonDiff logs 2024-03-11 12:34:03 +05:30
Manav Rathi
412c872266 [web] Fix shared albums (#1001)
This flow probably got broken when the select-all-for-a-day
functionality got added in https://github.com/ente-io/ente/pull/674
(haven't dug into this, I'm just guessing since that's where this all
got touched).

Thank you to @Bramas on our Discord for pointing this out, and also
providing the fix:
https://github.com/ente-io/ente/compare/main...Bramas:ente:fix-error-on-shared-album

Tested by
---------

Run normal web app on one terminal

NEXT_PUBLIC_ENTE_ENDPOINT=http://localhost:8080
NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT=http://localhost:3002 yarn dev

Run the albums listener on another port (need to do this since we're
running on localhost, and need to bind to a different origin):

NEXT_PUBLIC_ENTE_ENDPOINT=http://localhost:8080
NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT=http://localhost:3002 yarn dev:albums

Create a shared album, copy its public link, and verify that

- Before the fix we were getting an error (trying to access properties
of `galleryContext.selectedFile`)
- After the fix the shared album is visible
2024-03-11 12:05:07 +05:30
Manav Rathi
8fab6b5e48 [web] Fix shared albums
This flow probably got broken when the select-all-for-a-day functionality got
added in https://github.com/ente-io/ente/pull/674 (haven't dug into this, I'm
just guessing since that's where this all got touched).

Thank you to @Bramas on our Discord for pointing this out, and also providing the fix:
https://github.com/ente-io/ente/compare/main...Bramas:ente:fix-error-on-shared-album

Tested by
---------

Run normal web app on one terminal

    NEXT_PUBLIC_ENTE_ENDPOINT=http://localhost:8080 NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT=http://localhost:3002 yarn dev

Run the albums listener on another port (need to do this since we're running on
localhost, and need to bind to a different origin):

    NEXT_PUBLIC_ENTE_ENDPOINT=http://localhost:8080 NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT=http://localhost:3002 yarn dev:albums

Create a shared album, copy its public link, and verify that

- Before the fix we were getting an error (trying to access properties of `galleryContext.selectedFile`)
- After the fix the shared album is visible
2024-03-11 12:00:26 +05:30
Neeraj Gupta
4af3030c81 [server] Speed up RemoveComplianceHolds 2024-03-11 11:43:02 +05:30
Neeraj Gupta
c32f0a28f1 [server] Release previous locks taken by host on statup 2024-03-11 11:43:02 +05:30
Manav Rathi
38e8f7c8d7 [web] New translations (#748)
New translations from
[Crowdin](https://crowdin.com/project/ente-photos-web)
2024-03-11 11:09:18 +05:30
Vishnu Mohandas
72aa597f85 Update custom-icons.json (#979)
## Description

After this commit https://github.com/ente-io/ente/pull/430 those 6 icons
I tried to add do not shows the icon or default letter, but icon is
filled with hexcolor, so I guess hexcolor is not needed.
2024-03-11 11:01:44 +05:30
Manav Rathi
f759ce07ae [docs] Add a workflow to do a preflight verification of build on each PR (#999)
Tested by opening this PR and verifying that the verify build workflow
succeeded. Say verify one more time, I dare you...
2024-03-11 11:01:38 +05:30
Manav Rathi
976a76ae23 [docs] Add a workflow to do a preflight verification of build on each PR 2024-03-11 10:59:47 +05:30
Neeraj Gupta
89d761a096 Reduce lock duration 2024-03-11 10:39:46 +05:30
Neeraj Gupta
e667eef951 Tweak speed for crons 2024-03-11 10:39:46 +05:30
Manav Rathi
192caedeb9 [docs] Fix build + run prettier (#997) 2024-03-11 10:32:21 +05:30
Manav Rathi
fc482609b6 Fix build 2024-03-11 10:31:42 +05:30
Manav Rathi
e1e0c45d88 yarn pretty 2024-03-11 10:31:07 +05:30
Manav Rathi
8f384247ba [docs] Add more self-hosting instructions (#996)
- How to set custom server
- How to build for mobile

/ping @ua741 @vishnukvmd
2024-03-11 10:29:07 +05:30
Manav Rathi
b0d396a5bd [docs] Add more self-hosting instructions
- How to set custom server
- How to build for mobile
2024-03-11 10:28:20 +05:30
Crowdin Bot
2354f5bc7e New Crowdin translations by GitHub Action 2024-03-11 01:36:36 +00:00
Vishnu Mohandas
73b4f54d42 [auth] v2.0.42 (#989) 2024-03-10 21:54:22 +05:30
vishnukvmd
ccd9e2ecaf [auth] v2.0.42 2024-03-10 21:53:05 +05:30
Vishnu Mohandas
4f3d9d0798 [docs] Add article on sharing (#984) 2024-03-09 23:31:40 +05:30
vishnukvmd
5ffa8ffe2b [docs] Add article on sharing 2024-03-09 23:31:20 +05:30
Vishnu Mohandas
50cb7f7aaf [mob][photos] Update version and change log (#981)
## Description

- Updated change log.
- Some final changes.
- Bumped up to `v0.8.66`

### Change Log

#### iOS


https://github.com/ente-io/ente/assets/77285023/10e0d1a3-cdfc-4431-be6c-1eb8718d35db

#### Android


https://github.com/ente-io/ente/assets/77285023/fed1e917-da2e-4001-b167-25fa72a1ff8c



## Tests
- [x] Builds fine on iOS and Android.
2024-03-09 19:26:32 +05:30
ashilkn
8daa7d8a8e remove worker manager related changes 2024-03-09 17:19:15 +05:30
ashilkn
5dbc300056 bump up to v0.8.66 2024-03-09 17:17:18 +05:30
iamgitcat
c7a4507f96 Update custom-icons.json
After this commit https://github.com/ente-io/ente/pull/430 those 6 icons I tried to add do not shows the icon or default letter, but icon is filled with hexcolor, so I guess hexcolor is not needed.
2024-03-09 14:45:44 +03:00
ashilkn
b812827480 update currentChangeLogVersion 2024-03-09 17:15:08 +05:30
ashilkn
f9051c94da update change log 2024-03-09 17:12:57 +05:30
ashilkn
4c3642526e minor fix 2024-03-09 17:05:23 +05:30
ashilkn
7c86e8f903 update limit of elemets shown in each search section 2024-03-09 16:52:02 +05:30
ashilkn
b4cf5761fa remove unused translation 2024-03-09 16:44:24 +05:30
ashilkn
881ece525f fix alignment of 'search' hint text in search field 2024-03-09 16:39:54 +05:30
Prateek Sunal
5acef45118 [FIX] HomeWidget improvements (#737)
## Description
- Use Background fetch as workmanager is not working as expected.
- Use new asset for empty state.
2024-03-09 16:21:04 +05:30
Vishnu Mohandas
96d1b09147 [auth] v2.0.41 (#777) 2024-03-09 11:24:44 +05:30
vishnukvmd
fc390d69c7 [auth] v2.0.41 2024-03-09 11:24:15 +05:30
Manav Rathi
c49cee8be6 [web] Fix a warning about MUI component switch state on using the select all checkbox (#774)
The issue here was that since the checkbox property would get
initialized to an undefined value, React would consider it to be
uncontrolled. But later on we'd try to set a value, which'd cause React
to complain.

Ref:
- Material-UI: A component is changing the uncontrolled checked state of
SwitchBase to be controlled

https://stackoverflow.com/questions/69259429/material-ui-a-component-is-changing-the-uncontrolled-checked-state-of-switchbas
2024-03-09 09:47:46 +05:30
Manav Rathi
8c9a11fc62 [web] Fix a warning about MUI component switch state on using the select all checkbox
The issue here was that since the checkbox property would get initialized to an
undefined value, React would consider it to be uncontrolled. But later on we'd
try to set a value, which'd cause React to complain.

Ref:
- Material-UI: A component is changing the uncontrolled checked state of SwitchBase to be controlled
  https://stackoverflow.com/questions/69259429/material-ui-a-component-is-changing-the-uncontrolled-checked-state-of-switchbas
2024-03-09 09:42:33 +05:30
Vishnu Mohandas
50b50409b1 [auth] Remove awaits (#773) 2024-03-09 08:42:30 +05:30
Vishnu Mohandas
b52cf1605d Merge branch 'main' into remove_awaits 2024-03-09 08:42:04 +05:30
Manav Rathi
c14f2ddbd7 [docs] Add a note about how to get the OTT on self hosted instance (#771)
Documentation only change.
2024-03-08 23:07:37 +05:30
Manav Rathi
a9c6196142 [docs] Add a note about how to get the OTT on self hosted instance 2024-03-08 23:06:50 +05:30
Manav Rathi
2ed8429df8 [docs] Fix build error and add a Yarn troubleshooting page (#770)
Docs only change.
2024-03-08 21:22:05 +05:30
Manav Rathi
c899984f76 Add yarn troubleshooting page 2024-03-08 21:21:10 +05:30
Manav Rathi
ed3b165b4b Don't consider localhost links as dead
Fixes the following error when building the site

    (!) Found dead link http://localhost:3000 in file self-hosting/index.md
    x Build failed in 1.72s
    If this is expected, you can disable this check via config. Refer: https://vitepress.dev/reference/site-config#ignoredeadlinks
2024-03-08 21:17:00 +05:30
Manav Rathi
20940293d3 [docs] Add self-hosting section and add getting started docs (#769)
Documentation only changed. Verified the preview by running docs server
locally.
2024-03-08 21:11:47 +05:30
Manav Rathi
f040ffad13 [docs] Add self-hosting section and add getting started docs 2024-03-08 21:09:49 +05:30
vishnukvmd
33f12ffd9d Up version 2024-03-08 20:14:07 +05:30
vishnukvmd
b0f8e331e6 Remove unnecessary awaits 2024-03-08 20:13:41 +05:30
Vishnu Mohandas
e9d46a8093 [auth] Up version (#760) 2024-03-08 18:28:49 +05:30
vishnukvmd
ca90727c78 [auth] Up version 2024-03-08 18:28:04 +05:30
Vishnu Mohandas
440b6b2833 Update links to Auth releases (#758) 2024-03-08 17:41:52 +05:30
vishnukvmd
3e7862fe4e Update links to Auth releases 2024-03-08 17:41:34 +05:30
Manav Rathi
f0f81d2ec2 Update sharing-logs.md (#757)
Test commit to test the flow.
2024-03-08 17:21:18 +05:30
Manav Rathi
eb28279de0 Update sharing-logs.md 2024-03-08 17:20:21 +05:30
Vishnu Mohandas
c75e45897c [Photos] Fix auto scaling on loading final image (#749)
## Description

When an image is zoomed in, there were cases where when then final image
is loaded and rendered on screen, the image gets zoomed even more.

#### Case 1
Double tapping twice to zoom and then the final image is loaded.

##### Before


https://github.com/ente-io/ente/assets/77285023/7d9acb0f-2849-4ffb-863d-cab61ef4bd16

##### After


https://github.com/ente-io/ente/assets/77285023/89fb1238-1444-4681-a94f-7a58679f9350



#### Case 2
When screen is pressed or dragged (any contact with screen).

##### Before


https://github.com/ente-io/ente/assets/77285023/3aefdec5-3bc3-42ef-9442-592442273569

##### After


https://github.com/ente-io/ente/assets/77285023/7b458184-5abe-4983-ba21-f38080cc8bee

## Tests

Tested for regressions. Found one, which it not quite a regression as
the issue was already present but reproducible in a different flow. It
happens too fast for screen recording to capture it.

Not a blocker as it doesn't affect usability in anyway and since this
fix (auto scaling on final image loading) is an important fix.
2024-03-08 17:05:24 +05:30
ashilkn
62b05513a2 Remove late initialisations 2024-03-08 17:00:48 +05:30
Manav Rathi
353eb67cab [web] Finalize deployment process (#755)
These changes were all made in previous PRs, this PR just ties up the
loose ends and updates the documentation etc.
2024-03-08 16:46:59 +05:30
Manav Rathi
290196ee9e Add fixed nightly mappings 2024-03-08 16:36:06 +05:30
ashilkn
ea63ea1c55 Make _photoViewController late 2024-03-08 16:32:16 +05:30
Manav Rathi
2d14cc5899 Mention fast forwards 2024-03-08 16:29:35 +05:30
Manav Rathi
f612e0b69f Published 2024-03-08 16:24:37 +05:30
Manav Rathi
929e1bbac1 Less preachy 2024-03-08 16:17:53 +05:30
ashilkn
1dd183c4bd Make scaleStateController final and dispose it when widget gets disposed 2024-03-08 15:54:38 +05:30
Manav Rathi
bcb0e2fcc3 [web] Add production deployment templates (#751)
Also update the documentation.
2024-03-08 15:50:39 +05:30
Manav Rathi
25a04fbc5f Update docs README 2024-03-08 15:49:13 +05:30
Manav Rathi
0b585ce3a5 Update the documentation 2024-03-08 15:40:35 +05:30
Manav Rathi
8058d2bfd4 Add production deployments 2024-03-08 15:20:16 +05:30
Neeraj Gupta
5a04030766 Fix lint 2024-03-08 15:17:28 +05:30
Neeraj Gupta
04e508561b [photos] Update copy for passkey verification 2024-03-08 15:17:28 +05:30
Neeraj Gupta
57d5647a39 [photos] Add support for by-passing passkey 2024-03-08 15:17:28 +05:30
Neeraj Gupta
9f28e5ef79 Rename 2024-03-08 15:17:28 +05:30
Neeraj Gupta
42d9ad4206 [photos] Register passkey reset key 2024-03-08 15:17:28 +05:30
Neeraj Gupta
907a0bd456 Reformat 2024-03-08 15:17:28 +05:30
Neeraj Gupta
8f37af3985 [mobile] Add TwoFactorType enum & pass it during recovery 2024-03-08 15:17:28 +05:30
Neeraj Gupta
6e160dca43 Fix minor bugs 2024-03-08 15:15:00 +05:30
Neeraj Gupta
1f7d9dbb86 Rename passKey to passkey 2024-03-08 15:15:00 +05:30
Neeraj Gupta
a780598607 Fix sql query 2024-03-08 15:15:00 +05:30
Neeraj Gupta
7f66714d96 Refactor + bug fixes 2024-03-08 15:15:00 +05:30
Neeraj Gupta
980ab6c49c Refactor: extend totp recovery API to recover passkey 2024-03-08 15:15:00 +05:30
Neeraj Gupta
fe181fecbe Rename 2024-03-08 15:15:00 +05:30
Neeraj Gupta
50c3a7a8e5 Store resetSecret in encrypted form 2024-03-08 15:15:00 +05:30
Neeraj Gupta
f766484b2e Rename account_recovery -> two_factor_recovery 2024-03-08 15:15:00 +05:30
Neeraj Gupta
42e4364fda Add APIs to allow user to skip passkey based two-fa 2024-03-08 15:15:00 +05:30
Neeraj Gupta
09a7d557d2 Add API to get account two recovery status 2024-03-08 15:15:00 +05:30
Neeraj Gupta
13bae268ec Add models passkey reset via recoveryKey 2024-03-08 15:15:00 +05:30
Neeraj Gupta
23fcce245d Add table for account recovery 2024-03-08 15:15:00 +05:30
ashilkn
69c5d4f645 Added comment for context of line 2024-03-08 14:57:32 +05:30
Manav Rathi
d7854fa6c0 [web] Add nightly deployments of web projects (#746)
- Fix the branch name for the docs deployment
- Add web-nightly deployments
- Tweak the cron specification in existing workflows
2024-03-08 14:14:34 +05:30
ashilkn
cf8e684cb3 Revert "Remove unnecessary check"
This reverts commit a9631c09c8.
2024-03-08 14:09:43 +05:30
Manav Rathi
5d4486fce3 Add web-nightly and update existing 2024-03-08 14:09:41 +05:30
ashilkn
a9385f2132 Remove unnecessary mixin 2024-03-08 14:05:42 +05:30
Manav Rathi
37913ffbc7 Fix the branch name 2024-03-08 13:38:34 +05:30
Manav Rathi
215e89427d [WIP] [docs] Deploy docs (#742)
Add a GitHub workflow to deploy docs. If this works, will start using
this same approach for the other web apps in our repo.
2024-03-08 13:30:28 +05:30
Manav Rathi
2eb95ab215 Move the docs deployment to the new project, its final resting place 2024-03-08 13:26:41 +05:30
Manav Rathi
4b05dd49f6 [web] New translations (#739)
New translations from
[Crowdin](https://crowdin.com/project/ente-photos-web)
2024-03-08 13:16:22 +05:30
Manav Rathi
00f3a0ce90 [web] Document the need for yarn classic (#745)
People often run into issues with `yarn install` because they're using a
newer yarn. The situation is generally bad - we don't want to update to
Yarn v4 yet because it is marked experimental and is not the default
yarn that gets installed by node currently. We could add a
`packageManager` field to our package.json, but this will only fail the
build with a better (hopefully) error message, and will necessitate the
user to `corepack_enable`.

I'm not sure what's the best approach right now to make the initial
setup be seamless (I think we're using the approach that works for the
maximum of all the alternatives, but I'm not sure). At least, let me add
a note about it.

Ref:
* https://github.com/yarnpkg/berry/issues/5912
2024-03-08 13:15:28 +05:30
Manav Rathi
9e4f4c4670 Document the need for yarn classic 2024-03-08 13:11:04 +05:30
Manav Rathi
236d7e2f49 Fix the deploy workflow path 2024-03-08 13:03:50 +05:30
Vishnu Mohandas
205dd302da [auth] Update workflow (#744) 2024-03-08 12:54:53 +05:30
vishnukvmd
0ca29d2f83 [auth] Update workflow 2024-03-08 12:53:59 +05:30
Vishnu Mohandas
232dbde8a3 Update scripts for Auth (#743) 2024-03-08 12:38:33 +05:30
vishnukvmd
70435cfccf [auth] v2.0.36 2024-03-08 12:38:04 +05:30
vishnukvmd
bd455825e6 Enable AAB upload to PlayStore 2024-03-08 12:37:39 +05:30
vishnukvmd
b15d05050a Lower Flutter version 2024-03-08 12:37:27 +05:30
Manav Rathi
05d13979db Remove GitHub deployments
Not integrating GitHub deployments for now since creating a deployment
(anywhere) causes "This branch has not been deployed" message to appear on
unrelated branches.

Also, the Discord /github webhook doesn't support deployment status events
anyway - Discord accepts and responds to the webhook with a 204 but it doesn't
appear in the channel. This is not a big issue, we can easily massage the
payload ourselves, but just mentioning this for posterity. Refs:

* [Corresponding issue on Discord](https://github.com/discord/discord-api-docs/issues/6203#issuecomment-1608151265)

* [A general recipe](https://gist.github.com/jagrosh/5b1761213e33fc5b54ec7f6379034a22)

---

For deleting the existing deployment I had to

```
gh api /repos/ente-io/ente/deployments --jq=".[].id"
gh api --method DELETE -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" "/repos/ente-io/ente/deployments/1375794893"
```

This helpful hint taken from https://github.com/orgs/community/discussions/46375. Thanks!
2024-03-08 12:30:58 +05:30
ashilkn
454f5cdead Merge branch 'main' into fix_auto_scaling_on_loading_final_image 2024-03-08 12:17:04 +05:30
ashilkn
3f45345aad fix: when image is fully loaded after scaling the image, stay at the same position 2024-03-08 12:13:50 +05:30
ashilkn
8da57bd575 fix: auto scaling when final image is loaded case 2
When an image is magnified and a user is pressing down (onPressed) on the image, once the final image is loaded, the image auto scales
2024-03-08 12:09:33 +05:30
Manav Rathi
e47bcf2774 Merge for now 2024-03-08 12:05:12 +05:30
ashilkn
f14b499ffe fix: auto scaling after image is fully loaded after double tapping twice 2024-03-08 11:20:19 +05:30
Manav Rathi
4da96a3b76 Add the workflow 2024-03-08 11:19:04 +05:30
github-actions[bot]
58958cd705 [auth] New translations (#738)
New translations from
[Crowdin](https://crowdin.com/project/ente-authenticator-app)

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-03-08 10:39:02 +05:30
Manav Rathi
e3118ee7b0 Start adding notes 2024-03-08 10:37:40 +05:30
Manav Rathi
e3f2a77d2c Add workflow (sans the deployment)
Ref: https://vitepress.dev/reference/cli
2024-03-08 09:53:26 +05:30
Manav Rathi
10b5771445 Fix dead links 2024-03-08 09:48:49 +05:30
Crowdin Bot
835c6dea73 New Crowdin translations by GitHub Action 2024-03-08 00:09:25 +00:00
Manav Rathi
457b1c1abd [docs] Prepare for first publish (#735)
Finalize the structure of the docs, and go through all of the existing
content, cleaning it up and making it a bit more consistent.

Apart from the photos/troubleshooting/sharing-logs page, there are no
other placeholders left. The content is still not complete, but things
are at a state where we can publish this and then iteratively keep
adding to it.
2024-03-07 17:09:11 +05:30
Manav Rathi
1955508a90 Audit and edit 2024-03-07 17:05:21 +05:30
Neeraj Gupta
a9cb6f3077 [mobile] Remove unused db for public keys (#734)
## Description
To keep the changes simple, we are not deleting any existing database
instances. In case we need to store this information in the future, we
will reuse the existing database instead of creating a new one.
2024-03-07 16:37:20 +05:30
Manav Rathi
b66cdb8942 Finalize structure 2024-03-07 15:38:41 +05:30
Vishnu Mohandas
a8922203bf Fix warnings around missing awaits (#733)
Fix warnings around missing awaits
2024-03-07 15:21:27 +05:30
Manav Rathi
7e2410a0ed More 2024-03-07 15:15:18 +05:30
Manav Rathi
3e614e66f5 Scaffold a few pages 2024-03-07 13:59:29 +05:30
vishnukvmd
3b498f1be6 Fix warnings around missing awaits 2024-03-07 13:46:05 +05:30
Manav Rathi
cf75ac58b9 Update to latest vitepress 2024-03-07 13:28:36 +05:30
Vishnu Mohandas
90bbc54bb5 [Auth] Allow for configuring a custom server (#726)
## Description
Users can now tap on the onboarding screen **7 times** to bring up a
page where they can configure the endpoint the app should be connecting
to.


![self-host](https://github.com/ente-io/ente/assets/1161789/10f61f6d-0fb3-4f5b-889e-806ca7607525)



## Tests
- [x] Verified that production flows are working as expected
- [x] Verified that configuring the endpoint to a local instance lets
you
  - [x] Connect to that instance 
  - [x] Create an account
  - [x] Add a key
  - [x] Modify a key
  - [x] Logout and log back in
2024-03-07 13:24:30 +05:30
vishnukvmd
fe0697fccb Reuse existing widget to render the custom endpoint (if any) 2024-03-07 13:22:13 +05:30
vishnukvmd
b9078eadc0 Show custom endpoint within Settings 2024-03-07 13:19:26 +05:30
Manav Rathi
06c4f5791b [cli] Improve docs (#730)
- Improve CLI installation documentation, as requested in
https://github.com/ente-io/ente/discussions/709.
- Document BuildKit requirement, as requested in
https://github.com/ente-io/ente/issues/712.
2024-03-07 13:12:57 +05:30
Neeraj Gupta
e3655c4513 [auth] Add linter rule for missing await (#732)
## Description

## Tests
2024-03-07 13:11:42 +05:30
vishnukvmd
293246ce92 Show configured endpoint on OnboardingPage 2024-03-07 13:10:24 +05:30
Neeraj Gupta
8186246690 [server] Update freq for removing compliance hold (#731)
## Description
- Taking lock at host level to avoid redundant downstream request when
multiple host pick same items.

## Tests
Tested locally.
2024-03-07 13:08:04 +05:30
Manav Rathi
239d7f33ed Document BuildKit requirement
See https://github.com/ente-io/ente/issues/712
2024-03-07 12:56:58 +05:30
Manav Rathi
85af7f920c Improve CLI documentation 2024-03-07 12:53:12 +05:30
vishnukvmd
7bb65af482 /ping to validate the endpoint 2024-03-07 12:51:18 +05:30
Manav Rathi
2101d06d32 export CGO_ENABLED=0
The configuration used by goreleaser (which we previously used to build the
release binaries) had set CGO_ENABLED=0. Since we don't specifically need this
to be on, revert to that configuration.

Also add a few more go build flags to reduce the size of the produced binaries

Ref:
- https://github.com/wangyoucao577/go-release-action/issues/33
- https://github.com/wangyoucao577/go-release-action
- https://github.com/ente-io/cli/blob/main/.goreleaser.yaml#L18

Related:
- https://github.com/ente-io/ente/issues/727
2024-03-07 12:00:17 +05:30
Manav Rathi
b70ca6bec2 Remove environment variable unset
exports don't survive the shell script
2024-03-07 11:15:45 +05:30
github-actions[bot]
1a6644e28c New translations (mobile) (#667)
New translations via [Crowdin GH
Action](https://github.com/crowdin/github-action)

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-03-07 08:35:58 +05:30
Neeraj Gupta
97792f064b [passkey] Fixed base64 decode + bump version (#728)
## Description
## Tests
2024-03-07 08:26:00 +05:30
ashilkn
a9631c09c8 Remove unnecessary check
_loadedFinalImage will be true only after _loadedSmallThumbnail becomes true as first the small thumbnail is loaded and then the final image
2024-03-07 07:29:39 +05:30
ashilkn
c4ec818fb8 Remove unnecessary SingleTickerProviderStateMixin 2024-03-07 07:27:51 +05:30
Neeraj Gupta
dd323316f7 Update logs & undo build file changes 2024-03-07 07:02:17 +05:30
Neeraj Gupta
846ecadc59 [auth] Update flutter submodule to v3.16.9 2024-03-07 07:01:02 +05:30
Neeraj Gupta
cd328687e2 [passkey] Fix base64 decoding 2024-03-07 06:22:03 +05:30
vishnukvmd
981e3866d3 Extract string 2024-03-06 20:47:52 +05:30
vishnukvmd
7ca217f753 Update iOS project files 2024-03-06 20:34:01 +05:30
vishnukvmd
07b496be4c Ensure the AuthenticatorGateway does not cache a stale endpoint 2024-03-06 20:33:47 +05:30
vishnukvmd
a45129b75b Make sure Network uses the latest network configuration 2024-03-06 20:33:18 +05:30
vishnukvmd
3593ee4931 Update the endpoint in configuration 2024-03-06 20:32:56 +05:30
vishnukvmd
690f90d296 Remove unused method 2024-03-06 20:00:07 +05:30
ashilkn
d6fc57fc3f Merge branch 'main' into fix_auto_scaling 2024-03-06 19:09:27 +05:30
vishnukvmd
b893affbfa Add DeveloperSettingsPage 2024-03-06 18:35:38 +05:30
Manav Rathi
9feb5397a8 [doc] Add more links to README (#725)
- Add download from GitHub button for auth
- Add Twitter and Mastodon links
- Remove mention of unused Xcode Cloud integration
2024-03-06 18:14:28 +05:30
Manav Rathi
d3f3adc1f2 Tweak the Mastodon color 2024-03-06 18:12:48 +05:30
Manav Rathi
758d6a53e0 Use a color which works for both light and dark 2024-03-06 18:08:57 +05:30
Manav Rathi
45406d3486 Update badge 2024-03-06 18:04:32 +05:30
vishnukvmd
07e48ce318 Add an action within OnboardingPage to access developer settings 2024-03-06 18:03:16 +05:30
Manav Rathi
53d70387a4 Try to get the fill to work on GitHub 2024-03-06 17:58:42 +05:30
Manav Rathi
d7d127cb55 Tweak 2024-03-06 17:55:08 +05:30
Manav Rathi
cd60f4e590 Add download from GitHub button for auth
Icon made by Pragadees
2024-03-06 17:50:09 +05:30
Neeraj Gupta
2eaa741f1a Bump auth version 2024-03-06 17:40:51 +05:30
Neeraj Gupta
ce94a17b8a [passkey] Add additional logs 2024-03-06 17:39:34 +05:30
Manav Rathi
b53eb2e0bb Remove mention of unused Xcode Cloud integration 2024-03-06 17:37:12 +05:30
Manav Rathi
f9b5cdb1e7 Tweak the icons to make them fit better 2024-03-06 17:34:48 +05:30
Manav Rathi
a053dbf872 Add Twitter and Mastodon links to README
Icons from Bootstrap icons (MIT licensed):
- https://icons.getbootstrap.com/icons/twitter/
- https://icons.getbootstrap.com/icons/mastodon/
2024-03-06 17:28:52 +05:30
Vishnu Mohandas
6a6cc6b2ba [auth] Set high refresh rates only on Android (#724) 2024-03-06 17:14:24 +05:30
vishnukvmd
45416d5ba0 [auth] Set high refresh rates only on Android 2024-03-06 17:13:42 +05:30
Neeraj Gupta
0de6ba722a [photos] Bump version: 0.8.65+585 2024-03-06 16:58:34 +05:30
Neeraj Gupta
8101ddf4bc [photos]Always reset volatile password on login 2024-03-06 16:58:34 +05:30
Neeraj Gupta
bf923007e8 Fix arb 2024-03-06 16:58:34 +05:30
Vishnu Mohandas
30d3c738f5 [doc] Update the launch configuration for Auth (#705) 2024-03-06 16:30:56 +05:30
vishnukvmd
4f89bf9eb5 [doc] Update the launch configuration for Auth 2024-03-06 16:23:40 +05:30
Neeraj Gupta
4744434a62 [auth][photos] Support for passkey (#435)
<!--
  Thanks for contributing!

Provide a description of your changes below and a general summary in the
title

Please look at the following checklist to ensure that your PR can be
accepted quickly:
-->

## Description

Passkey implementation (similar will be done in ente Photos)

<!--- Describe your changes in detail -->

## Type of Change

<!--- Put an `x` in all the boxes that apply: -->

- [ ] 🖼️ New icon
- [x]  New feature (non-breaking change which adds functionality)
- [ ] 🛠️ Bug fix (non-breaking change which fixes an issue)
- [ ]  Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] 🧹 Code refactor
- [ ]  Build configuration change
- [ ] 📝 Documentation
- [ ] 🗑️ Chore
2024-03-06 15:55:53 +05:30
Manav Rathi
b36c136662 [web] UI tweaks to the select all checkbox (#704)
This select-all checkbox was added in
https://github.com/ente-io/ente/pull/674. These are some changes to how
it looks /cc @vishnukvmd

- Move the checkbox to the left
- Reduce the size of the checkbox

The diff is bigger than it needs to be because I also had to fix some
formatting issues (the prettier rules had changed since work had started
on that PR).

## Tested by

Running locally
2024-03-06 15:54:28 +05:30
Manav Rathi
d57e59493b Fix formatting issues 2024-03-06 15:51:26 +05:30
Manav Rathi
af2ccf7449 UI tweaks to the select all checkbox
This select-all checkbox was added in https://github.com/ente-io/ente/pull/674.
These are some changes to how it looks:

- Move the checkbox to the left
- Reduce the size of the checkbox
2024-03-06 15:50:20 +05:30
Neeraj Gupta
82573f20d7 [auth]Enable passkey for internal users 2024-03-06 15:47:37 +05:30
Vishnu Mohandas
a857a86608 Added selectAll checkbox to select all files on a day (#674)
## Description

fixes https://github.com/ente-io/ente/issues/535

This PR will add checkbox to all select files on a day, this will also
handle manual selection of files and select all checkbox on a day.
2024-03-06 15:33:20 +05:30
Manav Rathi
3c53381691 [auth] Verify that the custom icon JSON is valid as part of lint checks (#698)
## Tested by

Purposely opening this PR against a known bad state to verify that the
check catches this issue. And it worked.

<img width="590" alt="Screenshot 2024-03-06 at 14 04 40"
src="https://github.com/ente-io/ente/assets/24503581/e67bac5c-4191-4497-a771-b73f7c850379">

Have now rebased the PR of main, to also check that the verification
succeeds in a known good state.
2024-03-06 14:58:08 +05:30
Manav Rathi
47f6b9b690 [cli] Use the tag: qualifier to better filter the releases (#700)
@ua741 We still need to provide at least the major version, and we'll
need to remember to update these links when the major version changes.

Ref:
-
https://docs.github.com/en/repositories/releasing-projects-on-github/searching-a-repositorys-releases
2024-03-06 14:57:51 +05:30
Neeraj Gupta
c175973ff0 Passkey changes 2024-03-06 14:54:45 +05:30
Manav Rathi
d411efe57c [web] Fixes for the accounts deployment (#701)
- Update the URLs for opening new deployment PRs
- Include translations
2024-03-06 14:51:25 +05:30
Manav Rathi
951ace6183 Include translations
There needs to be a better way, but this unblocks our current deployment
2024-03-06 14:50:00 +05:30
Manav Rathi
d26d193a41 Update the URLs for opening new deployment PRs
Note that the three dot syntax is needed, the two dot compare doesn't open a pull request.

Ref:
- https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/using-query-parameters-to-create-a-pull-request
- https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-comparing-branches-in-pull-requests#three-dot-and-two-dot-git-diff-comparisons
2024-03-06 14:48:28 +05:30
Manav Rathi
636454faa2 Use the tag: qualifier to better filter the releases
We still need to provide at least the major version, and we'll need to remember
to update these links when the major version changes.

Ref:
- https://docs.github.com/en/repositories/releasing-projects-on-github/searching-a-repositorys-releases
2024-03-06 14:42:49 +05:30
Manav Rathi
c469e68f7a Mention GitHub Discussion labels (#699)
Documentation only change.
2024-03-06 14:24:41 +05:30
Manav Rathi
5530287197 Mention GitHub Discussion labels 2024-03-06 14:23:38 +05:30
Manav Rathi
d316a3049c [auth] Verify that the custom icon JSON is valid as part of lint checks
Purposely opening this PR against a known bad state to verify that the check
catches this issue.
2024-03-06 14:05:09 +05:30
Neeraj Gupta
024c45ec5c [auth] Fix custom-icons.json (#696)
## Description

## Tests
2024-03-06 13:49:10 +05:30
Neeraj Gupta
942da28b53 Merge branch 'main' into passkeys 2024-03-06 13:21:42 +05:30
Neeraj Gupta
69a900aa56 [mobile] Move recovery option under account section (#695)
## Description

## Tests
2024-03-06 12:57:03 +05:30
Manav Rathi
518a2a0632 Web deployment (#693)
Update the web deployment process for the new monorepo. This is
currently untested, I'll try to a do a preview deployment next of the
accounts or cast app (since they're not yet on production).
2024-03-06 12:04:56 +05:30
Manav Rathi
7057b53d2c Add deployment shortcuts 2024-03-06 11:58:41 +05:30
Manav Rathi
92b5411cb2 Update deployment script 2024-03-06 11:44:30 +05:30
Manav Rathi
f199ce0e83 Update deployment instructions 2024-03-06 11:44:09 +05:30
Manav Rathi
9d55b9936d Document NODE_VERSION 2024-03-06 11:09:19 +05:30
Manav Rathi
698abe0bb8 [workflows] Use the same prefix title format as our own PRs for translation PRs (#692) 2024-03-06 10:51:51 +05:30
Neeraj Gupta
7dc5ccb154 [auth] Integrated decryption in CLI, removing standalone tool (#691)
## Description

## Tests
2024-03-06 10:49:51 +05:30
Manav Rathi
0204fce22a [workflows] Use the same prefix title format as our own PRs for translation PRs 2024-03-06 10:48:27 +05:30
Manav Rathi
f02b99b40e [cli] Update the link to the CLI downloads in README (#690)
I will also update the old CLI repository's README to point to this
repository.
2024-03-06 10:40:59 +05:30
Manav Rathi
a4618faa55 [cli] Update the link to the CLI downloads in README 2024-03-06 10:37:16 +05:30
Manav Rathi
41dc0984cc [web] Disable the Cloudflare upload proxy when connecting to a custom endpoint (#688)
This simplifies use cases like
https://github.com/ente-io/ente/discussions/685#discussioncomment-8682588.
There is one less thing for folks to do if they want to run locally or
self-host.

## Tested by

Starting a local museum, then connecting to it from the web app

    NEXT_PUBLIC_ENTE_ENDPOINT=http://localhost:8080 yarn dev

And uploading a photo.
2024-03-06 10:17:13 +05:30
Manav Rathi
b4ac991986 [web] Disable the Cloudflare upload proxy when connecting to a custom endpoint
This simplifies use cases like
https://github.com/ente-io/ente/discussions/685#discussioncomment-8682588. There
is one less thing for folks to do if they want to run locally or self-host.

Starting a local museum, then connecting to it from the web app

    NEXT_PUBLIC_ENTE_ENDPOINT=http://localhost:8080 yarn dev

And uploading a photo.
2024-03-06 09:51:19 +05:30
Manav Rathi
3a0dc05e3d Add mobile release workflow (#684)
## Description

Resurrect the existing mobile independent release GitHub workflow,
adapting it to the new monorepo structure but otherwise keeping it
unchanged.

## Tests

Currently untested.
2024-03-06 09:11:01 +05:30
github-actions[bot]
946a78ae47 New translations (auth) (#686)
New translations via [Crowdin GH
Action](https://github.com/crowdin/github-action)

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-03-06 09:09:51 +05:30
Manav Rathi
8943484aa8 New translations (web) (#687)
New translations via [Crowdin GH
Action](https://github.com/crowdin/github-action)
2024-03-06 09:06:00 +05:30
Crowdin Bot
9d0aee87d1 New Crowdin translations by GitHub Action 2024-03-06 00:09:13 +00:00
Manav Rathi
36d5aa9f01 Add mobile release workflow 2024-03-05 20:36:07 +05:30
Manav Rathi
8da4dbeb12 CLI packaging cleanup (#682)
This is a continuation of the changes in
https://github.com/ente-io/ente/pull/681.

- git rm cli/config.yaml (this file was gitignored but it seems to be
present in the tree too)
- Remove goreleaser config since we're not using it anymore

I'm not sure if there is something in the goreleaser config that we also
need to pass to the the new action we're using to build the binaries
now, so please take a quick scan to see if something reminds you. If
needed, we should be able to set the corresponding build flag in the
[new release action](https://github.com/wangyoucao577/go-release-action)
too.
2024-03-05 18:07:59 +05:30
Manav Rathi
2a525da8ad Fix museum deployment (#683)
- Add museum lint action (triggered automatically on any push that
touches server/*)
- Add museum release action (triggered manually - that's the behaviour
we want)
- Also fix a lint issue

It's not showing me the workflow_trigger option so can't actually test
the build yet. We can merge this, and if something's not working will
fix that in another PR.
2024-03-05 18:07:24 +05:30
Manav Rathi
795c2baf0f Fix lint 2024-03-05 16:53:15 +05:30
Manav Rathi
9127d9540c Handle subdir 2024-03-05 16:49:45 +05:30
Manav Rathi
3cad79dfa9 Add museum release action 2024-03-05 16:39:11 +05:30
Manav Rathi
0b37f74636 Add museum lint action 2024-03-05 16:33:20 +05:30
Manav Rathi
00253a923a Remove goreleaser config since we're not using it anymore 2024-03-05 16:21:43 +05:30
Manav Rathi
e97468180f git rm cli/config.yaml
This file is gitignored but it seems to be present in our repo.
2024-03-05 16:21:14 +05:30
Manav Rathi
748e715ae8 [cli] Add release workflow (#681)
@ua741 I've done a test run, it seems to be working fine. You can use it
to release cli by pushing the `cli-v0.1.11` tag. This'll create a draft
release, which we can then publish.
2024-03-05 16:13:22 +05:30
Manav Rathi
50a9b38727 Disable md5s, keep only sha256es 2024-03-05 16:07:30 +05:30
Manav Rathi
e29704c381 Fix matrix 2024-03-05 16:03:14 +05:30
Manav Rathi
5bc43aa0c5 Create a job matrix 2024-03-05 16:01:31 +05:30
Manav Rathi
ff0e5dd29c Ask for the project which requires new translations (#680)
This came up in https://github.com/ente-io/ente/issues/678

Tested by opening the URL manually.
2024-03-05 15:32:10 +05:30
Manav Rathi
1a4631db03 Ask for the project which requires new translations
This came up in https://github.com/ente-io/ente/issues/678
2024-03-05 15:30:17 +05:30
Manav Rathi
52d1540ad6 release-tag isn't working, go back to release-name but customize the asset title 2024-03-05 15:20:04 +05:30
Neeraj Gupta
34743171f9 [server] Add API to re-queue item for processing (#679)
## Description

## Tests
2024-03-05 15:15:23 +05:30
Manav Rathi
197ea28f76 Restore permissions 2024-03-05 15:14:14 +05:30
Manav Rathi
a80c2b4d83 Retain the name to allow the action to find the existing release 2024-03-05 15:04:13 +05:30
Manav Rathi
d5f4c8d358 Fix the name of the generated assets 2024-03-05 15:01:39 +05:30
Manav Rathi
79f4c9ebd9 Try with reduced permissions 2024-03-05 14:59:29 +05:30
Neeraj Gupta
3513d7a297 [server] Remove entry from object_keys before marking del as completed (#677)
## Description

## Tests
2024-03-05 14:56:02 +05:30
Manav Rathi
cec1d2cf4e Provide it a release_name to get it to pick the latest draft
Ref: https://github.com/wangyoucao577/go-release-action
2024-03-05 14:53:08 +05:30
Manav Rathi
e376decec1 Try with packages 2024-03-05 14:51:26 +05:30
Manav Rathi
d6ac40b047 Need to create a release first 2024-03-05 14:37:57 +05:30
Manav Rathi
d627fdc0fd Add permissions
Ref:
- https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs
- https://github.com/wangyoucao577/go-release-action/issues/119
2024-03-05 14:35:42 +05:30
Manav Rathi
0e5a0120b2 Create a release first 2024-03-05 14:30:16 +05:30
Manav Rathi
fb6dbde198 Try an alternative action that allows tags prefixes
goreleases doesn't like it if we give it a tag with a prefix, as is our case
with "cli-...".

> ⨯ release failed after 0s error=failed to parse tag 'cli-v0.0.0-rc2' as
    semver: Invalid Semantic Version
2024-03-05 14:17:24 +05:30
Manav Rathi
76968d915e Te 2024-03-05 13:55:19 +05:30
Manav Rathi
4d33fb63d0 Test 2024-03-05 13:52:42 +05:30
Manav Rathi
4c1bb65620 Remove deprecated flag
From the action run:

> DEPRECATED: --rm-dist was deprecated in favor of --clean, check https://goreleaser.com/deprecations#-rm-dist for more details
2024-03-05 13:47:08 +05:30
Manav Rathi
1b9af538fd Add CLI release workflow 2024-03-05 13:42:25 +05:30
Manav Rathi
ef553d9401 Stop unnecessary lint workflows from running on pushing tags (#676)
Ref:
-
https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#onpushbranchestagsbranches-ignoretags-ignore
-
https://stackoverflow.com/questions/77667104/how-to-make-github-action-trigger-on-push-paths-and-push-tags
- https://github.com/orgs/community/discussions/26273
2024-03-05 13:15:34 +05:30
Manav Rathi
cbf0223f07 Stop unnecessary lint workflows from running on pushing tags
Ref:
- https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#onpushbranchestagsbranches-ignoretags-ignore
- https://stackoverflow.com/questions/77667104/how-to-make-github-action-trigger-on-push-paths-and-push-tags
- https://github.com/orgs/community/discussions/26273
2024-03-05 13:10:03 +05:30
github-actions[bot]
235393235b New translations (auth) (#666)
New translations via [Crowdin GH
Action](https://github.com/crowdin/github-action)

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-03-05 12:38:33 +05:30
Neeraj Gupta
85cc8f0173 [cli] Bump version to 0.1.11 2024-03-05 12:31:25 +05:30
Neeraj Gupta
d36be6efbd [cli] Add command to decrypt auth export 2024-03-05 12:31:25 +05:30
Manav Rathi
59560cc870 Add GitHub Action for releasing the auth app (mobile and desktop) (#671)
## Description

This action will take the latest auth code, build the iOS/Android apps,
and create a draft release using it.

It will also then create a desktop build of auth. Since the desktop
build is currently in beta, this might be neutered when we merge this
into main for now.

## Tests

This PR _is_ the test. I'll be adding commits as I fill this in. These
commits will create a test tag (that I'll also delete later).
2024-03-05 12:30:01 +05:30
Manav Rathi
a20e1bc0d4 Remove temporary testing code 2024-03-05 12:24:41 +05:30
aakankshabhende
b7503897c5 fixed spacing
Signed-off-by: aakankshabhende <aakanksha0407@gmail.com>
2024-03-05 10:48:18 +05:30
aakankshabhende
4a579a93bb Added selectAll checkbox to select all files on a day
Signed-off-by: aakankshabhende <aakanksha0407@gmail.com>
2024-03-05 10:42:57 +05:30
Manav Rathi
9a3c450d34 Update dev.md (#670)
## Description
minor fix
## Tests
2024-03-05 10:06:13 +05:30
Zxhir
b29addccbd Add Bloom Host (#672)
## Description

This PR add the Bloom Host icon to ente auth. Bloom Host is a dedicated
Minecraft hosting, VPS and a bare metal server hosting company.
2024-03-05 08:29:55 +05:30
Prateek Sunal
ad542429a4 feat: passkeys for mobile branch 2024-03-05 00:25:52 +05:30
Ikko Eltociear Ashimine
e13edc7486 Update dev.md
minor fix
2024-03-05 00:11:27 +09:00
Prateek Sunal
b35d942eac feat: auth passkeys 2024-03-04 13:46:05 +05:30
ashilkn
939e76d696 remove unnecessary mixin 2024-03-02 15:38:03 +05:30
491 changed files with 20406 additions and 6273 deletions

3
.gitattributes vendored Normal file
View File

@@ -0,0 +1,3 @@
# Set line endings of shell scripts to LF, even on Windows, otherwise execution
# within Docker fails.
*.sh text eol=lf

BIN
.github/assets/github-badge.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

3
.github/assets/mastodon.svg vendored Normal file
View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" fill="#6364ff" viewBox="0 0 16 16">
<path d="M11.19 12.195c2.016-.24 3.77-1.475 3.99-2.603.348-1.778.32-4.339.32-4.339 0-3.47-2.286-4.488-2.286-4.488C12.062.238 10.083.017 8.027 0h-.05C5.92.017 3.942.238 2.79.765c0 0-2.285 1.017-2.285 4.488l-.002.662c-.004.64-.007 1.35.011 2.091.083 3.394.626 6.74 3.78 7.57 1.454.383 2.703.463 3.709.408 1.823-.1 2.847-.647 2.847-.647l-.06-1.317s-1.303.41-2.767.36c-1.45-.05-2.98-.156-3.215-1.928a4 4 0 0 1-.033-.496s1.424.346 3.228.428c1.103.05 2.137-.064 3.188-.189zm1.613-2.47H11.13v-4.08c0-.859-.364-1.295-1.091-1.295-.804 0-1.207.517-1.207 1.541v2.233H7.168V5.89c0-1.024-.403-1.541-1.207-1.541-.727 0-1.091.436-1.091 1.296v4.079H3.197V5.522q0-1.288.66-2.046c.456-.505 1.052-.764 1.793-.764.856 0 1.504.328 1.933.983L8 4.39l.417-.695c.429-.655 1.077-.983 1.934-.983.74 0 1.336.259 1.791.764q.662.757.661 2.046z"/>
</svg>

After

Width:  |  Height:  |  Size: 925 B

3
.github/assets/twitter.svg vendored Normal file
View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#1e9bf0" viewBox="0 0 16 16">
<path d="M5.026 15c6.038 0 9.341-5.003 9.341-9.334q.002-.211-.006-.422A6.7 6.7 0 0 0 16 3.542a6.7 6.7 0 0 1-1.889.518 3.3 3.3 0 0 0 1.447-1.817 6.5 6.5 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.32 9.32 0 0 1-6.767-3.429 3.29 3.29 0 0 0 1.018 4.382A3.3 3.3 0 0 1 .64 6.575v.045a3.29 3.29 0 0 0 2.632 3.218 3.2 3.2 0 0 1-.865.115 3 3 0 0 1-.614-.057 3.28 3.28 0 0 0 3.067 2.277A6.6 6.6 0 0 1 .78 13.58a6 6 0 0 1-.78-.045A9.34 9.34 0 0 0 5.026 15"/>
</svg>

After

Width:  |  Height:  |  Size: 560 B

View File

@@ -3,15 +3,16 @@ name: "Sync Crowdin translations (auth)"
on:
push:
paths:
# Run action when auth's intl_en.arb is changed
# Run workflow when auth's intl_en.arb is changed
- "mobile/lib/l10n/arb/app_en.arb"
# Or the workflow itself is changed
- ".github/workflows/auth-crowdin.yml"
branches: [main]
schedule:
# Run every 24 hours - https://crontab.guru/#0_*/24_*_*_*
- cron: "0 */24 * * *"
workflow_dispatch: # Allow manually running the action
# See: [Note: Run every 24 hours]
- cron: "50 1 * * *"
# Also allow manually running the workflow
workflow_dispatch:
jobs:
synchronize-with-crowdin:
@@ -32,8 +33,8 @@ jobs:
localization_branch_name: crowdin-translations-auth
create_pull_request: true
skip_untranslated_strings: true
pull_request_title: "New translations (auth)"
pull_request_body: "New translations via [Crowdin GH Action](https://github.com/crowdin/github-action)"
pull_request_title: "[auth] New translations"
pull_request_body: "New translations from [Crowdin](https://crowdin.com/project/ente-authenticator-app)"
pull_request_base_branch_name: "main"
project_id: 575169
env:

View File

@@ -1,14 +1,16 @@
name: "Lint (auth)"
on:
# Run on every push (this also covers pull requests)
# Run on every push to a branch other than main that changes auth/
push:
branches-ignore: [main]
paths:
# - But only if something changes inside auth
- "auth/**"
# - Or if the there is some change in workflow itself
- ".github/workflows/auth-lint.yml"
env:
FLUTTER_VERSION: "3.16.9"
jobs:
lint:
runs-on: ubuntu-latest
@@ -16,20 +18,21 @@ jobs:
run:
working-directory: auth
steps:
# Checkout our code, including submodules
- uses: actions/checkout@v4
- name: Checkout code and submodules
uses: actions/checkout@v4
with:
submodules: recursive
# Install Flutter
- uses: subosito/flutter-action@v2
- name: Install Flutter ${{ env.FLUTTER_VERSION }}
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: "3.13.4"
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
# Install dependencies
- run: flutter pub get
# Lint
- run: flutter analyze --no-fatal-infos
- name: Verify custom icon JSON
run: cat assets/custom-icons/_data/custom-icons.json | jq empty

View File

@@ -1,14 +1,13 @@
name: "Release (auth)"
# [Note: Testing release workflows that are triggered by tags]
#
# To test this out, push a tag with a pre-release version. The version number
# should be the version number of the next actual release.
#
# > When major, minor, and patch are equal, a pre-release version has lower
# > precedence than a normal version:
# >
# > Example: 1.0.0-alpha < 1.0.0.
# >
# > - https://semver.org
# > precedence than a normal version. Example: 1.0.0-alpha < 1.0.0.
# > https://semver.org
#
# So if the next release we intend to put out is 1.2.3, you can:
#
@@ -30,7 +29,7 @@ on:
- "auth-v*"
env:
FLUTTER_VERSION: "3.16.9"
FLUTTER_VERSION: "3.13.4"
jobs:
build-ubuntu:
@@ -38,7 +37,6 @@ jobs:
defaults:
run:
# Run all the "run" steps inside the auth directory
working-directory: auth
steps:
@@ -63,7 +61,7 @@ jobs:
- name: Create artifacts directory
run: mkdir artifacts
- name: Build Android APK
- name: Build independent APK
run: |
flutter build apk --release --flavor independent --dart-define=app.flavor=independent
mv build/app/outputs/flutter-apk/app-independent-release.apk artifacts/ente-${{ github.ref_name }}.apk
@@ -115,19 +113,16 @@ jobs:
uses: ncipollo/release-action@v1
with:
artifacts: "auth/artifacts/*"
prerelease: true
draft: true
allowUpdates: true
updateOnlyUnreleased: true
- name: Upload AAB to PlayStore
# Temporarily disable GP upload
if: false
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
packageName: io.ente.auth
releaseFiles: build/app/outputs/bundle/playstoreRelease/app-playstore-release.aab
releaseFiles: auth/build/app/outputs/bundle/playstoreRelease/app-playstore-release.aab
track: internal
build-windows:
@@ -135,7 +130,6 @@ jobs:
defaults:
run:
# Run all the "run" steps inside the auth directory
working-directory: auth
steps:
@@ -185,19 +179,10 @@ jobs:
if: false
run: tar.exe -a -c -f auth/artifacts/ente-${{ github.ref_name }}-windows.zip auth/ente-${{ github.ref_name }}-windows
- name: Temporary action
# TODO: Remove me when desktop builds are enabled
if: true
run: echo test > artifacts/ente-example.txt
- name: Generate checksums
run: sha256sum artifacts/ente-* > artifacts/sha256sum-win
- name: Create a draft GitHub release
uses: ncipollo/release-action@v1
with:
artifacts: "auth/artifacts/*"
prerelease: true
draft: true
allowUpdates: true
updateOnlyUnreleased: true
@@ -207,7 +192,6 @@ jobs:
defaults:
run:
# Run all the "run" steps inside the auth directory
working-directory: auth
steps:
@@ -295,19 +279,10 @@ jobs:
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
- name: Temporary action
# TODO: Remove me when desktop builds are enabled
if: true
run: echo test > artifacts/ente-example.txt
- name: Generate checksums
run: sha256sum artifacts/ente-* > artifacts/sha256sum-mac
- name: Create a draft GitHub release
uses: ncipollo/release-action@v1
with:
artifacts: "auth/artifacts/*"
prerelease: true
draft: true
allowUpdates: true
updateOnlyUnreleased: true

54
.github/workflows/cli-release.yml vendored Normal file
View File

@@ -0,0 +1,54 @@
name: "Release (cli)"
on:
push:
# Run when a tag matching the pattern "cli-v*"" is pushed
#
# Tip: to test this workflow, push at tag with a pre-release version,
# e.g. `cli-v1.2.3-test`, where 1.2.3 is the expected version number of
# the next release that'll go out.
#
# See: [Note: Testing release workflows that are triggered by tags]
tags:
- "cli-v*"
jobs:
draft-release:
runs-on: ubuntu-latest
steps:
- name: Create a draft GitHub release
uses: ncipollo/release-action@v1
with:
draft: true
build:
runs-on: ubuntu-latest
needs: draft-release
strategy:
matrix:
goos: [linux, windows, darwin]
goarch: ["386", amd64, arm64]
exclude:
- goarch: "386"
goos: darwin
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Build binaries and add to the release
uses: wangyoucao577/go-release-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
goos: ${{ matrix.goos }}
goarch: ${{ matrix.goarch }}
asset_name: ente-${{ github.ref_name }}-${{ matrix.goos }}-${{ matrix.goarch }}
release_name: ${{ github.ref_name }}
goversion: "1.20"
project_path: "./cli"
pre_command: export CGO_ENABLED=0
build_flags: "-trimpath"
ldflags: "-X main.AppVersion=${{ github.ref_name }} -s -w"
md5sum: false
sha256sum: true

47
.github/workflows/docs-deploy.yml vendored Normal file
View File

@@ -0,0 +1,47 @@
name: "Deploy (docs)"
on:
# Run on every push to main that changes docs/
push:
branches: [main]
paths:
- "docs/**"
- ".github/workflows/docs-deploy.yml"
# Also allow manually running the workflow
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
defaults:
run:
working-directory: docs
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup node and enable yarn caching
uses: actions/setup-node@v4
with:
node-version: 20
cache: "yarn"
cache-dependency-path: "docs/yarn.lock"
- name: Install dependencies
run: yarn install
- name: Build production site
# Will create docs/.vitepress/dist
run: yarn build
- name: Publish
uses: cloudflare/pages-action@1
with:
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
projectName: ente
branch: help
directory: docs/docs/.vitepress/dist
wranglerVersion: "3"

37
.github/workflows/docs-verify-build.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: "Verify build (docs)"
# Preflight build of docs. This allows us to ensure that yarn build is
# succeeding before we merge the PR into main.
on:
# Run on every push to a branch other than main that changes docs/
push:
branches-ignore: [main]
paths:
- "docs/**"
- ".github/workflows/docs-verify-build.yml"
jobs:
verify-build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: docs
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup node and enable yarn caching
uses: actions/setup-node@v4
with:
node-version: 20
cache: "yarn"
cache-dependency-path: "docs/yarn.lock"
- name: Install dependencies
run: yarn install
- name: Build production site
run: yarn build

View File

@@ -3,15 +3,16 @@ name: "Sync Crowdin translations (mobile)"
on:
push:
paths:
# Run action when mobiles's intl_en.arb is changed
# Run workflow when mobiles's intl_en.arb is changed
- "mobile/lib/l10n/intl_en.arb"
# Or the workflow itself is changed
- ".github/workflows/mobile-crowdin.yml"
branches: [main]
schedule:
# Run every 24 hours - https://crontab.guru/#0_*/24_*_*_*
- cron: "0 */24 * * *"
workflow_dispatch: # Allow manually running the action
# See: [Note: Run every 24 hours]
- cron: "40 1 * * *"
# Also allow manually running the workflow
workflow_dispatch:
jobs:
synchronize-with-crowdin:
@@ -32,8 +33,8 @@ jobs:
localization_branch_name: crowdin-translations-mobile
create_pull_request: true
skip_untranslated_strings: true
pull_request_title: "New translations (mobile)"
pull_request_body: "New translations via [Crowdin GH Action](https://github.com/crowdin/github-action)"
pull_request_title: "[mobile] New translations"
pull_request_body: "New translations from [Crowdin](https://crowdin.com/project/ente-photos-app)"
pull_request_base_branch_name: "main"
project_id: 574741
env:

View File

@@ -1,14 +1,16 @@
name: "Lint (mobile)"
on:
# Run on every push (this also covers pull requests)
# Run on every push to a branch other than main that changes mobile/
push:
branches-ignore: [main]
paths:
# - But only if something changes inside mobile
- "mobile/**"
# - Or if the there is some change in workflow itself
- ".github/workflows/mobile-lint.yml"
env:
FLUTTER_VERSION: "3.13.4"
jobs:
lint:
runs-on: ubuntu-latest
@@ -16,20 +18,18 @@ jobs:
run:
working-directory: mobile
steps:
# Checkout our code, including submodules
- uses: actions/checkout@v4
- name: Checkout code and submodules
uses: actions/checkout@v4
with:
submodules: recursive
# Install Flutter
- uses: subosito/flutter-action@v2
- name: Install Flutter ${{ env.FLUTTER_VERSION }}
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: "3.13.4"
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
# Install dependencies
- run: flutter pub get
# Lint
- run: flutter analyze --no-fatal-infos

58
.github/workflows/mobile-release.yml vendored Normal file
View File

@@ -0,0 +1,58 @@
name: "Release (photos independent)"
on:
workflow_dispatch: # Allow manually running the action
push:
# Run when a tag matching the pattern "photos-v*"" is pushed
# See: [Note: Testing release workflows that are triggered by tags]
tags:
- "photos-v*"
env:
FLUTTER_VERSION: "3.13.4"
jobs:
build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: mobile
steps:
- name: Checkout code and submodules
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Flutter ${{ env.FLUTTER_VERSION }}
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Setup keys
uses: timheuer/base64-to-file@v1
with:
fileName: "keystore/ente_photos_key.jks"
encodedString: ${{ secrets.SIGNING_KEY_PHOTOS }}
- name: Build independent APK
run: |
flutter build apk --release --flavor independent
mv build/app/outputs/flutter-apk/app-independent-release.apk build/app/outputs/flutter-apk/ente-${{ github.ref_name }}.apk
env:
SIGNING_KEY_PATH: "/home/runner/work/_temp/keystore/ente_photos_key.jks"
SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS_PHOTOS }}
SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD_PHOTOS }}
SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD_PHOTOS }}
- name: Checksum
run: sha256sum build/app/outputs/flutter-apk/ente.apk > build/app/outputs/flutter-apk/sha256sum
- name: Create a draft GitHub release
uses: ncipollo/release-action@v1
with:
artifacts: "mobile/build/app/outputs/flutter-apk/ente-${{ github.ref_name }}.apk,mobile/build/app/outputs/flutter-apk/sha256sum"
draft: true

31
.github/workflows/server-lint.yml vendored Normal file
View File

@@ -0,0 +1,31 @@
name: "Lint (server)"
on:
# Run on every push to a branch other than main that changes server/
push:
branches-ignore: [main]
paths:
- "server/**"
- ".github/workflows/server-lint.yml"
jobs:
lint:
runs-on: ubuntu-latest
defaults:
run:
working-directory: server
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup go
uses: actions/setup-go@v5
with:
go-version-file: "server/go.mod"
cache: true
- name: Install dependencies
run: sudo apt-get update && sudo apt-get install libsodium-dev
- name: Lint
run: "./scripts/lint.sh"

View File

@@ -1,16 +1,10 @@
name: Prod CI
name: "Release (server)"
on:
workflow_dispatch:
# Enable manual run
push:
# Sequence of patterns matched against refs/tags
tags:
- "v*" # Push events to matching v*, i.e. v4.2.0
workflow_dispatch: # Run manually
jobs:
build:
# This job will run on ubuntu virtual machine
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -19,6 +13,8 @@ jobs:
- uses: mr-smithers-excellent/docker-build-push@v6
name: Build & Push
with:
dockerfile: server/Dockerfile
directory: server
image: ente/museum-prod
registry: rg.fr-par.scw.cloud
enableBuildKit: true

View File

@@ -3,15 +3,16 @@ name: "Sync Crowdin translations (web)"
on:
push:
paths:
# Run action when web's en-US/translation.json is changed
# Run workflow when web's en-US/translation.json is changed
- "web/apps/photos/public/locales/en-US/translation.json"
# Or the workflow itself is changed
- ".github/workflows/web-crowdin.yml"
branches: [main]
schedule:
# Run every 24 hours - https://crontab.guru/#0_*/24_*_*_*
- cron: "0 */24 * * *"
workflow_dispatch: # Allow manually running the action
# See: [Note: Run every 24 hours]
- cron: "20 1 * * *"
# Also allow manually running the workflow
workflow_dispatch:
jobs:
synchronize-with-crowdin:
@@ -32,8 +33,8 @@ jobs:
localization_branch_name: crowdin-translations-web
create_pull_request: true
skip_untranslated_strings: true
pull_request_title: "New translations (web)"
pull_request_body: "New translations via [Crowdin GH Action](https://github.com/crowdin/github-action)"
pull_request_title: "[web] New translations"
pull_request_body: "New translations from [Crowdin](https://crowdin.com/project/ente-photos-web)"
pull_request_base_branch_name: "main"
project_id: 569613
env:

View File

@@ -0,0 +1,43 @@
name: "Deploy (accounts)"
on:
push:
# Run workflow on pushes to the deploy/accounts
branches: [deploy/accounts]
jobs:
deploy:
runs-on: ubuntu-latest
defaults:
run:
working-directory: web
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup node and enable yarn caching
uses: actions/setup-node@v4
with:
node-version: 20
cache: "yarn"
cache-dependency-path: "docs/yarn.lock"
- name: Install dependencies
run: yarn install
- name: Build accounts
run: yarn build:accounts
- name: Publish accounts
uses: cloudflare/pages-action@1
with:
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
projectName: ente
branch: deploy/accounts
directory: web/apps/accounts/out
wranglerVersion: "3"

43
.github/workflows/web-deploy-auth.yml vendored Normal file
View File

@@ -0,0 +1,43 @@
name: "Deploy (auth)"
on:
push:
# Run workflow on pushes to the deploy/auth
branches: [deploy/auth]
jobs:
deploy:
runs-on: ubuntu-latest
defaults:
run:
working-directory: web
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup node and enable yarn caching
uses: actions/setup-node@v4
with:
node-version: 20
cache: "yarn"
cache-dependency-path: "docs/yarn.lock"
- name: Install dependencies
run: yarn install
- name: Build auth
run: yarn build:auth
- name: Publish auth
uses: cloudflare/pages-action@1
with:
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
projectName: ente
branch: deploy/auth
directory: web/apps/auth/out
wranglerVersion: "3"

43
.github/workflows/web-deploy-cast.yml vendored Normal file
View File

@@ -0,0 +1,43 @@
name: "Deploy (cast)"
on:
push:
# Run workflow on pushes to the deploy/cast
branches: [deploy/cast]
jobs:
deploy:
runs-on: ubuntu-latest
defaults:
run:
working-directory: web
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup node and enable yarn caching
uses: actions/setup-node@v4
with:
node-version: 20
cache: "yarn"
cache-dependency-path: "docs/yarn.lock"
- name: Install dependencies
run: yarn install
- name: Build cast
run: yarn build:cast
- name: Publish cast
uses: cloudflare/pages-action@1
with:
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
projectName: ente
branch: deploy/cast
directory: web/apps/cast/out
wranglerVersion: "3"

43
.github/workflows/web-deploy-photos.yml vendored Normal file
View File

@@ -0,0 +1,43 @@
name: "Deploy (photos)"
on:
push:
# Run workflow on pushes to the deploy/photos
branches: [deploy/photos]
jobs:
deploy:
runs-on: ubuntu-latest
defaults:
run:
working-directory: web
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup node and enable yarn caching
uses: actions/setup-node@v4
with:
node-version: 20
cache: "yarn"
cache-dependency-path: "docs/yarn.lock"
- name: Install dependencies
run: yarn install
- name: Build photos
run: yarn build:photos
- name: Publish photos
uses: cloudflare/pages-action@1
with:
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
projectName: ente
branch: deploy/photos
directory: web/apps/photos/out
wranglerVersion: "3"

View File

@@ -1,12 +1,11 @@
name: "Lint (web)"
on:
# Run on every push (this also covers pull requests)
# Run on every push to a branch other than main that changes web/
push:
branches-ignore: [main]
paths:
# - But only if something changes inside web
- "web/**"
# - Or if the there is some change in workflow itself
- ".github/workflows/web-lint.yml"
jobs:
@@ -16,11 +15,16 @@ jobs:
run:
working-directory: web
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- name: Checkout code
uses: actions/checkout@v4
- name: Setup node and enable yarn caching
uses: actions/setup-node@v4
with:
node-version: 20
cache: "yarn"
cache-dependency-path: "web/yarn.lock"
- run: yarn install
- run: yarn lint

94
.github/workflows/web-nightly.yml vendored Normal file
View File

@@ -0,0 +1,94 @@
name: "Nightly (web)"
on:
schedule:
# [Note: Run every 24 hours]
#
# Run every 24 hours - First field is minute, second is hour of the day
# This runs 23:15 UTC everyday - 1 and 15 are just arbitrary offset to
# avoid scheduling it on the exact hour, as suggested by GitHub.
#
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule
# https://crontab.guru/
#
- cron: "15 23 * * *"
# Also allow manually running the workflow
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
defaults:
run:
working-directory: web
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup node and enable yarn caching
uses: actions/setup-node@v4
with:
node-version: 20
cache: "yarn"
cache-dependency-path: "docs/yarn.lock"
- name: Install dependencies
run: yarn install
- name: Build accounts
run: yarn build:accounts
- name: Publish accounts
uses: cloudflare/pages-action@1
with:
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
projectName: ente
branch: n-accounts
directory: web/apps/accounts/out
wranglerVersion: "3"
- name: Build auth
run: yarn build:auth
- name: Publish auth
uses: cloudflare/pages-action@1
with:
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
projectName: ente
branch: n-auth
directory: web/apps/auth/out
wranglerVersion: "3"
- name: Build cast
run: yarn build:cast
- name: Publish cast
uses: cloudflare/pages-action@1
with:
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
projectName: ente
branch: n-cast
directory: web/apps/cast/out
wranglerVersion: "3"
- name: Build photos
run: yarn build:photos
env:
NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT: https://albums.ente.sh
- name: Publish photos
uses: cloudflare/pages-action@1
with:
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
projectName: ente
branch: n-photos
directory: web/apps/photos/out
wranglerVersion: "3"

52
.github/workflows/web-preview.yml vendored Normal file
View File

@@ -0,0 +1,52 @@
name: "Preview (web)"
on:
workflow_dispatch:
inputs:
app:
description: "App to build and deploy"
type: choice
required: true
default: "photos"
options:
- "accounts"
- "auth"
- "cast"
- "photos"
jobs:
deploy:
runs-on: ubuntu-latest
defaults:
run:
working-directory: web
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup node and enable yarn caching
uses: actions/setup-node@v4
with:
node-version: 20
cache: "yarn"
cache-dependency-path: "docs/yarn.lock"
- name: Install dependencies
run: yarn install
- name: Build ${{ inputs.app }}
run: yarn build:${{ inputs.app }}
- name: Publish ${{ inputs.app }} to preview
uses: cloudflare/pages-action@1
with:
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
projectName: ente
branch: preview
directory: web/apps/${{ inputs.app }}/out
wranglerVersion: "3"

4
.gitmodules vendored
View File

@@ -9,10 +9,6 @@
[submodule "auth/assets/simple-icons"]
path = auth/assets/simple-icons
url = https://github.com/simple-icons/simple-icons.git
[submodule "desktop/thirdparty/next-electron-server"]
path = desktop/thirdparty/next-electron-server
url = https://github.com/ente-io/next-electron-server.git
branch = desktop
[submodule "mobile/thirdparty/flutter"]
path = mobile/thirdparty/flutter
url = https://github.com/flutter/flutter.git

View File

@@ -42,7 +42,7 @@ projects to get started:
If your language is not listed for translation, please [create a GitHub
issue](https://github.com/ente-io/ente/issues/new?title=Request+for+New+Language+Translation&body=Language+name%3A)
issue](https://github.com/ente-io/ente/issues/new?title=Request+for+New+Language+Translation&body=Language+name%3A+%0AProject%3A+auth%2Fphotos%2Fboth)
to have it added. It is okay to have partial translations. Once ~90% of the
strings in a language get translated, we will start surfacing it in the apps.
@@ -50,21 +50,22 @@ Thank you for your support.
## Document
_Coming soon!_
The help guides and FAQs for users of Ente products are also open source, and
can be edited in a wiki-esque manner by our community members. More than the
quantity, we feel this helps improve the quality and approachability of the
documentation by bringing in more diverse viewpoints and familiarity levels.
See [docs/](docs/README.md) for how to edit these documents.
## Code contributions
If you'd like to contribute code, it is best to start small.
Each of the individual product/platform specific directories in this repository
have instructions on setting up a dev environment and making changes. The issues
labelled "good first issues" should be good starting points. Once you have a
bearing, you can head on to issues labelled "help wanted".
and discussions (feature requests) labelled "good first issues" should be good
starting points. Once you have a bearing, you can head on to issues or
discussions labelled "help wanted".
If you're planning on adding a new feature or making any other substantial
change, please [discuss it with

View File

@@ -70,6 +70,7 @@ existing users will be grandfathered in.
[<img height="42" src=".github/assets/app-store-badge.svg">](https://apps.apple.com/app/id6444121398)
[<img height="42" src=".github/assets/play-store-badge.png">](https://play.google.com/store/apps/details?id=io.ente.auth)
[<img height="42" src=".github/assets/f-droid-badge.png">](https://f-droid.org/packages/io.ente.auth/)
[<img height="42" src=".github/assets/github-badge.png">](https://github.com/ente-io/ente/releases?q=tag%3Aauth-v2)
[<img height="42" src=".github/assets/web-badge.svg">](https://auth.ente.io)
</div>
@@ -98,6 +99,8 @@ connect with the community.
[![Discord](https://img.shields.io/discord/948937918347608085?style=for-the-badge&logo=Discord&logoColor=white&label=Discord)](https://discord.gg/z2YVKkycX3)
[![Ente's Blog RSS](https://img.shields.io/badge/blog-rss-F88900?style=for-the-badge&logo=rss&logoColor=white)](https://ente.io/blog/rss.xml)
[![Twitter](.github/assets/twitter.svg)](https://twitter.com/enteio) &nbsp; [![Mastodon](.github/assets/mastodon.svg)](https://mstdn.social/@ente)
---
## Security

View File

@@ -7,7 +7,7 @@ details as possible about whatever it is that you need help with, and we will
get back to you as soon as possible.
In some cases, your query might already have been answered in our help
documentation (_Coming soon!_).
documentation at [help.ente.io](https://help.ente.io).
Other ways to get in touch are:

View File

@@ -12,7 +12,7 @@ multi-device sync.
### Android
This repository's [GitHub
releases](https://github.com/ente-io/ente/releases/latest/download/ente-auth.apk)
releases](https://github.com/ente-io/ente/releases?q=tag%3Aauth-v2)
contains APKs, built straight from source. These builds keep themselves updated,
without relying on third party stores.

View File

@@ -27,6 +27,7 @@ linter:
- use_rethrow_when_possible
- directives_ordering
- always_use_package_imports
- unawaited_futures
analyzer:
errors:
@@ -45,6 +46,8 @@ analyzer:
prefer_const_declarations: warning
prefer_const_constructors_in_immutables: ignore # too many warnings
unawaited_futures: warning # convert to warning after fixing existing issues
avoid_renaming_method_parameters: ignore # incorrect warnings for `equals` overrides
exclude:

View File

@@ -56,11 +56,11 @@ android {
signingConfigs {
release {
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : file(System.getenv("SIGNING_KEY_PATH"))
keyAlias keystoreProperties['keyAlias'] ? keystoreProperties['keyAlias'] : System.getenv("SIGNING_KEY_ALIAS")
keyPassword keystoreProperties['keyPassword'] ? keystoreProperties['keyPassword'] : System.getenv("SIGNING_KEY_PASSWORD")
storePassword keystoreProperties['storePassword'] ? keystoreProperties['storePassword'] : System.getenv("SIGNING_STORE_PASSWORD")
}
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : System.getenv("SIGNING_KEY_PATH") ? file(System.getenv("SIGNING_KEY_PATH")) : null
keyAlias keystoreProperties['keyAlias'] ? keystoreProperties['keyAlias'] : System.getenv("SIGNING_KEY_ALIAS")
keyPassword keystoreProperties['keyPassword'] ? keystoreProperties['keyPassword'] : System.getenv("SIGNING_KEY_PASSWORD")
storePassword keystoreProperties['storePassword'] ? keystoreProperties['storePassword'] : System.getenv("SIGNING_STORE_PASSWORD")
}
}
flavorDimensions "default"

View File

@@ -35,6 +35,13 @@
<data android:scheme="otpauth" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="enteauth" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.

View File

@@ -30,11 +30,14 @@
{
"title": "Bitwarden"
},
{
"title": "Bloom Host",
"slug": "bloom_host"
},
{
"title": "BorgBase",
"altNames": ["borg"],
"slug": "BorgBase",
"hex": "222C31"
"slug": "BorgBase"
},
{
"title": "Brave Creators",
@@ -105,12 +108,11 @@
{
"title": "Gosuslugi",
"altNames": ["Госуслуги"],
"slug": "Gosuslugi",
"hex": "EE2F53"
"slug": "Gosuslugi"
},
{
"title": "Healthchecks.io",
"slug": "healthchecks",
"slug": "healthchecks"
},
{
"title": "ING"
@@ -123,13 +125,11 @@
},
{
"title": "IVPN",
"slug": "IVPN",
"hex": "FA3243"
"slug": "IVPN"
},
{
"title": "IceDrive",
"slug": "Icedrive",
"hex": "1F4FD0"
"slug": "Icedrive"
},
{
"title": "Jagex",
@@ -150,8 +150,7 @@
"title": "Kite"
},
{
"title": "Koofr",
"hex": "71BA05"
"title": "Koofr"
},
{
"title": "Kraken",
@@ -180,8 +179,7 @@
{
"title": "Murena",
"altNames": ["eCloud"],
"slug": "ecloud",
"hex": "EC6A55"
"slug": "ecloud"
},
{
"title": "Microsoft"
@@ -226,8 +224,7 @@
},
{
"title": "pCloud",
"slug": "pCloud",
"hex": "1EBCC5"
"slug": "pCloud"
},
{
"title": "Peerberry",
@@ -298,7 +295,7 @@
},
{
"title": "Synology DSM",
"slug": "synology_dsm",
"slug": "synology_dsm"
},
{
"title": "TCPShield",
@@ -367,8 +364,7 @@
{
"title": "Yandex",
"altNames": ["Ya", "Яндекс"],
"slug": "Yandex",
"hex": "FC3F1D"
"slug": "Yandex"
}
]
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -1,3 +1,12 @@
## Developer docs
Documentation and notes about more advanced or infrequently needed details.
### Running
If you're using VSCode, you can setup the launch configuration by copying the
template into your `.vscode` folder at the root of the project.
```bash
cp auth/docs/vscode/launch.json .vscode/
```

View File

@@ -1,26 +1,22 @@
# Releases
Create a PR to bump up the version number in `pubspec.yaml`.
Create a PR to bump up the version in `pubspec.yaml`. Once that is merged, tag
main, and push the tag.
Once that is merged, tag main (using the `auth-v1.2.3` format), and push the
tag. This'll trigger a GitHub workflow that:
```sh
git tag auth-v1.2.3
git push origin auth-v1.2.3
```
This'll trigger a GitHub workflow that:
* Creates a new draft GitHub release and attaches all the build artifacts to it
(mobile APKs and various desktop packages),
* Creates a new release in the internal track on Play Store.
(TODO(MR): Fix this after the monorepo move) Xcode Cloud has already been
configured and will automatically build and release to TestFlight when step 1
was merged to main (you can see logs under the PR checks).
If you want to make changes to the workflow itself, or test it out, you can push
a tag like `auth-v1.2.3-test` (where v1.2.3 is the next expected version that'll
go out). For more details, see the comments on top of the [auth-release
workflow](.github/workflows/auth-release.yml).
Once the workflow completes, go to the draft GitHub release it that was created.
Use the "Generate release notes" button after setting the "Previous tag" for the
last release of auth. The generated release note will contain all PRs and new
contributors from all the releases in the monorepo, so you'll need to filter
them to keep only the things that relate to auth.
Once the workflow completes, go to the draft GitHub release that was created.
Set "Previous tag" to the last release of auth and press "Generate release
notes". The generated release note will contain all PRs and new contributors
from all the releases in the monorepo, so you'll need to filter them to keep
only the things that relate to the auth.

View File

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

View File

@@ -71,8 +71,6 @@ PODS:
- move_to_background (0.0.1):
- Flutter
- MTBBarcodeScanner (5.0.11)
- open_filex (0.0.2):
- Flutter
- OrderedSet (5.0.0)
- package_info_plus (0.4.5):
- Flutter
@@ -126,7 +124,6 @@ DEPENDENCIES:
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
- local_auth_ios (from `.symlinks/plugins/local_auth_ios/ios`)
- move_to_background (from `.symlinks/plugins/move_to_background/ios`)
- open_filex (from `.symlinks/plugins/open_filex/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- privacy_screen (from `.symlinks/plugins/privacy_screen/ios`)
@@ -183,8 +180,6 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/local_auth_ios/ios"
move_to_background:
:path: ".symlinks/plugins/move_to_background/ios"
open_filex:
:path: ".symlinks/plugins/open_filex/ios"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
path_provider_foundation:
@@ -226,7 +221,6 @@ SPEC CHECKSUMS:
local_auth_ios: c6cf091ded637a88f24f86a8875d8b0f526e2605
move_to_background: 39a5b79b26d577b0372cbe8a8c55e7aa9fcd3a2d
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
open_filex: 6e26e659846ec990262224a12ef1c528bb4edbe4
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943

View File

@@ -37,6 +37,7 @@
<key>CFBundleURLSchemes</key>
<array>
<string>otpauth</string>
<string>enteauth</string>
</array>
</dict>
</array>

View File

@@ -6,6 +6,7 @@ import 'dart:typed_data';
import 'package:bip39/bip39.dart' as bip39;
import 'package:ente_auth/core/constants.dart';
import 'package:ente_auth/core/event_bus.dart';
import 'package:ente_auth/events/endpoint_updated_event.dart';
import 'package:ente_auth/events/signed_in_event.dart';
import 'package:ente_auth/events/signed_out_event.dart';
import 'package:ente_auth/models/key_attributes.dart';
@@ -42,6 +43,7 @@ class Configuration {
static const userIDKey = "user_id";
static const hasMigratedSecureStorageKey = "has_migrated_secure_storage";
static const hasOptedForOfflineModeKey = "has_opted_for_offline_mode";
static const endPointKey = "endpoint";
final List<String> onlineSecureKeys = [
keyKey,
secretKeyKey,
@@ -317,7 +319,12 @@ class Configuration {
}
String getHttpEndpoint() {
return endpoint;
return _preferences.getString(endPointKey) ?? endpoint;
}
Future<void> setHttpEndpoint(String endpoint) async {
await _preferences.setString(endPointKey, endpoint);
Bus.instance.fire(EndpointUpdatedEvent());
}
String? getToken() {

View File

@@ -167,7 +167,7 @@ class SuperLogging {
await setupLogDir();
}
if (sentryIsEnabled) {
setupSentry();
setupSentry().ignore();
}
Logger.root.level = Level.ALL;
@@ -250,7 +250,7 @@ class SuperLogging {
// add error to sentry queue
if (sentryIsEnabled && rec.error != null) {
_sendErrorToSentry(rec.error!, null);
_sendErrorToSentry(rec.error!, null).ignore();
}
}
@@ -289,7 +289,7 @@ class SuperLogging {
SuperLogging.setUserID(await _getOrCreateAnonymousUserID());
await for (final error in sentryQueueControl.stream.asBroadcastStream()) {
try {
Sentry.captureException(
await Sentry.captureException(
error,
);
} catch (e) {

View File

@@ -2,28 +2,24 @@ import 'dart:io';
import 'package:dio/dio.dart';
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/core/constants.dart';
import 'package:ente_auth/core/event_bus.dart';
import 'package:ente_auth/events/endpoint_updated_event.dart';
import 'package:fk_user_agent/fk_user_agent.dart';
import 'package:flutter/foundation.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:uuid/uuid.dart';
int kConnectTimeout = 15000;
class Network {
// apiEndpoint points to the Ente server's API endpoint
static const apiEndpoint = String.fromEnvironment(
"endpoint",
defaultValue: kDefaultProductionEndpoint,
);
late Dio _dio;
late Dio _enteDio;
Future<void> init() async {
await FkUserAgent.init();
final packageInfo = await PackageInfo.fromPlatform();
final preferences = await SharedPreferences.getInstance();
final endpoint = Configuration.instance.getHttpEndpoint();
_dio = Dio(
BaseOptions(
connectTimeout: kConnectTimeout,
@@ -34,10 +30,10 @@ class Network {
},
),
);
_dio.interceptors.add(RequestIdInterceptor());
_enteDio = Dio(
BaseOptions(
baseUrl: apiEndpoint,
baseUrl: endpoint,
connectTimeout: kConnectTimeout,
headers: {
HttpHeaders.userAgentHeader: FkUserAgent.userAgent,
@@ -46,7 +42,13 @@ class Network {
},
),
);
_enteDio.interceptors.add(EnteRequestInterceptor(preferences, apiEndpoint));
_setupInterceptors(endpoint);
Bus.instance.on<EndpointUpdatedEvent>().listen((event) {
final endpoint = Configuration.instance.getHttpEndpoint();
_enteDio.options.baseUrl = endpoint;
_setupInterceptors(endpoint);
});
}
Network._privateConstructor();
@@ -55,34 +57,41 @@ class Network {
Dio getDio() => _dio;
Dio get enteDio => _enteDio;
void _setupInterceptors(String endpoint) {
_dio.interceptors.clear();
_dio.interceptors.add(RequestIdInterceptor());
_enteDio.interceptors.clear();
_enteDio.interceptors.add(EnteRequestInterceptor(endpoint));
}
}
class RequestIdInterceptor extends Interceptor {
@override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
// ignore: prefer_const_constructors
options.headers.putIfAbsent("x-request-id", () => Uuid().v4().toString());
options.headers
.putIfAbsent("x-request-id", () => const Uuid().v4().toString());
return super.onRequest(options, handler);
}
}
class EnteRequestInterceptor extends Interceptor {
final SharedPreferences _preferences;
final String enteEndpoint;
final String endpoint;
EnteRequestInterceptor(this._preferences, this.enteEndpoint);
EnteRequestInterceptor(this.endpoint);
@override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
if (kDebugMode) {
assert(
options.baseUrl == enteEndpoint,
"interceptor should only be used for API endpoint",
options.baseUrl == endpoint,
"interceptor should only be used for API endpoint",
);
}
// ignore: prefer_const_constructors
options.headers.putIfAbsent("x-request-id", () => Uuid().v4().toString());
final String? tokenValue = _preferences.getString(Configuration.tokenKey);
options.headers
.putIfAbsent("x-request-id", () => const Uuid().v4().toString());
final String? tokenValue = Configuration.instance.getToken();
if (tokenValue != null) {
options.headers.putIfAbsent("X-Auth-Token", () => tokenValue);
}

View File

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

View File

@@ -1,43 +1,29 @@
import 'package:dio/dio.dart';
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/core/errors.dart';
import 'package:ente_auth/core/network.dart';
import 'package:ente_auth/models/authenticator/auth_entity.dart';
import 'package:ente_auth/models/authenticator/auth_key.dart';
class AuthenticatorGateway {
final Dio _dio;
final Configuration _config;
late String _basedEndpoint;
late Dio _enteDio;
AuthenticatorGateway(this._dio, this._config) {
_basedEndpoint = _config.getHttpEndpoint() + "/authenticator";
AuthenticatorGateway() {
_enteDio = Network.instance.enteDio;
}
Future<void> createKey(String encKey, String header) async {
await _dio.post(
_basedEndpoint + "/key",
await _enteDio.post(
"/authenticator/key",
data: {
"encryptedKey": encKey,
"header": header,
},
options: Options(
headers: {
"X-Auth-Token": _config.getToken(),
},
),
);
}
Future<AuthKey> getKey() async {
try {
final response = await _dio.get(
_basedEndpoint + "/key",
options: Options(
headers: {
"X-Auth-Token": _config.getToken(),
},
),
);
final response = await _enteDio.get("/authenticator/key");
return AuthKey.fromMap(response.data);
} on DioError catch (e) {
if (e.response != null && (e.response!.statusCode ?? 0) == 404) {
@@ -51,17 +37,12 @@ class AuthenticatorGateway {
}
Future<AuthEntity> createEntity(String encryptedData, String header) async {
final response = await _dio.post(
_basedEndpoint + "/entity",
final response = await _enteDio.post(
"/authenticator/entity",
data: {
"encryptedData": encryptedData,
"header": header,
},
options: Options(
headers: {
"X-Auth-Token": _config.getToken(),
},
),
);
return AuthEntity.fromMap(response.data);
}
@@ -71,50 +52,35 @@ class AuthenticatorGateway {
String encryptedData,
String header,
) async {
await _dio.put(
_basedEndpoint + "/entity",
await _enteDio.put(
"/authenticator/entity",
data: {
"id": id,
"encryptedData": encryptedData,
"header": header,
},
options: Options(
headers: {
"X-Auth-Token": _config.getToken(),
},
),
);
}
Future<void> deleteEntity(
String id,
) async {
await _dio.delete(
_basedEndpoint + "/entity",
await _enteDio.delete(
"/authenticator/entity",
queryParameters: {
"id": id,
},
options: Options(
headers: {
"X-Auth-Token": _config.getToken(),
},
),
);
}
Future<List<AuthEntity>> getDiff(int sinceTime, {int limit = 500}) async {
try {
final response = await _dio.get(
_basedEndpoint + "/entity/diff",
final response = await _enteDio.get(
"/authenticator/entity/diff",
queryParameters: {
"sinceTime": sinceTime,
"limit": limit,
},
options: Options(
headers: {
"X-Auth-Token": _config.getToken(),
},
),
);
final List<AuthEntity> authEntities = <AuthEntity>[];
final diff = response.data["diff"] as List;

View File

@@ -1 +1,408 @@
{}
{
"account": "حسابي",
"unlock": "فتح القفل",
"recoveryKey": "مفتاح الاسترداد",
"counterAppBarTitle": "العداد",
"@counterAppBarTitle": {
"description": "Text shown in the AppBar of the Counter Page"
},
"onBoardingBody": "النسخ الاحتياطي لأوامر 2FA",
"onBoardingGetStarted": "إبدأ الآن",
"setupFirstAccount": "إعداد الحساب الأول الخاص بك",
"importScanQrCode": "مسح رمز QR",
"qrCode": "رمز QR",
"importEnterSetupKey": "أدخِل مفتاح الإعداد",
"importAccountPageTitle": "أدخل تفاصيل الحساب",
"secretCanNotBeEmpty": "لا يمكن أن يكون رمز السر فارغ",
"bothIssuerAndAccountCanNotBeEmpty": "لا يمكن أن يكون المُصدر والحساب فارغًا",
"incorrectDetails": "بيانات غير صحيحة",
"pleaseVerifyDetails": "من فضلك تأكد من بياناتك وحاول مرة أخرى",
"codeIssuerHint": "المصدِّر",
"codeSecretKeyHint": "الرمز السري",
"codeAccountHint": "الحساب (you@domain.com)",
"accountKeyType": "نوع المفتاح",
"sessionExpired": "انتهت صلاحية الجلسة",
"@sessionExpired": {
"description": "Title of the dialog when the users current session is invalid/expired"
},
"pleaseLoginAgain": "الرجاء تسجيل الدخول مرة أخرى",
"loggingOut": "جاري تسجيل الخروج...",
"timeBasedKeyType": "على أساس الوقت (TOTP)",
"counterBasedKeyType": "القائم على العداد (HOTP)",
"saveAction": "حفظ",
"nextTotpTitle": "التالي",
"deleteCodeTitle": "حذف الرمز؟",
"deleteCodeMessage": "هل أنت متأكد من أنك تريد حذف هذا الرمز ؟ هذا الإجراء لا رجعة فيه.",
"viewLogsAction": "عرض السجل",
"sendLogsDescription": "هذا سوف يرسل عبر السجلات لمساعدتنا على تصحيح مشكلتك. وبينما نتخذ الاحتياطات لضمان عدم تسجيل المعلومات الحساسة، نشجعك على رؤية هذه السجلات قبل تقاسمها.",
"preparingLogsTitle": "جاري إعداد السجلات...",
"emailLogsTitle": "سجلات البريد الإلكتروني",
"emailLogsMessage": "الرجاء إرسال السجلات إلى {email}",
"@emailLogsMessage": {
"placeholders": {
"email": {
"type": "String"
}
}
},
"copyEmailAction": "نسخ البريد الإلكتروني",
"exportLogsAction": "تصدير السجلات",
"reportABug": "الابلاغ عن خلل تقني",
"crashAndErrorReporting": "الإبلاغ عن الأعطال والأخطاء",
"reportBug": "الإبلاغ عن خلل",
"emailUsMessage": "الرجاء مراسلتنا على {email}",
"@emailUsMessage": {
"placeholders": {
"email": {
"type": "String"
}
}
},
"contactSupport": "الاتصال بالدعم",
"rateUsOnStore": "قم بتقييمنا على {storeName}",
"blog": "المدونة",
"merchandise": "إدارة المنتجات",
"verifyPassword": "التحقق من كلمة المرور",
"pleaseWait": "الرجاء الإنتظار...",
"generatingEncryptionKeysTitle": "توليد مفاتيح التشفير...",
"recreatePassword": "إعادة كتابة كلمة المرور",
"recreatePasswordMessage": "الجهاز الحالي ليس قويًا بما يكفي للتحقق من كلمة المرور الخاصة بك، لذا نحتاج إلى إعادة إنشائها مرة واحدة بطريقة تعمل مع جميع الأجهزة.\n\nالرجاء تسجيل الدخول باستخدام مفتاح الاسترداد وإعادة إنشاء كلمة المرور الخاصة بك (يمكنك استخدام نفس كلمة المرور مرة أخرى إذا كنت ترغب في ذلك).",
"useRecoveryKey": "استخدم مفتاح الاسترداد",
"incorrectPasswordTitle": "كلمة المرور غير صحيحة",
"welcomeBack": "مرحبًا مجددًا!",
"madeWithLoveAtPrefix": "مصنوعة مع ❤️ في ",
"supportDevs": "اشترك في <bold-green>ente</bold-green> لدعمنا",
"supportDiscount": "استخدم رمز القسيمة \"AUTH\" للحصول على 10% خصم من السنة الأولى",
"changeEmail": "تغيير البريد الإلكتروني",
"changePassword": "تغيير كلمة المرور",
"data": "البيانات",
"importCodes": "رمزالاستيراد",
"importTypePlainText": "نص عادي",
"importTypeEnteEncrypted": "تصدير مشفر ente",
"passwordForDecryptingExport": "كلمة المرور لفك تشفير التصدير",
"passwordEmptyError": "لا يمكن أن تكون كلمة المرور فارغة",
"importFromApp": "استيراد الرموز من {appName}",
"importGoogleAuthGuide": "قم بتصدير حساباتك من Google Authenticator إلى رمز QR code باستخدام خيار \"Transfer Accounts\" ثم استخدم جهازًا آخر لمسح رمز الاستجابة السريعة ضوئيًا.\n\nنصيحة: يمكنك استخدام كاميرا الويب الخاصة بالكمبيوتر المحمول لالتقاط صورة لرمز الاستجابة السريعة.",
"importSelectJsonFile": "حدد ملف JSON",
"importSelectAppExport": "حدد ملف التصدير {appName}",
"importEnteEncGuide": "حدد ملف JSON المشفر الذي تم تصديره من ente",
"importRaivoGuide": "استخدم خيار تصدير OTP إلى أرشيف Zip في إعدادات Raivo.\n\nاستخرج ملف zip واسترد ملف JSON.",
"importBitwardenGuide": "استخدم خيار \"تصدير خزانة\" داخل أدوات Bitwarden واستيراد ملف JSON غير مشفر.",
"importAegisGuide": "استخدم خيار \"Export the vault\" في إعدادات Aegis.\n\nإذا كان المخزن الخاص بك مشفرًا، فستحتاج إلى إدخال كلمة مرور المخزن لفك تشفير المخزن.",
"import2FasGuide": "استخدم خيار \"الإعدادات -> النسخ الاحتياطي - التصدير\" في 2FAS.\n\nإذا تم تشفير النسخة الاحتياطية، سوف تحتاج إلى إدخال كلمة المرور لفك تشفير النسخة الاحتياطية",
"importLastpassGuide": "استخدم خيار \"حسابات النقل\" ضمن إعدادات مصادقة Lastpass، واضغط على \"تصدير الحسابات إلى الملف\". استيراد JSON الذي تم تنزيله.",
"exportCodes": "تصدير الرموز",
"importLabel": "استيراد",
"importInstruction": "الرجاء تحديد ملف يحتوي على قائمة بالرموز الخاصة بك بالشكل التالي",
"importCodeDelimiterInfo": "يمكن فصل الرموز بفاصلة أو سطر جديد",
"selectFile": "اختيار الملف",
"emailVerificationToggle": "تأكيد عنوان البريد الإلكتروني",
"emailVerificationEnableWarning": "لتجنب إقفال حسابك، تأكد من تخزين نسخة من بريدك الإلكتروني 2FA خارج Ente Auth قبل تمكين التحقق من البريد الإلكتروني.",
"authToChangeEmailVerificationSetting": "الرجاء المصادقة لتغيير التحقق من البريد الإلكتروني",
"authToViewYourRecoveryKey": "الرجاء المصادقة لعرض مفتاح الاسترداد الخاص بك",
"authToChangeYourEmail": "الرجاء المصادقة لتغيير بريدك الإلكتروني",
"authToChangeYourPassword": "الرجاء المصادقة لتغيير كلمة المرور الخاصة بك",
"authToViewSecrets": "الرجاء المصادقة لعرض مفتاح الاسترداد الخاص بك",
"authToInitiateSignIn": "الرجاء المصادقة لبدء تسجيل الدخول للنسخ الاحتياطي.",
"ok": "حسناً",
"cancel": "إلغاء",
"yes": "نعم",
"no": "لا",
"email": "البريد الإلكتروني",
"support": "الدعم",
"general": "العامة",
"settings": "الإعدادات",
"copied": "تم النسخ",
"pleaseTryAgain": "حاول مرة اخرى",
"existingUser": "المستخدم موجود",
"newUser": "جديد إلى Ente",
"delete": "حذف",
"enterYourPasswordHint": "أدخل كلمة المرور الخاصة بك",
"forgotPassword": "هل نسيت كلمة المرور",
"oops": "عذرًا",
"suggestFeatures": "اقتراح ميزة",
"faq": "الأسئلة الأكثر شيوعاً",
"faq_q_1": "ما مدى أمان المصادقة؟",
"faq_a_1": "يتم تشفير جميع الرموز التي تقوم بنسخها احتياطا عبر Ente. وهذا يعني أنه يمكنك فقط الوصول إلى الرموز الخاصة بك. تطبيقاتنا مفتوحة المصدر وقد تم مراجعة التشفير خارجيا.",
"faq_q_2": "هل يمكنني الوصول إلى رموزي على سطح المكتب؟",
"faq_a_2": "يمكنك الوصول إلى رموزك على الويب @ auth.ente.io.",
"faq_q_3": "كيف يمكنني حذف الرموز؟",
"faq_a_3": "يمكنك حذف الرمز عن طريق السحب لليسار على هذا العنصر.",
"faq_q_4": "كيف يمكنني دعم هذا المشروع؟",
"faq_a_4": "يمكنك دعم تطوير هذا المشروع عن طريق الاشتراك في تطبيق الصور @ ente.io.",
"faq_q_5": "كيف يمكنني تمكين قفل FaceID في المصادقة Ente",
"faq_a_5": "يمكنك تمكين قفل FaceID تحت الإعدادات => الحماية => قفل الشاشة.",
"somethingWentWrongMessage": "حدث خطأ ما، يرجى المحاولة مرة أخرى",
"leaveFamily": "مغادرة خطة العائلة",
"leaveFamilyMessage": "هل أنت متأكد من الخروج من خطة العائلة؟",
"inFamilyPlanMessage": "أنت مندرج ضمن خطة عائلية!",
"swipeHint": "اسحب لليسار لتحرير أو إزالة الرموز",
"scan": "مسح",
"scanACode": "فحص رمز Qr",
"verify": "التحقق",
"verifyEmail": "تأكيد البريد الإلكتروني",
"enterCodeHint": "أدخل الرمز المكون من 6 أرقام من\nتطبيق المصادقة",
"lostDeviceTitle": "جهاز مفقود ؟",
"twoFactorAuthTitle": "المصادقة الثنائية",
"recoverAccount": "إسترجاع الحساب",
"enterRecoveryKeyHint": "أدخل رمز الاسترداد",
"recover": "استرداد",
"contactSupportViaEmailMessage": "الرجاء إسقاط بريد إلكتروني إلى {email} من عنوان بريدك الإلكتروني المسجل",
"@contactSupportViaEmailMessage": {
"placeholders": {
"email": {
"type": "String"
}
}
},
"noRecoveryKeyTitle": "لا يوجد مفتاح استرجاع؟",
"enterEmailHint": "أدخل عنوان البريد الإلكتروني الخاص بك",
"invalidEmailTitle": "عنوان البريد الإلكتروني غير صالح",
"invalidEmailMessage": "الرجاء إدخال بريد إلكتروني صالح.",
"deleteAccount": "إزالة الحساب",
"deleteAccountQuery": "سوف نأسف لرؤيتك تذهب. هل تواجه بعض المشاكل؟",
"yesSendFeedbackAction": "نعم، ارسل الملاحظات",
"noDeleteAccountAction": "لا، حذف الحساب",
"initiateAccountDeleteTitle": "الرجاء المصادقة لبدء حذف الحساب",
"sendEmail": "ارسل بريد الكتروني",
"createNewAccount": "إنشاء حساب جديد",
"weakStrength": "ضعيف",
"strongStrength": "قوي",
"moderateStrength": "متوسط",
"confirmPassword": "تأكيد كلمة المرور",
"close": "إغلاق",
"oopsSomethingWentWrong": "المعذرة! حدث خطأ ما.",
"selectLanguage": "اختر اللغة",
"language": "اللغة",
"social": "وسائل التواصل",
"security": "الأمان",
"lockscreen": "شاشة القفل",
"authToChangeLockscreenSetting": "الرجاء المصادقة لتغيير إعدادات شاشة القفل",
"lockScreenEnablePreSteps": "لتمكين شاشة القفل، الرجاء إعداد رمز مرور الجهاز أو قفل الشاشة في إعدادات النظام الخاص بك.",
"viewActiveSessions": "عرض الجلسات النشطة",
"authToViewYourActiveSessions": "الرجاء المصادقة لعرض جلساتك النشطة",
"searchHint": "بحث...",
"search": "بحث",
"sorryUnableToGenCode": "عذراً، غير قادر على إنشاء رمز ل {issuerName}",
"noResult": "لا توجد نتيجة",
"addCode": "أضف رمز",
"scanAQrCode": "مسح رمز QR",
"enterDetailsManually": "أدخل التفاصيل يدوياً",
"edit": "تعديل",
"copiedToClipboard": "تم النسخ إلى الحافظة",
"copiedNextToClipboard": "تم نسخ الرموز التالية إلى الحافظة",
"error": "خطأ",
"recoveryKeyCopiedToClipboard": "تم نسخ عبارة الاسترداد للحافظة",
"recoveryKeyOnForgotPassword": "إذا نسيت كلمة المرور الخاصة بك، فالطريقة الوحيدة التي يمكنك بها استرداد بياناتك هي بهذا المفتاح.",
"recoveryKeySaveDescription": "نحن لا نخزن هذا المفتاح، يرجى حفظ مفتاح الـ 24 كلمة هذا في مكان آمن.",
"doThisLater": "قم بهذا لاحقاً",
"saveKey": "حفظ المفتاح",
"back": "الرجوع",
"createAccount": "إنشاء حساب",
"passwordStrength": "قوة كلمة المرور: {passwordStrengthValue}",
"@passwordStrength": {
"description": "Text to indicate the password strength",
"placeholders": {
"passwordStrengthValue": {
"description": "The strength of the password as a string",
"type": "String",
"example": "Weak or Moderate or Strong"
}
},
"message": "Password Strength: {passwordStrengthText}"
},
"password": "كلمة المرور",
"signUpTerms": "أوافق على <u-terms>شروط الخدمة</u-terms> و<u-policy>سياسة الخصوصية</u-policy>",
"privacyPolicyTitle": "سياسة الخصوصية",
"termsOfServicesTitle": "الشروط",
"encryption": "التشفير",
"setPasswordTitle": "تعيين كلمة المرور",
"changePasswordTitle": "تغيير كلمة المرور",
"resetPasswordTitle": "إعادة تعيين كلمة المرور",
"encryptionKeys": "مفاتيح التشفير",
"passwordWarning": "نحن لا نقوم بتخزين كلمة المرور هذه، لذا إذا نسيتها، <underline>لا يمكننا فك تشفير بياناتك</underline>",
"enterPasswordToEncrypt": "أدخل كلمة المرور التي يمكننا استخدامها لتشفير بياناتك",
"enterNewPasswordToEncrypt": "أدخل كلمة مرور جديدة يمكننا استخدامها لتشفير بياناتك",
"passwordChangedSuccessfully": "تم تغيير كلمة المرور بنجاح",
"generatingEncryptionKeys": "توليد مفاتيح التشفير...",
"continueLabel": "المتابعة",
"insecureDevice": "جهاز غير آمن",
"sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "عذرًا، لم نتمكن من إنشاء مفاتيح آمنة على هذا الجهاز.\n\nيرجى التسجيل من جهاز مختلف.",
"howItWorks": "كيف يعمل",
"ackPasswordLostWarning": "أنا أفهم أنه إذا فقدت كلمة المرور الخاصة بي، قد أفقد بياناتي لأن بياناتي هي <underline>مشفرة من الند للند</underline>.",
"loginTerms": "بالنقر على تسجيل الدخول، أوافق على شروط الخدمة <u-terms></u-terms> و <u-policy>سياسة الخصوصية</u-policy>",
"logInLabel": "تسجيل الدخول",
"logout": "تسجيل الخروج",
"areYouSureYouWantToLogout": "هل أنت متأكد من أنك تريد تسجيل الخروج؟",
"yesLogout": "نعم، تسجيل الخروج",
"exit": "خروج",
"verifyingRecoveryKey": "التحقق من مفتاح الاسترداد...",
"recoveryKeyVerified": "تم التحقق من مفتاح الاسترداد",
"recoveryKeySuccessBody": "رائع! مفتاح الاسترداد الخاص بك صالح. شكرا لك على التحقق.\n\nيرجى تذكر الاحتفاظ بنسخة احتياطية من مفتاح الاسترداد بشكل آمن.",
"invalidRecoveryKey": "مفتاح الاسترداد الذي أدخلته غير صالح. الرجاء التأكد من أنه يحتوي على 24 كلمة، والتحقق من تهجئة كل منها.\n\nإذا قمت بإدخال رمز الاسترداد القديم، تأكد من أن طوله 64 حرفاً، وتحقق من كل منها.",
"recreatePasswordTitle": "إعادة كتابة كلمة المرور",
"recreatePasswordBody": "الجهاز الحالي ليس قويًا بما يكفي للتحقق من كلمة المرور الخاصة بك، لذا نحتاج إلى إعادة إنشائها مرة واحدة بطريقة تعمل مع جميع الأجهزة.\n\nالرجاء تسجيل الدخول باستخدام مفتاح الاسترداد وإعادة إنشاء كلمة المرور الخاصة بك (يمكنك استخدام نفس كلمة المرور مرة أخرى إذا كنت ترغب في ذلك).",
"invalidKey": "المفتاح غير صالح",
"tryAgain": "حاول مرة أخرى",
"viewRecoveryKey": "عرض مفتاح الاسترداد",
"confirmRecoveryKey": "تأكيد مفتاح الاسترداد",
"recoveryKeyVerifyReason": "مفتاح الاسترداد الخاص بك هو الطريقة الوحيدة لاسترداد صورك إذا نسيت كلمة المرور الخاصة بك. يمكنك العثور على مفتاح الاسترداد الخاص بك في الإعدادات > الحساب.\n\nالرجاء إدخال مفتاح الاسترداد الخاص بك هنا للتحقق من أنك قمت بحفظه بشكل صحيح.",
"confirmYourRecoveryKey": "تأكيد مفتاح الاسترداد",
"confirm": "تأكيد",
"emailYourLogs": "إرسال السجلات عبر البريد الإلكتروني",
"pleaseSendTheLogsTo": "الرجاء إرسال السجلات إلى {toEmail}",
"copyEmailAddress": "نسخ عنوان البريد الإلكتروني",
"exportLogs": "تصدير السجلات",
"enterYourRecoveryKey": "أدخل رمز الاسترداد",
"tempErrorContactSupportIfPersists": "يبدو أنه حدث خطأ ما. الرجاء إعادة المحاولة لاحقا. إذا استمر الخطأ، يرجى الاتصال بفريق الدعم.",
"itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "يبدو أنه حدث خطأ ما. الرجاء إعادة المحاولة لاحقا. إذا استمر الخطأ، يرجى الاتصال بفريق الدعم.",
"about": "حول",
"weAreOpenSource": "الخدمة مفتوحة المصدر!",
"privacy": "الخصوصية",
"terms": "الشروط",
"checkForUpdates": "بحث عن تحديثات",
"downloadUpdate": "تحميل",
"criticalUpdateAvailable": "تحديث حاسم متوفر",
"updateAvailable": "التحديث متاح",
"update": "تحديث",
"checking": "جارٍ التحقق...",
"youAreOnTheLatestVersion": "أنت في الإصدار الأخير",
"warning": "تحذير",
"exportWarningDesc": "الملف الذي تم تصديره يحتوي على معلومات حساسة. الرجاء تخزين هذا بشكل آمن.",
"iUnderStand": "فهمت",
"@iUnderStand": {
"description": "Text for the button to confirm the user understands the warning"
},
"authToExportCodes": "الرجاء المصادقة لتصدير الرموز الخاصة بك",
"importSuccessTitle": "مرحى!",
"importSuccessDesc": "لقد استوردت {count} رمز!",
"@importSuccessDesc": {
"placeholders": {
"count": {
"description": "The number of codes imported",
"type": "int",
"example": "1"
}
}
},
"sorry": "المعذرة",
"importFailureDesc": "تعذر تحليل الملف المحدد.\nالرجاء الكتابة إلى support@ente.io إذا كنت بحاجة إلى مساعدة!",
"pendingSyncs": "تحذير",
"pendingSyncsWarningBody": "لم يتم نسخ بعض رموزك احتياطيًا.\n\nيرجى التأكد من أن لديك نسخة احتياطية لهذه الرموز قبل تسجيل الخروج.",
"checkInboxAndSpamFolder": "الرجاء التحقق من صندوق الوارد (والرسائل غير المرغوب فيها) لإكمال التحقق",
"tapToEnterCode": "انقر لإدخال الرمز",
"resendEmail": "إعادة إرسال البريد الإلكتروني",
"weHaveSendEmailTo": "لقد أرسلنا رسالة إلى <green>{email}</green>",
"@weHaveSendEmailTo": {
"description": "Text to indicate that we have sent a mail to the user",
"placeholders": {
"email": {
"description": "The email address of the user",
"type": "String",
"example": "example@ente.io"
}
}
},
"activeSessions": "الجلسات النشطة",
"somethingWentWrongPleaseTryAgain": "حدث خطأ ما، يرجى المحاولة مرة أخرى",
"thisWillLogYouOutOfThisDevice": "سيؤدي هذا إلى تسجيل خروجك من هذا الجهاز!",
"thisWillLogYouOutOfTheFollowingDevice": "سيؤدي هذا إلى تسجيل خروجك من هذا الجهاز:",
"terminateSession": "إنهاء الجلسة؟",
"terminate": "إنهاء",
"thisDevice": "هذا الجهاز",
"toResetVerifyEmail": "لإعادة تعيين كلمة المرور الخاصة بك، يرجى التحقق من بريدك الإلكتروني أولاً.",
"thisEmailIsAlreadyInUse": "هذا البريد مستخدم مسبقاً",
"verificationFailedPleaseTryAgain": "فشل في المصادقة ، يرجى المحاولة مرة أخرى في وقت لاحق",
"yourVerificationCodeHasExpired": "انتهت صلاحية رمز التحقق",
"incorrectCode": "رمز غير صحيح",
"sorryTheCodeYouveEnteredIsIncorrect": "عذراً، الرمز الذي أدخلته غير صحيح",
"emailChangedTo": "تم تغيير البريد الإلكتروني إلى {newEmail}",
"authenticationFailedPleaseTryAgain": "فشلت المصادقة. الرجاء المحاولة مرة أخرى",
"authenticationSuccessful": "تمت المصادقة بنجاح!",
"twofactorAuthenticationSuccessfullyReset": "تم تحديث المصادقة الثنائية بنجاح",
"incorrectRecoveryKey": "مفتاح الاسترداد غير صحيح",
"theRecoveryKeyYouEnteredIsIncorrect": "مفتاح الاسترداد الذي أدخلته غير صحيح",
"enterPassword": "أدخل كلمة المرور",
"selectExportFormat": "اختر صيغة التصدير",
"exportDialogDesc": "سيتم حماية الصادرات المشفرة بكلمة مرور من اختيارك.",
"encrypted": "مشفَّرة",
"plainText": "نص عادي",
"passwordToEncryptExport": "كلمة المرور لتشفير التصدير",
"export": "تصدير",
"useOffline": "استخدام بدون نسخ إحتياطية",
"signInToBackup": "قم بتسجيل الدخول للنسخ الاحتياطي للرموز الخاصة بك",
"singIn": "تسجل الدخول",
"sigInBackupReminder": "يرجى تصدير الرموز الخاصة بك للتأكد من أن لديك نسخة احتياطية يمكنك استعادتها منها.",
"offlineModeWarning": "لقد اخترت المضي قدما بدون نسخ احتياطية. يرجى أخذ نسخ احتياطية يدوية للتأكد من سلامة الرموز الخاصة بك.",
"showLargeIcons": "إظهار أيقونات كبيرة",
"shouldHideCode": "إخفاء الرموز",
"doubleTapToViewHiddenCode": "يمكنك النقر مرتين على أي عنصر لعرض الرمز",
"focusOnSearchBar": "التركيز على البحث عند بدء التطبيق",
"confirmUpdatingkey": "هل أنت متأكد من أنك تريد تحديث المفتاح السري؟",
"minimizeAppOnCopy": "تصغير التطبيق عند النسخ",
"editCodeAuthMessage": "المصادقة لتعديل الرمز",
"deleteCodeAuthMessage": "المصادقة لحذف الرمز",
"showQRAuthMessage": "المصادقة لإظهار رمز QR",
"confirmAccountDeleteTitle": "تأكيد حذف الحساب",
"confirmAccountDeleteMessage": "هذا الحساب مرتبط بتطبيقات Ente أخرى، إذا كنت تستخدم أي منها.\n\nبياناتك التي تم تحميلها، عبر جميع تطبيقات Ente سيتم جدولتها للحذف، وسيتم حذف حسابك بشكل دائم.",
"androidBiometricHint": "التحقق من الهوية",
"@androidBiometricHint": {
"description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters."
},
"androidBiometricNotRecognized": "لم يتم التعرف عليه. حاول مرة أخرى.",
"@androidBiometricNotRecognized": {
"description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters."
},
"androidBiometricSuccess": "تم بنجاح",
"@androidBiometricSuccess": {
"description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters."
},
"androidCancelButton": "إلغاء",
"@androidCancelButton": {
"description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters."
},
"androidSignInTitle": "المصادقة مطلوبة",
"@androidSignInTitle": {
"description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters."
},
"androidBiometricRequiredTitle": "البيومترية مطلوبة",
"@androidBiometricRequiredTitle": {
"description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters."
},
"androidDeviceCredentialsRequiredTitle": "بيانات اعتماد الجهاز مطلوبة",
"@androidDeviceCredentialsRequiredTitle": {
"description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters."
},
"androidDeviceCredentialsSetupDescription": "بيانات اعتماد الجهاز مطلوبة",
"@androidDeviceCredentialsSetupDescription": {
"description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side."
},
"goToSettings": "الانتقال إلى الإعدادات",
"@goToSettings": {
"description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters."
},
"androidGoToSettingsDescription": "لم يتم إعداد المصادقة الحيوية على جهازك. انتقل إلى 'الإعدادات > الأمن' لإضافة المصادقة البيومترية.",
"@androidGoToSettingsDescription": {
"description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side."
},
"iOSLockOut": "المصادقة البيومترية معطلة. الرجاء قفل الشاشة وفتح القفل لتفعيلها.",
"@iOSLockOut": {
"description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side."
},
"iOSGoToSettingsDescription": "لم يتم إعداد المصادقة البيومترية على جهازك. الرجاء تمكين معرف اللمس أو معرف الوجه على هاتفك.",
"@iOSGoToSettingsDescription": {
"description": "Message advising the user to go to the settings and configure Biometrics for their device. It shows in a dialog on iOS side."
},
"iOSOkButton": "حسناً",
"@iOSOkButton": {
"description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters."
},
"noInternetConnection": "لا يوجد اتصال بالإنترنت",
"pleaseCheckYourInternetConnectionAndTryAgain": "يرجى التحقق من اتصالك بالإنترنت ثم المحاولة من جديد.",
"signOutFromOtherDevices": "تسجيل الخروج من الأجهزة الأخرى",
"signOutOtherBody": "إذا كنت تعتقد أن شخصا ما يعرف كلمة المرور الخاصة بك، يمكنك إجبار جميع الأجهزة الأخرى الستخدمة حاليا لحسابك على تسجيل الخروج.",
"signOutOtherDevices": "تسجيل الخروج من الأجهزة الأخرى",
"doNotSignOut": "لا تقم بتسجيل الخروج",
"hearUsWhereTitle": "كيف سمعت عن Ente؟ (اختياري)",
"hearUsExplanation": "نحن لا نتتبع تثبيت التطبيق. سيكون من المفيد إذا أخبرتنا أين وجدتنا!"
}

View File

@@ -59,7 +59,7 @@
}
},
"contactSupport": "Contact support",
"rateUsOnStore" : "Rate us on {storeName}",
"rateUsOnStore": "Rate us on {storeName}",
"blog": "Blog",
"merchandise": "Merchandise",
"verifyPassword": "Verify password",
@@ -133,7 +133,6 @@
"faq_q_5": "How can I enable FaceID lock in ente Auth",
"faq_a_5": "You can enable FaceID lock under Settings → Security → Lockscreen.",
"somethingWentWrongMessage": "Something went wrong, please try again",
"leaveFamily": "Leave family",
"leaveFamilyMessage": "Are you sure that you want to leave the family plan?",
"inFamilyPlanMessage": "You are on a family plan!",
@@ -145,6 +144,8 @@
"enterCodeHint": "Enter the 6-digit code from\nyour authenticator app",
"lostDeviceTitle": "Lost device?",
"twoFactorAuthTitle": "Two-factor authentication",
"passkeyAuthTitle": "Passkey verification",
"verifyPasskey": "Verify passkey",
"recoverAccount": "Recover account",
"enterRecoveryKeyHint": "Enter your recovery key",
"recover": "Recover",
@@ -337,10 +338,10 @@
"offlineModeWarning": "You have chosen to proceed without backups. Please take manual backups to make sure your codes are safe.",
"showLargeIcons": "Show large icons",
"shouldHideCode": "Hide codes",
"doubleTapToViewHiddenCode" : "You can double tap on an entry to view code",
"doubleTapToViewHiddenCode": "You can double tap on an entry to view code",
"focusOnSearchBar": "Focus search on app start",
"confirmUpdatingkey": "Are you sure you want to update the secret key?",
"minimizeAppOnCopy": "Minimize app on copy",
"minimizeAppOnCopy": "Minimize app on copy",
"editCodeAuthMessage": "Authenticate to edit code",
"deleteCodeAuthMessage": "Authenticate to delete code",
"showQRAuthMessage": "Authenticate to show QR code",
@@ -405,5 +406,15 @@
"signOutOtherDevices": "Sign out other devices",
"doNotSignOut": "Do not sign out",
"hearUsWhereTitle": "How did you hear about Ente? (optional)",
"hearUsExplanation": "We don't track app installs. It'd help if you told us where you found us!"
}
"hearUsExplanation": "We don't track app installs. It'd help if you told us where you found us!",
"waitingForBrowserRequest": "Waiting for browser request...",
"waitingForVerification": "Waiting for verification...",
"passkey": "Passkey",
"developerSettingsWarning":"Are you sure that you want to modify Developer settings?",
"developerSettings": "Developer settings",
"serverEndpoint": "Server endpoint",
"invalidEndpoint": "Invalid endpoint",
"invalidEndpointMessage": "Sorry, the endpoint you entered is invalid. Please enter a valid endpoint and try again.",
"endpointUpdatedMessage": "Endpoint updated successfully",
"customEndpoint": "Connected to {endpoint}"
}

View File

@@ -1,5 +1,6 @@
{
"account": "Cuenta",
"unlock": "Desbloquear",
"recoveryKey": "Clave de recuperación",
"counterAppBarTitle": "Contador",
"@counterAppBarTitle": {
@@ -83,9 +84,13 @@
"importFromApp": "Importar códigos de {appName}",
"importGoogleAuthGuide": "Exportar tus cuentas desde Google Authenticator a un código QR usando la opción \"Transferir Cuentas\". A continuación, usando otro dispositivo, escanee el código QR.\n\nConsejo: Puede usar la webcam de su portátil para tomar una foto del código QR.",
"importSelectJsonFile": "Seleccione el archivo JSON",
"importSelectAppExport": "Seleccione el archivo de exportación de {appName}",
"importEnteEncGuide": "Seleccione el archivo JSON cifrado exportado desde ente",
"importRaivoGuide": "Utilice la opción \"Exportar códigos a un archivo de Zip\" en la configuración de Raivo.\n\nExtraiga el archivo zip e importe el archivo JSON.",
"importBitwardenGuide": "Use la opción \"Exportar caja fuerte\" dentro del menú Herramientas de Bitwarden e importe el fichero JSON no crifrado.",
"importAegisGuide": "Utilice la opción \"Exportar la bóveda\" en ajustes de Aegis.\n\nSi tu bóveda es cifrada, necesitara entrar contraseña de bóveda para descifrar la bóveda.",
"import2FasGuide": "Use la opción \"Configuración→Copia de seguridad→Exportar\" en 2FAS\n\nSi su copia de seguridad está cifrada, necesitará introducir la contraseña para descifrarla",
"importLastpassGuide": "Utilice la opción \"Transferir cuentas\" en la configuración del autenticador de Lastpass y pulse \"Exportar cuentas al archivo\". Importe el archivo JSON descargado.",
"exportCodes": "Exportar códigos",
"importLabel": "Importar",
"importInstruction": "Por favor, seleccione un archivo que contenga una lista de sus códigos en el siguiente formato",
@@ -97,6 +102,8 @@
"authToViewYourRecoveryKey": "Por favor, autentifíquese para ver su clave de recuperación",
"authToChangeYourEmail": "Por favor, autentifíquese para cambiar su correo electrónico",
"authToChangeYourPassword": "Por favor, autentifíquese para cambiar su contraseña",
"authToViewSecrets": "Por favor, autentifíquese para ver sus secretos",
"authToInitiateSignIn": "Por favor, autentifíquese para iniciar la sesión para realizar la copia de seguridad.",
"ok": "Ok",
"cancel": "Cancelar",
"yes": "Si",
@@ -329,6 +336,7 @@
"offlineModeWarning": "Ha elegido proceder sin copia de seguridad. Por favor, tome copias de seguridad manuales para asegurarse de que sus códigos están seguros.",
"showLargeIcons": "Mostrar iconos grandes",
"shouldHideCode": "Ocultar códigos",
"doubleTapToViewHiddenCode": "Puedes tocar dos veces en una entrada para ver el código",
"focusOnSearchBar": "Enfocar búsqueda al iniciar la aplicación",
"confirmUpdatingkey": "¿Estás seguro de que deseas actualizar la clave secreto?",
"minimizeAppOnCopy": "Minimizar aplicación al copiar",
@@ -336,5 +344,65 @@
"deleteCodeAuthMessage": "Autenticar para borrar código",
"showQRAuthMessage": "Autenticar para mostrar código QR",
"confirmAccountDeleteTitle": "Confirmar eliminación de la cuenta",
"confirmAccountDeleteMessage": "Esta cuenta está vinculada a otras aplicaciones de ente, si utiliza alguna.\n\nSe programará la eliminación de los datos que cargue en todas las aplicaciones de ente y su cuenta se eliminará permanentemente."
"confirmAccountDeleteMessage": "Esta cuenta está vinculada a otras aplicaciones de ente, si utiliza alguna.\n\nSe programará la eliminación de los datos que cargue en todas las aplicaciones de ente y su cuenta se eliminará permanentemente.",
"androidBiometricHint": "Verificar identidad",
"@androidBiometricHint": {
"description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters."
},
"androidBiometricNotRecognized": "No reconocido. Inténtelo de nuevo.",
"@androidBiometricNotRecognized": {
"description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters."
},
"androidBiometricSuccess": "Realizado correctamente",
"@androidBiometricSuccess": {
"description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters."
},
"androidCancelButton": "Cancelar",
"@androidCancelButton": {
"description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters."
},
"androidSignInTitle": "Se requiere autenticación",
"@androidSignInTitle": {
"description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters."
},
"androidBiometricRequiredTitle": "Biométrica necesaria",
"@androidBiometricRequiredTitle": {
"description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters."
},
"androidDeviceCredentialsRequiredTitle": "Se necesitan credenciales de dispositivo",
"@androidDeviceCredentialsRequiredTitle": {
"description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters."
},
"androidDeviceCredentialsSetupDescription": "Se necesitan credenciales de dispositivo",
"@androidDeviceCredentialsSetupDescription": {
"description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side."
},
"goToSettings": "Ir a Ajustes",
"@goToSettings": {
"description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters."
},
"androidGoToSettingsDescription": "La autenticación biométrica no está configurada en su dispositivo. Vaya a 'Ajustes > Seguridad' para añadir autenticación biométrica.",
"@androidGoToSettingsDescription": {
"description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side."
},
"iOSLockOut": "La autenticación biométrica está deshabilitada. Por favor bloquee y desbloquee la pantalla para habilitarla.",
"@iOSLockOut": {
"description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side."
},
"iOSGoToSettingsDescription": "La autenticación biométrica no está configurada en tu dispositivo. Por favor, activa Touch ID o Face ID en tu teléfono.",
"@iOSGoToSettingsDescription": {
"description": "Message advising the user to go to the settings and configure Biometrics for their device. It shows in a dialog on iOS side."
},
"iOSOkButton": "Aceptar",
"@iOSOkButton": {
"description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters."
},
"noInternetConnection": "No hay conexión a Internet",
"pleaseCheckYourInternetConnectionAndTryAgain": "Compruebe su conexión a Internet e inténtelo de nuevo.",
"signOutFromOtherDevices": "Cerrar sesión desde otros dispositivos",
"signOutOtherBody": "Si cree que alguien puede conocer su contraseña, puede forzar a todos los demás dispositivos que usen su cuenta a cerrar la sesión.",
"signOutOtherDevices": "Cerrar la sesión de otros dispositivos",
"doNotSignOut": "No cerrar la sesión",
"hearUsWhereTitle": "¿Cómo conoció Ente? (opcional)",
"hearUsExplanation": "No rastreamos las aplicaciones instaladas. ¡Nos ayudaría si nos dijera dónde nos encontró!"
}

View File

@@ -8,7 +8,7 @@
},
"onBoardingBody": "Храните ваши коды двухфакторной аутентификации в безопасности",
"onBoardingGetStarted": "Начать",
"setupFirstAccount": "Настройте свою первую учетную запись",
"setupFirstAccount": "Настройте свой первый аккаунт",
"importScanQrCode": "Сканировать QR-код",
"qrCode": "QR-код",
"importEnterSetupKey": "Ввести ключ настройки",
@@ -71,8 +71,8 @@
"incorrectPasswordTitle": "Неправильный пароль",
"welcomeBack": "С возвращением!",
"madeWithLoveAtPrefix": "сделана с ❤️ в ",
"supportDevs": "Подпишитесь на <bold-green>ente</bold-green> для поддержки этого проекта.",
"supportDiscount": "Используйте код скидки \"AUTH\", чтобы получить скидку 10% в первый год",
"supportDevs": "Подпишитесь на <bold-green>ente</bold-green> для поддержки нашего проекта",
"supportDiscount": "Используйте код скидки \"AUTH\", чтобы получить скидку 10% на первый год",
"changeEmail": "Изменить почту",
"changePassword": "Изменить пароль",
"data": "Данные",
@@ -84,10 +84,12 @@
"importFromApp": "Импорт кодов из {appName}",
"importGoogleAuthGuide": "Экспортируйте учетные записи из Google Authenticator в QR-код, используя опцию «Перенести учетные записи». Затем с помощью другого устройства отсканируйте QR-код.\n\nСовет: Чтобы сфотографировать QR-код, можно воспользоваться веб-камерой ноутбука.",
"importSelectJsonFile": "Выбрать JSON-файл",
"importSelectAppExport": "Выбрать файл экспорта {appName}",
"importEnteEncGuide": "Выберите зашифрованный JSON-файл, экспортированный из ente",
"importRaivoGuide": "Используйте опцию «Export OTPs to Zip archive» в настройках Raivo.\n\nРаспакуйте zip-архив и импортируйте JSON-файл.",
"importBitwardenGuide": "Используйте опцию \"Экспортировать хранилище\" в Bitwarden Tools и импортируйте незашифрованный JSON файл.",
"importAegisGuide": "Используйте опцию «Экспортировать хранилище» в настройках Aegis.\n\nЕсли ваше хранилище зашифровано, то для его расшифровки потребуется ввести пароль хранилища.",
"import2FasGuide": "Используйте опцию \"Settings->Backup -Export\" в 2FAS.\n\nЕсли ваша резервная копия зашифрована, то для расшифровки резервной копии необходимо ввести пароль",
"exportCodes": "Экспортировать коды",
"importLabel": "Импорт",
"importInstruction": "Пожалуйста, выберите файл, содержащий список ваших кодов в следующем формате",

View File

@@ -0,0 +1,147 @@
{
"account": "Konto",
"unlock": "Lås upp",
"recoveryKey": "Återställningsnyckel",
"onBoardingBody": "Säkerhetskopiera dina 2FA-koder",
"onBoardingGetStarted": "Kom igång",
"setupFirstAccount": "Konfigurera ditt första konto",
"importScanQrCode": "Skanna en QR-kod",
"qrCode": "QR-kod",
"importEnterSetupKey": "Ange en konfigurationskod",
"importAccountPageTitle": "Ange kontodetaljer",
"secretCanNotBeEmpty": "Secret kan inte vara tomt",
"bothIssuerAndAccountCanNotBeEmpty": "Både utgivare och konto kan inte vara tomma",
"incorrectDetails": "Felaktiga uppgifter",
"pleaseVerifyDetails": "Kontrollera dina detaljer och försök igen",
"codeIssuerHint": "Utfärdare",
"codeSecretKeyHint": "Secret Key",
"codeAccountHint": "Konto (du@domän.com)",
"accountKeyType": "Typ av nyckel",
"sessionExpired": "Sessionen har gått ut",
"@sessionExpired": {
"description": "Title of the dialog when the users current session is invalid/expired"
},
"pleaseLoginAgain": "Vänligen logga in igen",
"loggingOut": "Loggar ut...",
"saveAction": "Spara",
"nextTotpTitle": "nästa",
"deleteCodeMessage": "Vill du ta bort den här koden? Det går inte att ångra den här åtgärden.",
"viewLogsAction": "Visa loggar",
"emailLogsTitle": "E-posta loggar",
"emailLogsMessage": "Skicka loggarna till {email}",
"@emailLogsMessage": {
"placeholders": {
"email": {
"type": "String"
}
}
},
"copyEmailAction": "Kopiera e-post",
"exportLogsAction": "Exportera loggar",
"reportABug": "Rapportera en bugg",
"crashAndErrorReporting": "Krasch och felrapportering",
"reportBug": "Rapportera bugg",
"emailUsMessage": "Skicka e-mail till {email}",
"@emailUsMessage": {
"placeholders": {
"email": {
"type": "String"
}
}
},
"contactSupport": "Kontakta support",
"rateUsOnStore": "Betygsätt på {storeName}",
"blog": "Blogg",
"merchandise": "Merchandise",
"verifyPassword": "Bekräfta lösenord",
"pleaseWait": "Vänligen vänta...",
"generatingEncryptionKeysTitle": "Skapar krypteringsnycklar...",
"recreatePassword": "Återskapa lösenord",
"useRecoveryKey": "Använd återställningsnyckel",
"incorrectPasswordTitle": "Felaktigt lösenord",
"welcomeBack": "Välkommen tillbaka!",
"changePassword": "Ändra lösenord",
"cancel": "Avbryt",
"yes": "Ja",
"no": "Nej",
"settings": "Inställningar",
"pleaseTryAgain": "Försök igen",
"existingUser": "Befintlig användare",
"delete": "Radera",
"enterYourPasswordHint": "Ange ditt lösenord",
"forgotPassword": "Glömt lösenord",
"oops": "Hoppsan",
"suggestFeatures": "Föreslå funktionalitet",
"faq": "FAQ",
"faq_q_1": "Hur säkert är ente Auth?",
"scan": "Skanna",
"twoFactorAuthTitle": "Tvåfaktorsautentisering",
"enterRecoveryKeyHint": "Ange din återställningsnyckel",
"noRecoveryKeyTitle": "Ingen återställningsnyckel?",
"enterEmailHint": "Ange din e-postadress",
"invalidEmailTitle": "Ogiltig e-postadress",
"invalidEmailMessage": "Ange en giltig e-postadress.",
"deleteAccount": "Radera konto",
"yesSendFeedbackAction": "Ja, skicka feedback",
"noDeleteAccountAction": "Nej, radera konto",
"createNewAccount": "Skapa nytt konto",
"weakStrength": "Svag",
"strongStrength": "Stark",
"moderateStrength": "Måttligt",
"confirmPassword": "Bekräfta lösenord",
"close": "Stäng",
"language": "Språk",
"searchHint": "Sök...",
"search": "Sök",
"sorryUnableToGenCode": "Tyvärr, det gick inte att generera en kod för {issuerName}",
"noResult": "Inga resultat",
"addCode": "Lägg till kod",
"scanAQrCode": "Skanna en QR-kod",
"enterDetailsManually": "Ange uppgifter manuellt",
"edit": "Redigera",
"copiedToClipboard": "Kopierat till urklipp",
"copiedNextToClipboard": "Kopierade nästa kod till urklipp",
"error": "Fel",
"recoveryKeyCopiedToClipboard": "Återställningsnyckel kopierad till urklipp",
"recoveryKeyOnForgotPassword": "Om du glömmer ditt lösenord är det enda sättet du kan återställa dina data med denna nyckel.",
"saveKey": "Spara nyckel",
"back": "Tillbaka",
"createAccount": "Skapa konto",
"password": "Lösenord",
"privacyPolicyTitle": "Integritetspolicy",
"termsOfServicesTitle": "Villkor",
"encryption": "Kryptering",
"changePasswordTitle": "Ändra lösenord",
"resetPasswordTitle": "Återställ lösenord",
"encryptionKeys": "Krypteringsnycklar",
"continueLabel": "Fortsätt",
"logInLabel": "Logga in",
"logout": "Logga ut",
"areYouSureYouWantToLogout": "Är du säker på att du vill logga ut?",
"yesLogout": "Ja, logga ut",
"invalidKey": "Ogiltig nyckel",
"tryAgain": "Försök igen",
"viewRecoveryKey": "Visa återställningsnyckel",
"confirmRecoveryKey": "Bekräfta återställningsnyckel",
"confirmYourRecoveryKey": "Bekräfta din återställningsnyckel",
"confirm": "Bekräfta",
"copyEmailAddress": "Kopiera e-postadress",
"exportLogs": "Exportera loggar",
"enterYourRecoveryKey": "Ange din återställningsnyckel",
"about": "Om",
"terms": "Villkor",
"warning": "Varning",
"pendingSyncs": "Varning",
"activeSessions": "Aktiva sessioner",
"enterPassword": "Ange lösenord",
"export": "Exportera",
"singIn": "Logga in",
"androidCancelButton": "Avbryt",
"@androidCancelButton": {
"description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters."
},
"iOSOkButton": "OK",
"@iOSOkButton": {
"description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters."
}
}

View File

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

View File

@@ -144,6 +144,7 @@
"enterCodeHint": "从你的身份验证器应用中\n输入6位数字代码",
"lostDeviceTitle": "丢失了设备吗?",
"twoFactorAuthTitle": "双因素认证",
"passkeyAuthTitle": "通行密钥认证",
"recoverAccount": "恢复账户",
"enterRecoveryKeyHint": "输入您的恢复密钥",
"recover": "恢复",
@@ -404,5 +405,8 @@
"signOutOtherDevices": "登出其他设备",
"doNotSignOut": "不要退登",
"hearUsWhereTitle": "您是如何知道Ente的 (可选的)",
"hearUsExplanation": "我们不跟踪应用程序安装情况。如果您告诉我们您是在哪里找到我们的,将会有所帮助!"
"hearUsExplanation": "我们不跟踪应用程序安装情况。如果您告诉我们您是在哪里找到我们的,将会有所帮助!",
"waitingForBrowserRequest": "正在等待浏览器请求...",
"launchPasskeyUrlAgain": "再次启动 通行密钥 URL",
"passkey": "通行密钥"
}

View File

@@ -1,3 +1,6 @@
import 'dart:async';
import 'dart:io';
import 'package:adaptive_theme/adaptive_theme.dart';
import 'package:computer/computer.dart';
import "package:ente_auth/app/view/app.dart";
@@ -33,7 +36,9 @@ void main() async {
WidgetsFlutterBinding.ensureInitialized();
await _runInForeground();
await _setupPrivacyScreen();
FlutterDisplayMode.setHighRefreshRate();
if (Platform.isAndroid) {
FlutterDisplayMode.setHighRefreshRate().ignore();
}
}
Future<void> _runInForeground() async {
@@ -42,7 +47,7 @@ Future<void> _runInForeground() async {
_logger.info("Starting app in foreground");
await _init(false, via: 'mainMethod');
final Locale locale = await getLocale();
UpdateService.instance.showUpdateNotification();
unawaited(UpdateService.instance.showUpdateNotification());
runApp(
AppLock(
builder: (args) => App(locale: locale),
@@ -79,7 +84,7 @@ Future _runWithLogs(Function() function, {String prefix = ""}) async {
Future<void> _init(bool bool, {String? via}) async {
// Start workers asynchronously. No need to wait for them to start
Computer.shared().turnOn(workersCount: 4, verbose: kDebugMode);
Computer.shared().turnOn(workersCount: 4, verbose: kDebugMode).ignore();
CryptoUtil.init();
await PreferenceService.instance.init();
await CodeStore.instance.init();

View File

@@ -0,0 +1,13 @@
enum TwoFactorType { totp, passkey }
// ToString for TwoFactorType
String twoFactorTypeToString(TwoFactorType type) {
switch (type) {
case TwoFactorType.totp:
return "totp";
case TwoFactorType.passkey:
return "passkey";
default:
return type.name;
}
}

View File

@@ -17,6 +17,8 @@ import 'package:ente_auth/ui/common/gradient_button.dart';
import 'package:ente_auth/ui/components/buttons/button_widget.dart';
import 'package:ente_auth/ui/components/models/button_result.dart';
import 'package:ente_auth/ui/home_page.dart';
import 'package:ente_auth/ui/settings/developer_settings_page.dart';
import 'package:ente_auth/ui/settings/developer_settings_widget.dart';
import 'package:ente_auth/ui/settings/language_picker.dart';
import 'package:ente_auth/utils/dialog_util.dart';
import 'package:ente_auth/utils/navigation_util.dart';
@@ -33,8 +35,12 @@ class OnboardingPage extends StatefulWidget {
}
class _OnboardingPageState extends State<OnboardingPage> {
static const kDeveloperModeTapCountThreshold = 7;
late StreamSubscription<TriggerLogoutEvent> _triggerLogoutEvent;
int _developerModeTapCount = 0;
@override
void initState() {
_triggerLogoutEvent =
@@ -56,114 +62,143 @@ class _OnboardingPageState extends State<OnboardingPage> {
final l10n = context.l10n;
return Scaffold(
body: SafeArea(
child: Center(
child: SingleChildScrollView(
child: Padding(
padding:
const EdgeInsets.symmetric(vertical: 40.0, horizontal: 40),
child: Column(
children: [
Column(
children: [
kDebugMode
? GestureDetector(
child: const Align(
alignment: Alignment.topRight,
child: Text("Lang"),
),
onTap: () async {
final locale = await getLocale();
routeToPage(
context,
LanguageSelectorPage(
appSupportedLocales,
(locale) async {
await setLocale(locale);
App.setLocale(context, locale);
},
locale,
child: GestureDetector(
onTap: () async {
_developerModeTapCount++;
if (_developerModeTapCount >= kDeveloperModeTapCountThreshold) {
_developerModeTapCount = 0;
final result = await showChoiceDialog(
context,
title: l10n.developerSettings,
firstButtonLabel: l10n.yes,
body: l10n.developerSettingsWarning,
isDismissible: false,
);
if (result?.action == ButtonAction.first) {
await Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {
return const DeveloperSettingsPage();
},
),
);
setState(() {});
}
}
},
child: Center(
child: SingleChildScrollView(
child: Padding(
padding:
const EdgeInsets.symmetric(vertical: 40.0, horizontal: 40),
child: Column(
children: [
Column(
children: [
kDebugMode
? GestureDetector(
child: const Align(
alignment: Alignment.topRight,
child: Text("Lang"),
),
onTap: () async {
final locale = await getLocale();
// ignore: unawaited_futures
routeToPage(
context,
LanguageSelectorPage(
appSupportedLocales,
(locale) async {
await setLocale(locale);
App.setLocale(context, locale);
},
locale,
),
).then((value) {
setState(() {});
});
},
)
: const SizedBox(),
Image.asset(
"assets/sheild-front-gradient.png",
width: 200,
height: 200,
),
const SizedBox(height: 12),
const Text(
"ente",
style: TextStyle(
fontWeight: FontWeight.bold,
fontFamily: 'Montserrat',
fontSize: 42,
),
),
const SizedBox(height: 4),
Text(
"Authenticator",
style: Theme.of(context).textTheme.headlineMedium,
),
const SizedBox(height: 32),
Text(
l10n.onBoardingBody,
textAlign: TextAlign.center,
style:
Theme.of(context).textTheme.titleLarge!.copyWith(
color: Colors.white38,
),
).then((value) {
setState(() {});
});
},
)
: const SizedBox(),
Image.asset(
"assets/sheild-front-gradient.png",
width: 200,
height: 200,
),
const SizedBox(height: 12),
const Text(
"ente",
style: TextStyle(
fontWeight: FontWeight.bold,
fontFamily: 'Montserrat',
fontSize: 42,
),
],
),
const SizedBox(height: 100),
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 20),
child: GradientButton(
onTap: _navigateToSignUpPage,
text: l10n.newUser,
),
const SizedBox(height: 4),
Text(
"Authenticator",
style: Theme.of(context).textTheme.headlineMedium,
),
const SizedBox(height: 32),
Text(
l10n.onBoardingBody,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleLarge!.copyWith(
color: Colors.white38,
),
const SizedBox(height: 4),
Container(
width: double.infinity,
padding: const EdgeInsets.fromLTRB(20, 12, 20, 0),
child: Hero(
tag: "log_in",
child: ElevatedButton(
style: Theme.of(context)
.colorScheme
.optionalActionButtonStyle,
onPressed: _navigateToSignInPage,
child: Text(
l10n.existingUser,
style: const TextStyle(
color: Colors.black, // same for both themes
),
),
],
),
const SizedBox(height: 100),
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 20),
child: GradientButton(
onTap: _navigateToSignUpPage,
text: l10n.newUser,
),
),
const SizedBox(height: 4),
Container(
width: double.infinity,
padding: const EdgeInsets.fromLTRB(20, 12, 20, 0),
child: Hero(
tag: "log_in",
child: ElevatedButton(
style: Theme.of(context)
.colorScheme
.optionalActionButtonStyle,
onPressed: _navigateToSignInPage,
child: Text(
l10n.existingUser,
style: const TextStyle(
color: Colors.black, // same for both themes
),
),
),
),
),
const SizedBox(height: 4),
Container(
width: double.infinity,
padding: const EdgeInsets.only(top: 20, bottom: 20),
child: GestureDetector(
onTap: _optForOfflineMode,
child: Center(
child: Text(
l10n.useOffline,
style: body.copyWith(
color: Theme.of(context).colorScheme.mutedTextColor,
const SizedBox(height: 4),
Container(
width: double.infinity,
padding: const EdgeInsets.only(top: 20, bottom: 20),
child: GestureDetector(
onTap: _optForOfflineMode,
child: Center(
child: Text(
l10n.useOffline,
style: body.copyWith(
color:
Theme.of(context).colorScheme.mutedTextColor,
),
),
),
),
),
),
],
const DeveloperSettingsWidget(),
],
),
),
),
),
@@ -194,6 +229,7 @@ class _OnboardingPageState extends State<OnboardingPage> {
}
if (hasOptedBefore || result?.action == ButtonAction.first) {
await Configuration.instance.optForOfflineMode();
// ignore: unawaited_futures
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {

View File

@@ -0,0 +1,23 @@
import 'package:ente_auth/core/configuration.dart';
import 'package:flutter/foundation.dart';
class FeatureFlagService {
FeatureFlagService._privateConstructor();
static final FeatureFlagService instance =
FeatureFlagService._privateConstructor();
static final _internalUserIDs = const String.fromEnvironment(
"internal_user_ids",
defaultValue: "1,2,3,4,191,125,1580559962388044,1580559962392434,10000025",
).split(",").map((element) {
return int.parse(element);
}).toSet();
bool isInternalUserOrDebugBuild() {
final String? email = Configuration.instance.getEmail();
final userID = Configuration.instance.getUserID();
return (email != null && email.endsWith("@ente.io")) ||
_internalUserIDs.contains(userID) ||
kDebugMode;
}
}

View File

@@ -5,7 +5,6 @@ import 'dart:math';
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/core/errors.dart';
import 'package:ente_auth/core/event_bus.dart';
import 'package:ente_auth/core/network.dart';
import 'package:ente_auth/events/codes_updated_event.dart';
import 'package:ente_auth/events/signed_in_event.dart';
import 'package:ente_auth/events/trigger_logout_event.dart';
@@ -26,6 +25,7 @@ enum AccountMode {
online,
offline,
}
extension on AccountMode {
bool get isOnline => this == AccountMode.online;
bool get isOffline => this == AccountMode.offline;
@@ -56,7 +56,7 @@ class AuthenticatorService {
_prefs = await SharedPreferences.getInstance();
_db = AuthenticatorDB.instance;
_offlineDb = OfflineAuthenticatorDB.instance;
_gateway = AuthenticatorGateway(Network.instance.getDio(), _config);
_gateway = AuthenticatorGateway();
if (Configuration.instance.hasConfiguredAccount()) {
unawaited(onlineSync());
}
@@ -154,7 +154,7 @@ class AuthenticatorService {
} else {
debugPrint("Skipping delete since account mode is offline");
}
if(accountMode.isOnline) {
if (accountMode.isOnline) {
await _db.deleteByIDs(generatedIDs: [genID]);
} else {
await _offlineDb.deleteByIDs(generatedIDs: [genID]);
@@ -163,7 +163,7 @@ class AuthenticatorService {
Future<bool> onlineSync() async {
try {
if(getAccountMode().isOffline) {
if (getAccountMode().isOffline) {
debugPrint("Skipping sync since account mode is offline");
return false;
}
@@ -208,7 +208,7 @@ class AuthenticatorService {
if (deletedIDs.isNotEmpty) {
await _db.deleteByIDs(ids: deletedIDs);
}
_prefs.setInt(_lastEntitySyncTime, maxSyncTime);
await _prefs.setInt(_lastEntitySyncTime, maxSyncTime);
_logger.info("Setting synctime to " + maxSyncTime.toString());
if (result.length == fetchLimit) {
_logger.info("Diff limit reached, pulling again");
@@ -253,7 +253,7 @@ class AuthenticatorService {
}
Future<Uint8List> getOrCreateAuthDataKey(AccountMode mode) async {
if(mode.isOffline) {
if (mode.isOffline) {
return _config.getOfflineSecretKey()!;
}
if (_config.getAuthSecretKey() != null) {

View File

@@ -54,6 +54,7 @@ class LocalAuthenticationService {
.setEnabled(Configuration.instance.shouldShowLockScreen());
}
} else {
// ignore: unawaited_futures
showErrorDialog(
context,
errorDialogTitle,

View File

@@ -27,6 +27,7 @@ class NotificationService {
_flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>();
if (implementation != null) {
// ignore: unawaited_futures
implementation.requestPermission();
}
}

View File

@@ -0,0 +1,55 @@
import 'package:ente_auth/core/network.dart';
import 'package:ente_auth/utils/dialog_util.dart';
import 'package:flutter/widgets.dart';
import 'package:logging/logging.dart';
import 'package:url_launcher/url_launcher_string.dart';
class PasskeyService {
PasskeyService._privateConstructor();
static final PasskeyService instance = PasskeyService._privateConstructor();
final _enteDio = Network.instance.enteDio;
Future<String> getJwtToken() async {
final response = await _enteDio.get(
"/users/accounts-token",
);
return response.data!["accountsToken"] as String;
}
Future<bool> isPasskeyRecoveryEnabled() async {
final response = await _enteDio.get(
"/users/two-factor/recovery-status",
);
return response.data!["isPasskeyRecoveryEnabled"] as bool;
}
Future<void> configurePasskeyRecovery(
String secret,
String userEncryptedSecret,
String userSecretNonce,
) async {
await _enteDio.post(
"/users/two-factor/passkeys/configure-recovery",
data: {
"secret": secret,
"userSecretCipher": userEncryptedSecret,
"userSecretNonce": userSecretNonce,
},
);
}
Future<void> openPasskeyPage(BuildContext context) async {
try {
final jwtToken = await getJwtToken();
final url = "https://accounts.ente.io/account-handoff?token=$jwtToken";
await launchUrlString(
url,
mode: LaunchMode.externalApplication,
);
} catch (e) {
Logger('PasskeyService').severe("failed to open passkey page", e);
showGenericErrorDialog(context: context).ignore();
}
}
}

View File

@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:io';
import 'package:ente_auth/core/constants.dart';
@@ -70,9 +71,11 @@ class UpdateService {
if (shouldUpdate &&
hasBeen3DaysSinceLastNotification &&
_latestVersion!.shouldNotify!) {
NotificationService.instance.showNotification(
"Update available",
"Click to install our best version yet",
unawaited(
NotificationService.instance.showNotification(
"Update available",
"Click to install our best version yet",
),
);
await _prefs.setInt(kUpdateAvailableShownTimeKey, now);
} else {

View File

@@ -11,6 +11,7 @@ import 'package:ente_auth/core/event_bus.dart';
import 'package:ente_auth/core/network.dart';
import 'package:ente_auth/events/user_details_changed_event.dart';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/models/account/two_factor.dart';
import 'package:ente_auth/models/api/user/srp.dart';
import 'package:ente_auth/models/delete_account.dart';
import 'package:ente_auth/models/key_attributes.dart';
@@ -26,6 +27,7 @@ import 'package:ente_auth/ui/account/password_reentry_page.dart';
import 'package:ente_auth/ui/account/recovery_page.dart';
import 'package:ente_auth/ui/common/progress_dialog.dart';
import 'package:ente_auth/ui/home_page.dart';
import 'package:ente_auth/ui/passkey_page.dart';
import 'package:ente_auth/ui/two_factor_authentication_page.dart';
import 'package:ente_auth/ui/two_factor_recovery_page.dart';
import 'package:ente_auth/utils/crypto_util.dart';
@@ -146,18 +148,18 @@ class UserService {
final userDetails = UserDetails.fromMap(response.data);
if (shouldCache) {
if (userDetails.profileData != null) {
_preferences.setBool(
await _preferences.setBool(
kIsEmailMFAEnabled,
userDetails.profileData!.isEmailMFAEnabled,
);
_preferences.setBool(
await _preferences.setBool(
kCanDisableEmailMFA,
userDetails.profileData!.canDisableEmailMFA,
);
}
// handle email change from different client
if (userDetails.email != _config.getEmail()) {
setEmail(userDetails.email);
await setEmail(userDetails.email);
}
}
return userDetails;
@@ -264,6 +266,34 @@ class UserService {
}
}
Future<void> onPassKeyVerified(BuildContext context, Map response) async {
final userPassword = Configuration.instance.getVolatilePassword();
if (userPassword == null) throw Exception("volatile password is null");
await _saveConfiguration(response);
Widget page;
if (Configuration.instance.getEncryptedToken() != null) {
await Configuration.instance.decryptSecretsAndGetKeyEncKey(
userPassword,
Configuration.instance.getKeyAttributes()!,
);
page = const HomePage();
} else {
throw Exception("unexpected response during passkey verification");
}
// ignore: unawaited_futures
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (BuildContext context) {
return page;
},
),
(route) => route.isFirst,
);
}
Future<void> verifyEmail(
BuildContext context,
String ott, {
@@ -303,6 +333,7 @@ class UserService {
);
}
}
// ignore: unawaited_futures
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (BuildContext context) {
@@ -326,6 +357,7 @@ class UserService {
);
Navigator.of(context).pop();
} else {
// ignore: unawaited_futures
showErrorDialog(
context,
context.l10n.incorrectCode,
@@ -335,6 +367,7 @@ class UserService {
} catch (e) {
await dialog.hide();
_logger.severe(e);
// ignore: unawaited_futures
showErrorDialog(
context,
context.l10n.oops,
@@ -371,6 +404,7 @@ class UserService {
Bus.instance.fire(UserDetailsChangedEvent());
return;
}
// ignore: unawaited_futures
showErrorDialog(
context,
context.l10n.oops,
@@ -379,12 +413,14 @@ class UserService {
} on DioError catch (e) {
await dialog.hide();
if (e.response != null && e.response!.statusCode == 403) {
// ignore: unawaited_futures
showErrorDialog(
context,
context.l10n.oops,
context.l10n.thisEmailIsAlreadyInUse,
);
} else {
// ignore: unawaited_futures
showErrorDialog(
context,
context.l10n.incorrectCode,
@@ -394,6 +430,7 @@ class UserService {
} catch (e) {
await dialog.hide();
_logger.severe(e);
// ignore: unawaited_futures
showErrorDialog(
context,
context.l10n.oops,
@@ -487,9 +524,9 @@ class UserService {
final clientS = client.calculateSecret(serverB);
final clientM = client.calculateClientEvidenceMessage();
// ignore: unused_local_variable
late Response srpCompleteResponse;
late Response _;
if (setKeysRequest == null) {
srpCompleteResponse = await _enteDio.post(
_ = await _enteDio.post(
"/users/srp/complete",
data: {
'setupID': setupSRPResponse.setupID,
@@ -497,7 +534,7 @@ class UserService {
},
);
} else {
srpCompleteResponse = await _enteDio.post(
_ = await _enteDio.post(
"/users/srp/update",
data: {
'setupID': setupSRPResponse.setupID,
@@ -581,11 +618,15 @@ class UserService {
},
);
if (response.statusCode == 200) {
Widget page;
Widget? page;
final String passkeySessionID = response.data["passkeySessionID"];
final String twoFASessionID = response.data["twoFactorSessionID"];
Configuration.instance.setVolatilePassword(userPassword);
if (twoFASessionID.isNotEmpty) {
page = TwoFactorAuthenticationPage(twoFASessionID);
} else if (passkeySessionID.isNotEmpty) {
page = PasskeyPage(passkeySessionID);
} else {
await _saveConfiguration(response);
if (Configuration.instance.getEncryptedToken() != null) {
@@ -600,10 +641,11 @@ class UserService {
}
}
await dialog.hide();
// ignore: unawaited_futures
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (BuildContext context) {
return page;
return page!;
},
),
(route) => route.isFirst,
@@ -677,6 +719,7 @@ class UserService {
if (response.statusCode == 200) {
showShortToast(context, context.l10n.authenticationSuccessful);
await _saveConfiguration(response);
// ignore: unawaited_futures
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (BuildContext context) {
@@ -691,6 +734,7 @@ class UserService {
_logger.severe(e);
if (e.response != null && e.response!.statusCode == 404) {
showToast(context, "Session expired");
// ignore: unawaited_futures
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (BuildContext context) {
@@ -700,6 +744,7 @@ class UserService {
(route) => route.isFirst,
);
} else {
// ignore: unawaited_futures
showErrorDialog(
context,
context.l10n.incorrectCode,
@@ -709,6 +754,7 @@ class UserService {
} catch (e) {
await dialog.hide();
_logger.severe(e);
// ignore: unawaited_futures
showErrorDialog(
context,
context.l10n.oops,
@@ -717,7 +763,11 @@ class UserService {
}
}
Future<void> recoverTwoFactor(BuildContext context, String sessionID) async {
Future<void> recoverTwoFactor(
BuildContext context,
String sessionID,
TwoFactorType type,
) async {
final dialog = createProgressDialog(context, context.l10n.pleaseWait);
await dialog.show();
try {
@@ -725,13 +775,16 @@ class UserService {
_config.getHttpEndpoint() + "/users/two-factor/recover",
queryParameters: {
"sessionID": sessionID,
"twoFactorType": twoFactorTypeToString(type),
},
);
if (response.statusCode == 200) {
// ignore: unawaited_futures
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (BuildContext context) {
return TwoFactorRecoveryPage(
type,
sessionID,
response.data["encryptedSecret"],
response.data["secretDecryptionNonce"],
@@ -742,9 +795,11 @@ class UserService {
);
}
} on DioError catch (e) {
await dialog.hide();
_logger.severe(e);
if (e.response != null && e.response!.statusCode == 404) {
showToast(context, context.l10n.sessionExpired);
// ignore: unawaited_futures
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (BuildContext context) {
@@ -754,6 +809,7 @@ class UserService {
(route) => route.isFirst,
);
} else {
// ignore: unawaited_futures
showErrorDialog(
context,
context.l10n.oops,
@@ -761,7 +817,9 @@ class UserService {
);
}
} catch (e) {
await dialog.hide();
_logger.severe(e);
// ignore: unawaited_futures
showErrorDialog(
context,
context.l10n.oops,
@@ -774,6 +832,7 @@ class UserService {
Future<void> removeTwoFactor(
BuildContext context,
TwoFactorType type,
String sessionID,
String recoveryKey,
String encryptedSecret,
@@ -813,6 +872,7 @@ class UserService {
data: {
"sessionID": sessionID,
"secret": secret,
"twoFactorType": twoFactorTypeToString(type),
},
);
if (response.statusCode == 200) {
@@ -821,6 +881,7 @@ class UserService {
context.l10n.twofactorAuthenticationSuccessfullyReset,
);
await _saveConfiguration(response);
// ignore: unawaited_futures
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (BuildContext context) {
@@ -831,9 +892,11 @@ class UserService {
);
}
} on DioError catch (e) {
await dialog.hide();
_logger.severe(e);
if (e.response != null && e.response!.statusCode == 404) {
showToast(context, "Session expired");
// ignore: unawaited_futures
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (BuildContext context) {
@@ -843,6 +906,7 @@ class UserService {
(route) => route.isFirst,
);
} else {
// ignore: unawaited_futures
showErrorDialog(
context,
context.l10n.oops,
@@ -850,7 +914,9 @@ class UserService {
);
}
} catch (e) {
await dialog.hide();
_logger.severe(e);
// ignore: unawaited_futures
showErrorDialog(
context,
context.l10n.oops,
@@ -861,16 +927,19 @@ class UserService {
}
}
Future<void> _saveConfiguration(Response response) async {
await Configuration.instance.setUserID(response.data["id"]);
if (response.data["encryptedToken"] != null) {
Future<void> _saveConfiguration(dynamic response) async {
final responseData = response is Map ? response : response.data as Map?;
if (responseData == null) return;
await Configuration.instance.setUserID(responseData["id"]);
if (responseData["encryptedToken"] != null) {
await Configuration.instance
.setEncryptedToken(response.data["encryptedToken"]);
.setEncryptedToken(responseData["encryptedToken"]);
await Configuration.instance.setKeyAttributes(
KeyAttributes.fromMap(response.data["keyAttributes"]),
KeyAttributes.fromMap(responseData["keyAttributes"]),
);
} else {
await Configuration.instance.setToken(response.data["token"]);
await Configuration.instance.setToken(responseData["token"]);
}
}
@@ -890,7 +959,7 @@ class UserService {
"isEnabled": isEnabled,
},
);
_preferences.setBool(kIsEmailMFAEnabled, isEnabled);
await _preferences.setBool(kIsEmailMFAEnabled, isEnabled);
} catch (e) {
_logger.severe("Failed to update email mfa", e);
rethrow;

View File

@@ -15,7 +15,8 @@ class OfflineAuthenticatorDB {
static const entityTable = 'entities';
OfflineAuthenticatorDB._privateConstructor();
static final OfflineAuthenticatorDB instance = OfflineAuthenticatorDB._privateConstructor();
static final OfflineAuthenticatorDB instance =
OfflineAuthenticatorDB._privateConstructor();
static Future<Database>? _dbFuture;
@@ -26,7 +27,7 @@ class OfflineAuthenticatorDB {
Future<Database> _initDatabase() async {
final Directory documentsDirectory =
await getApplicationDocumentsDirectory();
await getApplicationDocumentsDirectory();
final String path = join(documentsDirectory.path, _databaseName);
debugPrint(path);
return await openDatabase(
@@ -70,10 +71,10 @@ class OfflineAuthenticatorDB {
}
Future<int> updateEntry(
int generatedID,
String encData,
String header,
) async {
int generatedID,
String encData,
String header,
) async {
final db = await instance.database;
final int timeInMicroSeconds = DateTime.now().microsecondsSinceEpoch;
int affectedRows = await db.update(

View File

@@ -7,10 +7,6 @@ class UserStore {
late SharedPreferences _preferences;
static final UserStore instance = UserStore._privateConstructor();
static const endpoint = String.fromEnvironment(
"endpoint",
defaultValue: "https://api.ente.io",
);
Future<void> init() async {
_preferences = await SharedPreferences.getInstance();

View File

@@ -236,7 +236,7 @@ class DeleteAccountPage extends StatelessWidget {
),
],
);
// ignore: unawaited_futures
showDialog(
context: context,
builder: (BuildContext context) {

View File

@@ -61,7 +61,7 @@ class _LoginPageState extends State<LoginPage> {
isFormValid: _emailIsValid,
buttonText: context.l10n.logInLabel,
onPressedFunction: () async {
UserService.instance.setEmail(_email!);
await UserService.instance.setEmail(_email!);
Configuration.instance.resetVolatilePassword();
SrpAttributes? attr;
bool isEmailVerificationEnabled = true;
@@ -74,6 +74,7 @@ class _LoginPageState extends State<LoginPage> {
}
}
if (attr != null && !isEmailVerificationEnabled) {
// ignore: unawaited_futures
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {

View File

@@ -23,6 +23,7 @@ Future<void> autoLogoutAlert(BuildContext context) async {
int pendingSyncCount =
await AuthenticatorDB.instance.getNeedSyncCount();
if (pendingSyncCount > 0) {
// ignore: unawaited_futures
showChoiceActionSheet(
context,
title: l10n.pendingSyncs,

View File

@@ -399,6 +399,7 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
} catch (e, s) {
_logger.severe(e, s);
await dialog.hide();
// ignore: unawaited_futures
showGenericErrorDialog(context: context);
}
}
@@ -446,6 +447,7 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
await UserService.instance.setAttributes(result);
await dialog.hide();
Configuration.instance.resetVolatilePassword();
// ignore: unawaited_futures
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (BuildContext context) {
@@ -457,10 +459,11 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
} catch (e, s) {
_logger.severe(e, s);
await dialog.hide();
// ignore: unawaited_futures
showGenericErrorDialog(context: context);
}
}
// ignore: unawaited_futures
routeToPage(
context,
RecoveryKeyPage(
@@ -476,12 +479,14 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
_logger.severe(e);
await dialog.hide();
if (e is UnsupportedError) {
// ignore: unawaited_futures
showErrorDialog(
context,
context.l10n.insecureDevice,
context.l10n.sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease,
);
} else {
// ignore: unawaited_futures
showGenericErrorDialog(context: context);
}
}

View File

@@ -116,6 +116,7 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
firstButtonLabel: context.l10n.useRecoveryKey,
);
if (dialogChoice!.action == ButtonAction.first) {
// ignore: unawaited_futures
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {

View File

@@ -54,6 +54,7 @@ class _RecoveryPageState extends State<RecoveryPage> {
await Configuration.instance.recover(_recoveryKey.text.trim());
await dialog.hide();
showToast(context, "Recovery successful!");
// ignore: unawaited_futures
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (BuildContext context) {
@@ -72,6 +73,7 @@ class _RecoveryPageState extends State<RecoveryPage> {
if (e is AssertionError) {
errMessage = '$errMessage : ${e.message}';
}
// ignore: unawaited_futures
showErrorDialog(context, "Incorrect recovery key", errMessage);
}
},

View File

@@ -78,7 +78,7 @@ class _RequestPasswordVerificationPageState
onPressedFunction: () async {
FocusScope.of(context).unfocus();
final dialog = createProgressDialog(context, context.l10n.pleaseWait);
dialog.show();
await dialog.show();
try {
final attributes = Configuration.instance.getKeyAttributes()!;
final Uint8List keyEncryptionKey = await CryptoUtil.deriveKey(
@@ -92,17 +92,18 @@ class _RequestPasswordVerificationPageState
keyEncryptionKey,
Sodium.base642bin(attributes.keyDecryptionNonce),
);
dialog.show();
await dialog.show();
// pop
await widget.onPasswordVerified(keyEncryptionKey);
dialog.hide();
await dialog.hide();
Navigator.of(context).pop(true);
} catch (e, s) {
_logger.severe("Error while verifying password", e, s);
dialog.hide();
await dialog.hide();
if (widget.onPasswordError != null) {
widget.onPasswordError!();
} else {
// ignore: unawaited_futures
showErrorDialog(
context,
context.l10n.incorrectPasswordTitle,

View File

@@ -121,6 +121,7 @@ class _SessionsPageState extends State<SessionsPage> {
} catch (e) {
await dialog.hide();
_logger.severe('failed to terminate');
// ignore: unawaited_futures
showErrorDialog(
context,
context.l10n.oops,
@@ -184,7 +185,7 @@ class _SessionsPageState extends State<SessionsPage> {
if (isLoggingOutFromThisDevice) {
await UserService.instance.logout(context);
} else {
_terminateSession(session);
await _terminateSession(session);
}
},
),

View File

@@ -92,6 +92,7 @@ class _VerifyRecoveryPageState extends State<VerifyRecoveryPage> {
String recoveryKey;
try {
recoveryKey = Sodium.bin2hex(Configuration.instance.getRecoveryKey());
// ignore: unawaited_futures
routeToPage(
context,
RecoveryKeyPage(
@@ -104,6 +105,7 @@ class _VerifyRecoveryPageState extends State<VerifyRecoveryPage> {
),
);
} catch (e) {
// ignore: unawaited_futures
showGenericErrorDialog(context: context);
return;
}

View File

@@ -356,6 +356,7 @@ class _CodeWidgetState extends State<CodeWidget> {
await FlutterClipboard.copy(content);
showToast(context, confirmationMessage);
if (Platform.isAndroid && shouldMinimizeOnCopy) {
// ignore: unawaited_futures
MoveToBackground.moveTaskToBack();
}
}
@@ -385,7 +386,7 @@ class _CodeWidgetState extends State<CodeWidget> {
),
);
if (code != null) {
CodeStore.instance.addCode(code);
await CodeStore.instance.addCode(code);
}
}

View File

@@ -146,6 +146,7 @@ class ProgressDialog {
try {
if (!_isShowing) {
_dialog = _Body();
// ignore: unawaited_futures
showDialog<dynamic>(
context: _context!,
barrierDismissible: _barrierDismissible,

View File

@@ -120,7 +120,7 @@ class _HomePageState extends State<HomePage> {
),
);
if (code != null) {
CodeStore.instance.addCode(code);
await CodeStore.instance.addCode(code);
// Focus the new code by searching
if (_codes.length > 2) {
_focusNewCode(code);
@@ -137,7 +137,7 @@ class _HomePageState extends State<HomePage> {
),
);
if (code != null) {
CodeStore.instance.addCode(code);
await CodeStore.instance.addCode(code);
}
}
@@ -151,6 +151,7 @@ class _HomePageState extends State<HomePage> {
return false;
}
if (Platform.isAndroid) {
// ignore: unawaited_futures
MoveToBackground.moveTaskToBack();
return false;
} else {

View File

@@ -0,0 +1,151 @@
import 'dart:convert';
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/models/account/two_factor.dart';
import 'package:ente_auth/services/user_service.dart';
import 'package:ente_auth/ui/components/buttons/button_widget.dart';
import 'package:ente_auth/ui/components/models/button_type.dart';
import 'package:ente_auth/utils/dialog_util.dart';
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:uni_links/uni_links.dart';
import 'package:url_launcher/url_launcher_string.dart';
class PasskeyPage extends StatefulWidget {
final String sessionID;
const PasskeyPage(
this.sessionID, {
Key? key,
}) : super(key: key);
@override
State<PasskeyPage> createState() => _PasskeyPageState();
}
class _PasskeyPageState extends State<PasskeyPage> {
final Logger _logger = Logger("PasskeyPage");
@override
void initState() {
launchPasskey();
_initDeepLinks();
super.initState();
}
@override
void dispose() {
super.dispose();
}
Future<void> launchPasskey() async {
await launchUrlString(
"https://accounts.ente.io/passkeys/flow?"
"passkeySessionID=${widget.sessionID}"
"&redirect=enteauth://passkey",
mode: LaunchMode.externalApplication,
);
}
Future<void> _handleDeeplink(String? link) async {
if (!context.mounted ||
Configuration.instance.hasConfiguredAccount() ||
link == null) {
_logger.warning(
'ignored deeplink: contextMounted ${context.mounted} hasConfiguredAccount ${Configuration.instance.hasConfiguredAccount()}',
);
return;
}
try {
if (mounted && link.toLowerCase().startsWith("enteauth://passkey")) {
final String? uri = Uri.parse(link).queryParameters['response'];
String base64String = uri!.toString();
while (base64String.length % 4 != 0) {
base64String += '=';
}
final res = utf8.decode(base64.decode(base64String));
final json = jsonDecode(res) as Map<String, dynamic>;
await UserService.instance.onPassKeyVerified(context, json);
} else {
_logger.info('ignored deeplink: $link mounted $mounted');
}
} catch (e, s) {
_logger.severe('passKey: failed to handle deeplink', e, s);
showGenericErrorDialog(context: context).ignore();
}
}
Future<bool> _initDeepLinks() async {
// Attach a listener to the stream
linkStream.listen(
_handleDeeplink,
onError: (err) {
_logger.severe(err);
},
);
return false;
}
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
return Scaffold(
appBar: AppBar(
title: Text(
l10n.passkeyAuthTitle,
),
),
body: _getBody(),
);
}
Widget _getBody() {
return Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 32),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
context.l10n.waitingForVerification,
style: const TextStyle(
height: 1.4,
fontSize: 16,
),
),
const SizedBox(height: 16),
ButtonWidget(
buttonType: ButtonType.primary,
labelText: context.l10n.verifyPasskey,
onTap: () => launchPasskey(),
),
const Padding(padding: EdgeInsets.all(30)),
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
UserService.instance.recoverTwoFactor(
context,
widget.sessionID,
TwoFactorType.passkey,
);
},
child: Container(
padding: const EdgeInsets.all(10),
child: Center(
child: Text(
context.l10n.recoverAccount,
style: const TextStyle(
decoration: TextDecoration.underline,
fontSize: 12,
),
),
),
),
),
],
),
),
);
}
}

View File

@@ -36,6 +36,7 @@ class AboutSectionWidget extends StatelessWidget {
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
onTap: () async {
// ignore: unawaited_futures
launchUrl(Uri.parse("https://github.com/ente-io/ente"));
},
),
@@ -68,6 +69,7 @@ class AboutSectionWidget extends StatelessWidget {
await UpdateService.instance.shouldUpdate();
await dialog.hide();
if (shouldUpdate) {
// ignore: unawaited_futures
showDialog(
context: context,
builder: (BuildContext context) {
@@ -115,6 +117,7 @@ class AboutMenuItemWidget extends StatelessWidget {
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
onTap: () async {
// ignore: unawaited_futures
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {

View File

@@ -1,5 +1,4 @@
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/services/local_authentication_service.dart';
import 'package:ente_auth/services/user_service.dart';
@@ -7,6 +6,7 @@ import 'package:ente_auth/theme/ente_theme.dart';
import 'package:ente_auth/ui/account/change_email_dialog.dart';
import 'package:ente_auth/ui/account/delete_account_page.dart';
import 'package:ente_auth/ui/account/password_entry_page.dart';
import 'package:ente_auth/ui/account/recovery_key_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';
@@ -14,6 +14,7 @@ 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';
import 'package:flutter_sodium/flutter_sodium.dart';
class AccountSectionWidget extends StatelessWidget {
AccountSectionWidget({Key? key}) : super(key: key);
@@ -47,6 +48,7 @@ class AccountSectionWidget extends StatelessWidget {
l10n.authToChangeYourEmail,
);
if (hasAuthenticated) {
// ignore: unawaited_futures
showDialog(
context: context,
builder: (BuildContext context) {
@@ -73,6 +75,7 @@ class AccountSectionWidget extends StatelessWidget {
l10n.authToChangeYourPassword,
);
if (hasAuthenticated) {
// ignore: unawaited_futures
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {
@@ -86,6 +89,43 @@ class AccountSectionWidget extends StatelessWidget {
},
),
sectionOptionSpacing,
MenuItemWidget(
captionedTextWidget: CaptionedTextWidget(
title: l10n.recoveryKey,
),
pressedColor: getEnteColorScheme(context).fillFaint,
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
onTap: () async {
final hasAuthenticated = await LocalAuthenticationService.instance
.requestLocalAuthentication(
context,
l10n.authToViewYourRecoveryKey,
);
if (hasAuthenticated) {
String recoveryKey;
try {
recoveryKey =
Sodium.bin2hex(Configuration.instance.getRecoveryKey());
} catch (e) {
// ignore: unawaited_futures
showGenericErrorDialog(context: context);
return;
}
// ignore: unawaited_futures
routeToPage(
context,
RecoveryKeyPage(
recoveryKey,
l10n.ok,
showAppBar: true,
onDone: () {},
),
);
}
},
),
sectionOptionSpacing,
MenuItemWidget(
captionedTextWidget: CaptionedTextWidget(
title: context.l10n.logout,
@@ -106,6 +146,7 @@ class AccountSectionWidget extends StatelessWidget {
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
onTap: () async {
// ignore: unawaited_futures
routeToPage(context, const DeleteAccountPage());
},
),
@@ -115,6 +156,7 @@ class AccountSectionWidget extends StatelessWidget {
children: children,
);
}
void _onLogoutTapped(BuildContext context) {
showChoiceActionSheet(
context,

View File

@@ -1,14 +1,7 @@
import 'dart:io';
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/core/network.dart';
import 'package:ente_auth/ente_theme_data.dart';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/services/update_service.dart';
import 'package:ente_auth/theme/ente_theme.dart';
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:open_filex/open_filex.dart';
import 'package:url_launcher/url_launcher_string.dart';
class AppUpdateDialog extends StatefulWidget {
@@ -114,115 +107,3 @@ class _AppUpdateDialogState extends State<AppUpdateDialog> {
);
}
}
class ApkDownloaderDialog extends StatefulWidget {
final LatestVersionInfo? versionInfo;
const ApkDownloaderDialog(this.versionInfo, {Key? key}) : super(key: key);
@override
State<ApkDownloaderDialog> createState() => _ApkDownloaderDialogState();
}
class _ApkDownloaderDialogState extends State<ApkDownloaderDialog> {
String? _saveUrl;
double? _downloadProgress;
@override
void initState() {
super.initState();
_saveUrl = Configuration.instance.getTempDirectory() +
"ente-" +
widget.versionInfo!.name! +
".apk";
_downloadApk();
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => false,
child: AlertDialog(
title: const Text(
"Downloading...",
style: TextStyle(
fontSize: 16,
),
textAlign: TextAlign.center,
),
content: LinearProgressIndicator(
value: _downloadProgress,
valueColor: AlwaysStoppedAnimation<Color>(
Theme.of(context).colorScheme.alternativeColor,
),
),
),
);
}
Future<void> _downloadApk() async {
try {
if (!File(_saveUrl!).existsSync()) {
await Network.instance.getDio().download(
widget.versionInfo!.url!,
_saveUrl,
onReceiveProgress: (count, _) {
setState(() {
_downloadProgress = count / widget.versionInfo!.size!;
});
},
);
}
Navigator.of(context, rootNavigator: true).pop('dialog');
OpenFilex.open(_saveUrl);
} catch (e) {
Logger("ApkDownloader").severe(e);
final AlertDialog alert = AlertDialog(
title: const Text("Sorry"),
content: const Text("The download could not be completed"),
actions: [
TextButton(
child: const Text(
"Ignore",
style: TextStyle(
color: Colors.white,
),
),
onPressed: () {
Navigator.of(context, rootNavigator: true).pop('dialog');
Navigator.of(context, rootNavigator: true).pop('dialog');
},
),
TextButton(
child: Text(
"Retry",
style: TextStyle(
color: Theme.of(context).colorScheme.alternativeColor,
),
),
onPressed: () {
Navigator.of(context, rootNavigator: true).pop('dialog');
Navigator.of(context, rootNavigator: true).pop('dialog');
showDialog(
context: context,
builder: (BuildContext context) {
return ApkDownloaderDialog(widget.versionInfo);
},
barrierDismissible: false,
);
},
),
],
);
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
barrierColor: Colors.black87,
);
return;
}
}
}

View File

@@ -46,6 +46,7 @@ class DangerSectionWidget extends StatelessWidget {
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
onTap: () async {
// ignore: unawaited_futures
routeToPage(context, const DeleteAccountPage());
},
),

View File

@@ -1,4 +1,3 @@
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/theme/ente_theme.dart';
import 'package:ente_auth/ui/components/captioned_text_widget.dart';
@@ -36,6 +35,7 @@ class DataSectionWidget extends StatelessWidget {
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
onTap: () async {
// ignore: unawaited_futures
routeToPage(context, ImportCodePage());
},
),

View File

@@ -98,7 +98,7 @@ Future<void> _requestForEncryptionPassword(
),
);
// get json value of data
_exportCodes(context, jsonEncode(data.toJson()));
await _exportCodes(context, jsonEncode(data.toJson()));
} catch (e, s) {
Logger("ExportWidget").severe(e, s);
showToast(context, "Error while exporting codes.");

View File

@@ -56,6 +56,7 @@ Future<void> showGoogleAuthInstruction(BuildContext context) async {
await CodeStore.instance.addCode(code, shouldSync: false);
}
unawaited(AuthenticatorService.instance.onlineSync());
// ignore: unawaited_futures
importSuccessDialog(context, codes.length);
}
}

View File

@@ -19,29 +19,29 @@ class ImportService {
Future<void> initiateImport(BuildContext context, ImportType type) async {
switch (type) {
case ImportType.plainText:
showImportInstructionDialog(context);
await showImportInstructionDialog(context);
break;
case ImportType.encrypted:
showEncryptedImportInstruction(context);
await showEncryptedImportInstruction(context);
break;
case ImportType.ravio:
showRaivoImportInstruction(context);
await showRaivoImportInstruction(context);
break;
case ImportType.googleAuthenticator:
showGoogleAuthInstruction(context);
await showGoogleAuthInstruction(context);
// showToast(context, 'coming soon');
break;
case ImportType.aegis:
showAegisImportInstruction(context);
await showAegisImportInstruction(context);
break;
case ImportType.twoFas:
show2FasImportInstruction(context);
await show2FasImportInstruction(context);
break;
case ImportType.bitwarden:
showBitwardenImportInstruction(context);
await showBitwardenImportInstruction(context);
break;
case ImportType.lastpass:
showLastpassImportInstruction(context);
await showLastpassImportInstruction(context);
break;
}
}

View File

@@ -105,7 +105,7 @@ class ImportCodePage extends StatelessWidget {
index != importOptions.length - 1,
isTopBorderRadiusRemoved: index != 0,
onTap: () async {
ImportService().initiateImport(context, type);
await ImportService().initiateImport(context, type);
// routeToPage(context, ImportCodePage());
// _showImportInstructionDialog(context);
},

View File

@@ -0,0 +1,90 @@
import 'package:dio/dio.dart';
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/ui/common/gradient_button.dart';
import 'package:ente_auth/utils/dialog_util.dart';
import 'package:ente_auth/utils/toast_util.dart';
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
class DeveloperSettingsPage extends StatefulWidget {
const DeveloperSettingsPage({super.key});
@override
_DeveloperSettingsPageState createState() => _DeveloperSettingsPageState();
}
class _DeveloperSettingsPageState extends State<DeveloperSettingsPage> {
final _logger = Logger('DeveloperSettingsPage');
final _urlController = TextEditingController();
@override
void dispose() {
_urlController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
_logger.info(
"Current endpoint is: " + Configuration.instance.getHttpEndpoint(),
);
return Scaffold(
appBar: AppBar(
title: Text(context.l10n.developerSettings),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
controller: _urlController,
decoration: InputDecoration(
labelText: context.l10n.serverEndpoint,
hintText: Configuration.instance.getHttpEndpoint(),
),
autofocus: true,
),
const SizedBox(height: 40),
GradientButton(
onTap: () async {
String url = _urlController.text;
_logger.info("Entered endpoint: " + url);
try {
final uri = Uri.parse(url);
if ((uri.scheme == "http" || uri.scheme == "https")) {
await _ping(url);
await Configuration.instance.setHttpEndpoint(url);
showToast(context, context.l10n.endpointUpdatedMessage);
Navigator.of(context).pop();
} else {
throw const FormatException();
}
} catch (e) {
// ignore: unawaited_futures
showErrorDialog(
context,
context.l10n.invalidEndpoint,
context.l10n.invalidEndpointMessage,
);
}
},
text: context.l10n.saveAction,
),
],
),
),
);
}
Future<void> _ping(String endpoint) async {
try {
final response = await Dio().get(endpoint + '/ping');
if (response.data['message'] != 'pong') {
throw Exception('Invalid response');
}
} catch (e) {
throw Exception('Error occurred: $e');
}
}
}

View File

@@ -0,0 +1,27 @@
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/core/constants.dart';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:flutter/material.dart';
class DeveloperSettingsWidget extends StatelessWidget {
const DeveloperSettingsWidget({super.key});
@override
Widget build(BuildContext context) {
final endpoint = Configuration.instance.getHttpEndpoint();
if (endpoint != kDefaultProductionEndpoint) {
final endpointURI = Uri.parse(endpoint);
return Padding(
padding: const EdgeInsets.only(bottom: 20),
child: Text(
context.l10n.customEndpoint(
endpointURI.host + ":" + endpointURI.port.toString(),
),
style: Theme.of(context).textTheme.bodySmall,
),
);
} else {
return const SizedBox.shrink();
}
}
}

View File

@@ -48,6 +48,7 @@ class _AdvancedSectionWidgetState extends State<AdvancedSectionWidget> {
trailingIconIsMuted: true,
onTap: () async {
final locale = await getLocale();
// ignore: unawaited_futures
routeToPage(
context,
LanguageSelectorPage(

View File

@@ -4,10 +4,11 @@ import 'dart:typed_data';
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/models/user_details.dart';
import 'package:ente_auth/services/auth_feature_flag.dart';
import 'package:ente_auth/services/local_authentication_service.dart';
import 'package:ente_auth/services/passkey_service.dart';
import 'package:ente_auth/services/user_service.dart';
import 'package:ente_auth/theme/ente_theme.dart';
import 'package:ente_auth/ui/account/recovery_key_page.dart';
import 'package:ente_auth/ui/account/request_pwd_verification_page.dart';
import 'package:ente_auth/ui/account/sessions_page.dart';
import 'package:ente_auth/ui/components/captioned_text_widget.dart';
@@ -20,7 +21,7 @@ import 'package:ente_auth/utils/dialog_util.dart';
import 'package:ente_auth/utils/navigation_util.dart';
import 'package:ente_auth/utils/toast_util.dart';
import 'package:flutter/material.dart';
import 'package:flutter_sodium/flutter_sodium.dart';
import 'package:logging/logging.dart';
class SecuritySectionWidget extends StatefulWidget {
const SecuritySectionWidget({Key? key}) : super(key: key);
@@ -32,6 +33,7 @@ class SecuritySectionWidget extends StatefulWidget {
class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
final _config = Configuration.instance;
late bool _hasLoggedIn;
final Logger _logger = Logger('SecuritySectionWidget');
@override
void initState() {
@@ -63,42 +65,21 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
// We don't know if the user can disable MFA yet, so we fetch the info
UserService.instance.getUserDetailsV2().ignore();
}
final bool isInternalUser =
FeatureFlagService.instance.isInternalUserOrDebugBuild();
children.addAll([
sectionOptionSpacing,
MenuItemWidget(
captionedTextWidget: CaptionedTextWidget(
title: l10n.recoveryKey,
if (isInternalUser) sectionOptionSpacing,
if (isInternalUser)
MenuItemWidget(
captionedTextWidget: CaptionedTextWidget(
title: l10n.passkey,
),
pressedColor: getEnteColorScheme(context).fillFaint,
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
onTap: () async => await onPasskeyClick(context),
),
pressedColor: getEnteColorScheme(context).fillFaint,
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
onTap: () async {
final hasAuthenticated = await LocalAuthenticationService.instance
.requestLocalAuthentication(
context,
l10n.authToViewYourRecoveryKey,
);
if (hasAuthenticated) {
String recoveryKey;
try {
recoveryKey =
Sodium.bin2hex(Configuration.instance.getRecoveryKey());
} catch (e) {
showGenericErrorDialog(context: context);
return;
}
routeToPage(
context,
RecoveryKeyPage(
recoveryKey,
l10n.ok,
showAppBar: true,
onDone: () {},
),
);
}
},
),
sectionOptionSpacing,
MenuItemWidget(
captionedTextWidget: CaptionedTextWidget(
title: l10n.emailVerificationToggle,
@@ -137,6 +118,7 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
context.l10n.authToViewYourActiveSessions,
);
if (hasAuthenticated) {
// ignore: unawaited_futures
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {
@@ -179,6 +161,31 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
);
}
Future<void> onPasskeyClick(BuildContext buildContext) async {
try {
final isPassKeyResetEnabled =
await PasskeyService.instance.isPasskeyRecoveryEnabled();
if (!isPassKeyResetEnabled) {
final Uint8List recoveryKey = Configuration.instance.getRecoveryKey();
final resetKey = CryptoUtil.generateKey();
final resetKeyBase64 = CryptoUtil.bin2base64(resetKey);
final encryptionResult = CryptoUtil.encryptSync(
resetKey,
recoveryKey,
);
await PasskeyService.instance.configurePasskeyRecovery(
resetKeyBase64,
CryptoUtil.bin2base64(encryptionResult.encryptedData!),
CryptoUtil.bin2base64(encryptionResult.nonce!),
);
}
PasskeyService.instance.openPasskeyPage(buildContext).ignore();
} catch (e, s) {
_logger.severe("failed to open passkey page", e, s);
await showGenericErrorDialog(context: context);
}
}
Future<void> updateEmailMFA(bool enableEmailMFA) async {
try {
final UserDetails details =

View File

@@ -81,6 +81,7 @@ class SocialsMenuItemWidget extends StatelessWidget {
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
onTap: () async {
// ignore: unawaited_futures
launchUrlString(
url,
mode: launchInExternalApp

View File

@@ -42,6 +42,7 @@ class _SupportSectionWidgetState extends State<SupportSectionWidget> {
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
onTap: () async {
// ignore: unawaited_futures
showModalBottomSheet<void>(
backgroundColor: Theme.of(context).colorScheme.background,
barrierColor: Colors.black87,
@@ -61,6 +62,7 @@ class _SupportSectionWidgetState extends State<SupportSectionWidget> {
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
onTap: () async {
// ignore: unawaited_futures
launchUrlString(
githubDiscussionsUrl,
mode: LaunchMode.externalApplication,

View File

@@ -16,6 +16,7 @@ import 'package:ente_auth/ui/settings/account_section_widget.dart';
import 'package:ente_auth/ui/settings/app_version_widget.dart';
import 'package:ente_auth/ui/settings/data/data_section_widget.dart';
import 'package:ente_auth/ui/settings/data/export_widget.dart';
import 'package:ente_auth/ui/settings/developer_settings_widget.dart';
import 'package:ente_auth/ui/settings/general_section_widget.dart';
import 'package:ente_auth/ui/settings/security_section_widget.dart';
import 'package:ente_auth/ui/settings/social_section_widget.dart';
@@ -149,6 +150,7 @@ class SettingsPage extends StatelessWidget {
sectionSpacing,
const AboutSectionWidget(),
const AppVersionWidget(),
const DeveloperSettingsWidget(),
const SupportDevWidget(),
const Padding(
padding: EdgeInsets.only(bottom: 60),

View File

@@ -56,6 +56,7 @@ class _LockScreenState extends State<LockScreen> with WidgetsBindingObserver {
text: context.l10n.unlock,
iconData: Icons.lock_open_outlined,
onTap: () async {
// ignore: unawaited_futures
_showLockScreen(source: "tapUnlock");
},
),

View File

@@ -1,4 +1,5 @@
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/models/account/two_factor.dart';
import 'package:ente_auth/services/user_service.dart';
import 'package:ente_auth/ui/lifecycle_event_handler.dart';
import 'package:flutter/material.dart';
@@ -119,7 +120,7 @@ class _TwoFactorAuthenticationPageState
child: OutlinedButton(
onPressed: _code.length == 6
? () async {
_verifyTwoFactorCode(_code);
await _verifyTwoFactorCode(_code);
}
: null,
child: Text(l10n.verify),
@@ -129,7 +130,11 @@ class _TwoFactorAuthenticationPageState
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
UserService.instance.recoverTwoFactor(context, widget.sessionID);
UserService.instance.recoverTwoFactor(
context,
widget.sessionID,
TwoFactorType.totp,
);
},
child: Container(
padding: const EdgeInsets.all(10),

View File

@@ -1,6 +1,7 @@
import 'dart:ui';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/models/account/two_factor.dart';
import 'package:ente_auth/services/user_service.dart';
import 'package:ente_auth/utils/dialog_util.dart';
import 'package:flutter/material.dart';
@@ -9,8 +10,10 @@ class TwoFactorRecoveryPage extends StatefulWidget {
final String sessionID;
final String encryptedSecret;
final String secretDecryptionNonce;
final TwoFactorType type;
const TwoFactorRecoveryPage(
this.type,
this.sessionID,
this.encryptedSecret,
this.secretDecryptionNonce, {
@@ -72,6 +75,7 @@ class _TwoFactorRecoveryPageState extends State<TwoFactorRecoveryPage> {
? () async {
await UserService.instance.removeTwoFactor(
context,
widget.type,
widget.sessionID,
_recoveryKey.text,
widget.encryptedSecret,

View File

@@ -62,6 +62,7 @@ Future<void> sendLogs(
],
),
onPressed: () async {
// ignore: unawaited_futures
showDialog(
context: context,
builder: (BuildContext context) {
@@ -118,6 +119,7 @@ Future<void> sendLogs(
),
),
);
// ignore: unawaited_futures
showDialog(
context: context,
builder: (_) {
@@ -159,7 +161,7 @@ Future<String> getZippedLogsFile(BuildContext context) async {
tempPath + "/logs-${Configuration.instance.getUserID() ?? 0}.zip";
final encoder = ZipFileEncoder();
encoder.create(zipFilePath);
encoder.addDirectory(logsDirectory);
await encoder.addDirectory(logsDirectory);
encoder.close();
await dialog.hide();
return zipFilePath;

View File

@@ -2,14 +2,14 @@ import 'package:ente_auth/ente_theme_data.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
Future showToast(
void showToast(
BuildContext context,
String message, {
toastLength = Toast.LENGTH_LONG,
iOSDismissOnTap = true,
}) async {
await Fluttertoast.cancel();
return Fluttertoast.showToast(
await Fluttertoast.showToast(
msg: message,
toastLength: toastLength,
gravity: ToastGravity.BOTTOM,
@@ -20,6 +20,6 @@ Future showToast(
);
}
Future<void> showShortToast(context, String message) {
return showToast(context, message, toastLength: Toast.LENGTH_SHORT);
void showShortToast(context, String message) {
showToast(context, message, toastLength: Toast.LENGTH_SHORT);
}

View File

@@ -203,7 +203,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 1300;
LastUpgradeCheck = 1430;
ORGANIZATIONNAME = "";
TargetAttributes = {
33CC10EC2044A3C60003C045 = {

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
LastUpgradeVersion = "1430"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

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