Compare commits

..

834 Commits

Author SHA1 Message Date
a5xwin
46db03c07b Merge remote-tracking branch 'origin/main' into multiselect 2025-09-11 20:41:29 +05:30
a5xwin
b877b0a4cc added unpin option for mixed pin state multiselect 2025-09-11 20:40:52 +05:30
a5xwin
920b3b1931 added unpin option for mixed pin state multiselect 2025-09-11 20:39:17 +05:30
Neeraj
ad95b5bd2d [mob] Add album join option for internal users (#7147)
## Summary
• Adds album join toggle in manage links widget behind internal user
flag
• Dynamic permission levels: Viewers or Collaborators based on collect
setting

## Test plan
- [ ] Verify toggle only appears for internal users
- [ ] Test toggle functionality
- [ ] Confirm description changes based on collect setting
2025-09-11 17:37:12 +05:30
Neeraj
6b1757fc36 Update internal changes log with new entries 2025-09-11 17:16:01 +05:30
Neeraj
42527c0cd5 [mob] Add album join option for internal users
Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-11 16:53:25 +05:30
Neeraj
8810f88236 [infra] web-based log parser for support (#7144)
## Description

## Tests
2025-09-11 14:30:22 +05:30
Neeraj
e1423f2030 Add web-based log viewer for Ente application logs
This adds a comprehensive web-based log viewer that provides similar
functionality to the mobile log viewer, allowing analysis of log files
from customer support requests.

Features:
- Upload and parse ZIP files containing daily log files
- Advanced filtering by log level, logger name, process, and timeline
- Text search with wildcard support (logger:Service*)
- Interactive analytics with click-to-filter charts
- Modern UI using Ente's design system and Material-UI components
- Infinite scroll for performance with large log files
- Export functionality for filtered results
- Responsive design for desktop and mobile

Technical highlights:
- Client-side ZIP processing with JSZip
- Efficient log parsing supporting Ente's super_logging format
- Real-time filtering with optimized algorithms
- Memory-efficient rendering with virtual scrolling
- CSS custom properties for theming consistency

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-11 13:55:24 +05:30
Neeraj
c2ba7c56be Add process prefix filtering and improve log filter dialog UI (#7142) 2025-09-11 11:43:03 +05:30
Neeraj
93618117c5 [mob] Bump version v1.2.6 2025-09-11 11:41:02 +05:30
Neeraj
08c38086a5 Add process prefix filtering and improve log filter dialog UI
- Add process prefix filter section with user-friendly display names
- Move process filter above loggers in dialog layout
- Compact dialog design: reduce sizes, padding, and font sizes
- Optimize filter chip layout for better mobile experience

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-11 11:32:58 +05:30
Neeraj
a4762d68f1 [mob] LockExist error fix & log view imporvement (#7141)
## Description

## Tests
2025-09-11 09:29:35 +05:30
Neeraj Gupta
936c6f1b61 Clean up 2025-09-11 07:53:13 +05:30
Neeraj Gupta
cfada04396 feat(log_viewer): Enhance search, filters, and UI
- Add logger name filtering via search box with logger:name syntax
- Support wildcard patterns (logger:Auth* matches all loggers starting with Auth)
- Make logger cards in statistics page tappable for quick filtering
- Set default filters to show WARNING, SEVERE, SHOUT levels
- Improve Filter Dialog UI with modern design and better spacing
- Reduce search box size with smaller font and padding
- Use proper theme colors for buttons (FilledButton)
- Remove Processes section from filter dialog for simplicity

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-11 07:49:29 +05:30
Neeraj Gupta
25287c64f5 Update log_viewer docs to reflect simplified integration API
- Add prefix parameter documentation to LogViewer.initialize()
- Remove callback-based integration examples
- Simplify SuperLogging integration to direct initialization
- Update all code examples to use LogViewer.openViewer()
- Correct database entry limit from 2000 to 10000
- Clarify automatic log capture via Logger.root.onRecord

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-11 06:25:19 +05:30
Neeraj Gupta
168254ba42 Merge remote-tracking branch 'origin/main' into misc_fixes 2025-09-11 06:04:32 +05:30
Neeraj Gupta
05f7792012 [mob] Fix incorrect casting 2025-09-11 06:04:22 +05:30
Neeraj
d5f2b6456e [mob] Fix build (#7135)
## Description

## Tests
2025-09-10 20:43:21 +05:30
Neeraj Gupta
ec6692b68a Merge remote-tracking branch 'origin/main' into fixBuild 2025-09-10 19:59:35 +05:30
Neeraj
eead32ffe2 Update internal changes log with new entries (#7134)
## Description

## Tests
2025-09-10 19:59:26 +05:30
Neeraj
e90814c16e Merge branch 'main' into ua741-patch-2 2025-09-10 19:58:58 +05:30
Neeraj Gupta
dbe0bbc9dc [mob] Fix build error 2025-09-10 19:58:04 +05:30
Laurens Priem
bbea022aef [mob][photos] Add text embeddings cache service (#7130)
## Description

Add text embeddings cache service to prevent recomputes for:
- Memories
- Magic cache

## Tests

Tested in debug mode on my pixel phone.
2025-09-10 18:01:53 +05:30
Laurens Priem
92c4b325ca Merge branch 'main' into text_embeddings_cache 2025-09-10 17:51:51 +05:30
laurenspriem
bc66c1519a put db method preference in claude 2025-09-10 17:49:54 +05:30
Neeraj
1e804d4829 Update internal changes log with new entries 2025-09-10 17:47:03 +05:30
laurenspriem
3a8c95123e Add database method best practice guideline
Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-10 17:42:31 +05:30
laurenspriem
54ad3e4abb Simplify getRepeatedTextEmbeddingCache method
Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-10 17:38:42 +05:30
laurenspriem
8e29a9e26b update internal change 2025-09-10 17:34:03 +05:30
Neeraj
c82b829fe3 [mobile] Add debug option to enable database logging (#7133)
Add option for internal users to enable database logging in release
builds for debugging purposes.
2025-09-10 17:33:56 +05:30
Neeraj
1dbdb270b4 [mob][i] Allow internal users to enable db logging 2025-09-10 17:28:12 +05:30
Neeraj
1d1efc286f [mob][internal] Add QR code sharing feature for album links (#7132)
## Summary
- Add QR code sharing feature for album links behind internal user flag
- Integrate QR option in manage links and share collection pages  
- Auto-close dialog after share operation for better UX

## Implementation
- **New QrCodeDialogWidget**: Custom dialog with album name, QR code,
and ente branding
- **Share functionality**: Captures QR as image and shares with album
context
- **Feature gating**: Hidden behind `flagService.internalUser` for
internal testing
- **UI integration**: Available in both share collection page and manage
links page
- **Dependencies**: Added `qr_flutter: ^4.1.0` for QR generation

## Test Plan
-  QR code generation works for album URLs
-  Share functionality captures and exports QR as image
-  Dialog auto-closes after share operation
-  Feature properly hidden behind internal user flag
-  UI integrates seamlessly with existing sharing flow
-  Visual hierarchy: QR primary, album name secondary, branding
tertiary
2025-09-10 16:48:36 +05:30
Neeraj
dc500795a1 Add QR code sharing feature for album links
- Add QrCodeDialogWidget with album branding and share functionality
- Integrate QR code option in manage links and share collection pages
- Feature gated behind flagService.internalUser for testing
- QR codes include album name, scannable link, and ente branding
- Auto-close dialog after share operation for better UX
- Add qr_flutter dependency for QR code generation

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-10 16:31:09 +05:30
Neeraj
11afcd92af [server] Support for changing server port (#7131)
## Description
Ref: https://github.com/ente-io/ente/issues/7122

## Tests
Tested locally
2025-09-10 16:22:43 +05:30
Manav Rathi
f20c8caff0 [server] Improve support for idn domains (#7124)
## Description

## Tests
2025-09-10 16:17:12 +05:30
laurenspriem
c691b545a2 Remove unnecessary cache lock from text embeddings service
Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-10 15:39:15 +05:30
laurenspriem
edcec3277e format 2025-09-10 15:37:30 +05:30
laurenspriem
cda3a5b149 Simplify text embeddings cache to use only database cache
Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-10 15:35:58 +05:30
laurenspriem
cc769fdd5b Remove assets folder 2025-09-10 15:20:24 +05:30
laurenspriem
b74fe86e87 Merge branch 'main' into text_embeddings_cache 2025-09-10 15:18:29 +05:30
Neeraj Gupta
074f68146f [server] Support for changing server port 2025-09-10 14:47:06 +05:30
laurenspriem
e420d7b86f Add text embeddings cache service
Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-10 14:24:08 +05:30
Neeraj
68caa3f7c6 [mob] Add in-app log viewer for mobile debugging (#7129)
## Description
Introduces a comprehensive log viewer package for Flutter mobile apps
with:
- Real-time log viewing with filtering by level, logger name, and search
- SQLite-based storage with automatic log rotation (10k entries default)
- Timeline visualization and export functionality
- Integration with SuperLogging for seamless log capture

Only enabled in debug mode to avoid production impact.

## Tests



https://github.com/user-attachments/assets/badb2a4a-a9a2-4aec-b0ae-d825cc4fe23e
2025-09-10 13:53:43 +05:30
Neeraj
5e5d5f4aad Lint fixes for log_viewer 2025-09-10 13:36:39 +05:30
Neeraj
8713dd0707 Do null check before try block 2025-09-10 13:19:33 +05:30
Neeraj
102313f686 Clean up 2025-09-10 13:16:19 +05:30
Neeraj
7ef9fdcaaa Add in-app log viewer for mobile debugging
Introduces a comprehensive log viewer package for Flutter mobile apps with:
- Real-time log viewing with filtering by level, logger name, and search
- SQLite-based storage with automatic log rotation (10k entries default)
- Timeline visualization and export functionality
- Integration with SuperLogging for seamless log capture

Only enabled in debug mode to avoid production impact.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-10 13:06:05 +05:30
Manav Rathi
d902733809 [mob][photos] symlink for agents.md (#7128)
## Description

symlink for [agents.md](https://agents.md)
2025-09-10 11:59:51 +05:30
laurenspriem
0ef990de5a Make CLAUDE.md agent-agnostic, add AGENTS.md symlink
Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-10 11:22:16 +05:30
Manav Rathi
7722c4e16b Fix command to reload Caddy (#7125)
```
$ sudo systemctl caddy reload
Unknown command verb 'caddy', did you mean 'cat'?
```
2025-09-10 09:23:46 +05:30
Hans Lemuet
6f5fdfb7b7 Fix command to reload Caddy
$ sudo systemctl caddy reload
Unknown command verb 'caddy', did you mean 'cat'?
2025-09-10 02:14:56 +02:00
Neeraj Gupta
135124a487 Improve err handling 2025-09-10 05:07:31 +05:30
Neeraj Gupta
d3c53794cf Add alert for exactDomain mismatch 2025-09-10 04:52:20 +05:30
Neeraj Gupta
270cee8b09 [server] Support for idn domain 2025-09-10 04:40:27 +05:30
Neeraj
9b05cc8c23 [server] Minor improvements in link middleware (#7104)
## Description

## Tests
2025-09-10 04:25:23 +05:30
Manav Rathi
5b6c3e1b6e [destkop] Update typo in translation (#7118)
See: https://github.com/ente-io/ente/pull/5546#issuecomment-3268874821

Updated the strings in crowdin by `gh workflow run
web-crowdin-push-both.yml`
2025-09-09 17:41:34 +05:30
Manav Rathi
636793d5b1 [destkop] Update typo in translation
See: https://github.com/ente-io/ente/pull/5546#issuecomment-3268874821
2025-09-09 17:32:18 +05:30
Manav Rathi
700e52d11a [web] Harden workflows (#7114) 2025-09-09 13:30:32 +05:30
Manav Rathi
82c7d1865c Update 2025-09-09 12:49:08 +05:30
Manav Rathi
f08ee15cea [web] Harden workflows 2025-09-09 12:00:56 +05:30
Laurens Priem
901bfc945e [mob][photos] Fix copy paste mistake in claude.md (#7109)
## Description

Fix copy paste mistake in claude.md
2025-09-08 16:48:46 +05:30
laurenspriem
6c25b094be Fix copy paste mistake in claude.md 2025-09-08 16:47:21 +05:30
a5xwin
3026ec5be3 fixed bug causing incorrect code count 2025-09-08 16:26:55 +05:30
Laurens Priem
4f5af8dcfa [mob][photos] Lower cluster size threshold on discover page (#7105)
## Description

Lower cluster size threshold on discover page
2025-09-08 15:15:12 +05:30
laurenspriem
8079d44c68 Lower cluster size threshold on discover page 2025-09-08 15:05:22 +05:30
Neeraj Gupta
575314c8a1 [server] Relax origin check for localhost for dev 2025-09-08 14:37:22 +05:30
Neeraj Gupta
2684f9ce11 [server] whitelist shared url 2025-09-08 14:33:31 +05:30
Neeraj
cd5582219c [mob] pin in_app_purchase to v3.2.1 (#7103)
## Description

Ref: https://github.com/flutter/flutter/issues/169335

## Tests
Tested via TF.
2025-09-08 14:27:19 +05:30
Neeraj Gupta
69332c78ad Update daily changelog 2025-09-08 14:26:00 +05:30
Aman Raj Singh Mourya
cba30e386d [locker] Update Locker asset & icons (#7102)
## Description
Add background images, svgs for locker.
Update imports & add `flutter_lints` dependencies for
`mobile/pubspec.yaml`
2025-09-08 13:49:57 +05:30
Neeraj Gupta
7663e76deb [mob] pin in_app_purchase to v3.2.1 2025-09-08 13:32:09 +05:30
AmanRajSinghMourya
697d6f854d Add lockscreen background photos 2025-09-08 13:02:42 +05:30
AmanRajSinghMourya
7aadb54ef1 Add icons directory to asset list in pubspec.yaml 2025-09-08 13:02:21 +05:30
AmanRajSinghMourya
2a2443efea Fix dependencies of mobile, add flutter_lints 2025-09-08 13:02:08 +05:30
AmanRajSinghMourya
d2bc2627a3 Move to /icons 2025-09-08 13:01:35 +05:30
AmanRajSinghMourya
b1971810fb Fix imports 2025-09-08 12:20:16 +05:30
AmanRajSinghMourya
bd25af2b4b Assets for legacy 2025-09-08 12:20:06 +05:30
Neeraj
833b4656fe [mob][photos] Dart format (#7101)
## Description

- Formats the flutter code in `/photos`
- Adds format instructions to claude.md
2025-09-08 11:38:42 +05:30
laurenspriem
315c4ae6b7 Tell Claude to format after every code change 2025-09-08 11:03:46 +05:30
laurenspriem
49d9b3c928 dart format . 2025-09-08 11:00:47 +05:30
a5xwin
440bafa56a Merge remote-tracking branch 'origin/main' into multiselect 2025-09-07 15:02:01 +05:30
a5xwin
87c236b629 added multiselect feature 2025-09-07 14:59:53 +05:30
Neeraj
ba6b326f97 [mob][n] Cast app for tvOS (#7085)
## Description

## Tests
2025-09-07 08:33:46 +05:30
Neeraj Gupta
aeea35e32a Fix empty album flash after auth expiry
- Invalidate slide timer during reset and guard nextSlide() when payload cleared to stop stale timer triggering false "No media files" state
- Remove sensitive logging from EnteCrypto and CastFileService
- Redact cast token from logs

Bug fix identified with GPT-5 preview assistance.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-07 08:28:51 +05:30
Neeraj Gupta
614c6c63aa Remove unused file 2025-09-07 07:48:48 +05:30
Manav Rathi
ba6cee23d9 [docs] Custom domains blog link (#7076) 2025-09-06 17:27:26 +05:30
Manav Rathi
e43266c176 [docs] Custom domains blog link 2025-09-06 17:24:54 +05:30
Manav Rathi
f4168cb9a3 [Docs] Similar images help entry (#6964)
## Description

Similar images help entry.
2025-09-06 16:46:31 +05:30
Neeraj Gupta
1e551b4084 Add Apple TV cast app for Ente Photos
Introduces a new tvOS application that enables users to cast and view
their Ente Photos on Apple TV. The app includes pairing functionality,
slideshow capabilities, and video playback support.

Key components:
- Cast app with SwiftUI interface for Apple TV
- EnteCast package for casting functionality and file management
- EnteNetwork package for API communication
- EnteCrypto package for secure authentication
- EnteCore package for shared utilities
- Custom fonts and branding assets
- Pairing view for device connection
- Slideshow and video player views
- Screen saver management

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-06 16:44:44 +05:30
Aman Raj Singh Mourya
df6392fd19 [locker] Update sharing package (#7071)
## Description
Move `notification_widget`, `user_avatar_widget` & `user_extension` to
`sharing` package.
Update imports, extract strings and update configuration.
2025-09-06 16:19:00 +05:30
Aman Raj Singh Mourya
e4a851072d [locker] Legacy package (#7072)
## Description

## Tests
2025-09-06 16:18:44 +05:30
AmanRajSinghMourya
f9c4442223 Update pubspec.lock 2025-09-06 15:47:33 +05:30
AmanRajSinghMourya
c4e7139ecb Fix sharing package dependencies 2025-09-06 15:15:25 +05:30
AmanRajSinghMourya
ddd4b733d3 Move user_extension to sharing package 2025-09-06 15:14:22 +05:30
Manav Rathi
3836cac109 [docs] Update custom domain docs (#7074) 2025-09-06 13:56:17 +05:30
Manav Rathi
06eda153be [docs] Update custom domain docs 2025-09-06 13:49:44 +05:30
Manav Rathi
6137d07ba8 Fix minor grammar error in deduplicate.md (#7073)
Just a minor doc fix :)
2025-09-06 13:40:22 +05:30
Waldir Pimenta
0f92b098b7 Fix minor grammar error in deduplicate.md 2025-09-06 07:51:10 +01:00
AmanRajSinghMourya
7bde215427 Extract strings 2025-09-06 06:54:43 +05:30
AmanRajSinghMourya
4953310876 Add ente_legacy package and integrate emergency services 2025-09-06 06:54:35 +05:30
AmanRajSinghMourya
2932ee7d4c Legacy package 2025-09-06 06:52:21 +05:30
AmanRajSinghMourya
0e0ba2d5af Extract strings 2025-09-06 06:47:22 +05:30
AmanRajSinghMourya
3b54fa41f6 Remove duplicate import of user_dialogs in collection_actions.dart 2025-09-06 06:45:17 +05:30
AmanRajSinghMourya
c51dff5a29 Move user_dialog to package 2025-09-06 06:45:02 +05:30
AmanRajSinghMourya
e985200e67 Minor fix 2025-09-06 06:11:31 +05:30
AmanRajSinghMourya
7e5e11ba87 Update package strings 2025-09-06 06:07:42 +05:30
AmanRajSinghMourya
13c9646f58 MInor fix 2025-09-06 05:51:39 +05:30
AmanRajSinghMourya
678b556f5f Refactor sharing components: move UserAvatarWidget and VerifyIdentityDialog to ente_sharing package, update imports, and adjust configurations 2025-09-06 05:51:30 +05:30
AmanRajSinghMourya
a3b432799a Update dependencies 2025-09-06 05:45:12 +05:30
AmanRajSinghMourya
8eaa2603dd Add VerifyIdentifyDialog widget to sharing package 2025-09-06 05:43:37 +05:30
AmanRajSinghMourya
b51febf8f5 Update dependencies 2025-09-05 23:48:32 +05:30
AmanRajSinghMourya
df522658bb Move user_avator_widget to sharing package 2025-09-05 23:46:59 +05:30
AmanRajSinghMourya
9a13b99b20 Extract strings 2025-09-05 23:45:09 +05:30
AmanRajSinghMourya
a142b660fd Add golden color properties to EnteColorScheme 2025-09-05 23:44:17 +05:30
AmanRajSinghMourya
b7dcb7b34c Add UserExtension to package 2025-09-05 23:43:53 +05:30
AmanRajSinghMourya
e8de5940fd Update dependency overrides to include ente_network and ente_sharing 2025-09-05 23:43:26 +05:30
AmanRajSinghMourya
d5f8c9eb24 Add notification widget to packages/ui 2025-09-05 23:40:15 +05:30
Neeraj
f092396133 [server] Support for coupons (#7065) 2025-09-05 15:01:15 +05:30
Neeraj Gupta
7f718438aa Minor refactor 2025-09-05 14:44:58 +05:30
Neeraj Gupta
cf7a4d989d [server] Support for coupons 2025-09-05 12:10:19 +05:30
Aman Raj Singh Mourya
e444c1801a [locker] Remove redundant packages (#7039)
## Description

## Tests
2025-09-04 22:38:47 +05:30
AmanRajSinghMourya
f2a2ee188c Update dependency & fix merge conflicts 2025-09-04 22:38:34 +05:30
AmanRajSinghMourya
356622cbb1 Merge branch 'main' into fix_packages 2025-09-04 22:25:49 +05:30
Aman Raj Singh Mourya
86c92a9217 [locker] Fix authentication not popping up on android (#7060)
## Description
Authentication service was not working on android as `local_auth`
requires the use of a `FragmentActivity` instead of an `Activity` in
`MainActivity.kt`

Also updated `AndroidManifest.xml` file to include the USE_BIOMETRIC
permissions:
2025-09-04 17:48:20 +05:30
AmanRajSinghMourya
bcc2a30105 Add USE_BIOMETRIC permission to AndroidManifest.xml 2025-09-04 16:01:33 +05:30
AmanRajSinghMourya
dcc36d2d35 Extract strings 2025-09-04 15:59:16 +05:30
AmanRajSinghMourya
d650886749 Check if isDeviceSupported for lockscreen 2025-09-04 15:58:49 +05:30
AmanRajSinghMourya
a73d5548a0 Fix: local_auth requires the use of a FragmentActivity instead of an Activity 2025-09-04 15:58:17 +05:30
Aman Raj Singh Mourya
bf0b11ebfd [locker] Locker sharing (#7013)
## Description

### Collection Sharing Feature Implementation
This PR implements a collection sharing functionality for locker,
allowing users to share collections with others and manage shared access
through various methods.

## Key Features
1. **Collection Sharing Mechanisms**
   - Share collections via links
   - Manage shared access with specific users
   - Configure link expiry and device limits

2. **User Management** in shared collections
   - Added participant (viewer/ collaborator)
   - Implemented leave collection functionality
   - Added user permissions and access controls

3. **UI Enhancements**
   - New collection view types (main, outgoing, incoming)
   - Grid view for collections
   - Enhanced menu sections and descriptions
   - Improved sharing dialogs and UI components
2025-09-04 12:32:02 +05:30
Neeraj
49c90a802a [mob] Fix changelog scrolling on small devices (#7059)
## Description

## Tests
2025-09-04 12:02:11 +05:30
Neeraj Gupta
8b2db5e576 [mob] Fix changelog scrolling on small devices 2025-09-04 12:00:11 +05:30
AmanRajSinghMourya
57382af3a2 Update imports 2025-09-03 15:44:19 +05:30
AmanRajSinghMourya
80bc848d1e Add ente_sharing package dependency to pubspec files 2025-09-03 15:41:43 +05:30
Aman Raj Singh Mourya
b11f86175e [packages] Sharing package (#7048)
## Description
Extract sharing related api to a common sharing package.
2025-09-03 15:38:48 +05:30
Neeraj
b5d4839e04 [mob] Update change log and bump version (#7052)
## Description

## Tests
2025-09-03 15:22:36 +05:30
Neeraj Gupta
ac57097eb4 Update change log and bump version 2025-09-03 15:21:12 +05:30
Ashil
4e08e38bf6 [mob][photos] Update claude md documentation (#7051)
## Description

See commit messages.
2025-09-03 13:29:55 +05:30
ashilkn
a7d3cf4178 Update storage dependencies to reflect current usage
Replace sqflite with sqlite_async as the primary database package since the project has migrated to using sqlite_async.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-03 13:21:35 +05:30
ashilkn
c63dfc36e9 Remove integration and performance test sections from CLAUDE.md
These test commands are not confirmed to be working correctly and have been removed from the documentation.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-03 13:20:12 +05:30
Manav Rathi
2985503254 Update CONTRIBUTING.md (#7050) 2025-09-03 12:54:24 +05:30
Laurens Priem
9be023d68a [mob][photos] Add claude.md (#7044)
## Description

Add claude.md
2025-09-03 12:35:53 +05:30
laurenspriem
6a6e1b3c47 Individual preferences 2025-09-03 12:03:51 +05:30
Neeraj
7516363715 [mob][photos] Prevent vectorDB index file corruption (#7049)
## Description

- Use `load` instead of `view`, since latter is read-only
- When loading fails in rust, delete index file in dart side and try
again
- Atomically save index file by first writing to temp file

## Tests

Tested in debug mode on my pixel phone.
2025-09-03 11:54:31 +05:30
laurenspriem
2b76b71db8 atomic save of index file 2025-09-03 11:15:07 +05:30
Manav Rathi
c32a70fb25 Update CONTRIBUTING.md 2025-09-03 10:52:03 +05:30
laurenspriem
4098c1a072 Delete index file on load error 2025-09-03 10:36:03 +05:30
laurenspriem
972be1f41e Use load for usearch index 2025-09-03 10:27:30 +05:30
AmanRajSinghMourya
2e58400962 Add analysis.yaml and minor fix 2025-09-03 10:07:27 +05:30
AmanRajSinghMourya
b0fce602aa Sharing package - extract all sharing api to a common package 2025-09-03 09:55:32 +05:30
laurenspriem
3acb2136d0 [mob][photos] Add documentation sync requirement to CLAUDE.md
Require updating associated spec documents when code changes are made

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-02 18:21:57 +05:30
laurenspriem
eba729625f commit instructions 2025-09-02 18:19:34 +05:30
Manav Rathi
a477742cd0 [web] Fix European date format search support (#7043)
Fixes #7025
2025-09-02 17:48:10 +05:30
laurenspriem
c974bde11c Don't go to setup on error 2025-09-02 17:02:51 +05:30
Manav Rathi
ecc654bae0 [web] Fix European date format search support
Fixes #7025

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-02 10:56:53 +00:00
Ashil
201ef88305 [mob][debug] Thumbnail issue debug (#7042)
## Description

For figuring out root cause of thumbnail not loading issue. This change
will not introduce any regressions or bugs.
2025-09-02 16:26:46 +05:30
Ashil
742035d7cc Merge branch 'main' into thunmbail_issue_debug 2025-09-02 16:20:43 +05:30
ashilkn
8f29d5aa19 Update internal change log 2025-09-02 16:18:15 +05:30
laurenspriem
8a4e76fb6f Small rectification 2025-09-02 16:17:10 +05:30
ashilkn
c03eaf83aa Complete completer with error if getThumbnailFromLocal throws error for task in local thumbnail task queue 2025-09-02 16:13:49 +05:30
laurenspriem
378878538d [mob][photos] Add critical coding requirements to CLAUDE.md
Add three mandatory development practices:
1. Run flutter analyze after every change - zero issues required
2. Always reuse existing components - search before creating
3. Use Ente design system - no hardcoded colors or text styles

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-02 16:06:42 +05:30
laurenspriem
01c3d6b105 [mob][photos] Add CLAUDE.md with initial project documentation
Create comprehensive development guide from /init command including:
- Project philosophy and privacy focus
- Monorepo context and structure
- Development commands (melos and flutter)
- Architecture overview with service patterns
- Security architecture details
- Development setup requirements

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-02 15:48:43 +05:30
AmanRajSinghMourya
2bdf62c490 Add melos support for photos/plugins 2025-09-02 14:08:04 +05:30
Neeraj
c6f5c68f1e [mob] Update copy (#7040)
## Description

## Tests
2025-09-02 13:52:53 +05:30
Neeraj Gupta
d0c8925ff3 Update playstore changelog 2025-09-02 13:42:46 +05:30
AmanRajSinghMourya
2cab943647 Organize imports 2025-09-02 13:41:52 +05:30
Neeraj Gupta
d6c84421ce [mob][photos] Update changelog copy translations
Updated cLTitle2 from "Manual video stream generation" to "Video streaming enhancements" across all supported locales to match the updated English copy.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-02 13:41:46 +05:30
AmanRajSinghMourya
990485d796 Remove redundant dependencies from locker 2025-09-02 13:41:43 +05:30
AmanRajSinghMourya
96e9030d40 Move list extension to packages 2025-09-02 12:37:59 +05:30
Neeraj
0d1f20f9e2 [mob][photos] Clear up flutter analyze (#7035)
## Description

- Replace withOpacity() with withValues(alpha:)
- Replace onPopInvoked with onPopInvokedWithResult
- Update MaterialState references to WidgetState
- Organize imports
- Remove unneeded nullability
- Dangling library docs
- collectionName deprecation warning
- TextInputWidget isPasswordInput deprecation warning
2025-09-02 12:29:26 +05:30
Ashil
c55447a08f [mob][debug] To debug thumbnail not loading (#7036) 2025-09-02 12:28:34 +05:30
Ashil
98d56e8fa4 Merge branch 'main' into thunmbail_issue_debug 2025-09-02 12:26:48 +05:30
ashilkn
f244c94ebf Update internal change log 2025-09-02 12:24:01 +05:30
laurenspriem
88f2b88f4d Remove deprecation warnings 2025-09-02 12:15:07 +05:30
Neeraj
db1fef40db [mob] Update changelog (#7034)
## Description

## Tests
2025-09-02 12:14:36 +05:30
laurenspriem
1fd29cdd13 dangling library doc 2025-09-02 12:02:53 +05:30
laurenspriem
947d294afe non nullable dialog 2025-09-02 12:02:36 +05:30
ashilkn
515715660e Add option to config local thumbnail queue to debug thumbnail not displaying issue + add more logging + show local ID of file on thumbnails (configurable) 2025-09-02 11:53:21 +05:30
laurenspriem
324221171d organize imports 2025-09-02 11:50:01 +05:30
laurenspriem
f5f2ff1b2c Fix Flutter deprecation warnings
- Replace withOpacity() with withValues(alpha:)
- Replace onPopInvoked with onPopInvokedWithResult
- Update MaterialState references to WidgetState

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-02 11:49:12 +05:30
Neeraj Gupta
244d41621c Bump version 2025-09-02 11:41:40 +05:30
Neeraj Gupta
91b6a08a35 Update changelog entries with new features
- Replace old changelog entries with new ones across all supported languages
- Add Similar Images, Manual video stream generation, and Performance Improvements features
- Remove outdated entries for Advanced Image Editor, Smart Albums, Improved Gallery, and Faster Scroll

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-02 11:39:54 +05:30
Neeraj
770a311da5 [auth] Fix manual app lock with macos touch id (#6793)
## Description
This fixes https://github.com/ente-io/ente/issues/3428
This was broken because of
https://github.com/eaceto/flutter_local_authentication/issues/8
I've also added that if the app is locked manually, the macOS Touch ID
API won't be called until the user either presses the unlock button
again or unfocuses the app and then focuses back on it. This behavior
also applies when the app window is closed and then reopened.
2025-09-02 10:46:56 +05:30
Neeraj
db76dee639 fix: only show when video streaming is enabled (#7031)
## Description

## Tests
2025-09-02 10:45:37 +05:30
Manav Rathi
20ce760e85 feat(rust): Initialize Rust CLI foundation (#6915)
## Summary
Rust CLI achieves feature parity with Go CLI for photos app core
functionality

## Changes
- Export, sync, and incremental updates working
- Hash-based deduplication and live photo support
- Public magic metadata for renamed files
- Progress indicators for downloads

## Remaining
- Export filters (album, date range)
- Resume interrupted downloads
- Shared/hidden album support
2025-09-02 09:57:56 +05:30
Prateek Sunal
df1bfbe839 fix: initialize compute controllers async with values 2025-09-02 02:51:09 +05:30
Prateek Sunal
27d72eb821 fix: make continuation and releasing compute better 2025-09-02 02:44:11 +05:30
Prateek Sunal
98786c5824 fix: move logs at better place 2025-09-02 01:04:24 +05:30
Prateek Sunal
d38a09c3f0 perf: optimize video stream processing state management
- Move isCurrentlyProcessing to widget state for better performance
- Only call setState when processing state actually changes
- Add comprehensive processing status handling (retry, compressing, uploading)
- Remove redundant service calls from build method
- Clean up unnecessary early returns and duplicate logic

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-01 18:50:00 +00:00
Aman Raj Singh Mourya
91785d8c90 Add Parallels custom icon (#7026)
## Description
This PR adds a custom icon for Parallels.

- Added `parallels.svg` under
`mobile/apps/auth/assets/custom-icons/icons/`
- Updated `custom-icons.json` with:
  - title: "Parallels"
  - slug: "parallels"
  - hex: #E61E25
  - altNames: ["Parallels Desktop", "Parallels VM"]

The icon is optimized (well under 50KB) and uses the official Parallels
red (#E61E25).
2025-09-02 00:19:46 +05:30
Prateek Sunal
b1f28e3f2e chore: update locks 2025-09-01 23:35:43 +05:30
Prateek Sunal
c155bdd058 chore: lint fixes 2025-09-01 23:35:30 +05:30
Prateek Sunal
a859f28e2c fix: show queueed or creatingStream based on context 2025-09-01 23:35:25 +05:30
Prateek Sunal
8d75528aa5 fix: introduce in queue and creating stream two types of statuses 2025-09-01 23:35:08 +05:30
Prateek Sunal
7f43c11985 fix: only show when video streaming is enabled 2025-09-01 21:22:17 +05:30
Manav Rathi
aadda7e3f6 feat(export): Add file deletion and rename detection to match Go CLI
Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-01 18:01:01 +05:30
Ashil
210c18d244 Update internal changes (#7030) 2025-09-01 17:32:12 +05:30
Ashil
6636849838 update internal changes 2025-09-01 17:26:20 +05:30
Neeraj
5500315351 [mob][photos] fix unsupported locales in language selector (#7029) 2025-09-01 17:24:32 +05:30
Prateek Sunal
562292e642 fix: remove unsupported languages from language picker
Remove languages from _getLanguageName that don't have >90% translation
coverage and aren't in appSupportedLocales (Finnish, Korean, Arabic).
Also improve Chinese locale display.

- Removed fi, ko, ar cases that don't meet translation threshold
- Fixed Chinese locale handling to properly show "中文 (简体)" for zh_CN
- Ensures only properly translated languages appear in the picker

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-01 17:13:58 +05:30
Prateek Sunal
4aa80edbcf fix: resolve unsupported locales appearing in language selector
Replace AppLocalizations.supportedLocales with a curated list of properly
translated locales in the Photos app. This fixes the issue where unsupported
language codes (Bg, Be, Ca, Cs, etc.) were appearing in the language selector
without proper language name formatting.

- Add custom appSupportedLocales list with only >90% translated languages
- Update all references throughout Photos app to use the custom locale list
- Ensures only properly supported languages appear in the language picker

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-01 17:02:15 +05:30
Neeraj
9524a639cd [server] Fix collection link for locker (#6961)
## Description

## Tests
2025-09-01 16:28:19 +05:30
Neeraj
b8eb793c16 [mobile/photos] New translations (#7022)
New translations from
[Crowdin](https://crowdin.com/project/ente-photos-app)
2025-09-01 16:26:51 +05:30
Ashil
4b514f1e1a [mob][photos] Revert "Revert diskLoadDeferDuration to 500ms" (#7028)
This reverts commit a295f223b6.
2025-09-01 16:25:59 +05:30
eYdr1en
bee2bb9621 remove unusuded variable 2025-09-01 12:45:27 +02:00
ashilkn
772121c22e Revert "Revert diskLoadDeferDuration to 500ms"
This reverts commit a295f223b6.
2025-09-01 16:07:40 +05:30
eYdr1en
3c49ca0f6e Merge branch 'main' into touch-id 2025-09-01 12:35:27 +02:00
Onurcan
f2e51893ad Update Parallels custom-icons.json 2025-09-01 11:59:32 +03:00
Onurcan
c08b78c775 dd Parallels custom icon 2025-09-01 11:58:26 +03:00
Manav Rathi
233f03355f Fix security issues and match Go CLI error handling
Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-01 13:16:03 +05:30
Laurens Priem
73ab50f113 [mob][photos] Run vectorDB migration is memory safe way (#7024)
## Description

- Add ability to block computeController temporarily
- Block computeController when vectorDB migration is running
2025-09-01 11:44:39 +05:30
laurenspriem
4a2346fe93 Block compute when vectorDB migration is happening 2025-09-01 11:11:10 +05:30
laurenspriem
68b5cce158 Add option to block compute tasks (ml, streaming) 2025-09-01 11:09:51 +05:30
laurenspriem
e907a9e8cb comment 2025-09-01 10:59:02 +05:30
Manav Rathi
92a40afca2 [web] New translations (#7021)
New translations from
[Crowdin](https://crowdin.com/project/ente-photos-web)
2025-09-01 10:22:50 +05:30
Aman Raj Singh Mourya
0c2b38c059 Fixing dev build on macos (#7012)
This should fix this error
https://github.com/ente-io/ente/pull/6768#discussion_r2310164866
2025-09-01 09:26:51 +05:30
Crowdin Bot
19650bcd57 New Crowdin translations by GitHub Action 2025-09-01 01:05:33 +00:00
Crowdin Bot
2b9ca073ce New Crowdin translations by GitHub Action 2025-09-01 00:45:39 +00:00
Manav Rathi
2257087bb2 Fix file rename handling to match Go CLI behavior
- Add rename detection by tracking files via ID in metadata
- Remove old files (including live photo MOV components) when renamed
- Copy live photo MOV components during hash deduplication
- Preserve file deduplication optimization while handling renames correctly

This ensures that when a file is renamed in Ente, the old file is removed
and replaced with the renamed version, matching the Go CLI's behavior.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-31 21:56:14 +05:30
Manav Rathi
2a5bce2ae4 Fix live photo export to preserve original file extensions
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-31 10:36:25 +05:30
Manav Rathi
1e0a6eb1ea Add persistent storage for public magic metadata
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-31 09:28:00 +05:30
Manav Rathi
187a729013 Update CLAUDE.md documentation to reflect current codebase
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-31 09:10:50 +05:30
Manav Rathi
c98f4dfffd fix(rust): Match Go CLI email filtering behavior
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-31 07:39:59 +05:30
Manav Rathi
4140a0f6fe feat(rust): Add shared album decryption support
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-31 06:54:33 +05:30
Aman Raj Singh Mourya
cf4b87dad9 [locker] Refactor theme handling in Locker to fix DynamicFAB style (#7016)
## Description
DynamicFAB theme was not getting applied from the commons package. This
PR fix that issue.

## Tests

**Before**

<img width="300" height="750" alt="Simulator Screenshot - iPhone 16 Plus
- 2025-08-30 at 15 53 26"
src="https://github.com/user-attachments/assets/17dfc778-b652-4e10-ad8f-3c8aed2656f6"
/>


**After**

<img width="300" height="750" alt="Simulator Screenshot - iPhone 16 Plus
- 2025-08-30 at 15 52 58"
src="https://github.com/user-attachments/assets/9e0c2feb-8204-4875-9bad-f9d4eaab8f36"
/>
2025-08-30 16:01:01 +05:30
AmanRajSinghMourya
3fd0db6a90 Refactor theme handling in locker to fix DynamicFAB 2025-08-30 15:52:34 +05:30
AmanRajSinghMourya
a9d5773b9a Fix sync after leaving collection 2025-08-30 15:12:37 +05:30
Manav Rathi
ac68b99ecf Fix shared collection deserialization
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-30 10:53:44 +05:30
Manav Rathi
82e1a0e358 Fix hidden album filtering to match Go CLI
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-30 10:37:45 +05:30
Laurens Priem
ce1701d211 [mob][photos] Similar small fixes (#7008)
## Description

Small design changes and fixes.

## Tests

Tested in debug mode on my pixel phone.
2025-08-30 07:29:27 +05:30
Manav Rathi
034e789242 fix(rust): Validate account exists before update/delete
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-29 22:02:35 +05:30
Manav Rathi
ccfec4071f fix(rust): Match Go CLI JSON field naming for ID fields
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-29 21:51:34 +05:30
Manav Rathi
c4830732fd fix(rust): Format timestamps as ISO 8601 in metadata JSON
Changed metadata export to match Go CLI's timestamp format.
Timestamps now serialize as ISO 8601 strings with timezone offset
(e.g., "2025-07-23T19:48:06.098+05:30") instead of Unix microseconds.

Also fixed clippy warnings to ensure CI compliance.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-29 21:41:33 +05:30
Manav Rathi
72dc56e41f fix(rust): Correct album-based export to match Go CLI
Fixed export to properly organize files into album folders by:
- Fetching files from all collections using /collections/v2/diff endpoint
- Decrypting encrypted collection names to get actual album names
- Using decrypted album names for folder organization

Files now export to proper album folders instead of all going to
"Uncategorized". Tested and verified with local data.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-29 21:20:30 +05:30
AmanRajSinghMourya
8dd3ad9f5b Extract strings + minor fix 2025-08-29 20:23:46 +05:30
AmanRajSinghMourya
2ebb920faa Show leave collection options 2025-08-29 20:20:49 +05:30
AmanRajSinghMourya
e9f55b968a Refactor home page to manage collection file counts separately for main, outgoing, and incoming collections 2025-08-29 20:20:26 +05:30
AmanRajSinghMourya
5036a8da59 Add method to leave collection 2025-08-29 20:20:16 +05:30
Laurens Priem
aaed336991 [infra] Release action changes (#7010)
## Description

- Disk space cleanup
- Generate rust bindings
2025-08-29 20:05:31 +05:30
eYdr1en
0b85dfe7e4 fixing dev build on macos 2025-08-29 15:53:56 +02:00
Prateek Sunal
68422b172f [mob][photos] fix manual video streaming when ML is enabled (#7009) 2025-08-29 18:53:53 +05:30
laurenspriem
db99dae3e1 log line 2025-08-29 18:48:00 +05:30
laurenspriem
3717a156d3 Logging unexpected embeddings 2025-08-29 18:47:01 +05:30
Prateek Sunal
ca9930e01b style: fix import directive ordering in thumbnail_widget 2025-08-29 12:50:13 +00:00
laurenspriem
eb23a4e770 rust bindings 2025-08-29 18:19:50 +05:30
laurenspriem
e03303e5b3 release workflow disk cleanup 2025-08-29 18:18:45 +05:30
laurenspriem
2ad27f1c6e Clear similar images json cache 2025-08-29 18:03:27 +05:30
Prateek Sunal
202e6a9f7c fix: trigger processing for already-queued manual stream files
When users click "Create Stream" on files already in queue from
previous sessions, ensure processing actually starts even if the
file was previously stalled due to ML blocking.

Add forceProcess parameter to queueFiles() to bypass the existing
queue check and trigger processing of stalled manual queue items.
2025-08-29 12:28:48 +00:00
laurenspriem
ceaedad327 debug option to delete vectorDB index 2025-08-29 17:57:21 +05:30
Prateek Sunal
fd963a1c8e fix: allow manual video stream creation when ML is waiting
When ML is enabled but not running, the compute controller blocks
all stream requests due to _waitingToRunML flag. This prevents
users from manually creating video streams even though ML isn't
actively using resources.

Add bypassMLWaiting parameter to allow manual stream creation
to proceed regardless of ML waiting state, improving UX.
2025-08-29 12:22:08 +00:00
laurenspriem
b40b5bb1ae delete progress 2025-08-29 17:07:44 +05:30
laurenspriem
91827626b2 dot dot dot 2025-08-29 17:04:33 +05:30
laurenspriem
42318335ae Fix issue with deleting favorites 2025-08-29 16:16:51 +05:30
laurenspriem
858db62385 Left align large files 2025-08-29 16:09:10 +05:30
laurenspriem
46e36612d3 Scroll to top after delete 2025-08-29 15:49:09 +05:30
laurenspriem
62cf236e3b Cycle through loading screen texts 2025-08-29 15:27:11 +05:30
Ashil
c2b1ab86f2 [mob][photos] Fix incorrect file deletion from db when widget unmounts during thumbnail loading (#7007)
## Description

Previously, when _loadWithRetry returned null due to widget unmounting,
the code incorrectly assumed the local file was deleted and would remove
database reference of the file and which would trigger re-upload of the
file.
2025-08-29 15:17:55 +05:30
laurenspriem
43adf42281 Don't auto select favorites for deletions 2025-08-29 15:00:42 +05:30
laurenspriem
1e2a65281c Fix delete button bug 2025-08-29 14:32:12 +05:30
ashilkn
70eb68b13c Fix incorrect file deletion when widget unmounts during thumbnail loading
Previously, when _loadWithRetry returned null due to widget unmounting,
the code incorrectly assumed the local file was deleted and would remove
database references or delete the file. This could lead to data loss.

Changes:
- Add new WidgetUnmountedException to centralized exceptions.dart for reuse
- Throw WidgetUnmountedException instead of returning null when widget unmounts
- Handle WidgetUnmountedException separately in error handler with appropriate logging
- Still set _errorLoadingLocalThumbnail flag to prevent retry attempts

Using Exception instead of Error follows Dart conventions:
- Exceptions are for recoverable runtime conditions (like widget unmounting)
- Errors are for programming mistakes that shouldn't be caught

This ensures that widget unmounting is properly distinguished from actual
file access failures.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-29 14:17:49 +05:30
Ashil
fa86b19307 [mob][photos] Update internal change log (#7006) 2025-08-29 14:10:19 +05:30
Ashil
e632dc7771 Merge branch 'main' into update_int_change_log 2025-08-29 14:08:58 +05:30
ashilkn
7fa9adb636 update internal change log 2025-08-29 14:08:00 +05:30
Ashil
83f885f158 [mob][photos] Revert diskLoadDeferDuration to 500ms (#7005)
## Description
2025-08-29 13:58:58 +05:30
ashilkn
a295f223b6 Revert diskLoadDeferDuration to 500ms
Reverts the change from commit 1f1cad181f
which reduced galleryThumbnailDiskLoadDeferDuration from 500ms to 80ms.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-29 12:57:42 +05:30
AmanRajSinghMourya
6775faf0d0 Fix naming 2025-08-29 12:43:30 +05:30
Laurens Priem
cc64ef8035 [infra] Create more space for internal release action (#7004)
## Description

- Removed redundant SDKs
- Removed redundant rust install (already pre-installed)
- Delete old other action
2025-08-29 12:41:28 +05:30
Ashil
69dd7b6233 [mob][photos] Spacing (#7002)
## Description

Add spacing in similar images page.
2025-08-29 12:41:11 +05:30
AmanRajSinghMourya
367dc18caa Add sharing functionality for collections and update routing logic 2025-08-29 12:33:41 +05:30
AmanRajSinghMourya
0c6db4661e Refractor item_list_view.dart and split code into multiple file for better redability 2025-08-29 12:33:17 +05:30
Ashil
bcc9f1be73 [mob][photos] Revert cache extent changes (#7000)
## Description

The reverted changes were intended to solve the issue #6957 fixed. So
these changes are no longer needed and there are doubts if they are
causing regressions related to thumbnail loading.

## Tests
2025-08-29 12:26:29 +05:30
Ashil
296b2a2a6c Merge branch 'main' into revert-cache-extent-changes 2025-08-29 12:24:03 +05:30
laurenspriem
6b48c9bc34 Remove ineffective cleanup steps 2025-08-29 12:23:47 +05:30
ashilkn
6a951bcc72 Update internal change log 2025-08-29 12:23:28 +05:30
laurenspriem
38914981a1 Fix disk space calculation in cleanup step 2025-08-29 11:52:36 +05:30
laurenspriem
66f4d5b1a6 Add disk cleanup step to free space in GitHub Actions
Removes unused pre-installed software to free ~30-45GB:
- .NET SDK (~20-25GB)
- Haskell compiler (~5-8GB)
- Boost libraries (~1-2GB)
- Cached tool versions (~5-10GB)

Includes timing and space metrics for each removal

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-29 11:43:07 +05:30
laurenspriem
9ee3781320 spacing 2025-08-29 10:30:05 +05:30
laurenspriem
907d1d2bb8 delete rust install (already on runner env by default) 2025-08-29 10:27:46 +05:30
laurenspriem
8218283463 delete old action 2025-08-29 10:26:32 +05:30
Laurens Priem
bd43385949 [mob][photos] Similar index clear (#6997)
## Description

- Clear vectorDB index on logout
- Revert to using `view` on index
- Use `.usearch` for index file
- Minor design changes

## Tests

Tested in debug mode on my pixel phone.
2025-08-28 18:57:28 +05:30
Laurens Priem
2e6a9acaf9 Merge branch 'main' into similar_index_clear 2025-08-28 18:57:11 +05:30
Ashil
a02dcace7d [mob][photos] New Ducky launcher icons (#6999) 2025-08-28 18:56:34 +05:30
Laurens Priem
cf4285de6d Merge branch 'main' into similar_index_clear 2025-08-28 18:50:12 +05:30
laurenspriem
f831491e4a log changes 2025-08-28 18:30:29 +05:30
laurenspriem
af154d82de ducky analyzing riv animation 2025-08-28 18:17:11 +05:30
Prateek Sunal
ff2f75ea74 [mob][photos] bypass size/duration limits for manual video stream requests (#6998)
## Summary
- Modified `_checkFileForPreviewCreation` method to accept `isManual`
parameter
- Bypass 500MB file size and 60 second duration limits when user
manually triggers video stream processing
- Maintains size/duration restrictions for automatic streaming to
preserve device performance

## Test plan
- [x] Manual Create/Recreate Stream button bypasses 500MB and 60 second
limits
- [x] Automatic streaming still respects size and duration restrictions
- [x] Files larger than 500MB or longer than 60 seconds can be manually
processed
2025-08-28 17:51:26 +05:30
laurenspriem
97e3ef819a Change migration key because index file was changed (internal only) 2025-08-28 17:43:18 +05:30
Prateek Sunal
3685cd2154 fix: don't show create stream if file size is null 2025-08-28 17:34:29 +05:30
laurenspriem
c64fff8ca4 empty tab state ducky 2025-08-28 17:25:54 +05:30
ashilkn
23dc809589 Remove hardcoded cacheExtent to use Flutter's default value 2025-08-28 17:07:46 +05:30
laurenspriem
33d1242c6d strings 2025-08-28 17:03:53 +05:30
laurenspriem
b8ee9fafd1 Better confirmation dialog 2025-08-28 17:03:24 +05:30
ashilkn
f72c9fa068 Revert "Different cache extents for different photoGridSizes"
This reverts commit 769adb75c5.
2025-08-28 16:55:39 +05:30
Ashil
1a7275a101 Merge branch 'main' into ducky_icon 2025-08-28 16:17:58 +05:30
Prateek Sunal
fa7ccbd180 fix: if fileSize is null for manual way then skip 10MB check 2025-08-28 16:16:15 +05:30
ashilkn
79e26d6993 Update internal change log 2025-08-28 16:16:04 +05:30
ashilkn
023135afb5 Remove green background from ducky icon display
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-28 16:07:32 +05:30
Prateek Sunal
04aaa3a5e4 fix: bypass size/duration limits for manual video stream requests
Allow manual stream requests to bypass the 500MB file size and 60-second
duration limits by passing isManual parameter to _checkFileForPreviewCreation.
This ensures users can manually process large files even if they exceed the
automatic streaming limits.
2025-08-28 10:25:45 +00:00
laurenspriem
848857f409 same close related 2025-08-28 15:27:33 +05:30
ashilkn
137033be67 Update Android ducky launcher icon
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-28 15:23:51 +05:30
AmanRajSinghMourya
b6489f4c41 Add color for avatar 2025-08-28 15:17:53 +05:30
AmanRajSinghMourya
e7d7f1cdd0 Add user management features to sharing collection page and actions 2025-08-28 15:17:42 +05:30
AmanRajSinghMourya
bbbdd96c9e Add functionality for managing album participants and sharing settings 2025-08-28 15:17:12 +05:30
AmanRajSinghMourya
3c23d3b480 Extract strings 2025-08-28 15:16:45 +05:30
AmanRajSinghMourya
3805cddeba Add ListExtension and UserExtension for enhanced list and user functionalities 2025-08-28 15:16:29 +05:30
AmanRajSinghMourya
824c324342 Add MenuSectionDescriptionWidget and MenuSectionTitle components 2025-08-28 15:16:18 +05:30
laurenspriem
04b6f4a765 right threshold 2025-08-28 15:05:58 +05:30
laurenspriem
2645ba0949 Change index file name to use usearch 2025-08-28 13:13:16 +05:30
laurenspriem
5958647fa8 Delete vectorDB index file on logout 2025-08-28 13:11:05 +05:30
laurenspriem
b7b91631f6 revert back to view on index 2025-08-28 13:01:26 +05:30
Prateek Sunal
67d7f586b2 [mob][photos] bypass interaction check for manual stream requests (#6993)
## Summary
- Manual Create/Recreate Stream button presses now bypass user
interaction timer for immediate processing
- Fixed multiple concurrent streaming processes bug in ComputeController
- Fixed video streaming description text display spacing in advanced
settings
- Maintains device health and ML priority checks for all streaming
requests

## Tests
- [x] Manual Create/Recreate Stream button bypasses interaction timer  
- [x] Automatic streaming still respects interaction timer
- [x] Only one streaming process allowed at a time
2025-08-27 21:15:55 +05:30
Prateek Sunal
7c22a8bb25 chore: lint fix 2025-08-27 21:10:18 +05:30
Prateek Sunal
ff3864a09a fix: check only if permission granted before chunking 2025-08-27 21:09:10 +05:30
Prateek Sunal
4484b9e4ad update: add video streaming improvements to change logs
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-27 13:21:59 +00:00
Prateek Sunal
e9554ffbcb fix: prevent multiple concurrent streaming processes
Remove condition allowing additional stream requests when already streaming to ensure only one stream process runs at a time.

Co-authored-by: Claude <noreply@anthropic.com>
2025-08-27 13:15:10 +00:00
Prateek Sunal
ad3901d484 fix: remove conditional clearQueue for manual processing
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-27 13:08:12 +00:00
Prateek Sunal
ecca4c3dc8 feat: bypass interaction check for manual stream requests
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-27 13:00:21 +00:00
Prateek Sunal
d05521f884 [mob][photos] video streaming description spacing and alignment (#6992)
## Summary
- Split videoStreamingDescription into separate line1/line2 localization
keys
- Remove TextAlign.justify from enabled state to fix awkward word
spacing
- Standardize text rendering between enabled and disabled states
- Both states now display description consistently without spacing
issues

## Test plan
- [x] Verify enabled state displays as single line without spacing
issues
- [x] Verify disabled state shows proper line breaks in onboarding
- [x] Confirm localization keys generate correctly
- [x] Run dart format and dart analyze (no issues)

Fixes video streaming settings page text display inconsistencies.
2025-08-27 18:08:28 +05:30
Prateek Sunal
ff37c4bf81 fix: video streaming description spacing and alignment
- Split videoStreamingDescription into separate line1/line2 localization keys
- Remove TextAlign.justify from enabled state to fix awkward word spacing
- Standardize text rendering between enabled and disabled states
- Both states now display description consistently without spacing issues

Co-authored-by: Claude <noreply@anthropic.com>
2025-08-27 12:03:31 +00:00
ashilkn
446df755fa Add ducky icon for iOS
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-27 17:23:31 +05:30
Manav Rathi
0f5e30e96b feat(rust): Add metadata export matching Go CLI format
Export album and file metadata to .meta folders within each album directory.
Enables incremental sync and compatibility with Go CLI exports.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-27 17:22:17 +05:30
Manav Rathi
35ded7bc59 fix(rust): Match Go CLI's album-based export directory structure
Switch from date-based (YYYY/MM-Month) to album-based directory structure
to ensure compatibility with Go CLI. Files now export to AlbumName/ folders
with "Uncategorized" for files without albums.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-27 17:06:40 +05:30
ashilkn
a7805784b7 Add ducky icon for Android
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-27 17:01:09 +05:30
Manav Rathi
8e3f6e56d2 feat(rust): Remove sync command to match Go CLI interface
Align with Go CLI by integrating sync into export workflow.
Update CLAUDE.md to prevent default template usage in commits.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-27 16:51:04 +05:30
AmanRajSinghMourya
6ded21fe87 Add CollectionViewType enum and update CollectionPage 2025-08-27 14:35:11 +05:30
AmanRajSinghMourya
be4b521879 Extract strings 2025-08-27 14:32:34 +05:30
AmanRajSinghMourya
326eb3ff8a Add getPublicKey method to UserService for retrieving public keys by email 2025-08-27 13:49:01 +05:30
AmanRajSinghMourya
adef8bd466 Extract strings & add constants 2025-08-27 13:48:08 +05:30
AmanRajSinghMourya
a1d9fb5969 Add function to handle sharing actions 2025-08-27 13:46:58 +05:30
AmanRajSinghMourya
6da615b7dc Refactor ManageSharedLinkWidget to enable link expiry and device limit features with updated UI components 2025-08-27 13:45:56 +05:30
AmanRajSinghMourya
41a268b1cb Add crypto, bip39, dotted_border packages 2025-08-27 13:43:57 +05:30
AmanRajSinghMourya
ed07e64fa5 Add new UI components and dialogs for sharing features 2025-08-27 13:42:56 +05:30
Neeraj
84a5ad0b86 [mob][photos] More minor design changes for similar images (#6986)
## Description

- Change tab order
- Make tabs distinct
- Change default ordering to size

## Tests

Tested in debug mode on my pixel phone.
2025-08-27 13:25:40 +05:30
laurenspriem
44ad11343a Better empty state prompt for emtpy tab 2025-08-27 13:17:41 +05:30
laurenspriem
07e50e3cfe Change default sort to size 2025-08-27 13:03:30 +05:30
laurenspriem
df8bbdb788 Make identical and similar distinct 2025-08-27 13:01:56 +05:30
laurenspriem
1ed381fe52 Change order of tabs 2025-08-27 12:52:45 +05:30
ashilkn
55090436ce Add new ducky-icon assets 2025-08-27 11:21:36 +05:30
Manav Rathi
150534aa1a feat(rust): Add deduplication, live photos, and update docs
- Hash-based file deduplication prevents duplicate exports
- Live photo extraction from ZIP archives
- Update conversion status documenting feature completion
- Make commit guidelines prominent in CLAUDE.md
- Remove redundant commit format section

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-27 07:53:22 +05:30
Laurens Priem
ddd1d5ac86 [mob][photos] Similar images UX changes (#6981)
## Description

Similar images UX changes

## Tests

Tested in debug mode on my pixel phone.
2025-08-26 23:50:33 +05:30
Laurens Priem
26845a502e [mob][photos] Use load instead of view on index (#6980)
## Description

Use load instead of index
2025-08-26 23:48:59 +05:30
laurenspriem
21aac29020 format count properly 2025-08-26 23:48:31 +05:30
AmanRajSinghMourya
bdfe363066 Minor UI fix 2025-08-26 23:46:38 +05:30
laurenspriem
c1ff02df14 Always select all on tab change 2025-08-26 23:39:21 +05:30
laurenspriem
e4927c4022 Merge branch 'main' into similar_ux_changes 2025-08-26 23:30:32 +05:30
laurenspriem
4fd797338b Empty state 2025-08-26 23:26:51 +05:30
laurenspriem
eca0e5943d tab button look 2025-08-26 23:24:37 +05:30
laurenspriem
56cc7309a5 Show progress only for multiple albums symlinking 2025-08-26 23:05:04 +05:30
laurenspriem
b740d1af05 Show modal on 100+ deleted files only 2025-08-26 23:01:30 +05:30
laurenspriem
6d21b73367 faster select 2025-08-26 22:59:30 +05:30
laurenspriem
a5704eef25 Use load instead of view on index 2025-08-26 22:46:33 +05:30
laurenspriem
7e83682686 tiny margin in threshold 2025-08-26 22:42:04 +05:30
laurenspriem
18d5aa61b0 Extract string 2025-08-26 22:34:59 +05:30
laurenspriem
7c2a719ba8 (un)select all 2025-08-26 22:32:11 +05:30
Manav Rathi
2a136ba087 fix(rust): Fix file counting logic in sync and export commands
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 21:46:52 +05:30
laurenspriem
47313a74ff Tab bar filter 2025-08-26 21:28:21 +05:30
Manav Rathi
3abb479fbf feat(rust): Add progress indicators for downloads
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 21:26:39 +05:30
Manav Rathi
7eda60a493 fix(rust): Fix incremental sync to properly track per-collection timestamps
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 21:15:11 +05:30
Manav Rathi
bb8c5caa8d feat(rust): Handle renamed files using public magic metadata
Check both public magic metadata (for edited names) and regular metadata
when determining file names during export and sync, matching Go CLI behavior

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 21:00:11 +05:30
Prateek Sunal
65a7a16298 [mob][photos] fixes (#6979)
## Description

- [x] Fix Spacing in Video streaming settings
- [x] Update copy in Video Streaming settings
- [x] Disable debug notifications for work manager in iOS

## Tests
2025-08-26 20:57:02 +05:30
Prateek Sunal
9251e4f5b6 fix: update spacing and remove cross icon from top right 2025-08-26 19:07:44 +05:30
Prateek Sunal
c4bc6abf83 fix: remove debug mode notification flag 2025-08-26 19:07:27 +05:30
Manav Rathi
0384819c01 Take 2 2025-08-26 18:06:05 +05:30
Manav Rathi
f55973367d feat(rust): Add retry logic and export filters
- Add configurable retry with exponential backoff for API calls
- Handle 429 and 5xx errors with automatic retries
- Add export filters for albums, shared, and hidden collections
- Fix formatting and clippy warnings to pass CI checks

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 17:54:57 +05:30
laurenspriem
3165289483 Remove header 2025-08-26 17:48:39 +05:30
laurenspriem
01aab41c25 Select all by default 2025-08-26 17:46:53 +05:30
laurenspriem
1826258161 Copy 2025-08-26 17:43:58 +05:30
laurenspriem
df5917060b Copy change 2025-08-26 17:40:20 +05:30
Prateek Sunal
b5aa05cc1b [mob][photos] merge migration scripts (#6974)
## Description

Fixes #6923

## Tests
2025-08-26 17:16:10 +05:30
Prateek Sunal
cd865992f2 chore: directly use database 2025-08-26 17:02:21 +05:30
Prateek Sunal
370c0ab54a fix: merge migration scripts 2025-08-26 16:02:15 +05:30
Manav Rathi
699794226f fix(rust): Fix sync command file downloads
- Handle non-interactive mode in account add command
- Fix cross-filesystem file move issue by using copy+delete instead of rename
- Successfully tested downloading files from local server

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 16:00:15 +05:30
Manav Rathi
dee68acfc3 docs(rust): Reduce verbosity
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 15:31:06 +05:30
eYdr1en
0bd5452837 Merge remote-tracking branch 'upstream/main' into touch-id 2025-08-26 11:07:30 +02:00
Neeraj
923f2484fb [auth] Fix missing token (#6971)
## Description
When using Auth without backup, it was giving a error `Offline key is
missing`

**Reason**: During the `init` of `BaseConfiguration` if the `tokenKey`
is not set, we clear all the keys in the secure storage, and in this
process the `offlineAuthSecretKey` was also getting cleared

**Fix** Fixed by skipping the deletion of `offlineAuthSecretKey`  

## Tests
[Test Video](https://wormhole.app/qz3mol#Dlhr0NRpVQVQsrid2X-quA)
2025-08-26 13:02:43 +05:30
AmanRajSinghMourya
37928cd2c6 Code refractoring 2025-08-26 12:40:03 +05:30
AmanRajSinghMourya
fc32ba97c1 Refactor BaseConfiguration to ensure preserved keys are not deleted 2025-08-26 12:26:01 +05:30
AmanRajSinghMourya
e49084867e Revert "Refactor BaseConfiguration to preserve offlineAuthSecretKey during logout"
This reverts commit 5b5f563d47.
2025-08-26 12:21:00 +05:30
Manav Rathi
e53ddb8b51 refactor(rust): Remove backward compatibility code
Since the CLI hasn't been released yet, we don't need to maintain
backward compatibility. This commit removes unnecessary compatibility
code to simplify the codebase.

Changes:
- Remove id field from Account struct (use user_id directly)
- Remove update_file_local_path legacy wrapper method
- Use mark_file_synced directly instead of the wrapper
- Update all references from account.id to account.user_id

This results in cleaner, more maintainable code without unnecessary
compatibility layers.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 10:12:26 +05:30
Laurens Priem
a046748ded [mob][photos] Minor fixes and changes (#6969)
## Description

Minor fixes and changes based on testing.

## Tests

Tested in debug mode on my pixel phone.
2025-08-26 10:08:42 +05:30
laurenspriem
047d708ef1 Merge branch 'main' into fix_modal 2025-08-26 10:08:18 +05:30
Manav Rathi
95d167878e refactor(rust): Eliminate redundant primary keys using global uniqueness
Since user_id is globally unique in Ente's system (like collection_id and
file_id), we can eliminate artificial primary keys and use the actual IDs
directly. This simplifies the schema and reduces redundancy.

Changes:
- Use (user_id, app) composite primary key in accounts table
- Use (user_id, app) composite primary key in secrets table
- Remove account_id references, use user_id directly
- Update collections table to use owner field (user_id)
- Update files table to use owner_id field (user_id)
- Remove account_id from album_files table
- Update sync_state table to use (user_id, app) primary key
- Update all storage methods to use new schema
- Update commands to pass correct parameters to storage methods
- Update indices for better query performance

This aligns with Ente's API design where these IDs are guaranteed to be
globally unique, eliminating the need for artificial primary keys.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 10:04:24 +05:30
AmanRajSinghMourya
5b5f563d47 Refactor BaseConfiguration to preserve offlineAuthSecretKey during logout 2025-08-26 10:02:19 +05:30
Ashil
2b60ad3748 [mob][packages] Organize imports (#6968) 2025-08-26 10:01:40 +05:30
Neeraj
1f70043c83 [mob][photos] video streaming settings & create/recreate stream (#6923) 2025-08-26 10:00:08 +05:30
laurenspriem
7ce6f6a346 Check symlink permissions 2025-08-26 09:55:17 +05:30
Manav Rathi
653fc47aed fix(rust): Fix clippy warning for collapsible if statement
Collapsed nested if statement in sync.rs to satisfy clippy's
collapsible-if lint rule. This change is required for CI to pass
with the updated Rust version.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 09:43:21 +05:30
ashilkn
03814bff0c Organize imports 2025-08-26 09:34:00 +05:30
Manav Rathi
34325691e7 refactor(rust): Use collection_id and file_id as primary keys
Since collection_id and file_id are globally unique across all users in
Ente's API, we can use them directly as primary keys instead of creating
artificial auto-increment IDs. This simplifies the schema and reduces
redundancy.

Changes:
- Use collection_id as primary key in collections table
- Use file_id as primary key in files table
- Use composite primary key (album_id, file_id) in album_files table
- Update all related SQL queries to match new schema
- Add appropriate foreign key constraints
- Optimize indices for the new structure

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 09:30:05 +05:30
laurenspriem
4c63a0ff13 Copy changes 2025-08-26 09:21:59 +05:30
Manav Rathi
e474114e22 fix(rust): Fix clippy warnings and improve CI documentation
- Fix collapsible if statement warnings in sync.rs and files.rs
- Update CLAUDE.md with clearer CI requirements
- Remove misleading auto-fix command that doesn't catch all issues
- Emphasize that ALL checks must pass before committing

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 09:14:33 +05:30
laurenspriem
93552fb872 vectorDB flag check ML enabled 2025-08-26 09:05:06 +05:30
laurenspriem
1b61becdcf Fix modal on group-level delete 2025-08-26 08:37:16 +05:30
Manav Rathi
80c07d36a9 feat(rust): Complete sync command with file downloads
- Integrated DownloadManager with sync command for actual file downloads
- Implemented proper sync state tracking using is_synced_locally flag
- Fixed database persistence by preserving sync state during updates
- Added proper collection key decryption for file downloads
- Files are only downloaded once and marked as synced
- Cleaned up schema - removed migrations since this is new code
- Fixed deserialization issues with RemoteFile thumbnail field
- Added proper error handling for missing collection keys

The sync command now:
1. Fetches metadata for collections and files
2. Downloads files that haven't been synced yet
3. Marks files as synced to avoid re-downloading
4. Properly handles existing files on disk

This matches the Go CLI's approach of using a synced flag rather than
checking file existence on every sync.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 06:27:09 +05:30
Manav Rathi
8581742a73 feat(rust): Integrate DownloadManager with sync command
- Added local_path column to files table for tracking downloaded files
- Implemented get_pending_downloads() to find files without local_path
- Integrated DownloadManager into sync command for full file downloads
- Added collection key decryption for file downloads
- Generate proper export paths with date/album structure
- Track successful downloads and update database with local paths
- Added migration to add local_path column to existing databases

The sync command now supports full file downloads (not just metadata).
Files are downloaded to the export directory with proper organization.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 05:55:03 +05:30
Manav Rathi
042dae8790 fix(rust): Apply cargo fmt and clippy fixes
- Fixed formatting issues in sync/engine.rs
- Added #[allow(dead_code)] for unused storage field in DownloadManager
- Replaced manual clamp with .clamp() method

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 05:41:33 +05:30
Prateek Sunal
0499cad3c9 chore: ignore generated mocks file 2025-08-26 04:48:32 +05:30
Prateek Sunal
79752ef4b8 chore: update file name 2025-08-26 04:35:51 +05:30
Prateek Sunal
c1bd6d3fdb Merge branch 'taking-streaming-oob' of https://github.com/ente-io/ente into taking-streaming-oob 2025-08-26 04:35:30 +05:30
Prateek Sunal
621423d9a4 fix: refactor code 2025-08-26 04:35:27 +05:30
Prateek Sunal
edb11c89ba fix: update mobile/apps/photos/lib/ui/settings/streaming/video_streaming_settings_page.dart
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-26 04:31:44 +05:30
Prateek Sunal
adb71fe09c fix: update mobile/apps/photos/lib/services/video_preview_service.dart
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-26 04:31:12 +05:30
Prateek Sunal
c20cee2406 feat: state update event, tests, total logic 2025-08-26 04:27:38 +05:30
Prateek Sunal
dcfad86c47 fix: ensure preview ids are present 2025-08-25 23:32:57 +05:30
Prateek Sunal
0a2bff67bf fix: delete file from upload locks db if not found 2025-08-25 23:31:51 +05:30
Prateek Sunal
7aaa689cfb fix: simplify logic 2025-08-25 23:26:34 +05:30
Prateek Sunal
ad2a0ce897 refactor: simplify StreamingStatus handling in VideoPreviewService 2025-08-25 23:22:39 +05:30
Prateek Sunal
d99615b24f fix: remove skipped 2025-08-25 22:16:22 +05:30
Prateek Sunal
09cc48ae55 fix: add comments 2025-08-25 21:50:07 +05:30
Prateek Sunal
6ab2223a80 fix: mostly all review comments 2025-08-25 21:42:40 +05:30
Prateek Sunal
6fd86162e0 Merge remote-tracking branch 'origin/main' into taking-streaming-oob 2025-08-25 20:58:09 +05:30
Prateek Sunal
707e8dbfcf fix: show processing status, fix when to not show popup buttons, update getFiles logic 2025-08-25 20:57:18 +05:30
Prateek Sunal
5869bec781 feat: create and recreate stream buttons 2025-08-25 20:11:08 +05:30
laurenspriem
45249e0cdf add similar images entry 2025-08-25 18:24:02 +05:30
laurenspriem
ebfcedac7b update outdated 2025-08-25 18:22:32 +05:30
Laurens Priem
e311a8bb32 [mob][photos] Similar images UI (#6963)
## Description

Minor fixes and UI changes.

## Tests

Tested in debug mode on my pixel phone.
2025-08-25 16:55:29 +05:30
laurenspriem
547ccfceca Use shared preferences for tracking migration 2025-08-25 16:53:33 +05:30
AmanRajSinghMourya
2900ca55f5 Extract Strings + minor fix 2025-08-25 15:10:08 +05:30
AmanRajSinghMourya
2a40aa472e Add parameters to share URL request 2025-08-25 14:53:03 +05:30
Keerthana
3a1917949b [mobile/photos] New translations (#6955)
New translations from
[Crowdin](https://crowdin.com/project/ente-photos-app)
2025-08-25 13:30:03 +05:30
Keerthana
3a1ce3258e [auth] New translations (#6956)
New translations from
[Crowdin](https://crowdin.com/project/ente-authenticator-app)
2025-08-25 13:10:39 +05:30
Prateek Sunal
13b2542bea fix: update logic 2025-08-25 12:02:56 +05:30
Prateek Sunal
6db3741a3b Merge remote-tracking branch 'origin/main' into taking-streaming-oob 2025-08-25 11:48:49 +05:30
AmanRajSinghMourya
62cb67f3bf Minor UI improvements 2025-08-25 11:47:11 +05:30
AmanRajSinghMourya
e393b92a3d Add sharing functionality to CollectionPage 2025-08-25 11:46:51 +05:30
AmanRajSinghMourya
e06d65e8a0 Minor fix 2025-08-25 11:46:08 +05:30
AmanRajSinghMourya
a4ec8c939a Add ManageSharedLinkWidget and ShareCollectionPage for link management functionality 2025-08-25 11:45:59 +05:30
AmanRajSinghMourya
b8dd379306 Enhance AllCollectionsPage to support multiple collection view types 2025-08-25 11:44:49 +05:30
AmanRajSinghMourya
42229bd331 Refactor HomePage to integrate shared collections 2025-08-25 11:41:49 +05:30
AmanRajSinghMourya
ad9a3977a3 add helper method & cache collections for faster access 2025-08-25 11:35:16 +05:30
AmanRajSinghMourya
afb93df48f Add CollectionFlexGridViewWidget and SectionTitle components 2025-08-25 11:31:32 +05:30
AmanRajSinghMourya
4ce38ecea0 Added sharedCollections 2025-08-25 11:30:37 +05:30
AmanRajSinghMourya
4c63c8fc25 Add shareURL methods to CollectionApiClient 2025-08-25 11:30:09 +05:30
laurenspriem
ce17eccd68 Fix delete issue 2025-08-25 11:11:57 +05:30
Ashil
95dc683088 Update internal change log (#6959) 2025-08-25 11:11:28 +05:30
Neeraj
cf9d5f72f7 [mob] Fix query for duplicate cleanup (#6962)
## Description
Only consider owned files
## Tests
2025-08-25 11:10:36 +05:30
Neeraj Gupta
3096e1550a Merge remote-tracking branch 'origin/main' into fixQuery 2025-08-25 11:03:45 +05:30
Neeraj Gupta
1b39435735 Fix query 2025-08-25 11:02:25 +05:30
laurenspriem
8f3d8505bb Better UI when selection is not possible 2025-08-25 11:02:03 +05:30
Manav Rathi
47e8aafe25 [desktop] Update changelog (#6960) 2025-08-25 10:50:10 +05:30
Neeraj Gupta
bc6506cb10 Fix link for locker 2025-08-25 10:50:05 +05:30
Manav Rathi
edf32d065e [desktop] Update changelog 2025-08-25 10:47:43 +05:30
ashilkn
1fa6a0c3b9 Update internal change log 2025-08-25 10:41:16 +05:30
Neeraj Gupta
f2a26ba391 Minor refactor 2025-08-25 10:35:47 +05:30
Manav Rathi
2388989dd0 [web] Enable Czech (#6958) 2025-08-25 10:35:06 +05:30
laurenspriem
9e392277b1 Fix initState issue 2025-08-25 10:31:41 +05:30
Neeraj
4609c375db Revert "[auth] Add smaller Activision icon" (#6953)
Reverts ente-io/ente#6950

I rushed a bit, sorry. The PR wasn't meant to be merged yet (if ever)
and it won't work right now anyway. It was meant to create conversation
on the topic and then possibly merged and there may be concerns to this
as a company may not want their logo/wordmark altered but I'm not well
versed in this topic (idk maybe I'm overthinking this).

Discussion: #6951
2025-08-25 10:31:20 +05:30
Manav Rathi
839c62ea72 [web] Enable Czech 2025-08-25 10:28:23 +05:30
Neeraj
dceef49f33 [mob][photos] Pre-cache thumbnails fetched from LRU cache to Flutter's ImageCache for faster rendering (#6957)
## Description

In Gallery, even if thumbnails are stored in LRU cache, there was a
delay in rendering thumbnails when scrolling fast enough. Pre-caching
these thumbnails to flutter's `ImageCache` right before they're rendered
has made the rendering fast enough for seamless UX.

#### Before:


https://github.com/user-attachments/assets/c47958fb-fbda-4e1f-9ce7-26b51ca87938

#### After:


https://github.com/user-attachments/assets/cbaf4427-f52f-4544-a0c2-820eb2b43953
2025-08-25 10:18:05 +05:30
laurenspriem
acbdc3111a Remove use of withOpacity 2025-08-25 10:15:49 +05:30
laurenspriem
98b91a6935 Remove duplicate string 2025-08-25 10:11:07 +05:30
laurenspriem
e1640e67d4 Extract strings 2025-08-25 10:09:03 +05:30
laurenspriem
e875758419 Simplify sort options 2025-08-25 09:48:33 +05:30
Manav Rathi
214b120472 [web] New translations (#6954)
New translations from
[Crowdin](https://crowdin.com/project/ente-photos-web)
2025-08-25 09:43:37 +05:30
ashilkn
f139e0a098 Fix flickering of GalleryFileWidget on hero animation after closing it's open full view 2025-08-25 09:21:57 +05:30
laurenspriem
e3c9a61887 Align 2025-08-25 09:20:19 +05:30
ashilkn
0da3dc5084 Skip clearing flutter image cache since default (current) limit is 100MB and the threshold to clear is 250MB 2025-08-25 09:20:08 +05:30
ashilkn
a856a82249 Refactor 2025-08-25 09:18:29 +05:30
ashilkn
fbdec00a62 Improve lru cache thumbnail rendering speed when scrolling gallery by precaching it it flutter's image cache 2025-08-25 09:16:59 +05:30
Crowdin Bot
6a7f980a0d New Crowdin translations by GitHub Action 2025-08-25 01:18:00 +00:00
Crowdin Bot
10a855fe27 New Crowdin translations by GitHub Action 2025-08-25 01:05:02 +00:00
Crowdin Bot
b4f8a2b27c New Crowdin translations by GitHub Action 2025-08-25 00:39:57 +00:00
dnred
89489b4d7c Revert "[auth] Add smaller Activision icon" 2025-08-24 23:48:21 +02:00
AmanRajSinghMourya
158b48e4dc Add SharingNotPermittedForFreeAccountsError error 2025-08-24 23:58:22 +05:30
Aman Raj Singh Mourya
50296f8dfa [auth] Add smaller Activision icon (#6950)
## Description

The current Activision icon is too wide and small to be nicely displayed
in Auth so this PR adds a smaller one, just like the favicon on
Activision's [website](https://activision.com).

I know the Activision icon is pulled from simple-icons and I don't want
to get rid of that, just add an option for a smaller one, but I see that
the smaller Allegro icon is also added but it isn't displayed in the
icon picker and the icon from simple-icons takes precedence so you'd
have to figure this out.

## Tests

I haven't tested this.
2025-08-24 23:15:44 +05:30
a5xwin
fe732f2778 Merge remote-tracking branch 'origin/main' into autobackup 2025-08-24 22:58:36 +05:30
a5xwin
ca8a067966 added support to view backup path dynamically & improved ui flow 2025-08-24 22:57:35 +05:30
dnred
f69cec864b Rename activision2.svg to activision.svg 2025-08-24 16:42:49 +02:00
dnred
73d5d33fc5 Update custom-icons.json 2025-08-24 16:25:47 +02:00
dnred
4d8ea12ddd Add logo 2025-08-24 16:17:25 +02:00
Prateek Sunal
7beb267ba7 chore: remove unused import 2025-08-24 02:15:09 +05:30
Prateek Sunal
7e13ef3537 chore: remove formatting for files_db 2025-08-24 02:10:06 +05:30
Prateek Sunal
47edca5bf5 chore: fix formatting 2025-08-24 02:08:00 +05:30
Prateek Sunal
925ba10b15 chore: revert remote_sync formatting 2025-08-24 02:06:28 +05:30
Prateek Sunal
db2d0bb7e9 fix: remove formatting from ml_service 2025-08-24 02:04:09 +05:30
Prateek Sunal
f3a2b2af0c fix: include it in if loop 2025-08-24 02:02:47 +05:30
Prateek Sunal
967e88f88d Merge remote-tracking branch 'origin/main' into taking-streaming-oob 2025-08-24 02:00:33 +05:30
Prateek Sunal
b44734a493 fix: add streaming static image 2025-08-24 01:59:23 +05:30
Manav Rathi
6478b08a19 [docs] Minor improvements to self-hosted docs (#6936)
## Description
- Small correction on the self-hosted docker exec command.
- Added tip on how to install Ente CLI.

In spirit of starting with a small PR :p
2025-08-23 21:54:47 +05:30
Laurens Priem
314e81565b Fix linter issues (#6939)
## Description

Linter was failing because it didn't first run
`flutter_rust_bridge_codegen generate` to generate the dart bindings to
rust code.
2025-08-23 20:34:02 +05:30
laurenspriem
f95e20d00f Consistent tap behaviour 2025-08-23 17:35:12 +05:30
laurenspriem
35a04d6e7e Don't unnecessarily sort 2025-08-23 17:34:53 +05:30
laurenspriem
403264d2c9 Fix linter issues 2025-08-23 17:20:22 +05:30
Manav Rathi
84f5a5ac3d feat(rust): Add sync command and fix database path
- Add new `sync` command to fetch collections and file metadata
- Change config directory from ~/.ente/ to ~/.config/ente-cli/ to avoid conflicts with Go CLI
- Fix sync engine to use correct API endpoints (/collections/v2/diff instead of /diff)
- Implement per-collection file syncing matching Go CLI behavior
- Fix foreign key constraints in database schema
- Add metadata-only and full sync options
- Store database path in Storage struct for creating new instances
- Successfully tested with real account: syncs 5 files and exports correctly

The sync command now properly fetches all collections and files from the API,
storing them in SQLite for offline access and incremental sync support.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-23 16:47:08 +05:30
Toby
6b06a4c388 Add instructions on how to install Ente CLI 2025-08-23 13:13:08 +02:00
Toby
678bce89b2 Add small corrections to docker commands 2025-08-23 13:06:34 +02:00
Manav Rathi
a00fc0b1be fix(rust): Remove sensitive information from logs and docs
Security improvements:
- Remove all debug logs that output tokens, keys, or credentials
- Remove email addresses from debug output
- Remove encrypted keys and nonces from logs
- Remove specific account references from documentation
- Add security guidelines to CLAUDE.md

No sensitive information (PII, credentials, tokens) should be logged
even in debug mode. Updated guidelines to prevent future occurrences.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-23 15:06:28 +05:30
Manav Rathi
f5347e7436 docs(rust): Update conversion plan with completed features
- Mark streaming XChaCha20-Poly1305 implementation as complete
- Document successful export functionality with all decryption working
- Update testing status with successful real account exports
- Add recent achievements section highlighting key milestones
- Update feature parity progress checklist
- Document what components are complete vs remaining

The export functionality is now fully working with proper decryption
of collections, files, and metadata. Updated PR description as well.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-23 14:26:09 +05:30
Manav Rathi
3f1d574d0c feat(rust): Add progress indicators to export
- Show progress for each exported file with count
- Improve export summary with emojis and better formatting
- Add contextual success messages based on export results
- Make export output more user-friendly

The export now provides clear feedback during the process and
a helpful summary at the end.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-23 14:14:59 +05:30
Manav Rathi
891b68c0f4 fix(rust): Add chunked decryption for large files
- Implement chunked streaming decryption matching Go's 4MB buffer size
- Update file decryption to use decrypt_file_data instead of decrypt_stream
- Successfully tested with 33MB RAW image file
- All test files now decrypt correctly

Large files are now properly handled with chunked decryption, preventing
memory issues and matching the Go implementation's behavior.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-23 14:12:06 +05:30
Manav Rathi
f050c6f9d7 feat(rust): Implement streaming XChaCha20-Poly1305 decryption
- Add streaming cipher module using libsodium's secretstream API
- Update file and metadata decryption to use streaming XChaCha20-Poly1305
- Fix decryption issues - files now properly decrypt
- Successfully tested with real account - exports working for smaller files

The export now correctly decrypts files using the same streaming cipher
as the Go implementation. Large files may need chunked decryption support.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-23 14:01:59 +05:30
Manav Rathi
2de67b619f feat(rust): Add metadata decryption for original filenames
- Create metadata module with FileMetadata struct
- Decrypt file metadata to extract original filename
- Use original filename in export path generation
- Add proper file type detection from metadata
- Implement filename sanitization for filesystem safety

Files are now exported with their original names instead of generic IDs.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-23 13:25:38 +05:30
Manav Rathi
828dde5ca7 feat(rust): Implement file decryption in export command
- Add ChaCha20-Poly1305 decryption for downloaded files
- Decrypt file keys using master key
- Extract nonce from encrypted file data
- Add basic filename generation with extension detection
- Comment out sync modules temporarily due to model mismatches

The export command now properly decrypts files instead of saving them encrypted.
Next steps: extract original filenames from decrypted metadata.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-23 13:03:46 +05:30
Manav Rathi
2526c69896 docs(rust): Update pre-commit checklist to match CI configuration
Ensure clippy commands use --all-targets --all-features flags to match
the CI environment exactly. This prevents CI failures from warnings that
weren't caught locally.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-23 12:09:42 +05:30
Laurens Priem
2f1d4b9f1a Update rust to solve bindings generation issue (#6935)
## Description

Update rust to potentially solve bindings generation issue
2025-08-23 10:44:27 +05:30
laurenspriem
af20eadff0 Update rust to solve bindings generation issue 2025-08-23 10:39:54 +05:30
Manav Rathi
6e64a2067f fix(rust): Resolve all clippy warnings for CI compliance
- Fix lifetime elision warnings in storage/mod.rs by adding explicit lifetimes
- Collapse nested if-let statements in export.rs using let-chains
- Code now passes: cargo clippy --all-targets --all-features

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-23 09:58:29 +05:30
Manav Rathi
ab4792518f docs(rust): Add mandatory pre-commit checklist to CLAUDE.md
Add explicit pre-commit commands that must be run before every commit
to ensure CI passes. These commands simulate the CI environment locally.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-23 09:29:28 +05:30
Manav Rathi
d4ae8d63fc fix(rust): Fix linting and formatting issues for CI
- Applied cargo fmt to ensure consistent formatting
- Fixed all clippy warnings (uninlined_format_args)
- Code now passes all CI checks with RUSTFLAGS="-D warnings"

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-23 09:01:56 +05:30
Prateek Sunal
3264ea046c [mob][photos] add named params for translations (#6932)
## Description

Parameters were sorted based on name by intl plugin which is a breaking
change.

We now have shifted to named params for translations so position won't
change.

## Tests
2025-08-22 22:50:07 +05:30
Manav Rathi
618753cb1a feat(rust): Implement export command with collection-based file fetching
- Fix token encoding to use base64 URL with padding (matching Go implementation)
- Add export command that iterates through collections and fetches files
- Update API models to handle actual server response field names (ownerID vs ownerId)
- Fix file download URLs for local/dev environments
- Implement proper directory structure creation (YYYY/MM-Month format)
- Add collection attributes and public URL models for complete API compatibility
- Successfully exports encrypted files from both local and production endpoints

The export command now:
- Fetches all collections for an account
- Iterates through each collection to get files
- Downloads encrypted files and saves them to the export directory
- Skips already downloaded files to support incremental exports

Note: Files are still encrypted; decryption will be implemented in a future commit.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-22 21:13:52 +05:30
Laurens Priem
d81a73c833 [mob][photos] Similar images various improvements (#6931)
## Description

- Put the rust generated bindings in gitignore
- Use `view` instead of `load` on VectorDB index to use less RAM
- Various UI changes

## Tests

Tested in debug mode on my pixel phone.
2025-08-22 21:12:00 +05:30
laurenspriem
ac9c63fe29 Log updates 2025-08-22 21:11:25 +05:30
laurenspriem
53cb217dbc Fix MediaQuery in initState issue (for large files view) 2025-08-22 21:06:59 +05:30
Manav Rathi
f84bd20bbf feat(rust): Store API endpoint per account for better environment isolation
- Add endpoint field to accounts database table with default to production API
- Update Account model to include endpoint field
- Add --endpoint flag to account add command only
- Remove ENTE_ENDPOINT environment variable support
- Update account list to display endpoints in readable format
- Each account now maintains its own endpoint, preventing confusion between test and production environments

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-22 20:25:39 +05:30
Prateek Sunal
fca9a42e0a fix: add named params for translations to fix position 2025-08-22 19:41:45 +05:30
a5xwin
5e3a779925 Merge remote-tracking branch 'origin/main' into autobackup 2025-08-22 19:32:03 +05:30
a5xwin
d1b06abada Improve backup flow: add user controlled path dialog, better default path, and update text casing 2025-08-22 19:21:05 +05:30
Prateek Sunal
8b708228be fix: add different ui for enabling it 2025-08-22 19:04:16 +05:30
laurenspriem
d379262f56 Small animation 2025-08-22 18:57:37 +05:30
Manav Rathi
6ae7aa70d6 fix(rust): Fix FFI type cast and clippy warnings for CI
- Use std::ffi::c_char for libsodium FFI context parameter cast
- Fix all clippy warnings to pass CI with RUSTFLAGS="-D warnings"
- Update CLAUDE.md with FFI casting guidance for future development

This ensures the code passes all CI checks including the stricter
clippy settings used in GitHub Actions.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-22 18:49:24 +05:30
Manav Rathi
48757af5d0 fix(rust): Fix SRP authentication implementation
Fixed issues preventing successful authentication:
- Corrected Argon2 memory limit handling (API sends bytes, not KB)
- Replaced Blake2b with crypto_kdf_derive_from_key for login subkey derivation
- Fixed serde field names to match API expectations (srpUserID, sessionID)
- Added non-interactive mode for CLI testing
- Added support for ENTE_ENDPOINT environment variable

The implementation now matches the web client's key derivation exactly,
enabling successful authentication with both local and production servers.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-22 18:23:16 +05:30
laurenspriem
9282632af1 Exclude dart linter checks for rust 2025-08-22 18:14:13 +05:30
laurenspriem
6a43d6a567 Generate rust bindings on internal release 2025-08-22 18:11:53 +05:30
laurenspriem
1cdbef1a01 More fancy loading 2025-08-22 15:12:12 +05:30
laurenspriem
fa84bb0845 Delete progress indicator 2025-08-22 14:33:19 +05:30
Manav Rathi
cd20a98850 feat(rust): Implement account management commands
Add comprehensive account management functionality with secure SRP authentication.
This enables users to add, list, update, and manage multiple Ente accounts
for photos, locker, and auth apps.

Key features:
- Complete account add flow with SRP authentication
- Two-factor authentication support (TOTP)
- Secure key decryption and storage
- Multi-account support with per-app configuration
- Account list and update commands
- Export directory management
- Interactive CLI prompts with dialoguer

The implementation integrates with the API client for authentication and
securely stores account credentials in SQLite.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-22 13:57:29 +05:30
laurenspriem
cbb6f07d0d Improve empty state 2025-08-22 12:38:37 +05:30
Manav Rathi
9ac9e6bd26 feat(rust): Implement comprehensive API client for Ente services
Add complete API client implementation with authentication, file operations,
and collection management. This enables the Rust CLI to interact with Ente
servers for photo backup and sync operations.

Key features:
- Multi-account token management with secure storage
- SRP authentication flow matching Go implementation
- Retry logic with exponential backoff for network resilience
- Full API coverage: auth, collections, files, trash, user details
- Request/response models for all API endpoints
- Separate download client for large file transfers
- Smart CDN routing for production file downloads

The implementation follows the conversion plan and maintains compatibility
with the existing Go CLI API patterns.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-22 12:14:05 +05:30
laurenspriem
fad9cf8559 Add rust bindings generation to readme 2025-08-22 11:57:45 +05:30
laurenspriem
371ba9c552 Put generated rust bindings in gitignore 2025-08-22 11:54:59 +05:30
laurenspriem
19086e43cc Remove generated rust bindings 2025-08-22 11:54:24 +05:30
laurenspriem
964c837c40 Remove scrollbar 2025-08-22 11:36:48 +05:30
laurenspriem
d85121862d Use view of index instead of loading in memory 2025-08-22 11:34:40 +05:30
Manav Rathi
0b640c9062 docs(rust): Enhance CLAUDE.md with comprehensive codebase guidance
Add detailed development commands, architecture overview, and module
descriptions to help future Claude instances understand the codebase
structure and development workflow.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-22 10:38:57 +05:30
Manav Rathi
2d87aba165 docs(rust): Add comprehensive conversion plan
- Document current implementation status
- Detail API client implementation steps
- List all remaining components with specifications
- Include testing strategy and migration notes
- Provide file structure reference for navigation
- Add implementation guidelines and environment variables

This plan enables any developer to understand the project state
and continue the conversion work from the current point.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-22 10:03:01 +05:30
Manav Rathi
7dffdfaecf feat(rust): Implement SQLite storage layer
- Replace sled with SQLite for better reliability and tooling
- Create schema with tables for accounts, secrets, collections, files, and sync state
- Implement account storage with multi-account support
- Add configuration and sync state management
- Support for storing encrypted credentials separately
- Add indices for performance optimization

SQLite provides ACID transactions, better debugging tools, and a proven
track record for reliability with user data.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-22 09:50:59 +05:30
Manav Rathi
a4da7b5555 main safeguard 2025-08-22 09:26:55 +05:30
Prateek Sunal
42d31a73a3 fix: update localization & fix lint 2025-08-21 23:32:45 +05:30
Prateek Sunal
946d2ae522 fix: remove unnecessary blank line in analysis_options.yaml + circular button 2025-08-21 22:51:01 +05:30
Prateek Sunal
8e9eb50783 fix: downgrade Dart SDK version to 3.7.2 2025-08-21 22:50:24 +05:30
Prateek Sunal
af3bc7757f fix: downgrade dart sdk again 2025-08-21 22:50:04 +05:30
Prateek Sunal
eda1d05216 fix: revert dart sdk 2025-08-21 22:49:49 +05:30
Prateek Sunal
b58e0f8331 fix: remove analysis options for now 2025-08-21 22:49:37 +05:30
Prateek Sunal
6dcf53650d fix: re-add captioned text 2025-08-21 22:48:42 +05:30
Prateek Sunal
bff53d9081 chore: add back space 2025-08-21 22:47:42 +05:30
Prateek Sunal
f3306e14c7 fix: revert 2025-08-21 22:47:30 +05:30
Prateek Sunal
b5c075bac4 fix: add space at end 2025-08-21 22:45:38 +05:30
Prateek Sunal
241d21c2aa fix: revert things 2025-08-21 22:44:34 +05:30
Prateek Sunal
789d77747c fix: update other contact page 2025-08-21 22:35:24 +05:30
Prateek Sunal
35050aa32f fix: delete generated intl_utils locale 2025-08-21 22:33:23 +05:30
Prateek Sunal
40e6bd9fae Merge remote-tracking branch 'origin/main' into taking-streaming-oob 2025-08-21 22:32:41 +05:30
Prateek Sunal
9fe15d7ff0 [mob][photos] remove generated locals (#6925)
## Description

We depended on intl_utils but that had problems + it was not
auto-generating things when running `flutter pub get`

Now we are using pure intl implementation of l10n, by which generated
code would be less.

- [x] Removes generated locals

## Tests
2025-08-21 22:27:09 +05:30
Prateek Sunal
28a2afe275 Merge remote-tracking branch 'origin/main' into remove-intl_utils 2025-08-21 22:26:54 +05:30
Aman Raj Singh Mourya
c072097c11 Add custom icons (#6924)
## Description
Adds custom SVG icons for "CoinTracing", "VHV Versicherungen (German
Insurance Company)" and "HR Document Box".
SVG sourced from official CoinTracking press assets.
SVG sourced from Wikipedia for VHV Versicherungen.
For HR Document Box was no svg available, so i used a png to svg
converter.

## Tests
2025-08-21 19:27:57 +05:30
Prateek Sunal
b317df2000 Merge branch 'main' into taking-streaming-oob 2025-08-21 18:11:42 +05:30
Prateek Sunal
dd420a80a4 Merge remote-tracking branch 'origin/main' into remove-intl_utils 2025-08-21 18:08:31 +05:30
Prateek Sunal
3dc0620e18 chore: update flutter again for auth 2025-08-21 18:04:29 +05:30
Prateek Sunal
173d075f8b feat: remove generated locals 2025-08-21 18:04:20 +05:30
Daniel
48283282e5 Merge branch 'ente-io:main' into main 2025-08-21 14:30:49 +02:00
Laurens Priem
fa555c448f [mob][photos] Various similar images improvements (#6922)
## Description

Various improvements for similar images, mainly caching and UI changes.

## Tests

Tested in debug mode on my pixel phone.
2025-08-21 17:59:13 +05:30
laurenspriem
6f6770d677 Selection options 2025-08-21 17:58:43 +05:30
Daniel
0b894e9724 Update custom-icons.json 2025-08-21 14:21:02 +02:00
Daniel
0670550cb1 Add files via upload 2025-08-21 14:18:48 +02:00
Daniel
45783cf527 Update custom-icons.json 2025-08-21 14:07:32 +02:00
Daniel
1615779eb8 Add files via upload 2025-08-21 14:05:48 +02:00
Daniel
02e4c9d8fd Add files via upload 2025-08-21 14:00:23 +02:00
Prateek Sunal
2e706228ee feat: separate settings page improvements 2025-08-21 17:22:57 +05:30
Daniel
6eab6457ee Update custom-icons.json 2025-08-21 13:34:14 +02:00
Daniel
25490a7238 Add files via upload 2025-08-21 13:30:49 +02:00
Manav Rathi
85b766b5d0 Safeguard 2025-08-21 16:31:35 +05:30
Manav Rathi
62f715d3c1 fix(rust): Use std::ffi::c_char for FFI type casting
- Replace libc::c_char with std::ffi::c_char for password parameter
- Remove unnecessary libc dependency
- Use standard library FFI types (available since Rust 1.64)

This fixes the CI build error where libsodium expects *const c_char
for the password parameter in crypto_pwhash.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-21 16:27:50 +05:30
Neeraj
f519ff8a51 [auth] Option to scan image using native plugin (#6920)
## Description

## Tests
Tested on physical device (android and ios)
2025-08-21 16:20:45 +05:30
Neeraj Gupta
afebe1ade1 Clean up 2025-08-21 16:15:44 +05:30
laurenspriem
3862644dd5 Show info at top 2025-08-21 16:07:52 +05:30
Neeraj Gupta
274a7d207d Improve scanning 2025-08-21 15:59:53 +05:30
laurenspriem
add2f0c8de Debug option for full refresh 2025-08-21 15:34:05 +05:30
Neeraj Gupta
8e807616e0 Bump version 2025-08-21 15:29:08 +05:30
laurenspriem
70f4325c71 Refresh for small size 2025-08-21 15:18:05 +05:30
Neeraj Gupta
38ea2248b8 Show option on mobile only 2025-08-21 15:16:21 +05:30
laurenspriem
9600b26359 More stable sort 2025-08-21 15:10:40 +05:30
Neeraj Gupta
5b3e996aaa Option to scan image using native plugin 2025-08-21 15:08:11 +05:30
laurenspriem
4d4cce091f Fix json decoding issue 2025-08-21 14:13:49 +05:30
Neeraj
aaca140d1b [mobile] Use same lint rule file (#6917)
## Description
Had verified that hash for these rules were same.
## Tests
2025-08-21 14:06:37 +05:30
Neeraj Gupta
596ffcd4c4 [mobile] Use same lint rule file 2025-08-21 14:04:04 +05:30
laurenspriem
41ef85a294 Add scroll bars 2025-08-21 13:08:51 +05:30
Neeraj
f722d82835 [server]: add one click verify button for verification email (#5654)
OG (ott.html):

<img width="701" height="590" alt="image"
src="https://github.com/user-attachments/assets/80b926d1-c65f-44a8-9de4-7b591258bf3c"
/>



New (ott_mobile.html):

<img width="642" height="811" alt="image"
src="https://github.com/user-attachments/assets/aa18a778-1161-4b4e-ad82-cf472da06ff7"
/>
2025-08-21 12:44:24 +05:30
Neeraj Gupta
cbb3096534 use mobile ott template for only photos 2025-08-21 12:41:56 +05:30
Manav Rathi
e35ae86fa5 Ask it to run cargo fmt etc
For the current session cc was able to use that instruction to figure out all the linters etc to run. If that doesn't work in future sessions, we can use a longer instruction, something like what it itself suggested

    ## CI/CD Requirements
    - Must pass `cargo fmt --check`
    - Must pass `cargo clippy --all-targets --all-features`
    - Must pass `RUSTFLAGS="-D warnings" cargo build`
    - Fix all formatting before committing
    - Address all clippy warnings
    - Use `#![allow(dead_code)]` during development for unused code

    ## Code Quality
    - Run `cargo fmt` before committing
    - Fix clippy warnings: remove unnecessary casts, use idiomatic Rust
    - Prefix unused variables with underscore
    - Remove unused imports
2025-08-21 12:33:14 +05:30
Manav Rathi
ea843eba7a fix(rust): Address cargo fmt and clippy warnings
- Fix code formatting with cargo fmt
- Remove unnecessary type casts
- Use range contains instead of manual comparison
- Prefix unused variables with underscore
- Remove unused imports
- Add allow(dead_code) for development phase

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-21 12:24:04 +05:30
laurenspriem
f635e1e856 Merge branch 'main' into smart_dedupe 2025-08-21 12:22:29 +05:30
laurenspriem
c6734a5cb7 More logging 2025-08-21 12:15:37 +05:30
laurenspriem
e26b4796d3 Match to closest group 2025-08-21 12:04:31 +05:30
laurenspriem
99c0194c0f Check cache parameters 2025-08-21 11:54:10 +05:30
laurenspriem
e824c02d7f Fix bug 2025-08-21 11:45:08 +05:30
laurenspriem
a11f66b51d Caching and partial compute logic for similar files calculation 2025-08-21 11:35:42 +05:30
laurenspriem
f202fef266 JSON caching of similar files 2025-08-21 11:35:12 +05:30
Neeraj
ff8cfd3e87 [auth] Additional import option (#6916)
## Description

## Tests
2025-08-21 11:22:04 +05:30
Neeraj Gupta
431ab7fcc7 [auth] Additional import option 2025-08-21 11:11:48 +05:30
Manav Rathi
b845f4d893 Keep co-author but remove the self promo link
Typical Claude Code commit message:
feat: implement user authentication

- Added login endpoint
- Implemented JWT tokens
- Created middleware

Created with Claude Code: https://claude.ai/code  # <-- The promotional link
Co-authored-by: Claude <claude@anthropic.com>      # <-- The co-author line

This memory is to ask claude to keep the co-author line but remove the self promo from the commit message it creates.
2025-08-21 06:51:22 +05:30
Manav Rathi
8ea36acb7a feat(rust): Initialize Rust CLI foundation with libsodium crypto
- Set up project structure mirroring Go CLI architecture
- Add dependencies with libsodium-sys-stable for all crypto operations
- Implement core crypto module with Argon2, ChaCha20-Poly1305, and Blake2b
- Create data models for accounts, files, and collections
- Set up Clap-based CLI framework with account, export, and version commands
- Add error handling with thiserror
- Configure for static linking to create standalone binaries

This establishes the foundation for converting the Ente CLI from Go to Rust,
with a focus on maintaining compatibility with existing libsodium-based crypto.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-21 06:50:23 +05:30
Ashil
2ac1d58dac [mob][photos] Fix jank when scrolling gallery when lot of selected items are in view (#6913)
## Description

Using `ColorFiltered` with `Blendmode.darken` introduces a performance
[issue](https://github.com/flutter/flutter/issues/174118) with flutter's
new rendering engine Impeller.

The fix uses an alternative method to that maintains the same UI
appearance as before.
2025-08-20 19:09:46 +05:30
ashilkn
5533e6a71d Fix performance issue (jank) when scrolling gallery when lot of selected items in view
This was only bad enough to be noticed on one device (Samsung A54) out of few
2025-08-20 18:46:32 +05:30
laurenspriem
a8ae0727a8 Fix potential duplicates 2025-08-20 18:24:21 +05:30
Aman Raj Singh Mourya
6955788724 Add custom icon for Mobile01 (#6908)
Custom icon for [Mobile01](https://www.mobile01.com/)

- Add optimised SVG file
- Add entry to custom-icons.json
2025-08-20 15:49:42 +05:30
Neeraj Gupta
f6dd35f5e7 Update templates 2025-08-20 15:11:28 +05:30
Neeraj Gupta
9148916d88 Merge remote-tracking branch 'origin/main' into ott_email_one_click 2025-08-20 15:02:57 +05:30
Neeraj
68545f8947 [auth] add import from gallery using zxing2 (#6909)
This pr implements the feature to add a 2FA account by importing a QR
code image from the device gallery..
Adds a new "Import from gallery" button to the Floating Action Button
menu.

The button's text is localized, and its styling is consistent with the
app's theme.

How to Test:

    Open the FAB menu and tap "Import from gallery".
    
1) Test with a valid 2FA QR code image. Expected: The account gets added
successfully.
2) Test with an image that has no QR code. Expected: A "No QR code
found" toast message appears.
3) Test with a QR code of plain text (like "hello "). Expected: An
"Invalid QR Code" dialog appears.
2025-08-20 14:44:02 +05:30
Prateek Sunal
fe40185889 fix: redirect video streaming to setting page 2025-08-20 14:43:54 +05:30
Prateek Sunal
6bb4428a8a chore: update generated translations 2025-08-20 14:41:57 +05:30
Neeraj
76c5c12c53 [mobile] Setup melos (#6907)
## Description

```
dart pub global activate melos
```
Run following command to get all dependencies for projects
```
melos bootstrap
```


## Tests
2025-08-20 14:36:10 +05:30
Prateek Sunal
0881685915 feat: add video streaming settings page 2025-08-20 14:34:16 +05:30
Prateek Sunal
3e13932d03 chore: format files and update analysis file 2025-08-20 14:33:35 +05:30
Dxball ☕
8d749a2dc8 Add custom icon fo Mobile01 2025-08-20 09:01:14 +00:00
a5xwin
56af818482 add qr code scanning from gallery using zxing2 2025-08-20 14:28:41 +05:30
Neeraj
fc1096c985 [mob] Include mobile flag during ott (#6906)
## Description
Need this to identify which OTT template to send from backend.
Ref: https://github.com/ente-io/ente/pull/5654
## Tests
2025-08-20 14:25:22 +05:30
Neeraj Gupta
88260a05e3 Setup melos 2025-08-20 14:14:35 +05:30
Neeraj Gupta
4b5f91a428 [mob] Send ismobile flag 2025-08-20 13:14:59 +05:30
Neeraj
29b12fc6b5 [docs] Lint (#6905)
## Description

## Tests
2025-08-20 11:01:44 +05:30
Neeraj Gupta
c3eec71d60 [docs] Lint 2025-08-20 11:01:25 +05:30
Neeraj
2fccdee0d6 [server] Add optional TLS/SSL encryption for SMTP (#6863)
## Description

Implement TLS/SSL encryption for sending emails via SMTP. When an SMTP
provider explicitly requires TLS/SSL communication the current
implementation runs in a timeout and fails. A new configuration
parameter for smtp was added to enable TLS/SSL communication.

This would solve #5958 

## Tests

I built a local docker image of my branch. The email provider I was
using is mailbox.org and using the tls configuration. Registering a new
user then resulted in a sent email containing the verification code.

I did not test a setup without TLS/SSL.
2025-08-20 11:00:36 +05:30
Neeraj
8f053e7a7b [m][photos] Fix for duplicate entries for local file (#6904)
## Description

## Tests
2025-08-20 10:42:41 +05:30
Neeraj
de7291f5d4 [mobile][photos] don't show lock screen on deep link (#6899)
## Description

Fixes the clicking on widget behavior

## Tests
2025-08-20 10:06:06 +05:30
Neeraj Gupta
e09c952198 Fix for duplicate enteries for file 2025-08-20 10:01:51 +05:30
laurenspriem
28a842b006 Show size 2025-08-20 09:51:27 +05:30
laurenspriem
19eb342f59 Move internal things to debug mode only 2025-08-20 09:47:36 +05:30
laurenspriem
2b82c79be9 Merge branch 'main' into smart_dedupe 2025-08-20 09:13:43 +05:30
Prateek Sunal
86a9dee49d fix: add note, revert old code, remove default redirect 2025-08-20 00:25:33 +05:30
Vishnu Mohandas
33b1a7e4f8 [mob] Remove generated code (#6901) 2025-08-19 20:22:51 +05:30
vishnukvmd
de783e91dc Ignore build 2025-08-19 20:22:22 +05:30
vishnukvmd
554c8f4b7a Remove noise 2025-08-19 20:22:11 +05:30
Vishnu Mohandas
f438142646 [locker] Icon (#6900) 2025-08-19 19:00:07 +05:30
vishnukvmd
aa6c010562 Update Android icon 2025-08-19 18:59:33 +05:30
vishnukvmd
2830c89bde Set icons 2025-08-19 18:58:21 +05:30
Prateek Sunal
4de530b882 fix: don't show lock sreen if not required 2025-08-19 18:27:33 +05:30
Vishnu Mohandas
62d7c87dc7 [locker] Update deps (#6898) 2025-08-19 18:23:51 +05:30
vishnukvmd
3a1b6dbf15 [locker] Update deps 2025-08-19 18:23:19 +05:30
laurenspriem
58182cc8ab Remove slider interface in release 2025-08-19 17:11:12 +05:30
Laurens Priem
5e0bba390b [mob][photos] Decoded image minor refactor (#6897)
## Description

Minor refactor (no functional change) in the way we're decoding and
returning images for indexing.
2025-08-19 16:42:24 +05:30
a5xwin
9e70dc4312 Merge remote-tracking branch 'origin/main' into autobackup 2025-08-19 15:54:01 +05:30
Vishnu Mohandas
df6a3b94db [mobile][locker] Add Drawer & Setting section in locker (#6895) 2025-08-19 15:35:41 +05:30
AmanRajSinghMourya
72b7e12768 Merge branch 'main' into drawer 2025-08-19 15:24:29 +05:30
a5xwin
541d71f65c added encryption feature to backups + refined backup logic 2025-08-19 15:07:23 +05:30
AmanRajSinghMourya
3a7d82a799 Add setting section 2025-08-19 14:58:15 +05:30
Neeraj
5f1cfb9ba5 [server] Fail request on customDomain mismatch (#6893)
## Description

## Tests
2025-08-19 14:23:00 +05:30
Neeraj Gupta
298e3695c7 [server] Fail request on customDomain mismatch 2025-08-19 12:34:31 +05:30
Vishnu Mohandas
621713d0b4 [mob] Remove unused import (#6892) 2025-08-19 12:33:15 +05:30
vishnukvmd
34813d2fae [mob] Remove unused import 2025-08-19 12:32:58 +05:30
laurenspriem
bb177bc3f6 Merge branch 'main' into smart_dedupe 2025-08-19 11:47:19 +05:30
Vishnu Mohandas
d8e4418d78 Remove ignore (#6890) 2025-08-19 11:43:36 +05:30
vishnukvmd
9771a5bc5d Remove ignore 2025-08-19 11:41:44 +05:30
laurenspriem
65f7e3f6c6 Exclude rust_builder in linter 2025-08-19 11:39:03 +05:30
laurenspriem
49b9d83f05 refactor to not use ui.image unnecessarily everywhere 2025-08-19 10:52:59 +05:30
laurenspriem
273d7bd00a ignore rust_builder linting errors 2025-08-19 10:38:08 +05:30
laurenspriem
4e8991dc10 remove unused import 2025-08-19 10:25:42 +05:30
Neeraj
aa4207f878 [auth] New translations (#6877)
New translations from
[Crowdin](https://crowdin.com/project/ente-authenticator-app)
2025-08-19 10:13:12 +05:30
Manav Rathi
3176ba8a93 pin to specific release of debian to avoid collation mismatch (#6886)
## Description

this pins the postgres container to a specific version of debian to
prevent issues with updated glibc in newer deb release

https://github.com/docker-library/postgres/issues/1356

## Tests
2025-08-19 09:09:04 +05:30
Matthias Wirtz
fcf038c4d8 pin to specific release of debian to avoid collation mismatch 2025-08-18 20:54:12 +02:00
Prateek Sunal
46aad76039 [mob][photos] fix widget tap on iOS (#6882)
## Description

Widget tap was not opening the specified photo because the app group id
was not setting correctly when the method was being called.

## Tests
2025-08-18 22:23:36 +05:30
Prateek Sunal
9a654988f8 chore: remove unused prefs 2025-08-18 22:23:15 +05:30
Prateek Sunal
1ce749e93e fix: update code to check correctly 2025-08-18 22:22:54 +05:30
Prateek Sunal
db88432b9d fix: update swift file to handle homeWidget deep link 2025-08-18 21:54:00 +05:30
Prateek Sunal
354bcc715f fix: remove iOS widget tap issue from change log 2025-08-18 20:56:46 +05:30
Prateek Sunal
5541198967 fix: combine functions 2025-08-18 20:55:07 +05:30
Prateek Sunal
3e2dbe2c1b fix: take prefs from ServiceLocation & setAppGroup whenever needed 2025-08-18 20:47:01 +05:30
Prateek Sunal
5d3d18f347 fix: add some logs + init prefs at one place 2025-08-18 19:59:15 +05:30
Prateek Sunal
9a876f3d59 fix: update internal and store change logs for widget tap issues on iOS 2025-08-18 19:11:10 +05:30
Prateek Sunal
3b7600ae7b chore: update lock files 2025-08-18 19:09:57 +05:30
Prateek Sunal
8bacf8ff93 fix: widget tap not working on iOS by giving preferences to init 2025-08-18 19:09:44 +05:30
Vishnu Mohandas
b356e5d0a5 Hey Locker! (#6881) 2025-08-18 18:44:44 +05:30
vishnukvmd
777516446d Hey Locker! 2025-08-18 18:43:14 +05:30
laurenspriem
37c1d0f6a8 Unify UI 2025-08-18 18:23:14 +05:30
laurenspriem
be1bf28cd8 Add cacheExtend for smoother scroll 2025-08-18 17:25:44 +05:30
Vishnu Mohandas
de5f0fbb39 [docs] update bucket-level configuration for S3 (#6873)
## Description

This PR updates documentation for S3 configuration with bucket-level
configuration for URL style and local buckets and updates needed
configuration that will make it intuitive for self-hosters.
2025-08-18 15:48:38 +05:30
Vishnu Mohandas
48fa2bd6de [mobile] Remove unused script for dependency fetching in mobile packages (#6878)
## Description

## Tests
2025-08-18 12:49:14 +05:30
AmanRajSinghMourya
df584f34e9 Remove unused script for dependency fetching in mobile packages 2025-08-18 12:36:05 +05:30
Vishnu Mohandas
0241ad1fe5 [mobile] Refactor project to extract and centralize common code into packages (#6768)
## Description
Refactored the codebase to move shared logic into reusable packages
under packages/.
Cleaned up old code, fixed builds, applied lint fixes, and updated
dependencies.

### Extracted reusable logic into dedicated packages:

- packages/accounts
- packages/base
- packages/configuration
- packages/lock screen
- packages/logging
- packages/network
- packages/strings
- packages/ui
- packages/utils

### Removed legacy or redundant code:
- Password re-entry
- Recovery key flow
- Password verification logic
- Applied lint fixes across multiple modules like accounts, base,
configuration, events, lock_screen, logging, network, strings, ui, utils

###  Build Fixes
Fixed Android build issues
Upgraded Flutter to version 3.24.3 and resolved related dependency
errors
2025-08-18 12:26:17 +05:30
AmanRajSinghMourya
435ed212c6 Fix build issues 2025-08-18 12:13:15 +05:30
AmanRajSinghMourya
1e9a6a1e14 Merge branch 'main' into test-com 2025-08-18 11:37:45 +05:30
Manav Rathi
a7ba3079cb [web] New translations (#6875)
New translations from
[Crowdin](https://crowdin.com/project/ente-photos-web)
2025-08-18 11:33:05 +05:30
Neeraj
97f1bb71c7 [mobile/photos] New translations (#6876)
New translations from
[Crowdin](https://crowdin.com/project/ente-photos-app)
2025-08-18 10:55:31 +05:30
laurenspriem
cad8613e81 Remove "select extra" button 2025-08-18 10:28:57 +05:30
laurenspriem
b46e51f64d Prefer smaller names 2025-08-18 10:25:06 +05:30
laurenspriem
e6bf64548c Use cached files 2025-08-18 10:21:28 +05:30
laurenspriem
5729e0cf3e no shared or hidden files 2025-08-18 10:11:04 +05:30
laurenspriem
b353539328 Sort by highest size first 2025-08-18 09:54:31 +05:30
Crowdin Bot
f2049ac7fa New Crowdin translations by GitHub Action 2025-08-18 01:18:04 +00:00
Crowdin Bot
e82ba882d6 New Crowdin translations by GitHub Action 2025-08-18 01:05:17 +00:00
Crowdin Bot
eecd7ed355 New Crowdin translations by GitHub Action 2025-08-18 00:43:39 +00:00
Keerthana
bc70b4e725 [docs] fix linting for self-hosting 2025-08-18 01:52:26 +05:30
Keerthana
a52cebf0e5 [cli] remove dead code in init 2025-08-18 00:49:29 +05:30
Keerthana
4c31a7bcd6 [docs] update bucket configuration 2025-08-17 23:55:43 +05:30
a5xwin
d8fc369a21 Merge remote-tracking branch 'origin/main' into autobackup 2025-08-17 00:15:50 +05:30
a5xwin
8efbebe9c4 Merge remote-tracking branch 'origin/main' into autobackup 2025-08-16 18:36:43 +05:30
Kilian Hohm
cf938eca91 Add CLI command to send a test email via admin API 2025-08-16 10:33:23 +02:00
Prateek Sunal
deef13ece9 [auth] update plugins & gradle (#6861)
## Description

Fix for compiling with flutter 3.32.8

## Tests
2025-08-15 19:35:21 +05:30
Kilian Hohm
a3d3ee24f8 Document optional TLS/SSL encryption for sending emails via SMTP 2025-08-15 15:48:59 +02:00
Kilian Hohm
6b37cc46a5 Add optional TLS/SSL encryption for sending emails via SMTP 2025-08-15 15:48:41 +02:00
Prateek Sunal
3132373c26 chore: update plugins & add desugaring 2025-08-15 18:13:08 +05:30
Prateek Sunal
5b4ff1d01a chore: update gradle and kotlin 2025-08-15 17:34:04 +05:30
a5xwin
a7300b7ac7 Restore local changes from stash 2025-08-15 16:07:51 +05:30
AmanRajSinghMourya
20d8a42239 Add broken-icon 2025-08-14 22:23:50 +05:30
AmanRajSinghMourya
b19814dd2c Add onBoardingBodyColor to CustomColorScheme extension for light and dark themes 2025-08-14 21:18:53 +05:30
laurenspriem
ad39694026 report changes at correct place 2025-08-14 19:06:03 +05:30
a5xwin
9224cea96f Fixed linting issues 2025-08-14 00:45:11 +05:30
AmanRajSinghMourya
72ede1a109 Add onLongPress callback to MenuItemWidget 2025-08-14 00:09:48 +05:30
a5xwin
9fbc618d69 Updated pubspec.lock regeneration 2025-08-14 00:02:04 +05:30
a5xwin
4614428f76 Resolved pubspec.lock conflict by taking main version 2025-08-13 23:36:49 +05:30
a5xwin
6fde4ee45f implemented an auto backup feature 2025-08-13 23:25:48 +05:30
eYdr1en
279df8ff57 fixes lint error 2025-08-13 12:52:13 +02:00
Adrián Horváth
d83994c692 Update mobile/apps/auth/lib/ui/tools/lock_screen.dart
Co-authored-by: Prateek Sunal <prtksunal@gmail.com>
2025-08-13 12:37:11 +02:00
AmanRajSinghMourya
5fe86858ef Update dependencies and upgrade SDK version; replace qr_code_scanner with qr_code_scanner_plus 2025-08-13 11:38:27 +05:30
AmanRajSinghMourya
7546d8cad2 Merge branch 'main' into test-com 2025-08-13 10:13:50 +05:30
AmanRajSinghMourya
a74908214d Update password entry page to use a fixed color for valid field value 2025-08-11 10:38:44 +05:30
eYdr1en
be506bdad1 fixes macos touch id lock 2025-08-08 17:22:12 +02:00
AmanRajSinghMourya
94e398dc89 Bump version 4.4.6+446 2025-08-08 12:40:30 +05:30
AmanRajSinghMourya
a5a19581fc Revert "Bump version to 4.4.6+445"
This reverts commit ea409fc266.
2025-08-08 12:39:12 +05:30
AmanRajSinghMourya
ea409fc266 Bump version to 4.4.6+445 2025-08-08 12:16:47 +05:30
Aman Raj Singh Mourya
0e9d7106f7 [mobile] Fix packages to support Auth theme (#6788) 2025-08-08 12:14:08 +05:30
AmanRajSinghMourya
6fe89fdc0e Add theme configuration and update color scheme in recovery key and lock screen pages 2025-08-08 12:05:08 +05:30
AmanRajSinghMourya
193e1374e1 Refactor GradientButton to use ClipRRect 2025-08-07 20:01:12 +05:30
AmanRajSinghMourya
e11a6ace80 Refactor theme usage in dynamic FAB and dialog widget; improve imports and color scheme references 2025-08-07 19:40:39 +05:30
AmanRajSinghMourya
9033bd8cec Fix formatting in custom-icons.json 2025-08-07 19:40:07 +05:30
AmanRajSinghMourya
085551b5a7 Add ente_strings package and update localization delegates 2025-08-07 19:39:58 +05:30
AmanRajSinghMourya
210a0a45c1 Bump version to 4.4.5+445 2025-08-06 16:44:08 +05:30
AmanRajSinghMourya
ee035681ab Bump version to 4.4.3+444 2025-08-06 15:40:14 +05:30
AmanRajSinghMourya
cc2d65d796 Fix string 2025-08-06 14:35:25 +05:30
AmanRajSinghMourya
0e61b3dfd4 Minor changes in packages's pubspec to match dependencies 2025-08-06 14:35:03 +05:30
AmanRajSinghMourya
0a3035e5d5 Update Android build configuration: enable core library desugaring, increase minSdkVersion to 22, and upgrade Gradle version to 8.4. Add ProGuard rules to suppress warnings. 2025-08-06 14:33:19 +05:30
AmanRajSinghMourya
7adb1c0a6c Minor fix 2025-08-06 12:07:21 +05:30
AmanRajSinghMourya
cb55be1e5c Fix android build 2025-08-06 11:38:10 +05:30
AmanRajSinghMourya
d7a7144b33 Fix android build 2025-08-06 11:38:04 +05:30
AmanRajSinghMourya
e6a867a859 Cleanup 2025-08-06 11:37:04 +05:30
AmanRajSinghMourya
a9c8e4476f Remove old password reentry, recovery key, recovery, and password verification pages; refactor imports and update references in settings and home page. 2025-08-06 11:36:51 +05:30
AmanRajSinghMourya
f914263b2f Remove old password reentry, recovery key, recovery, and password verification pages; refactor imports and update references in settings and home page. 2025-08-06 11:36:46 +05:30
AmanRajSinghMourya
6f94d91afb More code refractor in auth/accounts section 2025-08-06 11:36:26 +05:30
AmanRajSinghMourya
57569e79fe Add ente_ui dependency to pubspec.yaml and update its lock status in pubspec.lock 2025-08-06 11:35:49 +05:30
AmanRajSinghMourya
b695db80ab Refactor HomePage to extend BaseHomePage for improved structure 2025-08-06 11:35:32 +05:30
AmanRajSinghMourya
ae1a43b8bf Refractor accounts section to use common code from packages/accounts 2025-08-06 11:35:13 +05:30
AmanRajSinghMourya
d9d9acfa3e Refractor accounts section to use common code from packages/accounts 2025-08-06 11:35:06 +05:30
AmanRajSinghMourya
d430936ae8 Refractor accounts section to use common code from packages/accounts 2025-08-06 11:34:44 +05:30
AmanRajSinghMourya
b02acc579f Update ente_accounts dependency 2025-08-06 11:33:58 +05:30
AmanRajSinghMourya
e87be4b9af Update flutter_inappwebview dependencies 2025-08-06 11:33:36 +05:30
AmanRajSinghMourya
399148aa59 Introduced lock screen package from packages/lockscreen 2025-08-06 11:33:23 +05:30
AmanRajSinghMourya
abd733934b Introduced lock screen package from packages/lockscreen 2025-08-06 11:33:13 +05:30
vishnukvmd
661e1f92d5 Add developer settings page 2025-08-06 11:31:56 +05:30
vishnukvmd
97b36681dc Consistency 2025-08-06 11:31:23 +05:30
vishnukvmd
5dd5756a41 Lint utils 2025-08-06 11:29:02 +05:30
vishnukvmd
d2cfa374bd Lint utils 2025-08-06 11:28:54 +05:30
vishnukvmd
fb64c8aa4c Lint ui 2025-08-06 11:28:43 +05:30
vishnukvmd
86b54e2241 Lint strings 2025-08-06 11:28:27 +05:30
vishnukvmd
c33396ea60 Lint network 2025-08-06 11:28:11 +05:30
vishnukvmd
24f48b5054 Lint logging 2025-08-06 11:27:56 +05:30
vishnukvmd
094c92c8b6 Lint lock_screen 2025-08-06 11:27:41 +05:30
vishnukvmd
c2922a0cb2 Lint lock_screen 2025-08-06 11:27:36 +05:30
vishnukvmd
9a357a716d Lint events 2025-08-06 11:27:21 +05:30
vishnukvmd
5574fd748e Lint configuration 2025-08-06 11:27:06 +05:30
vishnukvmd
f2be25667f Lint base 2025-08-06 11:26:48 +05:30
vishnukvmd
053d0cfcaa Fix minor lint 2025-08-06 11:26:27 +05:30
vishnukvmd
0ff8184f47 Lint accounts 2025-08-06 11:26:11 +05:30
vishnukvmd
f971835ae7 Remove noise 2025-08-06 11:25:56 +05:30
vishnukvmd
694e3ca121 Remove changelog 2025-08-06 11:25:42 +05:30
vishnukvmd
be850c27c6 Fix dependency 2025-08-06 11:25:08 +05:30
vishnukvmd
3aaf11ba1d Setup common accounts package 2025-08-06 11:24:46 +05:30
vishnukvmd
3019d858c9 Setup common accounts package 2025-08-06 11:24:35 +05:30
vishnukvmd
81e926ef2d Setup common accounts package 2025-08-06 11:17:59 +05:30
vishnukvmd
016d646971 Update common ui 2025-08-06 11:14:26 +05:30
vishnukvmd
1f100566ad Update common lockscreen 2025-08-06 11:13:56 +05:30
vishnukvmd
d69595e744 Setup common lockscreen 2025-08-06 11:13:25 +05:30
vishnukvmd
7a91e714fe Setup common lockscreen 2025-08-06 11:13:13 +05:30
vishnukvmd
e352de8b9c Update strings 2025-08-06 11:11:18 +05:30
vishnukvmd
e36831b599 Update strings 2025-08-06 11:11:09 +05:30
vishnukvmd
df74c7d54d Update strings 2025-08-06 11:10:50 +05:30
vishnukvmd
d7986b5c7c Update utils 2025-08-05 23:58:46 +05:30
vishnukvmd
3e888876d1 Update utils 2025-08-05 23:58:41 +05:30
vishnukvmd
6af494206e Add developer settings page 2025-08-05 23:57:41 +05:30
vishnukvmd
93f32de8c1 Update strings 2025-08-05 23:57:27 +05:30
vishnukvmd
e0b3e6464e Update strings 2025-08-05 23:57:20 +05:30
vishnukvmd
c67d2f0836 Consistency 2025-08-05 23:56:50 +05:30
vishnukvmd
49106a3dd9 Pull existing translations 2025-08-05 23:56:39 +05:30
vishnukvmd
b61c75dc84 Lint utils 2025-08-05 23:56:28 +05:30
vishnukvmd
465fc7c7d3 Lint ui 2025-08-05 23:56:13 +05:30
vishnukvmd
487e4ef559 Lint strings 2025-08-05 23:56:03 +05:30
vishnukvmd
34bf4f6bba Lint network 2025-08-05 23:55:51 +05:30
vishnukvmd
af36978ede Lint logging 2025-08-05 23:55:41 +05:30
vishnukvmd
a375dfdc2e Lint lock_screen 2025-08-05 23:55:28 +05:30
vishnukvmd
a5b0e66e9d Lint lock_screen 2025-08-05 23:55:23 +05:30
vishnukvmd
2b3b7a5e2a Lint events 2025-08-05 23:54:44 +05:30
vishnukvmd
b1dc9272a0 Lint configuration 2025-08-05 23:54:30 +05:30
vishnukvmd
7c33c160b2 Lint base 2025-08-05 23:54:15 +05:30
vishnukvmd
be232efbc6 Fix minor lint 2025-08-05 23:53:59 +05:30
vishnukvmd
bbac09b4a6 Lint accounts 2025-08-05 23:53:41 +05:30
vishnukvmd
a4626ae7a1 Remove noise 2025-08-05 23:53:27 +05:30
vishnukvmd
e8551df8b9 Remove changelog 2025-08-05 23:53:10 +05:30
vishnukvmd
77cb40aef4 Update strings 2025-08-05 23:52:50 +05:30
vishnukvmd
827090fb24 Fix dependency 2025-08-05 23:52:31 +05:30
vishnukvmd
39b9670fcc Setup common accounts package 2025-08-05 23:52:11 +05:30
vishnukvmd
d227a2bf20 Setup common accounts package 2025-08-05 23:52:03 +05:30
vishnukvmd
0b766415a4 Setup common accounts package 2025-08-05 23:51:32 +05:30
vishnukvmd
3b727549d5 Update common ui 2025-08-05 23:49:03 +05:30
vishnukvmd
dbdf19ee8d Update common lockscreen 2025-08-05 23:48:45 +05:30
vishnukvmd
2e3d621077 Update strings 2025-08-05 23:44:26 +05:30
vishnukvmd
0455481f3d Lint lock_screen 2025-08-05 23:38:42 +05:30
vishnukvmd
c9a7918397 Lint events 2025-08-05 23:38:42 +05:30
vishnukvmd
957f0bc041 Lint configuration 2025-08-05 23:38:42 +05:30
vishnukvmd
24f5a5813a Lint base 2025-08-05 23:38:42 +05:30
vishnukvmd
860b2895f6 Fix minor lint 2025-08-05 23:38:42 +05:30
vishnukvmd
a510320d0e Lint accounts 2025-08-05 23:38:42 +05:30
vishnukvmd
de04f18cb2 Remove noise 2025-08-05 23:38:42 +05:30
vishnukvmd
b84b73fda2 Remove changelog 2025-08-05 23:38:42 +05:30
vishnukvmd
12b0618149 Update strings 2025-08-05 23:38:42 +05:30
vishnukvmd
28a43393f9 Fix dependency 2025-08-05 23:38:42 +05:30
vishnukvmd
21b3bdf204 Setup common accounts package 2025-08-05 23:38:42 +05:30
vishnukvmd
3436fb7fb1 Update common ui 2025-08-05 23:38:42 +05:30
vishnukvmd
045b40b2b2 Update common lockscreen 2025-08-05 23:38:42 +05:30
vishnukvmd
c09922d1a3 Update strings 2025-08-05 23:38:42 +05:30
vishnukvmd
3e9032588e Setup common lockscreen 2025-08-05 23:34:58 +05:30
vishnukvmd
14e570b676 Update strings 2025-08-05 23:34:49 +05:30
vishnukvmd
a412aa4886 Update strings 2025-08-05 23:34:44 +05:30
vishnukvmd
a7a162d375 Update utils 2025-08-05 23:34:38 +05:30
vishnukvmd
b444bdc5ec Add re-usable base home page 2025-08-05 23:34:33 +05:30
vishnukvmd
f9299e7950 Reduce noise 2025-08-05 23:34:27 +05:30
vishnukvmd
8a9f73ada5 Refactor utils 2025-08-05 23:34:10 +05:30
vishnukvmd
d5c1970ca2 Update common ui 2025-08-05 23:34:01 +05:30
vishnukvmd
f8aff3e12b Update common strings 2025-08-05 23:33:52 +05:30
vishnukvmd
490759243b Setup common UI components 2025-08-05 23:33:46 +05:30
vishnukvmd
e8a9e509a8 Update common strings 2025-08-05 23:33:39 +05:30
vishnukvmd
c392ad5dcb Update utils 2025-08-05 23:33:23 +05:30
vishnukvmd
9bb084d610 Add typedefs to base 2025-08-05 23:33:13 +05:30
vishnukvmd
00b05e2d7c Update common utils 2025-08-05 23:33:00 +05:30
vishnukvmd
93736fe57a Update common strings 2025-08-05 23:32:46 +05:30
vishnukvmd
995ae50418 Refactor common utils 2025-08-05 23:32:24 +05:30
vishnukvmd
42e6dff0f5 Remove redundant test 2025-08-05 23:32:17 +05:30
vishnukvmd
150513d3e5 Refactor common strings 2025-08-05 23:32:04 +05:30
vishnukvmd
13ed1e76bc Use common network package 2025-08-05 23:31:55 +05:30
vishnukvmd
a7d0e2eef5 Refactor network 2025-08-05 23:31:36 +05:30
vishnukvmd
134314c285 Common config 2025-08-05 23:30:56 +05:30
vishnukvmd
eb3e3db8e6 Base config 2025-08-05 23:30:33 +05:30
vishnukvmd
3b9b886ae9 Refactor 2025-08-05 23:30:20 +05:30
vishnukvmd
d88df36c3d Packages 2025-08-05 23:30:06 +05:30
vishnukvmd
62d7311780 ignore surprises 2025-08-05 23:29:42 +05:30
vishnukvmd
9d76ccc173 init config 2025-08-05 23:27:17 +05:30
anandbaburajan
1752192688 [email]: different ott templates for with and without verify btn 2025-06-09 19:21:49 +05:30
anandbaburajan
873ee3ac14 [email]: different views for mobile and desktop 2025-04-18 16:54:21 +05:30
anandbaburajan
cfce2d00f5 [email]: add one click verify button for verification email 2025-04-18 13:45:04 +05:30
1373 changed files with 152960 additions and 78075 deletions

View File

@@ -8,7 +8,7 @@ on:
env:
FLUTTER_VERSION: "3.32.8"
RUST_VERSION: "1.85.1"
RUST_VERSION: "1.86.0"
permissions:
contents: read
@@ -27,6 +27,38 @@ jobs:
with:
submodules: recursive
- name: Free up disk space
run: |
echo "Initial disk usage:"
df -h /
# Get available space in KB
INITIAL=$(df / | awk 'NR==2 {print $4}')
echo -e "\n=== Removing .NET SDK (~20-25GB) ==="
BEFORE=$(df / | awk 'NR==2 {print $4}')
START=$(date +%s)
sudo rm -rf /usr/share/dotnet
END=$(date +%s)
AFTER=$(df / | awk 'NR==2 {print $4}')
FREED=$(( (AFTER - BEFORE) / 1048576 )) # Convert KB to GB
echo "Time: $((END-START))s | Freed: ${FREED}GB"
echo -e "\n=== Removing cached tools (~5-10GB) ==="
BEFORE=$(df / | awk 'NR==2 {print $4}')
START=$(date +%s)
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
END=$(date +%s)
AFTER=$(df / | awk 'NR==2 {print $4}')
FREED=$(( (AFTER - BEFORE) / 1048576 ))
echo "Time: $((END-START))s | Freed: ${FREED}GB"
echo -e "\n=== Final Summary ==="
FINAL=$(df / | awk 'NR==2 {print $4}')
TOTAL_FREED=$(( (FINAL - INITIAL) / 1048576 ))
echo "Total space freed: ${TOTAL_FREED}GB"
echo "Final disk usage:"
df -h /
- name: Setup JDK 17
uses: actions/setup-java@v1
with:
@@ -39,14 +71,12 @@ jobs:
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Install Rust ${{ env.RUST_VERSION }}
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.RUST_VERSION }}
- name: Install Flutter Rust Bridge
run: cargo install flutter_rust_bridge_codegen
- name: Generate Rust bindings
run: flutter_rust_bridge_codegen generate
- name: Increment version code for build
run: |
CURRENT_VERSION=$(grep '^version:' pubspec.yaml | sed 's/version: //')

View File

@@ -1,77 +0,0 @@
name: "Old Internal release (photos)"
on:
workflow_dispatch: # Allow manually running the action
env:
FLUTTER_VERSION: "3.32.8"
RUST_VERSION: "1.85.1"
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: mobile/apps/photos
steps:
- name: Checkout code and submodules
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup JDK 17
uses: actions/setup-java@v1
with:
java-version: 17
- name: Install Flutter ${{ env.FLUTTER_VERSION }}
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Install Rust ${{ env.RUST_VERSION }}
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.RUST_VERSION }}
- name: Install Flutter Rust Bridge
run: cargo install flutter_rust_bridge_codegen
- name: Setup keys
uses: timheuer/base64-to-file@v1
with:
fileName: "keystore/ente_photos_key.jks"
encodedString: ${{ secrets.SIGNING_KEY_PHOTOS }}
- name: Build PlayStore AAB
run: |
flutter build appbundle --dart-define=cronetHttpNoPlay=true --release --flavor playstore
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: Upload AAB to PlayStore
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
packageName: io.ente.photos
releaseFiles: mobile/apps/photos/build/app/outputs/bundle/playstoreRelease/app-playstore-release.aab
track: internal
- name: Notify Discord
uses: sarisia/actions-status-discord@v1
with:
webhook: ${{ secrets.DISCORD_INTERNAL_RELEASE_WEBHOOK }}
nodetail: true
title: "🏆 Internal release Photos (Branch: ${{ github.ref_name }})"
description: "[Download](https://play.google.com/store/apps/details?id=io.ente.photos)"
color: 0x00ff00

View File

@@ -9,6 +9,7 @@ on:
env:
FLUTTER_VERSION: "3.32.8"
RUST_VERSION: "1.86.0"
permissions:
contents: read
@@ -31,7 +32,18 @@ jobs:
channel: "stable"
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- run: flutter pub get
- name: Install Rust ${{ env.RUST_VERSION }}
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.RUST_VERSION }}
- name: Install Flutter Rust Bridge
run: cargo install flutter_rust_bridge_codegen
- name: Generate Rust bindings
run: flutter_rust_bridge_codegen generate
- run: flutter analyze --no-fatal-infos

View File

@@ -28,6 +28,38 @@ jobs:
with:
submodules: recursive
- name: Free up disk space
run: |
echo "Initial disk usage:"
df -h /
# Get available space in KB
INITIAL=$(df / | awk 'NR==2 {print $4}')
echo -e "\n=== Removing .NET SDK (~20-25GB) ==="
BEFORE=$(df / | awk 'NR==2 {print $4}')
START=$(date +%s)
sudo rm -rf /usr/share/dotnet
END=$(date +%s)
AFTER=$(df / | awk 'NR==2 {print $4}')
FREED=$(( (AFTER - BEFORE) / 1048576 )) # Convert KB to GB
echo "Time: $((END-START))s | Freed: ${FREED}GB"
echo -e "\n=== Removing cached tools (~5-10GB) ==="
BEFORE=$(df / | awk 'NR==2 {print $4}')
START=$(date +%s)
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
END=$(date +%s)
AFTER=$(df / | awk 'NR==2 {print $4}')
FREED=$(( (AFTER - BEFORE) / 1048576 ))
echo "Time: $((END-START))s | Freed: ${FREED}GB"
echo -e "\n=== Final Summary ==="
FINAL=$(df / | awk 'NR==2 {print $4}')
TOTAL_FREED=$(( (FINAL - INITIAL) / 1048576 ))
echo "Total space freed: ${TOTAL_FREED}GB"
echo "Final disk usage:"
df -h /
- name: Setup JDK 17
uses: actions/setup-java@v1
with:
@@ -40,6 +72,12 @@ jobs:
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Install Flutter Rust Bridge
run: cargo install flutter_rust_bridge_codegen
- name: Generate Rust bindings
run: flutter_rust_bridge_codegen generate
- name: Setup keys
uses: timheuer/base64-to-file@v1
with:

View File

@@ -29,6 +29,8 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Setup node and enable yarn caching
uses: actions/setup-node@v4
@@ -38,7 +40,7 @@ jobs:
cache-dependency-path: "web/yarn.lock"
- name: Install dependencies
run: yarn install
run: yarn install --frozen-lockfile
- name: Build ${{ inputs.app }}
run: yarn build:${{ inputs.app }}

View File

@@ -29,6 +29,8 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Setup node and enable yarn caching
uses: actions/setup-node@v4
@@ -38,7 +40,7 @@ jobs:
cache-dependency-path: "web/yarn.lock"
- name: Install dependencies
run: yarn install
run: yarn install --frozen-lockfile
- name: Build ${{ inputs.app }}
run: yarn build:${{ inputs.app }}

View File

@@ -37,6 +37,7 @@ jobs:
uses: actions/checkout@v4
with:
ref: ${{ steps.select-branch.outputs.branch }}
persist-credentials: false
- name: Setup node and enable yarn caching
uses: actions/setup-node@v4
@@ -46,7 +47,7 @@ jobs:
cache-dependency-path: "web/yarn.lock"
- name: Install dependencies
run: yarn install
run: yarn install --frozen-lockfile
- name: Build photos
run: yarn build:photos

View File

@@ -33,6 +33,8 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Setup node and enable yarn caching
uses: actions/setup-node@v4
@@ -42,7 +44,15 @@ jobs:
cache-dependency-path: "web/yarn.lock"
- name: Install dependencies
run: yarn install
run: yarn install --frozen-lockfile
- name: Audit dependencies
run: |
yarn audit --level critical || exit_code=$?
if [[ $exit_code -ge 16 ]]; then
echo "::error::Yarn audit found critical issues"
exit 1
fi
- name: Build photos
run: yarn build:photos

View File

@@ -24,6 +24,8 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Setup node and enable yarn caching
uses: actions/setup-node@v4
@@ -32,6 +34,14 @@ jobs:
cache: "yarn"
cache-dependency-path: "web/yarn.lock"
- run: yarn install
- run: yarn install --frozen-lockfile
- run: yarn lint
- name: Audit dependencies
run: |
yarn audit --level critical || exit_code=$?
if [[ $exit_code -ge 16 ]]; then
echo "::error::Yarn audit found critical issues"
exit 1
fi

View File

@@ -48,7 +48,11 @@ 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. Consider some well-scoped changes, say like adding more [custom icons to auth](mobile/apps/auth/docs/adding-icons.md), or fixing a specific bug.
If you'd like to contribute code, it is best to start small. Consider some well-scoped changes, say like adding more [custom icons to auth](mobile/apps/auth/docs/adding-icons.md), or fixing a specific bug. There is a (possibly outdated) list of tasks with the ["help wanted" or "good first issue"](<https://github.com/ente-io/ente/issues?q=state%3Aopen%20(label%3A%22good%20first%20issue%22%20OR%20label%3A%22help%20wanted%22%20)>) label too.
If you use any form of AI assistance, please include a co-author attribution in the commit for transparency.
In your PR, please include before / after screenshots, and clearly indicate the tests that you performed.
Code that changes the behaviour of the product might not get merged, at least not initially. The PR can serve as a discussion bed, but you might find it easier to just start a discussion instead, or post your perspective in the (likely) existing thread about the behaviour change or new feature you wish for.

View File

@@ -142,6 +142,22 @@ var _updateFreeUserStorage = &cobra.Command{
},
}
var _sendMail = &cobra.Command{
Use: "send-mail <to-email> <from-email> <from-name>",
Args: cobra.ExactArgs(3),
Short: "Sends a test mail via the admin api",
RunE: func(cmd *cobra.Command, args []string) error {
recoverWithLog()
var flags = &model.AdminActionForUser{}
cmd.Flags().VisitAll(func(f *pflag.Flag) {
if f.Name == "admin-user" {
flags.AdminEmail = f.Value.String()
}
})
return ctrl.SendTestMail(context.Background(), *flags, args[0], args[1], args[2])
},
}
func init() {
rootCmd.AddCommand(_adminCmd)
_ = _userDetailsCmd.MarkFlagRequired("admin-user")
@@ -159,5 +175,6 @@ func init() {
_updateFreeUserStorage.Flags().StringP("user", "u", "", "The email of the user to update subscription for. (required)")
// add a flag with no value --no-limit
_updateFreeUserStorage.Flags().String("no-limit", "True", "When true, sets 100TB as storage limit, and expiry to current date + 100 years")
_adminCmd.AddCommand(_userDetailsCmd, _disable2faCmd, _disablePasskeyCmd, _updateFreeUserStorage, _listUsers, _deleteUser)
_sendMail.Flags().StringP("admin-user", "a", "", "The email of the admin user. ")
_adminCmd.AddCommand(_userDetailsCmd, _disable2faCmd, _disablePasskeyCmd, _updateFreeUserStorage, _listUsers, _deleteUser, _sendMail)
}

View File

@@ -2,11 +2,12 @@ package cmd
import (
"fmt"
"github.com/ente-io/cli/pkg"
"github.com/spf13/cobra/doc"
"os"
"runtime"
"github.com/ente-io/cli/pkg"
"github.com/spf13/cobra/doc"
"github.com/spf13/viper"
"github.com/spf13/cobra"
@@ -20,11 +21,6 @@ var ctrl *pkg.ClICtrl
var rootCmd = &cobra.Command{
Use: "ente",
Short: "CLI tool for exporting your photos from ente.io",
// Uncomment the following line if your bare application
// has an action associated with it:
Run: func(cmd *cobra.Command, args []string) {
fmt.Sprintf("Hello World")
},
}
func GenerateDocs() error {

View File

@@ -139,5 +139,28 @@ func (c *Client) UpdateFreePlanSub(ctx context.Context, userDetails *models.User
}
}
return nil
}
func (c *Client) SendTestMail(ctx context.Context, toEmail, fromEmail, fromName string) error {
body := map[string]interface{}{
"to": []string{toEmail},
"fromName": fromName,
"fromEmail": fromEmail,
"subject": "Test mail from Ente",
"body": "This is a test mail from Ente",
}
r, err := c.restClient.R().
SetContext(ctx).
SetBody(body).
Post("/admin/mail")
if err != nil {
return err
}
if r.IsError() {
return &ApiError{
StatusCode: r.StatusCode(),
Message: r.String(),
}
}
return nil
}

View File

@@ -156,6 +156,23 @@ func (c *ClICtrl) UpdateFreeStorage(ctx context.Context, params model.AdminActio
return nil
}
func (c *ClICtrl) SendTestMail(ctx context.Context, params model.AdminActionForUser, to, from, fromName string) error {
accountCtx, err := c.buildAdminContext(ctx, params.AdminEmail)
if err != nil {
return err
}
err = c.Client.SendTestMail(accountCtx, to, from, fromName)
if err != nil {
if apiErr, ok := err.(*api.ApiError); ok && apiErr.StatusCode == 400 && strings.Contains(apiErr.Message, "Token is too old") {
fmt.Printf("Error: old admin token, please re-authenticate using `ente account add` \n")
return nil
}
return err
}
fmt.Printf("Successfully sent test email to %s\n", to)
return nil
}
func (c *ClICtrl) buildAdminContext(ctx context.Context, adminEmail string) (context.Context, error) {
accounts, err := c.GetAccounts(ctx)
if err != nil {

View File

@@ -2,6 +2,8 @@
## v1.7.15 (Unreleased)
- Custom domains.
- Support Czech translations.
- .
## v1.7.14

0
docs/docs/cli/index.md Normal file
View File

View File

@@ -44,7 +44,7 @@ The first step is to let Ente know about the domain or subdomain you wish to use
> [!WARNING]
>
> Currently (Aug 2025) the ability to link a custom domain is only present in Ente's web app, [web.ente.io](https://web.ente.io). It will come to Ente mobile and desktop when their next versions get released.
> Currently (Sep 2025) the ability to link a custom domain is only present in Ente's web app, [web.ente.io](https://web.ente.io).
Head over to Preferences > Custom domains, in the domain field enter "pics.example.org" (replace with your subdomain) and press "Save". That's it. The linking is done.
@@ -94,7 +94,7 @@ Using is trivial. When you go to an album's sharing options and copy the link to
> [!WARNING]
>
> Currently (Aug 2025) the ability to automatically substitute your custom domain is only present in Ente's web app, [web.ente.io](https://web.ente.io). It will come to Ente mobile and desktop when their next versions get released.
> Currently (Sep 2025) the ability to automatically substitute your custom domain is present in Ente's web and mobile apps, but not in the desktop app (The next desktop version to be released will have that ability too).
## Unsetting
@@ -103,3 +103,7 @@ To stop using your custom domain, we need to undo the two steps we did during se
1. Unlink your domain in Ente. This can be done just by going to Preferences > Custom Domains, clearing the value in the "Domain" input and pressing "Update".
2. Remove the CNAME record you added during setup in your DNS provider.
## Implementation
Our engineers also wrote [explainer](https://ente.io/blog/custom-domains/) of how this works behind the scenes.

View File

@@ -6,7 +6,7 @@ description: Removing duplicates photos using Ente Photos
# Deduplicate
Ente performs two different duplicate detections: one during uploads, and one
that can be manually run afterwards to remove duplicates across albums.
that can be manually run afterwards to remove duplicates and very similar files across albums.
## During uploads
@@ -16,7 +16,7 @@ When uploading, Ente will ignore exact duplicate files. This allows you to
resume interrupted uploads, or drag and drop the same folder, or reinstall the
app, and expect Ente to automatically skip duplicates and only add new files.
The duplicate detection works slightly different on each platform, to cater to
The duplicate detection works slightly differently on each platform, to cater to
the platform's nuances.
#### Mobile
@@ -48,7 +48,7 @@ to album", and the actual files are not re-uploaded.
## Manual deduplication
Ente also provides a tool for manual de-duplication in _Settings → Backup →
Ente provides a tool for manual de-duplication in _Settings → Backup → Free up space →
Remove duplicates_. This is useful if you have an existing library with
duplicates across different albums, but wish to keep only one copy.
@@ -57,6 +57,13 @@ single copy, and add symlinks to this copy within all existing albums. So your
existing album structure remains unchanged, while the space consumed by the
duplicate data is freed up.
## Filtering similar images
Ente also provides a tool for manual removal of images that are similar, but not the exact same, using our private ML. This feature can be found in _Settings → Backup → Free up space →
Similar images_. This is useful if you've taken a lot of similar photos, potentiall even in different albums, and want to keep only the best ones.
During this filtering process you can choose which photos to keep and which to delete for each set of similar images. Ente will then automatically add symlinks for the kept photos to any albums that only had the deleted images. This way you can easily prune similar images, without worrying about accidentally removing the best ones from a certain album.
## Adding to Ente album creates symlinks
Note that once a file is in Ente, adding it to another Ente album will create a

View File

@@ -8,6 +8,12 @@ description: Guide to configuring Ente CLI for Self Hosted Instance
If you are self-hosting, you can configure Ente CLI to export data & perform
basic administrative actions.
::: tip Installing Ente CLI
For instructions on installing the Ente CLI, see the [README available on Github](https://github.com/ente-io/ente/tree/main/cli/README.md).
:::
## Step 1: Configure endpoint
To do this, first configure the CLI to use your server's endpoint.

View File

@@ -63,11 +63,20 @@ It has no relation to Backblaze, Wasabi or Scaleway.
Each bucket's endpoint, region, key and secret should be configured accordingly
if using an external bucket.
A sample configuration for `b2-eu-cen` is provided, which can be used for other
2 buckets as well:
If a bucket has SSL support enabled, set `s3.are_local_buckets` to `false`. Enable path-style URL by setting `s3.use_path_style_urls` to `true`.
::: note
You can configure this for individual buckets over defining top-level configuration if you are using the latest server image (August 2025)
:::
A sample configuration for `b2-eu-cen` is provided, which can be used for other 2 buckets as well:
```yaml
b2-eu-cen:
are_local_buckets: true
use_path_style_urls: true
key: <key>
secret: <secret>
endpoint: localhost:3200

View File

@@ -89,7 +89,7 @@ cast.ente.yourdomain.tld {
Reload Caddy for changes to take effect.
```shell
sudo systemctl caddy reload
sudo systemctl reload caddy
```
## Step 4: Verify the setup

View File

@@ -22,8 +22,7 @@ can achieve this the following steps:
# Change the DB name and DB user name if you use different
# values.
# If using Docker
docker exec -it <postgres-ente-container-name>
docker exec -it <postgres-ente-container-name> sh
psql -U pguser -d ente_db
# Or when using psql directly

View File

@@ -96,8 +96,8 @@ provide correct credentials for proper connectivity within Museum.
The `s3` section within `museum.yaml` is by default configured to use local
MinIO buckets when using `quickstart.sh` or Docker Compose.
If you wish to use an external S3 provider, you can edit the configuration with
your provider's credentials, and set `s3.are_local_buckets` to `false`.
If you wish to use an external S3 provider with SSL, you can edit the configuration with
your provider's credentials, and set `s3.are_local_buckets` to `false`. Additionally, you can configure this for specific buckets in the corresponding bucket sections in the Compose file.
If you are using default MinIO, it is accessible at port `3200`. Web Console can
be accessed by enabling port `3201` in the Compose file.
@@ -111,11 +111,11 @@ and [troubleshooting](/self-hosting/troubleshooting/uploads) sections.
| Variable | Description | Default |
| -------------------------------------- | -------------------------------------------- | ------- |
| `s3.b2-eu-cen` | Primary hot storage S3 config | |
| `s3.b2-eu-cen` | Primary hot storage bucket configuration | |
| `s3.wasabi-eu-central-2-v3.compliance` | Whether to disable compliance lock on delete | `true` |
| `s3.scw-eu-fr-v3` | Optional secondary S3 config | |
| `s3.wasabi-eu-central-2-derived` | Derived data storage | |
| `s3.are_local_buckets` | Use local MinIO-compatible storage | `false` |
| `s3.scw-eu-fr-v3` | Cold storage bucket configuration | |
| `s3.wasabi-eu-central-2-v3` | Secondary hot storage configuration | |
| `s3.are_local_buckets` | | `true` |
| `s3.use_path_style_urls` | Enable path-style URLs for MinIO | `false` |
### Encryption Keys
@@ -171,6 +171,8 @@ smtp:
email:
# Optional name for sender
sender-name:
# Optional encryption
encryption:
```
| Variable | Description | Default |
@@ -181,6 +183,7 @@ smtp:
| `smtp.password` | SMTP auth password | |
| `smtp.email` | Sender email address | |
| `smtp.sender-name` | Custom name for email sender | |
| `smtp.encryption` | Encryption method (tls, ssl) | |
| `transmail.key` | Zeptomail API key | |
### WebAuthn Passkey Support

View File

@@ -46,7 +46,7 @@ If running Museum without Docker, the code should be visible in the terminal
# Change the DB name and DB user name if you use different
# values.
# If using Docker docker exec -it <postgres-ente-container-name>
# If using Docker docker exec -it <postgres-ente-container-name> sh
psql -U pguser -d ente_db
# Or when using psql directly

View File

@@ -0,0 +1,167 @@
# Ente Log Viewer
A web-based log viewer for analyzing Ente application logs. This tool provides similar functionality to the mobile log viewer, allowing you to upload, filter, and analyze log files from customer support requests.
## Features
### 📁 File Upload
- Drag and drop ZIP files containing log files
- Automatic extraction and parsing of log files
- Support for daily log files format (YYYY-M-D.log)
### 🔍 Search and Filtering
- **Text Search**: Search through log messages, logger names, and error content
- **Logger Filtering**: Use `logger:ServiceName` syntax to filter by specific loggers
- **Wildcard Support**: Use `logger:Auth*` to match all loggers starting with "Auth"
- **Level Filtering**: Filter by log levels (SEVERE, WARNING, INFO, etc.)
- **Process Filtering**: Filter by foreground/background processes
- **Timeline Filtering**: Filter by date/time ranges
### 📊 Analytics
- Logger statistics showing most active components
- Log level distribution charts
- Click-to-filter from analytics charts
### 🎨 UI Features
- **Color-coded log levels**: Red for SEVERE, orange for WARNING, etc.
- **Process indicators**: Visual distinction between foreground and background processes
- **Active filter chips**: Visual indication of applied filters with easy removal
- **Log detail view**: Click any log entry for detailed information
- **Sort options**: Sort by newest first or oldest first
- **Responsive design**: Works on desktop and mobile devices
### 📤 Export
- Export filtered logs as text files
- Copy individual log entries to clipboard
- Maintain original formatting and error details
## Usage
### Starting the Application
1. **Local Development**:
```bash
cd infra/experiments/logs-viewer
python3 -m http.server 8080
```
Open http://localhost:8080 in your browser
2. **Upload Log Files**:
- Drag and drop a ZIP file containing `.log` files
- Or click "Choose ZIP File" to browse for files
### Log Format Support
The viewer understands the Ente log format as generated by `super_logging.dart`:
```
[process] [loggerName] [LEVEL] [timestamp] message
```
**Examples**:
- `[bg] [SyncService] [INFO] [2025-08-24 01:36:03.677678] Syncing started`
- `[CollectionsService] [WARNING] [2025-08-24 01:36:04.123456] Connection failed`
**Multi-line Error Support**:
- Automatically parses `` error detail lines
- Extracts stack traces and error IDs
- Handles inline error messages and exceptions
### Filtering Examples
- **Search by text**: `connection failed`
- **Filter by logger**: `logger:SyncService`
- **Multiple loggers**: `logger:Sync* logger:Collection*`
- **Combined search**: `logger:Auth* login failed`
### Keyboard Shortcuts
- **Search**: Click search bar or start typing
- **Clear search**: Click X button or clear the input
- **Sort toggle**: Click sort arrow button
- **Filter dialog**: Click filter button
## Technical Details
### Supported Log Levels
- **SHOUT**: Purple - Highest priority
- **SEVERE**: Red - Errors and exceptions
- **WARNING**: Orange - Warning conditions
- **INFO**: Blue - Informational messages
- **CONFIG**: Green - Configuration messages
- **FINE/FINER/FINEST**: Gray - Debug messages
### Process Types
- **Foreground**: Main app processes
- **Background (bg)**: Background tasks
- **Firebase Background (fbg)**: Firebase-related background processes
### Performance
- Lazy loading: Only renders visible log entries
- Efficient filtering: Client-side filtering with optimized algorithms
- Memory management: Handles large log files (tested with 100k+ entries)
## Sample Log Files
For testing, you can use any ZIP file containing `.log` files from Ente mobile app logs.
## Development
### File Structure
```
logs-viewer/
├── index.html # Main HTML structure
├── styles.css # CSS styling
├── script.js # JavaScript logic
├── README.md # This documentation
└── references/ # UI reference screenshots
```
### Key JavaScript Classes
- `LogViewer`: Main application class
- Log parsing logic in `parseLogFile()` and `parseLogLine()`
- Filter management in `applyCurrentFilter()`
- UI rendering in `renderLogs()` and `createLogEntryHTML()`
### Adding Features
1. **New Filter Types**: Extend `currentFilter` object and `matchesFilter()` method
2. **New Log Formats**: Update parsing patterns in `parseLogLine()`
3. **UI Components**: Add HTML elements and CSS classes, wire up in `initializeEventListeners()`
## Troubleshooting
### Common Issues
1. **ZIP file not loading**:
- Ensure ZIP contains `.log` files
- Check browser console for errors
- Try a smaller file first
2. **Logs not parsing correctly**:
- Check log format matches expected pattern
- Look for console warnings about parsing failures
- Verify timestamp format is correct
3. **Performance issues with large files**:
- The viewer handles pagination (100 logs at a time)
- Use filters to reduce the dataset
- Close other browser tabs to free memory
4. **Search not working**:
- Check for typos in logger names
- Use wildcard syntax: `logger:Service*`
- Search is case-insensitive for message content
### Browser Compatibility
- **Recommended**: Chrome 80+, Firefox 75+, Safari 13+
- **Required**: ES6 support, Fetch API, File API
- **Dependencies**: JSZip library for ZIP file handling
## Future Enhancements
- Real-time log streaming
- Advanced regex search
- Log correlation and grouping
- Performance metrics dashboard
- Dark theme support
- Export to JSON/CSV formats

View File

@@ -0,0 +1,110 @@
/* Ente Theme Variables based on web/packages/base/components/utils/theme.ts */
:root {
/* Light theme colors */
--ente-color-accent-photos: #1db954;
--ente-color-accent-auth: #9610d6;
--ente-color-accent-locker: #5ba8ff;
/* Background colors */
--ente-background-default: #fff;
--ente-background-paper: #fff;
--ente-background-paper2: #fbfbfb;
--ente-background-search: #f3f3f3;
/* Text colors */
--ente-text-base: #000;
--ente-text-muted: rgba(0, 0, 0, 0.60);
--ente-text-faint: rgba(0, 0, 0, 0.50);
/* Fill colors */
--ente-fill-base: #000;
--ente-fill-muted: rgba(0, 0, 0, 0.12);
--ente-fill-faint: rgba(0, 0, 0, 0.04);
--ente-fill-faint-hover: rgba(0, 0, 0, 0.08);
--ente-fill-fainter: rgba(0, 0, 0, 0.02);
/* Stroke colors */
--ente-stroke-base: #000;
--ente-stroke-muted: rgba(0, 0, 0, 0.24);
--ente-stroke-faint: rgba(0, 0, 0, 0.12);
--ente-stroke-fainter: rgba(0, 0, 0, 0.06);
/* Shadow */
--ente-shadow-paper: 0px 0px 10px rgba(0, 0, 0, 0.25);
--ente-shadow-menu: 0px 0px 6px rgba(0, 0, 0, 0.16), 0px 3px 6px rgba(0, 0, 0, 0.12);
--ente-shadow-button: 0px 4px 4px rgba(0, 0, 0, 0.25);
/* Fixed colors */
--ente-success: #1db954;
--ente-warning: #ffc107;
--ente-danger: #ea3f3f;
--ente-danger-dark: #f53434;
--ente-danger-light: #ff6565;
/* Secondary colors */
--ente-secondary-main: #f5f5f5;
--ente-secondary-hover: #e9e9e9;
/* Action colors */
--ente-action-hover: rgba(0, 0, 0, 0.08);
--ente-action-disabled: rgba(0, 0, 0, 0.50);
/* Typography */
--ente-font-family: "Inter Variable", sans-serif;
--ente-font-weight-regular: 500;
--ente-font-weight-medium: 600;
--ente-font-weight-bold: 700;
/* Border radius */
--ente-border-radius: 8px;
--ente-border-radius-small: 4px;
/* Spacing */
--ente-spacing-xs: 4px;
--ente-spacing-sm: 8px;
--ente-spacing-md: 12px;
--ente-spacing-lg: 16px;
--ente-spacing-xl: 24px;
}
/* Dark theme */
@media (prefers-color-scheme: dark) {
:root {
/* Background colors */
--ente-background-default: #000;
--ente-background-paper: #1b1b1b;
--ente-background-paper2: #252525;
--ente-background-search: #1b1b1b;
/* Text colors */
--ente-text-base: #fff;
--ente-text-muted: rgba(255, 255, 255, 0.70);
--ente-text-faint: rgba(255, 255, 255, 0.50);
/* Fill colors */
--ente-fill-base: #fff;
--ente-fill-muted: rgba(255, 255, 255, 0.16);
--ente-fill-faint: rgba(255, 255, 255, 0.12);
--ente-fill-faint-hover: rgba(255, 255, 255, 0.16);
--ente-fill-fainter: rgba(255, 255, 255, 0.05);
/* Stroke colors */
--ente-stroke-base: #fff;
--ente-stroke-muted: rgba(255, 255, 255, 0.24);
--ente-stroke-faint: rgba(255, 255, 255, 0.16);
--ente-stroke-fainter: rgba(255, 255, 255, 0.12);
/* Shadow */
--ente-shadow-paper: 0px 2px 12px rgba(0, 0, 0, 0.75);
--ente-shadow-menu: 0px 0px 6px rgba(0, 0, 0, 0.50), 0px 3px 6px rgba(0, 0, 0, 0.25);
--ente-shadow-button: 0px 4px 4px rgba(0, 0, 0, 0.75);
/* Secondary colors */
--ente-secondary-main: #2b2b2b;
--ente-secondary-hover: #373737;
/* Action colors */
--ente-action-hover: rgba(255, 255, 255, 0.16);
--ente-action-disabled: rgba(255, 255, 255, 0.50);
}
}

View File

@@ -0,0 +1,218 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ente Log Viewer</title>
<!-- Material-UI CSS -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mui/material@latest/dist/index.css" />
<!-- Ente theme -->
<link rel="stylesheet" href="ente-theme.css">
<!-- Custom styles -->
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="app">
<!-- Header -->
<header class="header">
<div class="header-content">
<h1>📋 Ente Log Viewer</h1>
<div class="header-actions">
<button id="filter-btn" class="mui-icon-btn" title="Filter logs">
<span class="material-icons">filter_list</span>
<span class="filter-count" id="filter-count" style="display: none;"></span>
</button>
<button id="sort-btn" class="mui-icon-btn" title="Sort order">
<span class="material-icons">arrow_downward</span>
</button>
<div class="dropdown">
<button class="mui-icon-btn dropdown-toggle" title="More actions">
<span class="material-icons">more_vert</span>
</button>
<div class="dropdown-menu mui-menu">
<button id="analytics-btn" class="dropdown-item mui-menu-item">
<span class="material-icons">analytics</span>
<span>Analytics</span>
</button>
<button id="export-btn" class="dropdown-item mui-menu-item">
<span class="material-icons">download</span>
<span>Export Logs</span>
</button>
<button id="clear-btn" class="dropdown-item mui-menu-item danger">
<span class="material-icons">delete</span>
<span>Clear Logs</span>
</button>
</div>
</div>
</div>
</div>
</header>
<!-- Upload Section -->
<div class="upload-section" id="upload-section">
<div class="upload-area" id="upload-area">
<div class="upload-content">
<div class="upload-icon">📁</div>
<h2>Upload Log Files</h2>
<p>Drag and drop a zip file containing log files, or click to browse</p>
<input type="file" id="file-input" accept=".zip" hidden>
<button id="browse-btn" class="primary-btn">Choose ZIP File</button>
</div>
</div>
</div>
<!-- Main Content -->
<div class="main-content" id="main-content" style="display: none;">
<!-- Search Bar -->
<div class="search-section">
<div class="search-bar mui-search-container">
<div class="mui-textfield">
<input type="text" id="search-input" placeholder="Search logs... (try 'logger:SyncService' or text search)" class="mui-input" />
<span class="mui-search-icon material-icons">search</span>
<button id="clear-search" class="mui-clear-btn" style="display: none;">
<span class="material-icons">clear</span>
</button>
</div>
</div>
</div>
<!-- Timeline Filter -->
<div class="timeline-section" id="timeline-section" style="display: none;">
<div class="timeline-header">
<span class="material-icons">timeline</span>
<span>Timeline Filter</span>
<button id="timeline-toggle" class="mui-icon-btn timeline-btn">
<span class="material-icons">timeline</span>
</button>
</div>
<div class="timeline-controls" id="timeline-controls" style="display: none;">
<div class="timeline-range">
<div class="mui-textfield">
<input type="datetime-local" id="start-time" class="mui-input" />
</div>
<span class="range-separator">to</span>
<div class="mui-textfield">
<input type="datetime-local" id="end-time" class="mui-input" />
</div>
<button id="reset-timeline" class="mui-button secondary">
<span class="material-icons">refresh</span>
<span>Reset</span>
</button>
</div>
</div>
</div>
<!-- Active Filters -->
<div class="active-filters" id="active-filters" style="display: none;">
<div class="filter-chips" id="filter-chips"></div>
</div>
<!-- Log Stats -->
<div class="log-stats" id="log-stats">
<span id="log-count">0 logs loaded</span>
<span id="filtered-count"></span>
</div>
<!-- Log List -->
<div class="log-list-container">
<div class="log-list" id="log-list">
<!-- Log entries will be populated here -->
</div>
<div class="loading" id="loading" style="display: none;">Loading...</div>
<div class="load-more" id="load-more" style="display: none;">
<button class="secondary-btn">Load More</button>
</div>
</div>
</div>
</div>
<!-- Filter Dialog -->
<div class="dialog-overlay" id="filter-dialog" style="display: none;">
<div class="dialog">
<div class="dialog-header">
<h2>Filter Logs</h2>
<button class="close-btn" id="close-filter"></button>
</div>
<div class="dialog-content">
<!-- Log Levels -->
<div class="filter-section">
<h3>Log Levels</h3>
<div class="level-chips" id="level-chips">
<!-- Level chips will be populated here -->
</div>
</div>
<!-- Process -->
<div class="filter-section">
<h3>Process</h3>
<div class="process-list" id="process-list">
<!-- Process checkboxes will be populated here -->
</div>
</div>
<!-- Loggers -->
<div class="filter-section">
<h3>Loggers</h3>
<div class="logger-list" id="logger-list">
<!-- Logger checkboxes will be populated here -->
</div>
</div>
</div>
<div class="dialog-actions">
<button id="clear-filters" class="secondary-btn">Clear All</button>
<button id="cancel-filter" class="secondary-btn">Cancel</button>
<button id="apply-filters" class="primary-btn">Apply</button>
</div>
</div>
</div>
<!-- Analytics Dialog -->
<div class="dialog-overlay" id="analytics-dialog" style="display: none;">
<div class="dialog">
<div class="dialog-header">
<h2>Logger Analytics</h2>
<button class="close-btn" id="close-analytics"></button>
</div>
<div class="dialog-content">
<div id="analytics-content">
<!-- Analytics charts will be populated here -->
</div>
</div>
<div class="dialog-actions">
<button id="close-analytics-btn" class="secondary-btn">Close</button>
</div>
</div>
</div>
<!-- Log Detail Dialog -->
<div class="dialog-overlay" id="detail-dialog" style="display: none;">
<div class="dialog large">
<div class="dialog-header">
<h2>Log Details</h2>
<button class="close-btn" id="close-detail"></button>
</div>
<div class="dialog-content">
<div id="detail-content">
<!-- Log details will be populated here -->
</div>
</div>
<div class="dialog-actions">
<button id="copy-log" class="secondary-btn">Copy</button>
<button id="close-detail-btn" class="secondary-btn">Close</button>
</div>
</div>
</div>
<!-- Material-UI JavaScript -->
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@mui/material@latest/umd/material-ui.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
<script src="script.js"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

45
mobile/.gitignore vendored Normal file
View File

@@ -0,0 +1,45 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
melos_*.iml
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release

View File

@@ -0,0 +1,72 @@
# For more linters, we can check https://dart-lang.github.io/linter/lints/index.html
# or https://pub.dev/packages/lint (Effective dart)
# use "flutter analyze ." or "dart analyze ." for running lint checks
include: package:flutter_lints/flutter.yaml
linter:
rules:
# Ref https://github.com/flutter/packages/blob/master/packages/flutter_lints/lib/flutter.yaml
# Ref https://dart-lang.github.io/linter/lints/
- avoid_print
- avoid_unnecessary_containers
- avoid_web_libraries_in_flutter
- no_logic_in_create_state
- prefer_const_constructors
- prefer_const_constructors_in_immutables
- prefer_const_declarations
- prefer_const_literals_to_create_immutables
- prefer_final_locals
- require_trailing_commas
- sized_box_for_whitespace
- use_full_hex_values_for_flutter_colors
- use_key_in_widget_constructors
- cancel_subscriptions
- avoid_empty_else
- exhaustive_cases
# just style suggestions
- sort_pub_dependencies
- use_rethrow_when_possible
- prefer_double_quotes
- directives_ordering
- always_use_package_imports
- sort_child_properties_last
- unawaited_futures
analyzer:
errors:
avoid_empty_else: error
exhaustive_cases: error
curly_braces_in_flow_control_structures: error
directives_ordering: error
require_trailing_commas: error
always_use_package_imports: warning
prefer_final_fields: error
unused_import: error
camel_case_types: error
prefer_is_empty: warning
use_rethrow_when_possible: info
unused_field: warning
use_key_in_widget_constructors: warning
sort_child_properties_last: warning
sort_pub_dependencies: warning
library_private_types_in_public_api: warning
constant_identifier_names: ignore
prefer_const_constructors: warning
prefer_const_declarations: warning
prefer_const_constructors_in_immutables: warning
prefer_final_locals: warning
unnecessary_const: error
cancel_subscriptions: error
unrelated_type_equality_checks: error
unnecessary_cast: info
unawaited_futures: warning # convert to warning after fixing existing issues
invalid_dependency: info
use_build_context_synchronously: ignore # experimental lint, requires many changes
prefer_interpolation_to_compose_strings: ignore # later too many warnings
prefer_double_quotes: ignore # too many warnings
avoid_renaming_method_parameters: ignore # incorrect warnings for `equals` overrides

View File

@@ -8,6 +8,7 @@
.buildlog/
.history
.svn/
android/app/build/
# Editors
.vscode/

View File

@@ -34,6 +34,9 @@ android {
ndkVersion flutter.ndkVersion
compileOptions {
// Flag to enable support for the new language APIs
coreLibraryDesugaringEnabled true
// Sets Java compatibility to Java 8
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
@@ -56,7 +59,7 @@ android {
applicationId "io.ente.auth"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion 21
minSdkVersion 22
targetSdkVersion 35
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
@@ -115,4 +118,7 @@ flutter {
source '../..'
}
dependencies {}
dependencies {
// For AGP 7.4+
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
}

View File

@@ -0,0 +1,6 @@
# Please add these rules to your existing keep rules in order to suppress warnings.
# This is generated automatically by the Android Gradle plugin.
-dontwarn com.google.errorprone.annotations.CanIgnoreReturnValue
-dontwarn com.google.errorprone.annotations.CheckReturnValue
-dontwarn com.google.errorprone.annotations.Immutable
-dontwarn com.google.errorprone.annotations.RestrictedApi

View File

@@ -6,6 +6,19 @@ allprojects {
}
rootProject.buildDir = '../build'
subprojects {
afterEvaluate { project ->
if (project.hasProperty('android')) {
project.android {
if (namespace == null) {
namespace project.group
}
}
}
}
}
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}

View File

@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip

View File

@@ -19,8 +19,8 @@ pluginManagement {
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "7.3.0" apply false
id "org.jetbrains.kotlin.android" version "1.8.22" apply false
id "com.android.application" version "8.6.0" apply false
id "org.jetbrains.kotlin.android" version "2.1.10" apply false
}
include ":app"

View File

@@ -382,6 +382,11 @@
{
"title": "CoinDCX"
},
{
"title": "CoinTracking",
"slug": "cointracking",
"altNames": ["cointracking.info", "Coin Tracking"]
},
{
"title": "colorado",
"altNames": [
@@ -735,6 +740,11 @@
{
"title": "Hivelocity"
},
{
"title": "HRDocumentBox",
"slug": "hrdocumentbox",
"altNames": ["HRDocumentBox", "HR Document Box"]
},
{
"title": "HSA Bank",
"slug": "hsa_bank",
@@ -1040,6 +1050,13 @@
"MistralAI"
]
},
{
"title": "Mobile01",
"slug": "mobile01",
"altNames": [
"M01"
]
},
{
"title": "Mozilla"
},
@@ -1219,6 +1236,12 @@
"title": "Parqet",
"slug": "parqet"
},
{
"title": "Parallels",
"slug": "parallels",
"hex": "#E61E25",
"altNames": ["Parallels Desktop", "Parallels VM"]
},
{
"title": "Parsec"
},
@@ -1773,6 +1796,11 @@
"uollet.com.br"
]
},
{
"title": "VHV",
"slug": "vhv",
"altNames": ["VHV", "VHV Versicherung"]
},
{
"title": "Vikunja"
},

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1220.8 227.9" style="enable-background:new 0 0 1220.8 227.9;" xml:space="preserve">
<style type="text/css">
.st0{fill:#0D253E;}
.st1{fill:#008AFB;}
</style>
<title>CoinTracking light</title>
<g id="Layer_2_1_">
<g id="Layer_1-2">
<path class="st0" d="M198.1,167c-30.2,0-54.7-24.5-54.7-54.7s24.5-54.7,54.7-54.7s54.7,24.5,54.7,54.7l0,0 C252.8,142.5,228.3,167,198.1,167z M198.1,81.6c-17,0-30.7,13.7-30.7,30.7s13.7,30.7,30.7,30.7s30.7-13.7,30.7-30.7l0,0 C228.8,95.4,215,81.6,198.1,81.6z"/>
<path class="st0" d="M292.2,59.5h-23.6v107.9h23.6V59.5z"/>
<path class="st0" d="M339.5,167.4h-23.8V59.5h23.8v16.2c6.2-12.5,21-18.5,33-18.5c26.1,0,41.1,16.9,41.1,47.3v62.8h-23.8v-60.1 c0-17.1-8.8-26.8-22.6-26.8c-14.1,0-27.7,7.6-27.7,28.9V167.4z"/>
<path class="st1" d="M390.6,8.2h151.7v22.9h-65.9v136.3h-25V31.1h-60.9V8.2H390.6z"/>
<path class="st1" d="M540.3,167.4h-23.8V59.5h23.8V79c7.4-13.5,15.4-19.5,29.6-21.8c7.5-1.2,15.7,1.8,19.2,4.2l-3.9,22 c-4.9-2.5-10.4-3.8-15.9-3.7c-20.3,0-28.9,20.3-28.9,49L540.3,167.4L540.3,167.4z"/>
<path class="st1" d="M680.7,151.9c-7.2,11.8-22.9,17.8-36.3,17.8c-29.1,0-54.8-21.9-54.8-56.4s25.6-56.1,54.8-56.1 c12.9,0,28.9,5.3,36.3,17.5V59.5h23.6v107.9h-23.6V151.9z M647.2,146.6c17.6,0,33.3-12.2,33.3-33.5s-17.1-32.8-33.3-32.8 c-18,0-33,12.9-33,32.8S629.2,146.6,647.2,146.6L647.2,146.6z"/>
<path class="st1" d="M778.4,57.2c17.1,0,32.6,6.7,42.7,18.7l-18.5,14.8c-5.8-6.7-14.8-10.4-24.3-10.4c-18,0-34,12.7-34,32.8 s15.9,33.7,34,33.7c9.5,0,18.5-3.9,24.3-10.6l18.7,14.6c-10.2,12-25.6,18.9-43,18.9c-31,0-57.5-22.4-57.5-56.6 S747.4,57.2,778.4,57.2z"/>
<path class="st1" d="M860.3,117.2v50.1h-23.6V0.8h23.6V95l34.2-35.6h31.9l-46,46.9l56.4,61h-30.5L860.3,117.2z"/>
<path class="st1" d="M967.4,59.5h-23.6v107.9h23.6V59.5z"/>
<path class="st1" d="M1014.7,167.4h-23.8V59.5h23.8v16.2c6.2-12.5,21-18.5,33-18.5c26.1,0,41.1,16.9,41.1,47.3v62.8H1065v-60.1 c0-17.1-8.8-26.8-22.6-26.8c-14.1,0-27.7,7.6-27.7,28.9V167.4z"/>
<path class="st1" d="M1220.2,111.5c0-12.3-4.5-24.2-12.5-33.6l13-17.1l-19.6-13l-11.9,15.7c-8.3-4.1-17.5-6.2-26.8-6.2 c-31.9,0-57.8,24.4-57.8,54.3s25.9,54.3,57.8,54.3c20.2,0,34.2,10.2,34.2,19.3s-14.1,19.3-34.2,19.3s-34.2-10.2-34.2-19.3h-23.6 c0,24,25.4,42.9,57.8,42.9s57.8-18.8,57.8-42.9c0-13.2-7.7-24.8-19.9-32.6C1212.5,142.5,1220.2,127.8,1220.2,111.5z M1128.2,111.5 c0-16.9,15.4-30.7,34.2-30.7s34.2,13.8,34.2,30.7s-15.4,30.7-34.2,30.7S1128.2,128.4,1128.2,111.5L1128.2,111.5z"/>
<path class="st0" d="M81.4,170.5C36.4,170.5,0,134,0,89c0-21.6,8.6-42.3,23.9-57.6c31.8-31.8,83.4-31.8,115.2,0l0,0l-17.7,17.7 C99.3,27,63.6,27,41.5,49.1s-22.1,57.8,0,79.9s57.8,22.1,79.9,0l17.7,17.7C123.8,162,103.1,170.6,81.4,170.5z"/>
<circle class="st0" cx="280.7" cy="15.5" r="15.5"/>
<circle class="st1" cx="954.7" cy="15.5" r="15.5"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1,26 @@
<?xml version="1.0"?>
<svg width="320" height="280" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" version="1.1">
<g class="layer">
<title>Layer 1</title>
<g id="Layer1000">
<g id="Layer1002">
<g id="Layer1003">
<path d="m123.08,34.29c-66.43,0 -120.27,53.85 -120.27,120.27c0,66.43 53.85,120.27 120.27,120.27c66.47,0 120.32,-53.85 120.32,-120.27c0,-66.43 -53.85,-120.27 -120.32,-120.27zm0,215.67c-52.67,0 -95.36,-42.73 -95.36,-95.4c0,-52.67 42.68,-95.4 95.36,-95.4c52.72,0 95.4,42.73 95.4,95.4c0,52.67 -42.68,95.4 -95.4,95.4z" fill="#2a5e00" fill-rule="evenodd" id="path7"/>
<g id="Layer1004">
<g id="Layer1005">
<path d="m138.72,146.29l59.61,-41.47l7.78,33.7l-67.39,7.78z" fill="#2a5e00" fill-rule="evenodd" id="path8"/>
<path d="m110.88,146.29l-59.61,-41.47l-7.78,33.7l67.39,7.78z" fill="#2a5e00" fill-rule="evenodd" id="path9"/>
</g>
<path d="m43.95,192.02l74.62,49.75l87.12,-78.8l-161.75,29.05z" fill="#2a5e00" fill-rule="evenodd" id="path10"/>
</g>
<path d="m94.24,59.29l-30.48,-55.1l54.26,33.24l-23.79,21.86z" fill="#2a5e00" fill-rule="evenodd" id="path11"/>
<path d="m202.64,78.1l30.43,-55.1l-54.22,33.24l23.79,21.86z" fill="#2a5e00" fill-rule="evenodd" id="path12"/>
</g>
<path d="m275.63,274.67l29.35,0l0,-240.76l-29.35,0l0,240.76z" fill="#2a5e00" fill-rule="evenodd" id="path13"/>
<path d="m317.94,125.93c0,15.3 -12.33,27.63 -27.63,27.63c-15.26,0 -27.63,-12.33 -27.63,-27.63c0,-15.26 12.37,-27.63 27.63,-27.63c15.3,0 27.63,12.37 27.63,27.63z" fill="#2a5e00" fill-rule="evenodd" id="path14"/>
<path d="m288.84,33.91l-41.76,0l16.76,45.99l23.58,0l1.42,-45.99z" fill="#2a5e00" fill-rule="evenodd" id="path15"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<rect x="20" y="10" width="10" height="80" rx="5" fill="#E61E25"/>
<rect x="50" y="10" width="10" height="80" rx="5" fill="#E61E25"/>
</svg>

After

Width:  |  Height:  |  Size: 207 B

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Created with Inkscape (http://www.inkscape.org/) by Marsupilami -->
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" width="1024" height="357" viewBox="-1.98252 -1.98252 201.02104 70.04904" id="svg3349">
<defs id="defs3351"/>
<path d="m 0,0 11.76,0 4.455,31.962 0.12,0 L 20.789,0 32.55,0 23.402,42.417 9.147,42.417 0,0" id="path3131" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
<path d="m 34.039,0 11.05,0 0,15.567 6.771,0 0,-15.567 11.05,0 0,42.417 -11.05,0 0,-17.465 -6.771,0 0,17.465 -11.05,0 0,-42.417" id="path3133" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
<path d="m 64.395,0 11.757,0 4.457,31.962 0.121,0 L 85.185,0 96.944,0 87.797,42.417 73.54,42.417 64.395,0" id="path3135" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
<path d="m 97.576,42.414 8.907,0 9.222,-42.41 -8.912,0 -9.217,42.41" id="path3137" style="fill:#f0ab00;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
<path d="m 111.026,42.424 8.908,-0.01 9.218,-42.41 -8.913,0 -9.213,42.42" id="path3139" style="fill:#f0ab00;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
<path d="m 124.564,42.414 8.906,0 9.217,-42.41 -8.906,0 -9.217,42.41" id="path3141" style="fill:#f0ab00;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
<path d="m 4.169,46.784 5.251,0 1.985,14.261 0.05,0 1.99,-14.261 5.247,0 -4.082,18.928 -6.363,0 -4.082,-18.928" id="path3143" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
<path d="m 19.356,46.784 11.216,0 0,4.031 -6.282,0 0,3.234 5.882,0 0,3.87 -5.882,0 0,3.763 6.517,0 0,4.03 -11.451,0 0,-18.928" id="path3145" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
<path d="m 54.93,52.247 0,-0.45 c 0,-1.117 -0.449,-2.043 -1.405,-2.043 -1.06,0 -1.538,0.823 -1.538,1.67 0,3.739 8.059,1.91 8.059,8.828 0,4.027 -2.357,5.832 -6.705,5.832 -4.082,0 -6.362,-1.404 -6.362,-5.331 l 0,-0.663 4.772,0 0,0.453 c 0,1.615 0.663,2.202 1.614,2.202 1.007,0 1.594,-0.798 1.594,-1.831 0,-3.739 -7.743,-1.88 -7.743,-8.586 0,-3.819 2.043,-5.913 6.205,-5.913 4.294,0 6.12,1.775 6.12,5.832 l -4.611,0" id="path3147" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
<path d="m 61.476,65.712 4.932,0 0,-18.928 -4.932,0 0,18.928 z" id="path3149" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
<path d="m 32.477,46.784 7.714,0 c 3.766,0 5.195,2.124 5.195,5.039 0,2.516 -0.979,4.16 -3.232,4.506 2.385,0.265 3.103,1.776 3.103,4.294 l 0,1.616 c 0,0.981 0,2.251 0.237,2.626 0.132,0.209 0.237,0.422 0.558,0.582 l 0,0.265 -5.25,0 C 40.325,64.705 40.325,62.9 40.325,62.109 l 0,-1.275 c 0,-2.147 -0.423,-2.704 -1.619,-2.704 l -1.296,0 0,7.582 -4.933,0 0,-18.928 z m 4.933,8.008 0.98,0 c 1.405,0 2.066,-0.9 2.066,-2.252 0,-1.541 -0.609,-2.202 -2.094,-2.202 l -0.952,0 0,4.454" id="path3151" style="fill:#1e1e1e;fill-opacity:1;fill-rule:evenodd;stroke:none"/>
<path d="m 75.975,52.54 c 0,-2.147 -0.396,-2.786 -1.35,-2.786 -1.513,0 -1.67,1.381 -1.67,6.494 0,5.117 0.157,6.497 1.67,6.497 1.22,0 1.485,-1.063 1.485,-4.64 l 4.771,0 0,1.405 c 0,5.3 -3.101,6.574 -6.256,6.574 -5.54,0 -6.76,-2.784 -6.76,-9.836 0,-7.236 1.642,-9.833 6.76,-9.833 4.451,0 6.12,2.334 6.12,5.992 l 0,1.19 -4.77,0 0,-1.057" id="path3153" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
<path d="m 82.524,46.784 4.928,0 0,6.948 3.02,0 0,-6.948 4.932,0 0,18.928 -4.932,0 0,-7.793 -3.02,0 0,7.793 -4.928,0 0,-18.928" id="path3155" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
<path d="m 97.606,46.784 11.21,0 0,4.031 -6.282,0 0,3.234 5.886,0 0,3.87 -5.886,0 0,3.763 6.522,0 0,4.03 -11.45,0 0,-18.928" id="path3157" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
<path d="m 130.526,46.784 0,13.655 c 0,1.824 0.559,2.306 1.485,2.306 0.928,0 1.485,-0.482 1.485,-2.306 l 0,-13.655 4.929,0 0,12.408 c 0,5.298 -2.28,6.892 -6.414,6.892 -4.135,0 -6.412,-1.594 -6.412,-6.892 l 0,-12.408 4.927,0" id="path3159" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
<path d="m 140.415,46.784 5.51,0 3.185,11.665 0.05,0 0,-11.665 4.614,0 0,18.928 -5.407,0 -3.288,-11.689 -0.05,0 0,11.689 -4.613,0 0,-18.928" id="path3161" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
<path d="m 110.726,46.784 7.716,0 c 3.76,0 5.192,2.124 5.192,5.039 0,2.516 -0.979,4.16 -3.234,4.506 2.383,0.265 3.101,1.776 3.101,4.294 l 0,1.616 c 0,0.981 0,2.251 0.238,2.626 0.131,0.209 0.24,0.422 0.558,0.582 l 0,0.265 -5.251,0 c -0.477,-1.007 -0.477,-2.812 -0.477,-3.603 l 0,-1.275 c 0,-2.147 -0.422,-2.704 -1.612,-2.704 l -1.301,0 0,7.582 -4.93,0 0,-18.928 z m 4.93,8.008 0.979,0 c 1.404,0 2.07,-0.9 2.07,-2.252 0,-1.541 -0.61,-2.202 -2.095,-2.202 l -0.954,0 0,4.454" id="path3163" style="fill:#1e1e1e;fill-opacity:1;fill-rule:evenodd;stroke:none"/>
<path d="m 162.334,55.428 6.361,0 0,10.284 -3.34,0 -0.113,-1.669 -0.05,0 c -0.662,1.617 -2.412,2.041 -4.082,2.041 -5.01,0 -5.459,-3.58 -5.459,-9.836 0,-6.336 1.22,-9.833 7.047,-9.833 3.502,0 5.993,1.775 5.993,6.36 l -4.771,0 c 0,-0.952 -0.07,-1.695 -0.266,-2.198 -0.185,-0.53 -0.554,-0.823 -1.139,-0.823 -1.612,0 -1.774,1.381 -1.774,6.494 0,5.117 0.161,6.497 1.67,6.497 1.034,0 1.639,-0.666 1.669,-3.978 l -1.75,0 0,-3.339" id="path3165" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
<path d="m 170.577,46.784 11.213,0 0,4.031 -6.28,0 0,3.234 5.885,0 0,3.87 -5.885,0 0,3.763 6.519,0 0,4.03 -11.452,0 0,-18.928" id="path3167" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
<path d="m 183.697,46.784 5.514,0 3.18,11.665 0.05,0 0,-11.665 4.615,0 0,18.928 -5.408,0 -3.284,-11.689 -0.05,0 0,11.689 -4.614,0 0,-18.928" id="path3169" style="fill:#1e1e1e;fill-opacity:1;fill-rule:nonzero;stroke:none"/>
</svg>
<!-- version: 20110311, original size: 197.056 66.084, border: 3% -->

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@@ -6,14 +6,14 @@
"request": "launch",
"type": "dart",
"flutterMode": "debug",
"program": "auth/lib/main.dart",
"program": "mobile/apps/auth/lib/main.dart",
"args": ["--dart-define", "endpoint=http://localhost:8080"]
},
{
"name": "Auth Android Dev",
"request": "launch",
"type": "dart",
"program": "auth/lib/main.dart",
"program": "mobile/apps/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": "auth/lib/main.dart",
"program": "mobile/apps/auth/lib/main.dart",
"args": ["--dart-define", "endpoint=http://192.168.1.30:8080"]
},
{
"name": "Auth iOS Prod",
"request": "launch",
"type": "dart",
"program": "auth/lib/main.dart",
"program": "mobile/apps/auth/lib/main.dart",
"args": ["--target", "lib/main.dart"]
},
{
"name": "Auth Android Prod",
"request": "launch",
"type": "dart",
"program": "auth/lib/main.dart",
"program": "mobile/apps/auth/lib/main.dart",
"args": ["--target", "lib/main.dart", "--flavor", "independent"]
}
]

View File

@@ -3,7 +3,6 @@ PODS:
- Flutter
- connectivity_plus (0.0.1):
- Flutter
- FlutterMacOS
- cupertino_http (0.0.1):
- Flutter
- FlutterMacOS
@@ -40,6 +39,8 @@ PODS:
- DKPhotoGallery/Resource (0.0.19):
- SDWebImage
- SwiftyGif
- ente_qr (0.0.1):
- Flutter
- file_picker (0.0.1):
- DKImagePickerController/PhotoGallery
- Flutter
@@ -61,13 +62,12 @@ PODS:
- Flutter
- flutter_local_notifications (0.0.1):
- Flutter
- flutter_native_splash (0.0.1):
- flutter_native_splash (2.4.3):
- Flutter
- flutter_secure_storage (6.0.0):
- Flutter
- fluttertoast (0.0.2):
- Flutter
- Toast
- local_auth_darwin (0.0.1):
- Flutter
- FlutterMacOS
@@ -87,9 +87,9 @@ PODS:
- qr_code_scanner (0.2.0):
- Flutter
- MTBBarcodeScanner
- SDWebImage (5.21.0):
- SDWebImage/Core (= 5.21.0)
- SDWebImage/Core (5.21.0)
- SDWebImage (5.21.1):
- SDWebImage/Core (= 5.21.1)
- SDWebImage/Core (5.21.1)
- Sentry/HybridSDK (8.46.0)
- sentry_flutter (8.14.2):
- Flutter
@@ -102,37 +102,43 @@ PODS:
- FlutterMacOS
- sodium_libs (2.2.1):
- Flutter
- sqflite (0.0.3):
- sqflite_darwin (0.0.4):
- Flutter
- FlutterMacOS
- "sqlite3 (3.46.1+1)":
- "sqlite3/common (= 3.46.1+1)"
- "sqlite3/common (3.46.1+1)"
- "sqlite3/dbstatvtab (3.46.1+1)":
- sqlite3 (3.50.2):
- sqlite3/common (= 3.50.2)
- sqlite3/common (3.50.2)
- sqlite3/dbstatvtab (3.50.2):
- sqlite3/common
- "sqlite3/fts5 (3.46.1+1)":
- sqlite3/fts5 (3.50.2):
- sqlite3/common
- "sqlite3/perf-threadsafe (3.46.1+1)":
- sqlite3/math (3.50.2):
- sqlite3/common
- "sqlite3/rtree (3.46.1+1)":
- sqlite3/perf-threadsafe (3.50.2):
- sqlite3/common
- sqlite3/rtree (3.50.2):
- sqlite3/common
- sqlite3_flutter_libs (0.0.1):
- Flutter
- "sqlite3 (~> 3.46.0+1)"
- FlutterMacOS
- sqlite3 (~> 3.50.1)
- sqlite3/dbstatvtab
- sqlite3/fts5
- sqlite3/math
- sqlite3/perf-threadsafe
- sqlite3/rtree
- SwiftyGif (5.4.5)
- Toast (4.1.1)
- ua_client_hints (1.4.1):
- Flutter
- url_launcher_ios (0.0.1):
- Flutter
DEPENDENCIES:
- app_links (from `.symlinks/plugins/app_links/ios`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/darwin`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- cupertino_http (from `.symlinks/plugins/cupertino_http/darwin`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- ente_qr (from `.symlinks/plugins/ente_qr/ios`)
- file_picker (from `.symlinks/plugins/file_picker/ios`)
- file_saver (from `.symlinks/plugins/file_saver/ios`)
- fk_user_agent (from `.symlinks/plugins/fk_user_agent/ios`)
@@ -155,8 +161,9 @@ DEPENDENCIES:
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sodium_libs (from `.symlinks/plugins/sodium_libs/ios`)
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
- sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`)
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
- sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/darwin`)
- ua_client_hints (from `.symlinks/plugins/ua_client_hints/ios`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
SPEC REPOS:
@@ -169,17 +176,18 @@ SPEC REPOS:
- Sentry
- sqlite3
- SwiftyGif
- Toast
EXTERNAL SOURCES:
app_links:
:path: ".symlinks/plugins/app_links/ios"
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/darwin"
:path: ".symlinks/plugins/connectivity_plus/ios"
cupertino_http:
:path: ".symlinks/plugins/cupertino_http/darwin"
device_info_plus:
:path: ".symlinks/plugins/device_info_plus/ios"
ente_qr:
:path: ".symlinks/plugins/ente_qr/ios"
file_picker:
:path: ".symlinks/plugins/file_picker/ios"
file_saver:
@@ -224,51 +232,54 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
sodium_libs:
:path: ".symlinks/plugins/sodium_libs/ios"
sqflite:
:path: ".symlinks/plugins/sqflite/darwin"
sqflite_darwin:
:path: ".symlinks/plugins/sqflite_darwin/darwin"
sqlite3_flutter_libs:
:path: ".symlinks/plugins/sqlite3_flutter_libs/ios"
:path: ".symlinks/plugins/sqlite3_flutter_libs/darwin"
ua_client_hints:
:path: ".symlinks/plugins/ua_client_hints/ios"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"
SPEC CHECKSUMS:
app_links: e7a6750a915a9e161c58d91bc610e8cd1d4d0ad0
connectivity_plus: ddd7f30999e1faaef5967c23d5b6d503d10434db
app_links: f3e17e4ee5e357b39d8b95290a9b2c299fca71c6
connectivity_plus: 2a701ffec2c0ae28a48cf7540e279787e77c447d
cupertino_http: 947a233f40cfea55167a49f2facc18434ea117ba
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
ente_qr: f39434aa69ea0e71047b49316365b2737f8a20aa
file_picker: b159e0c068aef54932bb15dc9fd1571818edaf49
file_saver: 503e386464dbe118f630e17b4c2e1190fa0cf808
fk_user_agent: 1f47ec39291e8372b1d692b50084b0d54103c545
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_email_sender: 10a22605f92809a11ef52b2f412db806c6082d40
flutter_email_sender: e03bdda7637bcd3539bfe718fddd980e9508efaa
flutter_inappwebview_ios: 6f63631e2c62a7c350263b13fa5427aedefe81d4
flutter_local_authentication: 1172a4dd88f6306dadce067454e2c4caf07977bb
flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086
flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778
flutter_local_notifications: df98d66e515e1ca797af436137b4459b160ad8c9
flutter_native_splash: df59bb2e1421aa0282cb2e95618af4dcb0c56c29
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
fluttertoast: e9a18c7be5413da53898f660530c56f35edfba9c
local_auth_darwin: 66e40372f1c29f383a314c738c7446e2f7fdadc3
fluttertoast: 21eecd6935e7064cc1fcb733a4c5a428f3f24f0f
local_auth_darwin: fa4b06454df7df8e97c18d7ee55151c57e7af0de
move_to_background: 39a5b79b26d577b0372cbe8a8c55e7aa9fcd3a2d
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
objective_c: 77e887b5ba1827970907e10e832eec1683f3431d
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
privacy_screen: 1a131c052ceb3c3659934b003b0d397c2381a24e
qr_code_scanner: bb67d64904c3b9658ada8c402e8b4d406d5d796e
SDWebImage: f84b0feeb08d2d11e6a9b843cb06d75ebf5b8868
SDWebImage: f29024626962457f3470184232766516dee8dfea
Sentry: da60d980b197a46db0b35ea12cb8f39af48d8854
sentry_flutter: 2df8b0aab7e4aba81261c230cbea31c82a62dd1b
share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
sodium_libs: 1faae17af662384acbd13e41867a0008cd2e2318
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
sqlite3: 0bb0e6389d824e40296f531b858a2a0b71c0d2fb
sqlite3_flutter_libs: c00457ebd31e59fa6bb830380ddba24d44fbcd3b
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
sqlite3: 3e82a2daae39ba3b41ae6ee84a130494585460fc
sqlite3_flutter_libs: 2c48c4ee7217fd653251975e43412250d5bcbbe2
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e
ua_client_hints: aeabd123262c087f0ce151ef96fa3ab77bfc8b38
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
PODFILE CHECKSUM: 78f002751f1a8f65042b8da97902ba4124271c5a

View File

@@ -2,20 +2,21 @@ import 'dart:async';
import 'dart:io';
import 'package:adaptive_theme/adaptive_theme.dart';
import 'package:ente_accounts/services/user_service.dart';
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/core/event_bus.dart';
import 'package:ente_auth/ente_theme_data.dart';
import 'package:ente_auth/events/signed_in_event.dart';
import 'package:ente_auth/events/signed_out_event.dart';
import "package:ente_auth/l10n/l10n.dart";
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/locale.dart';
import "package:ente_auth/onboarding/view/onboarding_page.dart";
import 'package:ente_auth/services/authenticator_service.dart';
import 'package:ente_auth/services/update_service.dart';
import 'package:ente_auth/services/user_service.dart';
import 'package:ente_auth/services/window_listener_service.dart';
import 'package:ente_auth/ui/home_page.dart';
import 'package:ente_auth/ui/settings/app_update_dialog.dart';
import 'package:ente_events/event_bus.dart';
import 'package:ente_events/models/signed_in_event.dart';
import 'package:ente_events/models/signed_out_event.dart';
import 'package:ente_strings/l10n/strings_localizations.dart';
import 'package:flutter/foundation.dart';
import "package:flutter/material.dart";
import 'package:flutter_localizations/flutter_localizations.dart';
@@ -131,6 +132,7 @@ class _AppState extends State<App>
localeListResolutionCallback: localResolutionCallBack,
localizationsDelegates: const [
AppLocalizations.delegate,
StringsLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
@@ -150,6 +152,7 @@ class _AppState extends State<App>
localeListResolutionCallback: localResolutionCallBack,
localizationsDelegates: const [
AppLocalizations.delegate,
StringsLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,

View File

@@ -1,95 +1,36 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io' as io;
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';
import 'package:ente_auth/models/key_gen_result.dart';
import 'package:ente_auth/models/private_key_attributes.dart';
import 'package:ente_auth/store/authenticator_db.dart';
import 'package:ente_auth/utils/directory_utils.dart';
import 'package:ente_base/models/database.dart';
import 'package:ente_configuration/base_configuration.dart';
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:logging/logging.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
import 'package:tuple/tuple.dart';
class Configuration {
class Configuration extends BaseConfiguration {
Configuration._privateConstructor();
static final Configuration instance = Configuration._privateConstructor();
static const endpoint = String.fromEnvironment(
"endpoint",
defaultValue: kDefaultProductionEndpoint,
);
static const emailKey = "email";
static const keyAttributesKey = "key_attributes";
static const lastTempFolderClearTimeKey = "last_temp_folder_clear_time";
static const keyKey = "key";
static const secretKeyKey = "secret_key";
static const authSecretKeyKey = "auth_secret_key";
static const offlineAuthSecretKey = "offline_auth_secret_key";
static const tokenKey = "token";
static const encryptedTokenKey = "encrypted_token";
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,
authSecretKeyKey,
];
final kTempFolderDeletionTimeBuffer = const Duration(days: 1).inMicroseconds;
static final _logger = Logger("Configuration");
String? _cachedToken;
late SharedPreferences _preferences;
String? _key;
String? _secretKey;
String? _authSecretKey;
String? _offlineAuthKey;
late FlutterSecureStorage _secureStorage;
late String _tempDirectory;
String? _volatilePassword;
Future<void> init() async {
@override
Future<void> init(List<EnteBaseDatabase> dbs) async {
await super.init(dbs);
_preferences = await SharedPreferences.getInstance();
sqfliteFfiInit();
_secureStorage = const FlutterSecureStorage(
iOptions: IOSOptions(
accessibility: KeychainAccessibility.first_unlock_this_device,
),
);
_tempDirectory = (await DirectoryUtils.getDirectoryForInit()).path;
final tempDirectory = io.Directory(_tempDirectory);
try {
final currentTime = DateTime.now().microsecondsSinceEpoch;
if (tempDirectory.existsSync() &&
(_preferences.getInt(lastTempFolderClearTimeKey) ?? 0) <
(currentTime - kTempFolderDeletionTimeBuffer)) {
await tempDirectory.delete(recursive: true);
await _preferences.setInt(lastTempFolderClearTimeKey, currentTime);
_logger.info("Cleared temp folder");
} else {
_logger.info("Skipping temp folder clear");
}
} catch (e) {
_logger.warning(e);
}
tempDirectory.createSync(recursive: true);
await _initOnlineAccount();
sqfliteFfiInit();
await _initOfflineAccount();
}
@@ -99,303 +40,10 @@ class Configuration {
);
}
Future<void> _initOnlineAccount() async {
if (!_preferences.containsKey(tokenKey)) {
for (final key in onlineSecureKeys) {
unawaited(
_secureStorage.delete(
key: key,
),
);
}
} else {
_key = await _secureStorage.read(
key: keyKey,
);
_secretKey = await _secureStorage.read(
key: secretKeyKey,
);
_authSecretKey = await _secureStorage.read(
key: authSecretKeyKey,
);
if (_key == null) {
await logout(autoLogout: true);
}
}
}
@override
Future<void> logout({bool autoLogout = false}) async {
await _preferences.clear();
for (String key in onlineSecureKeys) {
await _secureStorage.delete(
key: key,
);
}
await AuthenticatorDB.instance.clearTable();
_key = null;
_cachedToken = null;
_secretKey = null;
_authSecretKey = null;
Bus.instance.fire(SignedOutEvent());
}
Future<KeyGenResult> generateKey(String password) async {
// Create a master key
final masterKey = CryptoUtil.generateKey();
// Create a recovery key
final recoveryKey = CryptoUtil.generateKey();
// Encrypt master key and recovery key with each other
final encryptedMasterKey = CryptoUtil.encryptSync(masterKey, recoveryKey);
final encryptedRecoveryKey = CryptoUtil.encryptSync(recoveryKey, masterKey);
// Derive a key from the password that will be used to encrypt and
// decrypt the master key
final kekSalt = CryptoUtil.getSaltToDeriveKey();
final derivedKeyResult = await CryptoUtil.deriveSensitiveKey(
utf8.encode(password),
kekSalt,
);
final loginKey = await CryptoUtil.deriveLoginKey(derivedKeyResult.key);
// Encrypt the key with this derived key
final encryptedKeyData =
CryptoUtil.encryptSync(masterKey, derivedKeyResult.key);
// Generate a public-private keypair and encrypt the latter
final keyPair = CryptoUtil.generateKeyPair();
final encryptedSecretKeyData =
CryptoUtil.encryptSync(keyPair.secretKey.extractBytes(), masterKey);
final attributes = KeyAttributes(
CryptoUtil.bin2base64(kekSalt),
CryptoUtil.bin2base64(encryptedKeyData.encryptedData!),
CryptoUtil.bin2base64(encryptedKeyData.nonce!),
CryptoUtil.bin2base64(keyPair.publicKey),
CryptoUtil.bin2base64(encryptedSecretKeyData.encryptedData!),
CryptoUtil.bin2base64(encryptedSecretKeyData.nonce!),
derivedKeyResult.memLimit,
derivedKeyResult.opsLimit,
CryptoUtil.bin2base64(encryptedMasterKey.encryptedData!),
CryptoUtil.bin2base64(encryptedMasterKey.nonce!),
CryptoUtil.bin2base64(encryptedRecoveryKey.encryptedData!),
CryptoUtil.bin2base64(encryptedRecoveryKey.nonce!),
);
final privateAttributes = PrivateKeyAttributes(
CryptoUtil.bin2base64(masterKey),
CryptoUtil.bin2hex(recoveryKey),
CryptoUtil.bin2base64(keyPair.secretKey.extractBytes()),
);
return KeyGenResult(attributes, privateAttributes, loginKey);
}
Future<Tuple2<KeyAttributes, Uint8List>> getAttributesForNewPassword(
String password,
) async {
// Get master key
final masterKey = getKey();
// Derive a key from the password that will be used to encrypt and
// decrypt the master key
final kekSalt = CryptoUtil.getSaltToDeriveKey();
final derivedKeyResult = await CryptoUtil.deriveSensitiveKey(
utf8.encode(password),
kekSalt,
);
final loginKey = await CryptoUtil.deriveLoginKey(derivedKeyResult.key);
// Encrypt the key with this derived key
final encryptedKeyData =
CryptoUtil.encryptSync(masterKey!, derivedKeyResult.key);
final existingAttributes = getKeyAttributes();
final updatedAttributes = existingAttributes!.copyWith(
kekSalt: CryptoUtil.bin2base64(kekSalt),
encryptedKey: CryptoUtil.bin2base64(encryptedKeyData.encryptedData!),
keyDecryptionNonce: CryptoUtil.bin2base64(encryptedKeyData.nonce!),
memLimit: derivedKeyResult.memLimit,
opsLimit: derivedKeyResult.opsLimit,
);
return Tuple2(updatedAttributes, loginKey);
}
// decryptSecretsAndGetLoginKey decrypts the master key and recovery key
// with the given password and save them in local secure storage.
// This method also returns the keyEncKey that can be used for performing
// SRP setup for existing users.
Future<Uint8List> decryptSecretsAndGetKeyEncKey(
String password,
KeyAttributes attributes, {
Uint8List? keyEncryptionKey,
}) async {
_logger.info('Start decryptAndSaveSecrets');
keyEncryptionKey ??= await CryptoUtil.deriveKey(
utf8.encode(password),
CryptoUtil.base642bin(attributes.kekSalt),
attributes.memLimit,
attributes.opsLimit,
);
_logger.info('user-key done');
Uint8List key;
try {
key = CryptoUtil.decryptSync(
CryptoUtil.base642bin(attributes.encryptedKey),
keyEncryptionKey,
CryptoUtil.base642bin(attributes.keyDecryptionNonce),
);
} catch (e) {
_logger.severe('master-key failed, incorrect password?', e);
throw Exception("Incorrect password");
}
_logger.info("master-key done");
await setKey(CryptoUtil.bin2base64(key));
final secretKey = CryptoUtil.decryptSync(
CryptoUtil.base642bin(attributes.encryptedSecretKey),
key,
CryptoUtil.base642bin(attributes.secretKeyDecryptionNonce),
);
_logger.info("secret-key done");
await setSecretKey(CryptoUtil.bin2base64(secretKey));
final token = CryptoUtil.openSealSync(
CryptoUtil.base642bin(getEncryptedToken()!),
CryptoUtil.base642bin(attributes.publicKey),
secretKey,
);
_logger.info('appToken done');
await setToken(
CryptoUtil.bin2base64(token, urlSafe: true),
);
return keyEncryptionKey;
}
Future<void> recover(String recoveryKey) async {
// check if user has entered mnemonic code
if (recoveryKey.contains(' ')) {
final split = recoveryKey.split(' ');
if (split.length != mnemonicKeyWordCount) {
String wordThatIsFollowedByEmptySpaceInSplit = '';
for (int i = 0; i < split.length; i++) {
String word = split[i];
if (word.isEmpty) {
wordThatIsFollowedByEmptySpaceInSplit =
'\n\nExtra space after word at position $i';
break;
}
}
throw AssertionError(
'\nRecovery code should have $mnemonicKeyWordCount words, '
'found ${split.length} words instead.$wordThatIsFollowedByEmptySpaceInSplit',
);
}
recoveryKey = bip39.mnemonicToEntropy(recoveryKey);
}
final attributes = getKeyAttributes();
Uint8List masterKey;
try {
masterKey = await CryptoUtil.decrypt(
CryptoUtil.base642bin(attributes!.masterKeyEncryptedWithRecoveryKey),
CryptoUtil.hex2bin(recoveryKey),
CryptoUtil.base642bin(attributes.masterKeyDecryptionNonce),
);
} catch (e) {
_logger.severe(e);
rethrow;
}
await setKey(CryptoUtil.bin2base64(masterKey));
final secretKey = CryptoUtil.decryptSync(
CryptoUtil.base642bin(attributes.encryptedSecretKey),
masterKey,
CryptoUtil.base642bin(attributes.secretKeyDecryptionNonce),
);
await setSecretKey(CryptoUtil.bin2base64(secretKey));
final token = CryptoUtil.openSealSync(
CryptoUtil.base642bin(getEncryptedToken()!),
CryptoUtil.base642bin(attributes.publicKey),
secretKey,
);
await setToken(
CryptoUtil.bin2base64(token, urlSafe: true),
);
}
String getHttpEndpoint() {
return _preferences.getString(endPointKey) ?? endpoint;
}
Future<void> setHttpEndpoint(String endpoint) async {
await _preferences.setString(endPointKey, endpoint);
Bus.instance.fire(EndpointUpdatedEvent());
}
String? getToken() {
_cachedToken ??= _preferences.getString(tokenKey);
return _cachedToken;
}
bool isLoggedIn() {
return getToken() != null;
}
Future<void> setToken(String token) async {
_cachedToken = token;
await _preferences.setString(tokenKey, token);
Bus.instance.fire(SignedInEvent());
}
Future<void> setEncryptedToken(String encryptedToken) async {
await _preferences.setString(encryptedTokenKey, encryptedToken);
}
String? getEncryptedToken() {
return _preferences.getString(encryptedTokenKey);
}
String? getEmail() {
return _preferences.getString(emailKey);
}
Future<void> setEmail(String email) async {
await _preferences.setString(emailKey, email);
}
int? getUserID() {
return _preferences.getInt(userIDKey);
}
Future<void> setUserID(int userID) async {
await _preferences.setInt(userIDKey, userID);
}
Future<void> setKeyAttributes(KeyAttributes attributes) async {
await _preferences.setString(keyAttributesKey, attributes.toJson());
}
KeyAttributes? getKeyAttributes() {
final jsonValue = _preferences.getString(keyAttributesKey);
if (jsonValue == null) {
return null;
} else {
return KeyAttributes.fromJson(jsonValue);
}
}
Future<void> setKey(String key) async {
_key = key;
await _secureStorage.write(
key: keyKey,
value: key,
);
}
Future<void> setSecretKey(String? secretKey) async {
_secretKey = secretKey;
await _secureStorage.write(
key: secretKeyKey,
value: secretKey,
);
await super.logout();
}
Future<void> setAuthSecretKey(String? authSecretKey) async {
@@ -406,14 +54,6 @@ class Configuration {
);
}
Uint8List? getKey() {
return _key == null ? null : CryptoUtil.base642bin(_key!);
}
Uint8List? getSecretKey() {
return _secretKey == null ? null : CryptoUtil.base642bin(_secretKey!);
}
Uint8List? getAuthSecretKey() {
return _authSecretKey == null
? null
@@ -426,24 +66,6 @@ class Configuration {
: CryptoUtil.base642bin(_offlineAuthKey!);
}
Uint8List getRecoveryKey() {
final keyAttributes = getKeyAttributes()!;
return CryptoUtil.decryptSync(
CryptoUtil.base642bin(keyAttributes.recoveryKeyEncryptedWithMasterKey),
getKey()!,
CryptoUtil.base642bin(keyAttributes.recoveryKeyDecryptionNonce),
);
}
// Caution: This directory is cleared on app start
String getTempDirectory() {
return _tempDirectory;
}
bool hasConfiguredAccount() {
return getToken() != null && _key != null;
}
bool hasOptedForOfflineMode() {
return _preferences.getBool(hasOptedForOfflineModeKey) ?? false;
}
@@ -464,16 +86,4 @@ class Configuration {
}
await _preferences.setBool(hasOptedForOfflineModeKey, true);
}
void setVolatilePassword(String volatilePassword) {
_volatilePassword = volatilePassword;
}
void resetVolatilePassword() {
_volatilePassword = null;
}
String? getVolatilePassword() {
return _volatilePassword;
}
}

View File

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

View File

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

View File

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

View File

@@ -1,5 +0,0 @@
import 'package:ente_auth/events/event.dart';
// NotificationEvent event is used to re-fresh the UI to show latest notification
// (if any)
class NotificationEvent extends Event {}

View File

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

View File

@@ -1,3 +1 @@
import 'package:ente_auth/events/event.dart';
class SignedOutEvent extends Event {}
// TODO Implement this library.

View File

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

View File

@@ -1,8 +1,8 @@
import 'package:dio/dio.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';
import 'package:ente_network/network.dart';
class AuthenticatorGateway {
late Dio _enteDio;

View File

@@ -111,6 +111,7 @@
"importAegisGuide": "Použijte možnost \"Export the vault\" v nastavení aplikace Aegis.",
"import2FasGuide": "Použijte možnost \"Settings->Backup -Export\" v 2FA.\n\nPokud je Vaše záloha šifrovaná, budete muset zadat heslo pro její odemčení",
"importLastpassGuide": "V nastavení aplikace Lastpass Authenticator vyberte možnost \"Transfer accounts\" a poté \"Export accounts to file\". Vygenerovaný soubor JSON následně nahrajte sem.",
"importProtonAuthGuide": "K exportu kódů použijte možnost „Exportovat“ v nastavení aplikace Proton Authenticator.",
"exportCodes": "Exportovat kódy",
"importLabel": "Importovat",
"importInstruction": "Vyberte, prosím, soubor obsahující seznam Vašich kódů v následujícím formátu",
@@ -124,6 +125,7 @@
"authToChangeYourEmail": "Pro změnu svého e-mailu se, prosím, ověřte",
"authToChangeYourPassword": "Pro změnu svého hesla se, prosím, ověřte",
"authToViewSecrets": "Pro zobrazení svých tajných údajů se musíte ověřit",
"authToInitiateSignIn": "Proveďte ověření a přihlaste se k zálohování.",
"ok": "Ok",
"cancel": "Zrušit",
"yes": "Ano",
@@ -171,6 +173,7 @@
"invalidQRCode": "Neplatný QR kód",
"noRecoveryKeyTitle": "Nemáte obnovovací klíč?",
"enterEmailHint": "Zadejte svou e-mailovou adresu",
"enterNewEmailHint": "Zadejte svou novou e-mailovou adresu",
"invalidEmailTitle": "Neplatná e-mailová adresa",
"invalidEmailMessage": "Prosím, zadejte platnou e-mailovou adresu.",
"deleteAccount": "Odstranit účet",
@@ -509,6 +512,19 @@
"supportEnte": "Podpořte <bold-green>ente</bold-green>",
"giveUsAStarOnGithub": "Dejte nám hvězdu na Githubu",
"free5GB": "5GB zdarma na <bold-green>ente</bold-green> Fotky",
"loginWithAuthAccount": "Přihlaste se pomocí svého účtu Auth",
"freeStorageOffer": "10% sleva na <bold-green>ente</bold-green> fotky",
"freeStorageOfferDescription": "Použijte kód \"AUTH\" pro získání 10% slevy na první rok"
"freeStorageOfferDescription": "Použijte kód \"AUTH\" pro získání 10% slevy na první rok",
"advanced": "Pokročilé",
"algorithm": "Algoritmus",
"type": "Typ",
"period": "Období",
"digits": "Digitální",
"importFromGallery": "Importovat z galerie",
"errorCouldNotReadImage": "Nelze přečíst vybraný obrazový soubor.",
"errorInvalidQRCode": "Neplatný QR kód",
"errorInvalidQRCodeBody": "Naskenovaný QR kód není platným účtem 2FA.",
"errorNoQRCode": "Nenalezen žádný QR kód",
"errorGenericTitle": "Došlo k chybě",
"errorGenericBody": "Při importu došlo k neočekávané chybě."
}

View File

@@ -111,6 +111,7 @@
"importAegisGuide": "Verwenden Sie die Option \"Tresor exportieren\" in den Einstellungen von Aegis.\n\nFalls Ihr Tresor verschlüsselt ist, müssen Sie das Passwort für den Tresor eingeben, um ihn zu entschlüsseln.",
"import2FasGuide": "Verwenden Sie unter \"Einstellungen → Backup\" die Option \"Exportieren\" in 2FAS.\n\nFalls Ihr Backup verschlüsselt ist, müssen Sie das Passwort eingeben, um das Backup zu entschlüsseln.",
"importLastpassGuide": "Verwenden Sie die Option \"Konten übertragen → Konten in Datei exportieren\" in den Lastpass Authenticator Einstellungen. \nImportieren Sie anschließend die heruntergeladene JSON-Datei.",
"importProtonAuthGuide": "Verwenden Sie die Option \"Exportieren\" in den Proton Authenticator Settings um Ihre Codes zu exportieren.",
"exportCodes": "Codes exportieren",
"importLabel": "Importieren",
"importInstruction": "Bitte wählen Sie eine Datei die Codes in folgendem Format beinhaltet",
@@ -519,5 +520,12 @@
"algorithm": "Algorithmus",
"type": "Typ",
"period": "Periode",
"digits": "Ziffern"
"digits": "Ziffern",
"importFromGallery": "Aus Galerie importieren",
"errorCouldNotReadImage": "Die ausgewählte Bild-Datei konnte nicht verarbeitet werden.",
"errorInvalidQRCode": "Ungültiger QR-Code",
"errorInvalidQRCodeBody": "Der gescannte QR-Code ist kein gültiges 2FA-Konto.",
"errorNoQRCode": "Kein QR-Code gefunden",
"errorGenericTitle": "Ein Fehler ist aufgetreten",
"errorGenericBody": "Beim Importieren ist ein unerwarteter Fehler aufgetreten."
}

View File

@@ -88,6 +88,8 @@
"useRecoveryKey": "Χρήση κλειδιού ανάκτησης",
"incorrectPasswordTitle": "Λάθος κωδικός πρόσβασης",
"welcomeBack": "Καλωσορίσατε και πάλι!",
"emailAlreadyRegistered": "Το email είναι ήδη καταχωρημένο.",
"emailNotRegistered": "Το email δεν έχει καταχωρηθεί.",
"madeWithLoveAtPrefix": "φτιαγμένη με ❤️ στο ",
"supportDevs": "Εγγραφείτε στο <bold-green>ente</bold-green> για να μας υποστηρίξετε",
"supportDiscount": "Χρησιμοποιήστε τον κωδικό κουπονιού \"AUTH\" για να λάβετε 10% έκπτωση για τον πρώτο χρόνο",
@@ -171,6 +173,7 @@
"invalidQRCode": "Μη έγκυρος κωδικός QR",
"noRecoveryKeyTitle": "Χωρίς κλειδί ανάκτησης;",
"enterEmailHint": "Εισάγετε τη διεύθυνση email σας",
"enterNewEmailHint": "Εισάγετε την διεύθυνση ηλ. ταχυδρομείου σας",
"invalidEmailTitle": "Μη έγκυρη διεύθυνση email",
"invalidEmailMessage": "Παρακαλούμε εισάγετε μια έγκυρη διεύθυνση email.",
"deleteAccount": "Διαγραφή λογαριασμού",
@@ -258,6 +261,10 @@
"areYouSureYouWantToLogout": "Είστε σίγουροι ότι θέλετε να αποσυνδεθείτε;",
"yesLogout": "Ναι, αποσύνδεση",
"exit": "Εξοδος",
"theme": "Θέμα",
"lightTheme": "Φωτεινό",
"darkTheme": "Σκοτεινό",
"systemTheme": "Σύστημα",
"verifyingRecoveryKey": "Επαλήθευση κλειδιού ανάκτησης...",
"recoveryKeyVerified": "Το κλειδί ανάκτησης επαληθεύτηκε",
"recoveryKeySuccessBody": "Τέλεια! Το κλειδί ανάκτησης σας είναι έγκυρο. Σας ευχαριστούμε για την επαλήθευση.\n\nΠαρακαλώ θυμηθείτε να κρατήσετε το κλειδί ανάκτησης σας και σε αντίγραφο ασφαλείας.",
@@ -490,5 +497,24 @@
"appLockNotEnabled": "Το κλείδωμα εφαρμογής δεν είναι ενεργοποιημένο",
"appLockNotEnabledDescription": "Παρακαλώ ενεργοποιήστε το κλείδωμα εφαρμογής μέσω της επιλογής Ασφάλεια > Κλείδωμα εφαρμογής",
"authToViewPasskey": "Παρακαλώ πιστοποιηθείτε για να δείτε το κλειδί πρόσβασης",
"appLockOfflineModeWarning": "Έχετε επιλέξει να προχωρήσετε χωρίς αντίγραφα ασφαλείας. Αν ξεχάσετε τον κωδικό της εφαρμογής, θα κλειδωθείτε από την πρόσβαση στα δεδομένα σας."
"appLockOfflineModeWarning": "Έχετε επιλέξει να προχωρήσετε χωρίς αντίγραφα ασφαλείας. Αν ξεχάσετε τον κωδικό της εφαρμογής, θα κλειδωθείτε από την πρόσβαση στα δεδομένα σας.",
"duplicateCodes": "Διπλότυποι κωδικοί",
"noDuplicates": "✨ Δεν υπάρχουν διπλότυπα",
"youveNoDuplicateCodesThatCanBeCleared": "Δεν υπάρχουν διπλότυπα αρχεία που μπορούν να εκκαθαριστούν",
"deduplicateCodes": "Διπλότυποι κωδικοί",
"deselectAll": "Αποεπιλογή όλων",
"selectAll": "Επιλογή όλων",
"deleteDuplicates": "Διαγραφή διπλότυπων",
"plainHTML": "Απλό HTML",
"dropReviewiOS": "Αφήστε μια κριτική στο App Store",
"dropReviewAndroid": "Αφήστε μια κριτική στο Play Store",
"giveUsAStarOnGithub": "Δώστε μας ένα αστέρι στο Github",
"free5GB": "5GB δωρεάν στο <bold-green>ente</bold-green> Photos",
"freeStorageOffer": "10% έκπτωση στο <bold-green>ente</bold-green> photos",
"freeStorageOfferDescription": "Χρησιμοποιήστε τον κωδικό \"AUTH\" για να λάβετε 10% έκπτωση για τον πρώτο χρόνο",
"advanced": "Για προχωρημένους",
"algorithm": "Αλγόριθμος",
"type": "Τύπος",
"period": "Περίοδος",
"digits": "Ψηφία"
}

View File

@@ -11,6 +11,7 @@
"setupFirstAccount": "Setup your first account",
"importScanQrCode": "Scan a QR Code",
"qrCode": "QR Code",
"qr": "QR",
"importEnterSetupKey": "Enter a setup key",
"importAccountPageTitle": "Enter account details",
"secretCanNotBeEmpty": "Secret can not be empty",
@@ -111,6 +112,7 @@
"importAegisGuide": "Use the \"Export the vault\" option in Aegis's Settings.\n\nIf your vault is encrypted, you will need to enter vault password to decrypt the vault.",
"import2FasGuide": "Use the \"Settings->Backup -Export\" option in 2FAS.\n\nIf your backup is encrypted, you will need to enter the password to decrypt the backup",
"importLastpassGuide": "Use the \"Transfer accounts\" option within Lastpass Authenticator Settings and press \"Export accounts to file\". Import the JSON downloaded.",
"importProtonAuthGuide": "Use the \"Export\" option in Proton Authenticator Settings to export your codes.",
"exportCodes": "Export codes",
"importLabel": "Import",
"importInstruction": "Please select a file that contains a list of your codes in the following format",
@@ -138,6 +140,7 @@
"existingUser": "Existing User",
"newUser": "New to Ente",
"delete": "Delete",
"addTag": "Add tag",
"enterYourPasswordHint": "Enter your password",
"forgotPassword": "Forgot password",
"oops": "Oops",
@@ -519,5 +522,35 @@
"algorithm": "Algorithm",
"type": "Type",
"period": "Period",
"digits": "Digits"
"digits": "Digits",
"importFromGallery": "Import from gallery",
"errorCouldNotReadImage": "Could not read the selected image file.",
"errorInvalidQRCode": "Invalid QR Code",
"errorInvalidQRCodeBody": "The scanned QR code is not a valid 2FA account.",
"errorNoQRCode": "No QR code found",
"errorGenericTitle": "An Error Occurred",
"errorGenericBody": "An unexpected error occurred while importing.",
"localBackupSettingsTitle": "Local backup",
"localBackupSidebarTitle": "Local backup",
"enableAutomaticBackups": "Enable automatic backups",
"backupDescription": "This will automatically backup your data to an on-device location. Backups are updated whenever entries are added, edited or deleted",
"currentLocation": "Current backup location:",
"securityNotice": "Security notice",
"backupSecurityNotice": "This encrypted backup holds your 2FA keys. If lost, you may not be able to recover your accounts. Keep it safe!",
"locationUpdatedAndBackupCreated": "Location updated and initial backup created!",
"initialBackupCreated": "Initial backup created!",
"passwordTooShort": "Password must be at least 8 characters long.",
"noDefaultBackupFolder": "Could not create default backup folder.",
"backupLocationChoiceDescription": "Where do you want to save your backups?",
"chooseBackupLocation": "Choose a backup location",
"loadDefaultLocation": "Loading default location...",
"couldNotDetermineLocation":"Could not determine location...",
"saveAction":"Save",
"saveBackup":"Save backup",
"changeLocation": "Change location",
"changeCurrentLocation": "Change current location",
"done": "Done",
"addNew": "Add new",
"selected": "selected",
"moveMultipleToTrashMessage": "Are you sure you want to move {count} item(s) to the trash?"
}

View File

@@ -111,6 +111,7 @@
"importAegisGuide": "Utilisez l'option \"Exporter le coffre-fort\" dans les paramètres d'Aegis.\n\nSi votre coffre-fort est crypté, vous devrez saisir le mot de passe du coffre-fort pour déchiffrer le coffre-fort.",
"import2FasGuide": "Utilisez l'option \"Paramètres->Sauvegarde -Export\" dans 2FAS.\n\nSi votre sauvegarde est chiffrée, vous devrez entrer le mot de passe pour déchiffrer la sauvegarde",
"importLastpassGuide": "Utilisez l'option \"Transférer des comptes\" dans les paramètres de l'authentificateur Lastpass et appuyez sur \"Exporter des comptes vers un fichier\". Importez le JSON téléchargé.",
"importProtonAuthGuide": "Utilisez l'option \"Export\" dans les paramètres de Proton Authenticator pour exporter vos codes.",
"exportCodes": "Exporter les codes",
"importLabel": "Importer",
"importInstruction": "Veuillez sélectionner un fichier qui contient une liste de vos codes dans le format suivant",
@@ -519,5 +520,12 @@
"algorithm": "Algorithme",
"type": "Type",
"period": "Période",
"digits": "Chiffres"
"digits": "Chiffres",
"importFromGallery": "Importer depuis la galerie",
"errorCouldNotReadImage": "Impossible de lire le fichier sélectionné.",
"errorInvalidQRCode": "QR Code invalide",
"errorInvalidQRCodeBody": "Le code QR scanné n'est pas un compte 2FA valide.",
"errorNoQRCode": "Aucun code QR trouvé",
"errorGenericTitle": "Une erreur s'est produite",
"errorGenericBody": "Une erreur inattendue est survenue lors de l'importation."
}

View File

@@ -111,6 +111,7 @@
"importAegisGuide": "Naudokite „Aegis“ nustatymuose esančią parinktį Eksportuoti slėptuvę.\n\nJei jūsų saugykla užšifruota, turėsite įvesti saugyklos slaptažodį, kad iššifruotumėte saugyklą.",
"import2FasGuide": "Naudokite programoje 2FAS esančią parinktį „Settings->2FAS Backup->Export to file“.\n\nJei atsarginė kopija užšifruota, turėsite įvesti slaptažodį, kad iššifruotumėte atsarginę kopiją.",
"importLastpassGuide": "Naudokite „Lastpass Authenticator“ nustatymuose esančią parinktį „Transfer accounts“ (perkelti paskyras) ir paspauskite „Export accounts to file“ (eksportuoti paskyras į failą). Importuokite atsisiųstą JSON failą.",
"importProtonAuthGuide": "Naudokite „Proton Authenticator“ nustatymuose esančią parinktį „Export“ (eksportuoti), kad eksportuotumėte savo kodus.",
"exportCodes": "Eksportuoti kodus",
"importLabel": "Importuoti",
"importInstruction": "Pasirinkite failą, kuriame yra tokio formato jūsų kodų sąrašas",
@@ -519,5 +520,12 @@
"algorithm": "Algoritmas",
"type": "Tipas",
"period": "Laikotarpis",
"digits": "Skaitmenys"
"digits": "Skaitmenys",
"importFromGallery": "Importuoti iš galerijos",
"errorCouldNotReadImage": "Nepavyko perskaityti pasirinkto vaizdo failo.",
"errorInvalidQRCode": "Netinkamas QR kodas",
"errorInvalidQRCodeBody": "Nuskenuotas QR kodas nėra tinkama 2FA paskyra.",
"errorNoQRCode": "QR kodas nerastas.",
"errorGenericTitle": "Įvyko klaida",
"errorGenericBody": "Importuojant įvyko netikėta klaida."
}

View File

@@ -45,7 +45,7 @@
"timeBasedKeyType": "Oparte na czasie (TOTP)",
"counterBasedKeyType": "Oparte na liczniku (HOTP)",
"saveAction": "Zapisz",
"nextTotpTitle": "następny",
"nextTotpTitle": "dalej",
"deleteCodeTitle": "Usunąć kod?",
"deleteCodeMessage": "Czy na pewno chcesz usunąć ten kod? Ta akcja jest nieodwracalna.",
"trashCode": "Przenieść kod do kosza?",
@@ -111,6 +111,7 @@
"importAegisGuide": "Użyj opcji \"Eksportuj sejf\" w ustawieniach Aegis.\n\nJeśli twój sejf jest zaszyfrowany, musisz wprowadzić hasło sejfu, aby odszyfrować sejf.",
"import2FasGuide": "Użyj opcji \"Ustawienia->Kopia Zapasowa-Eksport\" w 2FAS.\n\nJeśli twoja kopia zapasowa jest zaszyfrowana, musisz wprowadzić hasło, aby odszyfrować kopię zapasową",
"importLastpassGuide": "Użyj opcji \"Przenieś konta\" w Ustawieniach Lastpass Authenticator i naciśnij \"Eksportuj konta do pliku\". Zaimportuj pobrany plik JSON.",
"importProtonAuthGuide": "Użyj opcji „Eksportuj” w ustawieniach Proton Authenticator, aby wyeksportować kody.",
"exportCodes": "Eksportuj kody",
"importLabel": "Importuj",
"importInstruction": "Wybierz plik, który zawiera listę twoich kodów w następującym formacie",
@@ -519,5 +520,12 @@
"algorithm": "Algorytm",
"type": "Rodzaj",
"period": "Okres",
"digits": "Cyfry"
"digits": "Cyfry",
"importFromGallery": "Importuj z galerii",
"errorCouldNotReadImage": "Nie można odczytać wybranego pliku obrazu.",
"errorInvalidQRCode": "Nieprawidłowy kod QR",
"errorInvalidQRCodeBody": "Zeskanowany kod QR nie wskazuje na prawidłowe konto 2FA.",
"errorNoQRCode": "Nie znaleziono kodu QR",
"errorGenericTitle": "Wystąpił błąd",
"errorGenericBody": "Podczas importowania wystąpił nieoczekiwany błąd."
}

View File

@@ -18,7 +18,7 @@
"incorrectDetails": "Felaktiga uppgifter",
"pleaseVerifyDetails": "Kontrollera dina detaljer och försök igen",
"codeIssuerHint": "Utfärdare",
"codeSecretKeyHint": "Secret Key",
"codeSecretKeyHint": "Hemlig nyckel",
"secret": "Säkerhetsnyckel",
"all": "Alla",
"notes": "Anteckningar",
@@ -33,7 +33,7 @@
}
}
},
"codeAccountHint": "Konto (du@domän.com)",
"codeAccountHint": "Konto (du@domain.com)",
"codeTagHint": "Tagg",
"accountKeyType": "Typ av nyckel",
"sessionExpired": "Sessionen har gått ut",
@@ -68,7 +68,7 @@
"reportABug": "Rapportera en bugg",
"crashAndErrorReporting": "Krasch och felrapportering",
"reportBug": "Rapportera bugg",
"emailUsMessage": "Skicka e-mail till {email}",
"emailUsMessage": "Skicka e-post till {email}",
"@emailUsMessage": {
"placeholders": {
"email": {
@@ -79,7 +79,7 @@
"contactSupport": "Kontakta support",
"rateUsOnStore": "Betygsätt på {storeName}",
"blog": "Blogg",
"merchandise": "Merchandise",
"merchandise": "Produkter",
"verifyPassword": "Bekräfta lösenord",
"pleaseWait": "Vänligen vänta...",
"generatingEncryptionKeysTitle": "Skapar krypteringsnycklar...",
@@ -104,13 +104,14 @@
"importFromApp": "Importera koder från {appName}",
"importGoogleAuthGuide": "Exportera dina konton från Google Authenticator till en QR-kod med alternativet \"Överföra konton\". Använd sedan en annan enhet och skanna QR-koden.\n\nTips: Du kan använda din bärbara dators webbkamera för att ta en bild av QR-koden.",
"importSelectJsonFile": "Välj JSON-fil",
"importSelectAppExport": "Välj {appName} exportfil",
"importSelectAppExport": "Välj {appName} exporteringsfil",
"importEnteEncGuide": "Välj den krypterade JSON-filen som exporteras från Ente",
"importRaivoGuide": "Använd alternativet \"Exportera OTPs till zip-arkiv\" i Raivos inställningar.\n\nExtrahera zip-filen och importera JSON-filen.",
"importBitwardenGuide": "Använd alternativet \"Exportera valv\" inom Bitwarden Tools och importera den okrypterade JSON-filen.",
"importAegisGuide": "Använd alternativet \"Exportera valvet\" i Aegis inställningar.\n\nOm ditt valv är krypterat måste du ange valvlösenordet för att dekryptera valvet.",
"import2FasGuide": "Använd alternativet \"Inställningar->Säkerhetskopiera -Exportera\" i 2FAS.\n\nOm din säkerhetskopia är krypterad måste du ange lösenordet för att dekryptera säkerhetskopian.",
"importLastpassGuide": "Använd alternativet \"Överför konton\" i LastPass Authenticators inställningar och tryck på \"Exportera konton till fil\". Importera JSON-filen som laddas ner.",
"importProtonAuthGuide": "Använd alternativet \"Exportera\" i Proton Authenticator-inställningarna för att exportera koder.",
"exportCodes": "Exportera koder",
"importLabel": "Importera",
"importInstruction": "Vänligen välj en fil som innehåller en lista över dina koder i följande format",
@@ -119,11 +120,11 @@
"emailVerificationToggle": "E-postverifiering",
"emailVerificationEnableWarning": "För att undvika att bli låst från ditt konto, se till att spara en kopia av din e-post 2FA utanför Ente Auth innan du aktiverar e-postverifiering.",
"authToChangeEmailVerificationSetting": "Autentisera för att ändra din e-postadress",
"authenticateGeneric": "Var god autentisera",
"authenticateGeneric": "Vänligen autentisera",
"authToViewYourRecoveryKey": "Autentisera för att visa din återställningsnyckel",
"authToChangeYourEmail": "Autentisera för att ändra din e-postadress",
"authToChangeYourPassword": "Autentisera för att ändra ditt lösenord",
"authToViewSecrets": "Autentisera för att visa din återställningsnyckel",
"authToViewSecrets": "Vänligen autentisera för att visa din återställningsnyckel",
"authToInitiateSignIn": "Vänligen autentisera för att initiera inloggning för säkerhetskopiering.",
"ok": "OK",
"cancel": "Avbryt",
@@ -147,7 +148,7 @@
"leaveFamily": "Lämna familjen",
"leaveFamilyMessage": "Är du säker på att du vill lämna familjeplanen?",
"inFamilyPlanMessage": "Du är på en familjeplan!",
"hintForMobile": "Håll i på en kod för att redigera eller ta bort.",
"hintForMobile": "Tryck länge på en kod för att redigera eller ta bort.",
"hintForDesktop": "Högerklicka på en kod för att redigera eller ta bort.",
"scan": "Skanna",
"scanACode": "Skanna kod",
@@ -191,7 +192,7 @@
"oopsSomethingWentWrong": "Hoppsan! Något gick fel.",
"selectLanguage": "Välj språk",
"language": "Språk",
"social": "Social",
"social": "Socialt",
"security": "Säkerhet",
"lockscreen": "Låsskärm",
"authToChangeLockscreenSetting": "Vänligen autentisera för att ändra låsskärms inställningar",
@@ -200,7 +201,7 @@
"authToViewYourActiveSessions": "Autentisera för att visa dina aktiva sessioner",
"searchHint": "Sök...",
"search": "Sök",
"sorryUnableToGenCode": "Tyvärr, det gick inte att generera en kod för {issuerName}",
"sorryUnableToGenCode": "Tyvärr, kunde inte generera en kod för {issuerName}",
"noResult": "Inga resultat",
"addCode": "Lägg till kod",
"scanAQrCode": "Skanna en QR-kod",
@@ -215,7 +216,7 @@
"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.",
"recoveryKeySaveDescription": "Vi lagrar inte och har därför inte åtkomst till denna nyckel, vänligen spara denna 24 ords nyckel på en säker plats.",
"recoveryKeySaveDescription": "Vi lagrar inte och har därför inte åtkomst till denna nyckel, vänligen spara denna 24 ordsnyckeln på en säker plats.",
"doThisLater": "Gör detta senare",
"saveKey": "Spara nyckel",
"save": "Spara",
@@ -254,7 +255,7 @@
"insecureDevice": "Osäker enhet",
"sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "Tyvärr, kunde vi inte generera säkra nycklar på den här enheten.\n\nvänligen registrera dig från en annan enhet.",
"howItWorks": "Så här fungerar det",
"ackPasswordLostWarning": "Jag förstår att om jag förlorar mitt lösenord kan jag förlora mina data eftersom min data är <underline>end-to-end-krypterad</underline>.",
"ackPasswordLostWarning": "Jag förstår att om jag förlorar mitt lösenord kan jag förlora mina data eftersom min data är <underline>totalsträckskrypterad</underline>.",
"loginTerms": "Jag samtycker till <u-terms>användarvillkoren</u-terms> och <u-policy>integritetspolicyn</u-policy>",
"logInLabel": "Logga in",
"logout": "Logga ut",
@@ -278,7 +279,7 @@
"recoveryKeyVerifyReason": "Din återställningsnyckel är det enda sättet att återställa dina foton om du glömmer ditt lösenord. Du hittar din återställningsnyckel i Inställningar > Säkerhet.\n\nAnge din återställningsnyckel här för att verifiera att du har sparat den ordentligt.",
"confirmYourRecoveryKey": "Bekräfta din återställningsnyckel",
"confirm": "Bekräfta",
"emailYourLogs": "Maila dina loggar",
"emailYourLogs": "E-posta dina loggar",
"pleaseSendTheLogsTo": "Vänligen skicka loggarna till \n{toEmail}",
"copyEmailAddress": "Kopiera e-postadress",
"exportLogs": "Exportera loggar",
@@ -297,7 +298,7 @@
"criticalUpdateAvailable": "Kritisk uppdatering tillgänglig",
"updateAvailable": "Uppdatering tillgänglig",
"update": "Uppdatera",
"checking": "Kontrollerar ...",
"checking": "Kontrollerar...",
"youAreOnTheLatestVersion": "Du är på den senaste versionen",
"warning": "Varning",
"exportWarningDesc": "Den exporterade filen innehåller känslig information. Förvara den på ett säkert sätt.",
@@ -306,7 +307,7 @@
"description": "Text for the button to confirm the user understands the warning"
},
"authToExportCodes": "Autentisera för att exportera dina koder",
"importSuccessTitle": "Jippi!",
"importSuccessTitle": "Hurra!",
"importSuccessDesc": "Du har importerat {count} koder!",
"@importSuccessDesc": {
"placeholders": {
@@ -324,7 +325,7 @@
"checkInboxAndSpamFolder": "Vänligen kontrollera din inkorg (och skräppost) för att slutföra verifieringen",
"tapToEnterCode": "Tryck för att ange kod",
"resendEmail": "Skicka e-post igen",
"weHaveSendEmailTo": "Vi har skickat ett mail till <green>{email}</green>",
"weHaveSendEmailTo": "Vi har skickat ett e-postmeddelande till <green>{email}</green>",
"@weHaveSendEmailTo": {
"description": "Text to indicate that we have sent a mail to the user",
"placeholders": {
@@ -362,7 +363,7 @@
"selectExportFormat": "Välj exportformat",
"exportDialogDesc": "Krypterad export skyddas av ett lösenord som du väljer.",
"encrypted": "Krypterad",
"plainText": "Enkel text",
"plainText": "Oformaterad text",
"passwordToEncryptExport": "Lösenord för att kryptera export",
"export": "Exportera",
"useOffline": "Använd utan säkerhetskopior",
@@ -374,14 +375,14 @@
"compactMode": "Kompakt läge",
"shouldHideCode": "Dölj koder",
"doubleTapToViewHiddenCode": "Du kan dubbeltrycka på en post för att visa koden",
"focusOnSearchBar": "Fokusera på sök vid appstart",
"focusOnSearchBar": "Fokusera på sök vid uppstart av app",
"confirmUpdatingkey": "Är du säker på att du vill uppdatera den hemliga nyckeln?",
"minimizeAppOnCopy": "Minimera appen vid kopiering",
"editCodeAuthMessage": "Autentisera för att redigera kod",
"deleteCodeAuthMessage": "Autentisera för att radera kod",
"showQRAuthMessage": "Autentisera för att visa QR-kod",
"confirmAccountDeleteTitle": "Bekräfta radering av kontot",
"confirmAccountDeleteMessage": "Detta konto är kopplat till andra Ente apps, om du använder någon.\n\nDina uppladdade data, över alla Ente appar, kommer att schemaläggas för radering och ditt konto kommer att raderas permanent.",
"confirmAccountDeleteMessage": "Detta konto är kopplat till andra Ente applikationer, om du använder någon.\n\nDina uppladdade data, över alla Ente applikationer, kommer att schemaläggas för radering och ditt konto kommer att raderas permanent.",
"androidBiometricHint": "Verifiera identitet",
"@androidBiometricHint": {
"description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters."
@@ -390,7 +391,7 @@
"@androidBiometricNotRecognized": {
"description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters."
},
"androidBiometricSuccess": "Slutförd",
"androidBiometricSuccess": "Lyckades",
"@androidBiometricSuccess": {
"description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters."
},
@@ -441,7 +442,7 @@
"signOutOtherDevices": "Logga ut andra enheter",
"doNotSignOut": "Logga inte ut",
"hearUsWhereTitle": "Hur hörde du talas om Ente? (valfritt)",
"hearUsExplanation": "Vi spårar inte appinstallationer, Det skulle hjälpa oss om du berättade var du hittade oss!",
"hearUsExplanation": "Vi spårar inte installationer. Det skulle hjälpa oss om du berättade hur du hittade oss!",
"recoveryKeySaved": "Återställningsnyckel sparad i nedladdningsmappen!",
"waitingForBrowserRequest": "Väntar på webbläsarbegäran...",
"waitingForVerification": "Väntar på verifiering...",
@@ -488,6 +489,8 @@
"hideContent": "Dölj innehåll",
"hideContentDescriptionAndroid": "Döljer appinnehåll i app-växlaren och inaktiverar skärmdumpar",
"hideContentDescriptioniOS": "Döljer appinnehåll i app-växlaren",
"autoLockFeatureDescription": "Tid efter vilken appen låses efter att ha satts i bakgrunden",
"appLockDescription": "Välj mellan enhetens förvalda låsskärm och en anpassad låsskärm med en PIN-kod eller lösenord.",
"pinLock": "Pinkodslås",
"enterPin": "Ange PIN-kod",
"setNewPin": "Ställ in ny PIN-kod",
@@ -498,9 +501,31 @@
"appLockOfflineModeWarning": "Du har valt att fortsätta utan säkerhetskopior. Om du glömmer ditt applås, kommer du att bli utelåst från att komma åt dina data.",
"duplicateCodes": "Dubblettkoder",
"noDuplicates": "✨ Inga dubbletter",
"youveNoDuplicateCodesThatCanBeCleared": "Du har inga dubbla koder som kan rensas",
"deduplicateCodes": "Deduplicera koder",
"deselectAll": "Avmarkera alla",
"selectAll": "Markera alla",
"deleteDuplicates": "Radera dubbletter",
"plainHTML": "Ren HTML"
"plainHTML": "Ren HTML",
"tellUsWhatYouThink": "Berätta vad du tycker",
"dropReviewiOS": "Skriv en recension på App Store",
"dropReviewAndroid": "Skriv en recension på Play Store",
"supportEnte": "Stöd <bold-green>ente</bold-green>",
"giveUsAStarOnGithub": "Ge oss en stjärna på Github",
"free5GB": "5 GB gratis på <bold-green>ente</bold-green> Foton",
"loginWithAuthAccount": "Logga in med ditt Auth-konto",
"freeStorageOffer": "10% rabatt på <bold-green>ente</bold-green> foton",
"freeStorageOfferDescription": "Använd koden \"AUTH\" för att få 10% rabatt första året",
"advanced": "Avancerad",
"algorithm": "Algoritm",
"type": "Typ",
"period": "Tidsperiod",
"digits": "Siffror",
"importFromGallery": "Importera från galleri",
"errorCouldNotReadImage": "Kunde inte läsa den valda bildfilen.",
"errorInvalidQRCode": "Ogiltig QR-kod",
"errorInvalidQRCodeBody": "Den skannade QR-koden är inte ett giltigt 2FA konto.",
"errorNoQRCode": "Ingen QR-kod hittades",
"errorGenericTitle": "Ett fel inträffade",
"errorGenericBody": "Ett oväntat fel inträffade vid import."
}

View File

@@ -6,7 +6,7 @@
"@counterAppBarTitle": {
"description": "Text shown in the AppBar of the Counter Page"
},
"onBoardingBody": "妥善保管您的两步验证码",
"onBoardingBody": "妥善保管您的双重认证代码",
"onBoardingGetStarted": "开始",
"setupFirstAccount": "设置您的第一个账户",
"importScanQrCode": "扫描二维码",
@@ -111,13 +111,14 @@
"importAegisGuide": "使用 Aegis 设置中的“导出密码库”选项。\n\n如果您的密码库已加密则需要输入密码库密码才能解密密码库。",
"import2FasGuide": "使用 2FAS 中的“设置 -> 备份 -> 导出”选项。\n\n如果您的备份已加密则需要输入密码来解密备份",
"importLastpassGuide": "使用 Lastpass Authenticator 设置中的“转移账户”选项,然后按“将账户导出到文件”。导入下载的 JSON。",
"importProtonAuthGuide": "使用 Proton Authenticator 设置中的“导出”选项导出您的代码。",
"exportCodes": "导出代码",
"importLabel": "导入",
"importInstruction": "请选择一个包含以下格式的代码列表的文件",
"importCodeDelimiterInfo": "代码可以用逗号或换行符分隔",
"selectFile": "选择文件",
"emailVerificationToggle": "电子邮件验证",
"emailVerificationEnableWarning": "为避免被锁在您的账户之外,请在启用电子邮件验证之前确保在 Ente Auth 之外存储电子邮件两步验证的副本。",
"emailVerificationEnableWarning": "为避免被锁在您的账户之外,请在启用电子邮件验证之前确保在 Ente Auth 之外存储电子邮件双重认证的副本。",
"authToChangeEmailVerificationSetting": "请进行身份验证以更改电子邮件验证",
"authenticateGeneric": "请验证",
"authToViewYourRecoveryKey": "请验证以查看您的恢复密钥",
@@ -155,7 +156,7 @@
"verifyEmail": "验证电子邮件",
"enterCodeHint": "从你的身份验证器应用中\n输入6位数字代码",
"lostDeviceTitle": "丢失了设备吗?",
"twoFactorAuthTitle": "两步验证",
"twoFactorAuthTitle": "双重认证",
"passkeyAuthTitle": "通行密钥验证",
"verifyPasskey": "验证通行密钥",
"loginWithTOTP": "使用 TOTP 登录",
@@ -519,5 +520,12 @@
"algorithm": "算法",
"type": "类型",
"period": "周期",
"digits": "数字"
"digits": "数字",
"importFromGallery": "从图库导入",
"errorCouldNotReadImage": "无法读取所选图片文件。",
"errorInvalidQRCode": "二维码无效",
"errorInvalidQRCodeBody": "扫描的二维码不是有效的双重认证账户。",
"errorNoQRCode": "未找到二维码",
"errorGenericTitle": "出错了",
"errorGenericBody": "导入时发生意外错误。"
}

View File

@@ -6,4 +6,4 @@ export "package:ente_auth/l10n/arb/app_localizations.dart"
extension AppLocalizationsX on BuildContext {
AppLocalizations get l10n => AppLocalizations.of(this);
}
}

View File

@@ -2,34 +2,38 @@ import 'dart:async';
import 'dart:io';
import 'package:adaptive_theme/adaptive_theme.dart';
import 'package:ente_accounts/services/user_service.dart';
import "package:ente_auth/app/view/app.dart";
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/core/constants.dart';
import 'package:ente_auth/core/logging/super_logging.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/locale.dart';
import 'package:ente_auth/services/authenticator_service.dart';
import 'package:ente_auth/services/billing_service.dart';
import 'package:ente_auth/services/notification_service.dart';
import 'package:ente_auth/services/preference_service.dart';
import 'package:ente_auth/services/update_service.dart';
import 'package:ente_auth/services/user_service.dart';
import 'package:ente_auth/services/window_listener_service.dart';
import 'package:ente_auth/store/authenticator_db.dart';
import 'package:ente_auth/store/code_display_store.dart';
import 'package:ente_auth/store/code_store.dart';
import 'package:ente_auth/ui/tools/app_lock.dart';
import 'package:ente_auth/ui/tools/lock_screen.dart';
import 'package:ente_auth/ui/home_page.dart';
import 'package:ente_auth/ui/utils/icon_utils.dart';
import 'package:ente_auth/utils/directory_utils.dart';
import 'package:ente_auth/utils/lock_screen_settings.dart';
import 'package:ente_auth/utils/platform_util.dart';
import 'package:ente_auth/utils/window_protocol_handler.dart';
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
import 'package:ente_lock_screen/lock_screen_settings.dart';
import 'package:ente_lock_screen/ui/app_lock.dart';
import 'package:ente_lock_screen/ui/lock_screen.dart';
import 'package:ente_logging/logging.dart';
import 'package:ente_network/network.dart';
import 'package:ente_strings/l10n/strings_localizations.dart';
import 'package:ente_ui/theme/theme_config.dart';
import 'package:flutter/foundation.dart';
import "package:flutter/material.dart";
import 'package:flutter_displaymode/flutter_displaymode.dart';
import 'package:logging/logging.dart';
import 'package:path_provider/path_provider.dart';
import 'package:tray_manager/tray_manager.dart';
import 'package:window_manager/window_manager.dart';
@@ -89,7 +93,9 @@ void main() async {
}
Future<void> _runInForeground() async {
AppThemeConfig.initialize(EnteApp.auth);
final savedThemeMode = _themeMode(await AdaptiveTheme.getThemeMode());
final configuration = Configuration.instance;
return await _runWithLogs(() async {
_logger.info("Starting app in foreground");
try {
@@ -103,12 +109,19 @@ Future<void> _runInForeground() async {
runApp(
AppLock(
builder: (args) => App(locale: locale),
lockScreen: const LockScreen(),
lockScreen: LockScreen(configuration),
enabled: await LockScreenSettings.instance.shouldShowLockScreen(),
locale: locale,
lightTheme: lightThemeData,
darkTheme: darkThemeData,
savedThemeMode: savedThemeMode,
localeListResolutionCallback: localResolutionCallBack,
localizationsDelegates: const [
...StringsLocalizations.localizationsDelegates,
...AppLocalizations.localizationsDelegates,
],
supportedLocales: appSupportedLocales,
backgroundLockLatency: const Duration(seconds: 0),
),
);
});
@@ -154,13 +167,13 @@ Future<void> _init(bool bool, {String? via}) async {
await PreferenceService.instance.init();
await CodeStore.instance.init();
await CodeDisplayStore.instance.init();
await Configuration.instance.init();
await Network.instance.init();
await UserService.instance.init();
await Configuration.instance.init([AuthenticatorDB.instance]);
await Network.instance.init(Configuration.instance);
await UserService.instance.init(Configuration.instance, const HomePage());
await AuthenticatorService.instance.init();
await BillingService.instance.init();
await NotificationService.instance.init();
await UpdateService.instance.init();
await IconUtils.instance.init();
await LockScreenSettings.instance.init();
await LockScreenSettings.instance.init(Configuration.instance);
}

View File

@@ -1,65 +0,0 @@
const freeProductID = "free";
const stripe = "stripe";
const appStore = "appstore";
const playStore = "playstore";
class Subscription {
final String productID;
final int storage;
final String originalTransactionID;
final String paymentProvider;
final int expiryTime;
final String price;
final String period;
final Attributes? attributes;
Subscription({
required this.productID,
required this.storage,
required this.originalTransactionID,
required this.paymentProvider,
required this.expiryTime,
required this.price,
required this.period,
this.attributes,
});
bool isValid() {
return expiryTime > DateTime.now().microsecondsSinceEpoch;
}
bool isYearlyPlan() {
return 'year' == period;
}
static fromMap(Map<String, dynamic>? map) {
if (map == null) return null;
return Subscription(
productID: map['productID'],
storage: map['storage'],
originalTransactionID: map['originalTransactionID'],
paymentProvider: map['paymentProvider'],
expiryTime: map['expiryTime'],
price: map['price'],
period: map['period'],
attributes: map["attributes"] != null
? Attributes.fromJson(map["attributes"])
: null,
);
}
}
class Attributes {
bool? isCancelled;
String? customerID;
Attributes({
this.isCancelled,
this.customerID,
});
Attributes.fromJson(dynamic json) {
isCancelled = json["isCancelled"];
customerID = json["customerID"];
}
}

View File

@@ -1,146 +0,0 @@
import 'dart:convert';
import 'dart:math';
import 'package:collection/collection.dart';
import 'package:ente_auth/models/subscription.dart';
class UserDetails {
final String email;
final int usage;
final int fileCount;
final int sharedCollectionsCount;
final Subscription subscription;
final FamilyData? familyData;
final ProfileData? profileData;
UserDetails(
this.email,
this.usage,
this.fileCount,
this.sharedCollectionsCount,
this.subscription,
this.familyData,
this.profileData,
);
bool isPartOfFamily() {
return familyData?.members?.isNotEmpty ?? false;
}
bool isFamilyAdmin() {
assert(isPartOfFamily(), "verify user is part of family before calling");
final FamilyMember currentUserMember = familyData!.members!
.firstWhere((element) => element.email.trim() == email.trim());
return currentUserMember.isAdmin;
}
// getFamilyOrPersonalUsage will return total usage for family if user
// belong to family group. Otherwise, it will return storage consumed by
// current user
int getFamilyOrPersonalUsage() {
return isPartOfFamily() ? familyData!.getTotalUsage() : usage;
}
int getFreeStorage() {
return max(
isPartOfFamily()
? (familyData!.storage - familyData!.getTotalUsage())
: (subscription.storage - (usage)),
0,
);
}
int getTotalStorage() {
return isPartOfFamily() ? familyData!.storage : subscription.storage;
}
factory UserDetails.fromMap(Map<String, dynamic> map) {
return UserDetails(
map['email'] as String,
map['usage'] as int,
(map['fileCount'] ?? 0) as int,
(map['sharedCollectionsCount'] ?? 0) as int,
Subscription.fromMap(map['subscription']),
FamilyData.fromMap(map['familyData']),
ProfileData.fromJson(map['profileData']),
);
}
}
class FamilyMember {
final String email;
final int usage;
final String id;
final bool isAdmin;
FamilyMember(this.email, this.usage, this.id, this.isAdmin);
factory FamilyMember.fromMap(Map<String, dynamic> map) {
return FamilyMember(
(map['email'] ?? '') as String,
map['usage'] as int,
map['id'] as String,
map['isAdmin'] as bool,
);
}
}
class ProfileData {
bool canDisableEmailMFA;
bool isEmailMFAEnabled;
bool isTwoFactorEnabled;
// Constructor with default values
ProfileData({
this.canDisableEmailMFA = false,
this.isEmailMFAEnabled = false,
this.isTwoFactorEnabled = false,
});
// Factory method to create ProfileData instance from JSON
factory ProfileData.fromJson(Map<String, dynamic>? json) {
if (json == null) null;
return ProfileData(
canDisableEmailMFA: json!['canDisableEmailMFA'] ?? false,
isEmailMFAEnabled: json['isEmailMFAEnabled'] ?? false,
isTwoFactorEnabled: json['isTwoFactorEnabled'] ?? false,
);
}
// Method to convert ProfileData instance to JSON
Map<String, dynamic> toJson() {
return {
'canDisableEmailMFA': canDisableEmailMFA,
'isEmailMFAEnabled': isEmailMFAEnabled,
'isTwoFactorEnabled': isTwoFactorEnabled,
};
}
String toJsonString() => json.encode(toJson());
}
class FamilyData {
final List<FamilyMember>? members;
// Storage available based on the family plan
final int storage;
final int expiryTime;
FamilyData(this.members, this.storage, this.expiryTime);
int getTotalUsage() {
return members!.map((e) => e.usage).toList().sum;
}
static fromMap(Map<String, dynamic>? map) {
if (map == null) return null;
assert(map['members'] != null && map['members'].length >= 0);
final members = List<FamilyMember>.from(
map['members'].map((x) => FamilyMember.fromMap(x)),
);
return FamilyData(
members,
map['storage'] as int,
map['expiryTime'] as int,
);
}
}

View File

@@ -1,19 +1,18 @@
import 'dart:async';
import 'dart:io';
import 'package:ente_accounts/pages/email_entry_page.dart';
import 'package:ente_accounts/pages/login_page.dart';
import 'package:ente_accounts/pages/password_entry_page.dart';
import 'package:ente_accounts/pages/password_reentry_page.dart';
import 'package:ente_auth/app/view/app.dart';
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/core/event_bus.dart';
import 'package:ente_auth/ente_theme_data.dart';
import 'package:ente_auth/events/trigger_logout_event.dart';
import "package:ente_auth/l10n/l10n.dart";
import 'package:ente_auth/locale.dart';
import 'package:ente_auth/theme/text_style.dart';
import 'package:ente_auth/ui/account/email_entry_page.dart';
import 'package:ente_auth/ui/account/login_page.dart';
import 'package:ente_auth/ui/account/logout_dialog.dart';
import 'package:ente_auth/ui/account/password_entry_page.dart';
import 'package:ente_auth/ui/account/password_reentry_page.dart';
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';
@@ -24,6 +23,7 @@ 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';
import 'package:ente_auth/utils/toast_util.dart';
import 'package:ente_events/event_bus.dart';
import 'package:flutter/foundation.dart';
import "package:flutter/material.dart";
import 'package:local_auth/local_auth.dart';
@@ -260,17 +260,22 @@ class _OnboardingPageState extends State<OnboardingPage> {
void _navigateToSignUpPage() {
Widget page;
if (Configuration.instance.getEncryptedToken() == null) {
page = const EmailEntryPage();
page = EmailEntryPage(Configuration.instance);
} else {
// No key
if (Configuration.instance.getKeyAttributes() == null) {
// Never had a key
page = const PasswordEntryPage(
mode: PasswordEntryMode.set,
page = PasswordEntryPage(
Configuration.instance,
PasswordEntryMode.set,
const HomePage(),
);
} else if (Configuration.instance.getKey() == null) {
// Yet to decrypt the key
page = const PasswordReentryPage();
page = PasswordReentryPage(
Configuration.instance,
const HomePage(),
);
} else {
// All is well, user just has not subscribed
page = const HomePage();
@@ -288,17 +293,22 @@ class _OnboardingPageState extends State<OnboardingPage> {
void _navigateToSignInPage() {
Widget page;
if (Configuration.instance.getEncryptedToken() == null) {
page = const LoginPage();
page = LoginPage(Configuration.instance);
} else {
// No key
if (Configuration.instance.getKeyAttributes() == null) {
// Never had a key
page = const PasswordEntryPage(
mode: PasswordEntryMode.set,
page = PasswordEntryPage(
Configuration.instance,
PasswordEntryMode.set,
const HomePage(),
);
} else if (Configuration.instance.getKey() == null) {
// Yet to decrypt the key
page = const PasswordReentryPage();
page = PasswordReentryPage(
Configuration.instance,
const HomePage(),
);
} else {
// All is well, user just has not subscribed
// page = getSubscriptionPage(isOnBoarding: true);

View File

@@ -1,6 +1,5 @@
import 'dart:async';
import 'package:ente_auth/core/event_bus.dart';
import 'package:ente_auth/events/codes_updated_event.dart';
import "package:ente_auth/l10n/l10n.dart";
import 'package:ente_auth/models/all_icon_data.dart';
@@ -23,6 +22,7 @@ import 'package:ente_auth/ui/utils/icon_utils.dart';
import 'package:ente_auth/utils/dialog_util.dart';
import 'package:ente_auth/utils/toast_util.dart';
import 'package:ente_auth/utils/totp_util.dart';
import 'package:ente_events/event_bus.dart';
import "package:flutter/material.dart";
import 'package:logging/logging.dart';

View File

@@ -54,9 +54,14 @@ class ViewQrPage extends StatelessWidget {
const SizedBox(
width: 10,
),
Text(
code?.account ?? '',
style: enteTextTheme.largeBold,
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Text(
code?.account ?? '',
style: enteTextTheme.largeBold,
),
),
),
],
),
@@ -73,9 +78,14 @@ class ViewQrPage extends StatelessWidget {
const SizedBox(
width: 10,
),
Text(
code?.issuer ?? '',
style: enteTextTheme.largeBold,
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Text(
code?.issuer ?? '',
style: enteTextTheme.largeBold,
),
),
),
],
),

View File

@@ -4,9 +4,7 @@ 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/events/codes_updated_event.dart';
import 'package:ente_auth/events/signed_in_event.dart';
import 'package:ente_auth/events/trigger_logout_event.dart';
import 'package:ente_auth/gateway/authenticator.dart';
import 'package:ente_auth/models/authenticator/auth_entity.dart';
@@ -17,6 +15,8 @@ import 'package:ente_auth/services/preference_service.dart';
import 'package:ente_auth/store/authenticator_db.dart';
import 'package:ente_auth/store/offline_authenticator_db.dart';
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
import 'package:ente_events/event_bus.dart';
import 'package:ente_events/models/signed_in_event.dart';
import 'package:flutter/foundation.dart';
import 'package:logging/logging.dart';
import 'package:shared_preferences/shared_preferences.dart';

View File

@@ -1,11 +1,11 @@
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:ente_accounts/ente_accounts.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/billing_plan.dart';
import 'package:ente_auth/models/subscription.dart';
import 'package:ente_auth/models/billing_plan.dart';
import 'package:ente_network/network.dart';
import 'package:logging/logging.dart';
const kWebPaymentRedirectUrl = "https://payments.ente.io/frameRedirect";

View File

@@ -0,0 +1,155 @@
import 'dart:convert';
import 'dart:io';
import 'package:ente_auth/models/export/ente.dart';
import 'package:ente_auth/store/code_store.dart';
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:intl/intl.dart'; //for time based file naming
import 'package:logging/logging.dart';
import 'package:shared_preferences/shared_preferences.dart';
class LocalBackupService {
final _logger = Logger('LocalBackupService');
static final LocalBackupService instance =
LocalBackupService._privateConstructor();
LocalBackupService._privateConstructor();
static const int _maxBackups = 2;
// to create an encrypted backup file if the toggle is on
Future<void> triggerAutomaticBackup() async {
try {
final prefs = await SharedPreferences.getInstance();
final isEnabled = prefs.getBool('isAutoBackupEnabled') ?? false;
if (!isEnabled) {
return;
}
final backupPath = prefs.getString('autoBackupPath');
if (backupPath == null) {
return;
}
const storage = FlutterSecureStorage();
final password = await storage.read(key: 'autoBackupPassword');
if (password == null || password.isEmpty) {
_logger.warning("Automatic backup skipped: password not set.");
return;
}
_logger.info("Change detected, triggering automatic encrypted backup...");
final plainTextContent = await CodeStore.instance.getCodesForExport();
if (plainTextContent.trim().isEmpty) {
return;
}
final kekSalt = CryptoUtil.getSaltToDeriveKey();
final derivedKeyResult = await CryptoUtil.deriveSensitiveKey(
utf8.encode(password),
kekSalt,
);
final encResult = await CryptoUtil.encryptData(
utf8.encode(plainTextContent),
derivedKeyResult.key,
);
final encContent = CryptoUtil.bin2base64(encResult.encryptedData!);
final encNonce = CryptoUtil.bin2base64(encResult.header!);
final EnteAuthExport data = EnteAuthExport(
version: 1,
encryptedData: encContent,
encryptionNonce: encNonce,
kdfParams: KDFParams(
memLimit: derivedKeyResult.memLimit,
opsLimit: derivedKeyResult.opsLimit,
salt: CryptoUtil.bin2base64(kekSalt),
),
);
final encryptedJson = jsonEncode(data.toJson());
final now = DateTime.now();
final formatter = DateFormat('yyyy-MM-dd_HH-mm-ss');
final formattedDate = formatter.format(now);
final fileName = 'ente-auth-auto-backup-$formattedDate.json';
final filePath = '$backupPath/$fileName';
final backupFile = File(filePath);
await backupFile.writeAsString(encryptedJson);
await _manageOldBackups(backupPath);
_logger.info('Automatic encrypted backup successful! Saved to: $filePath');
} catch (e, s) {
_logger.severe('Silent error during automatic backup', e, s);
}
}
Future<void> _manageOldBackups(String backupPath) async {
try {
_logger.info("Checking for old backups to clean up...");
final directory = Directory(backupPath);
// fetch all filenames in the folder, filter out ente backup files
final files = directory.listSync()
.where((entity) =>
entity is File &&
entity.path.split('/').last.startsWith('ente-auth-auto-backup-'),)
.map((entity) => entity as File)
.toList();
// sort the fetched files in asc order (oldest first because the name is a timestamp)
files.sort((a, b) => a.path.compareTo(b.path));
// if we have more files than our limit, delete the oldest ones (current limit=_maxBackups)
while (files.length > _maxBackups) {
// remove the oldest file (at index 0) from the list
final fileToDelete = files.removeAt(0);
// and delete it from the device's storage..
await fileToDelete.delete();
_logger.info('Deleted old backup: ${fileToDelete.path}');
}
_logger.info('Backup count is now ${files.length}. Cleanup complete.');
} catch (e, s) {
_logger.severe('Error during old backup cleanup', e, s);
}
}
Future<void> deleteAllBackupsIn(String path) async {
try {
_logger.info("Deleting all backups in old location: $path");
final directory = Directory(path);
if (!await directory.exists()) {
_logger.warning("Old backup directory not found. Nothing to delete.");
return;
}
final files = directory.listSync()
.where((entity) =>
entity is File &&
entity.path.split('/').last.startsWith('ente-auth-auto-backup-'),)
.map((entity) => entity as File)
.toList();
if (files.isEmpty) {
_logger.info("No old backup files found to delete.");
return;
}
for (final file in files) {
await file.delete();
_logger.info('Deleted: ${file.path}');
}
_logger.info("Successfully cleaned up old backup location.");
} catch (e, s) {
_logger.severe('Error during full backup cleanup of old directory', e, s);
}
}
}

View File

@@ -1,5 +1,5 @@
import 'package:ente_auth/core/event_bus.dart';
import 'package:ente_auth/events/icons_changed_event.dart';
import 'package:ente_events/event_bus.dart';
import 'package:shared_preferences/shared_preferences.dart';
enum CodeSortKey {

View File

@@ -2,9 +2,9 @@ import 'dart:async';
import 'dart:io';
import 'package:ente_auth/core/constants.dart';
import 'package:ente_auth/core/network.dart';
import 'package:ente_auth/services/notification_service.dart';
import 'package:ente_auth/utils/platform_util.dart';
import 'package:ente_network/network.dart';
import 'package:logging/logging.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:shared_preferences/shared_preferences.dart';

View File

@@ -4,13 +4,14 @@ import 'dart:io';
import 'package:ente_auth/models/authenticator/auth_entity.dart';
import 'package:ente_auth/models/authenticator/local_auth_entity.dart';
import 'package:ente_auth/utils/directory_utils.dart';
import 'package:ente_base/ente_base.dart';
import 'package:flutter/foundation.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
class AuthenticatorDB {
class AuthenticatorDB extends EnteBaseDatabase {
static const _databaseName = "ente.authenticator.db";
static const _databaseVersion = 1;

View File

@@ -14,6 +14,31 @@ class CodeDisplayStore {
late CodeStore _codeStore;
final ValueNotifier<bool> isSelectionModeActive = ValueNotifier(false);
final ValueNotifier<Set<String>> selectedCodeIds = ValueNotifier(<String>{});
// toggles the selection status of a code
void toggleSelection(String codeId){
final newSelection = Set<String>.from(selectedCodeIds.value);
if(newSelection.contains(codeId)){
newSelection.remove(codeId);
}
else{
newSelection.add(codeId);
}
selectedCodeIds.value = newSelection; //if we selected atleast one code, then we're in selection mode.. else: exit selection mode
isSelectionModeActive.value = newSelection.isNotEmpty;
}
//method to clear the entire selection
void clearSelection(){
selectedCodeIds.value = <String>{};
isSelectionModeActive.value = false;
}
Future<void> init() async {
_codeStore = CodeStore.instance;
}

View File

@@ -3,12 +3,13 @@ import 'dart:convert';
import 'package:collection/collection.dart';
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/core/event_bus.dart';
import 'package:ente_auth/events/codes_updated_event.dart';
import 'package:ente_auth/models/authenticator/entity_result.dart';
import 'package:ente_auth/models/code.dart';
import 'package:ente_auth/services/authenticator_service.dart';
import 'package:ente_auth/services/local_backup_service.dart';
import 'package:ente_auth/store/offline_authenticator_db.dart';
import 'package:ente_events/event_bus.dart';
import 'package:logging/logging.dart';
class CodeStore {
@@ -64,6 +65,27 @@ class CodeStore {
return true;
}
Future<void> updateCode(Code originalCode, Code updatedCode, {bool shouldSync = true}) async {
if (updatedCode.generatedID == null) return;
await _authenticatorService.updateEntry(
updatedCode.generatedID!,
updatedCode.toOTPAuthUrlFormat(),
shouldSync,
_authenticatorService.getAccountMode(),
);
Bus.instance.fire(CodesUpdatedEvent());
final bool isMajorChange = originalCode.issuer != updatedCode.issuer ||
originalCode.account != updatedCode.account ||
originalCode.secret != updatedCode.secret ||
originalCode.display.note != updatedCode.display.note;
if (isMajorChange) {
LocalBackupService.instance.triggerAutomaticBackup().ignore();
}
}
Future<List<Code>> getAllCodes({
AccountMode? accountMode,
bool sortCodes = true,
@@ -95,7 +117,6 @@ class CodeStore {
}
if (sortCodes) {
// sort codes by issuer,account
codes.sort((firstCode, secondCode) {
if (secondCode.isPinned && !firstCode.isPinned) return 1;
if (!secondCode.isPinned && firstCode.isPinned) return -1;
@@ -121,12 +142,15 @@ class CodeStore {
AccountMode? accountMode,
List<Code>? existingAllCodes,
}) async {
final mode = accountMode ?? _authenticatorService.getAccountMode();
final allCodes = existingAllCodes ?? (await getAllCodes(accountMode: mode));
bool isExistingCode = false;
bool hasSameCode = false;
for (final existingCode in allCodes) {
if (existingCode.hasError) continue;
if (code.generatedID != null &&
existingCode.generatedID == code.generatedID) {
isExistingCode = true;
@@ -155,6 +179,7 @@ class CodeStore {
shouldSync,
mode,
);
LocalBackupService.instance.triggerAutomaticBackup().ignore();
}
Bus.instance.fire(CodesUpdatedEvent());
return result;
@@ -164,6 +189,7 @@ class CodeStore {
final mode = accountMode ?? _authenticatorService.getAccountMode();
await _authenticatorService.deleteEntry(code.generatedID!, mode);
Bus.instance.fire(CodesUpdatedEvent());
LocalBackupService.instance.triggerAutomaticBackup().ignore();
}
bool _isOfflineImportRunning = false;
@@ -214,7 +240,6 @@ class CodeStore {
'importingCode: genID ${eachCode.generatedID} & isAlreadyPresent $alreadyPresent',
);
if (!alreadyPresent) {
// Avoid conflict with generatedID of online codes
eachCode.generatedID = null;
final AddResult result = await CodeStore.instance.addCode(
eachCode,
@@ -236,10 +261,21 @@ class CodeStore {
_isOfflineImportRunning = false;
}
}
Future<String> getCodesForExport() async {
final allCodes = await getAllCodes(sortCodes: false);
String data = "";
for (final code in allCodes) {
if (code.hasError) continue;
data += "${code.toOTPAuthUrlFormat()}\n";
}
return data;
}
}
enum AddResult {
newCode,
duplicate,
updateCode,
}
}

View File

@@ -1,7 +1,7 @@
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/store/authenticator_db.dart';
import 'package:ente_auth/utils/dialog_util.dart';
import 'package:ente_auth/utils/dialog_util.dart';
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';

View File

@@ -9,12 +9,11 @@ import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/models/code.dart';
import 'package:ente_auth/onboarding/view/setup_enter_secret_key_page.dart';
import 'package:ente_auth/onboarding/view/view_qr_page.dart';
import 'package:ente_auth/services/local_authentication_service.dart';
import 'package:ente_auth/services/preference_service.dart';
import 'package:ente_auth/store/code_display_store.dart';
import 'package:ente_auth/store/code_store.dart';
import 'package:ente_auth/theme/ente_theme.dart';
import 'package:ente_auth/ui/code_timer_progress.dart';
import 'package:ente_auth/ui/components/bottom_action_bar_widget.dart';
import 'package:ente_auth/ui/components/models/button_type.dart';
import 'package:ente_auth/ui/share/code_share.dart';
import 'package:ente_auth/ui/utils/icon_utils.dart';
@@ -22,6 +21,7 @@ import 'package:ente_auth/utils/dialog_util.dart';
import 'package:ente_auth/utils/platform_util.dart';
import 'package:ente_auth/utils/toast_util.dart';
import 'package:ente_auth/utils/totp_util.dart';
import 'package:ente_lock_screen/local_authentication_service.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_context_menu/flutter_context_menu.dart';
@@ -103,7 +103,6 @@ class _CodeWidgetState extends State<CodeWidget> {
@override
Widget build(BuildContext context) {
ignorePin = widget.sortKey != null && widget.sortKey == CodeSortKey.manual;
final colorScheme = getEnteColorScheme(context);
if (isMaskingEnabled != PreferenceService.instance.shouldHideCodes()) {
isMaskingEnabled = PreferenceService.instance.shouldHideCodes();
_hideCode = isMaskingEnabled;
@@ -118,96 +117,110 @@ class _CodeWidgetState extends State<CodeWidget> {
}
final l10n = context.l10n;
Widget getCardContents(AppLocalizations l10n) {
return Stack(
Widget getCardContents(AppLocalizations l10n, {required bool isSelected}) {
final colorScheme = getEnteColorScheme(context);
return Stack(
children: [
if (!ignorePin && widget.code.isPinned)
Align(
alignment: Alignment.topRight,
child: CustomPaint(
painter: PinBgPainter(
color: colorScheme.pinnedBgColor,
),
size: widget.isCompactMode
? const Size(24, 24)
: const Size(39, 39),
),
),
if (widget.code.isTrashed && kDebugMode)
Align(
alignment: Alignment.topLeft,
child: CustomPaint(
painter: PinBgPainter(
color: colorScheme.warning700,
),
size: const Size(39, 39),
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (!ignorePin && widget.code.isPinned)
Align(
alignment: Alignment.topRight,
child: CustomPaint(
painter: PinBgPainter(
color: colorScheme.pinnedBgColor,
),
size: widget.isCompactMode
? const Size(24, 24)
: const Size(39, 39),
),
if (widget.code.type.isTOTPCompatible)
CodeTimerProgress(
key: ValueKey('period_${widget.code.period}'),
period: widget.code.period,
isCompactMode: widget.isCompactMode,
timeOffsetInMilliseconds:
PreferenceService.instance.timeOffsetInMilliSeconds(),
),
if (widget.code.isTrashed && kDebugMode)
Align(
alignment: Alignment.topLeft,
child: CustomPaint(
painter: PinBgPainter(
color: colorScheme.warning700,
),
size: const Size(39, 39),
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
widget.isCompactMode
? const SizedBox(height: 4)
: const SizedBox(height: 28),
Row(
children: [
if (widget.code.type.isTOTPCompatible)
CodeTimerProgress(
key: ValueKey('period_${widget.code.period}'),
period: widget.code.period,
isCompactMode: widget.isCompactMode,
timeOffsetInMilliseconds:
PreferenceService.instance.timeOffsetInMilliSeconds(),
_shouldShowLargeIcon ? _getIcon() : const SizedBox.shrink(),
Expanded(
child: Column(
children: [
_getTopRow(isSelected: isSelected),
widget.isCompactMode
? const SizedBox.shrink()
: const SizedBox(height: 4),
_getBottomRow(l10n),
],
),
widget.isCompactMode
? const SizedBox(height: 4)
: const SizedBox(height: 28),
Row(
children: [
_shouldShowLargeIcon ? _getIcon() : const SizedBox.shrink(),
Expanded(
child: Column(
children: [
_getTopRow(),
widget.isCompactMode
? const SizedBox.shrink()
: const SizedBox(height: 4),
_getBottomRow(l10n),
],
),
),
],
),
widget.isCompactMode
? const SizedBox(height: 4)
: const SizedBox(height: 32),
],
),
if (!ignorePin && widget.code.isPinned) ...[
Align(
alignment: Alignment.topRight,
child: Padding(
padding: widget.isCompactMode
? const EdgeInsets.only(right: 4, top: 4)
: const EdgeInsets.only(right: 6, top: 6),
child: SvgPicture.asset(
"assets/svg/pin-card.svg",
width: widget.isCompactMode ? 8 : null,
height: widget.isCompactMode ? 8 : null,
),
),
),
],
widget.isCompactMode
? const SizedBox(height: 4)
: const SizedBox(height: 32),
],
);
}
),
if (!ignorePin && widget.code.isPinned) ...[
Align(
alignment: Alignment.topRight,
child: Padding(
padding: widget.isCompactMode
? const EdgeInsets.only(right: 4, top: 4)
: const EdgeInsets.only(right: 6, top: 6),
child: SvgPicture.asset(
"assets/svg/pin-card.svg",
width: widget.isCompactMode ? 8 : null,
height: widget.isCompactMode ? 8 : null,
),
),
),
],
],
);
}
Widget clippedCard(AppLocalizations l10n) {
final colorScheme = getEnteColorScheme(context);
return ValueListenableBuilder<Set<String>>(
valueListenable: CodeDisplayStore.instance.selectedCodeIds,
builder: (context, selectedIds, child) {
final isSelected = selectedIds.contains(widget.code.secret);
Widget clippedCard(AppLocalizations l10n) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Theme.of(context).colorScheme.codeCardBackgroundColor,
color: isSelected
? colorScheme.primary400.withValues(alpha: 0.08)
: Theme.of(context).colorScheme.codeCardBackgroundColor,
//add purple overlay when selected
border: isSelected
? Border.all(color: colorScheme.primary400, width: 2)
: null,
boxShadow:
widget.code.isPinned ? colorScheme.pinnedCardBoxShadow : [],
(widget.code.isPinned && !isSelected) ? colorScheme.pinnedCardBoxShadow : [],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
borderRadius: BorderRadius.circular(6),
child: Material(
color: Colors.transparent,
child: InkWell(
@@ -215,7 +228,12 @@ class _CodeWidgetState extends State<CodeWidget> {
borderRadius: BorderRadius.circular(10),
),
onTap: () {
_copyCurrentOTPToClipboard();
final store = CodeDisplayStore.instance;
if (store.isSelectionModeActive.value) {
store.toggleSelection(widget.code.secret);
} else {
_copyCurrentOTPToClipboard();
}
},
onDoubleTap: isMaskingEnabled
? () {
@@ -229,30 +247,16 @@ class _CodeWidgetState extends State<CodeWidget> {
onLongPress: widget.isReordering
? null
: () {
showModalBottomSheet(
context: context,
builder: (_) {
return BottomActionBarWidget(
code: widget.code,
showPin: !ignorePin,
onEdit: () => _onEditPressed(true),
onShare: () => _onSharePressed(true),
onPin: () => _onPinPressed(true),
onTrashed: () => _onTrashPressed(true),
onDelete: () => _onDeletePressed(true),
onRestore: () => _onRestoreClicked(true),
onShowQR: () => _onShowQrPressed(true),
onCancel: () => Navigator.of(context).pop(),
);
},
);
CodeDisplayStore.instance.toggleSelection(widget.code.secret);
},
child: getCardContents(l10n),
child: getCardContents(l10n, isSelected: isSelected),
),
),
),
);
}
},
);
}
return Container(
margin: widget.isCompactMode
@@ -273,7 +277,7 @@ class _CodeWidgetState extends State<CodeWidget> {
),
if (!widget.code.isTrashed)
MenuItem(
label: 'QR',
label: context.l10n.qr,
icon: Icons.qr_code_2_outlined,
onSelected: () => _onShowQrPressed(null),
),
@@ -307,7 +311,7 @@ class _CodeWidgetState extends State<CodeWidget> {
const MenuDivider(),
MenuItem(
label: widget.code.isTrashed ? l10n.delete : l10n.trash,
value: "Delete",
value: l10n.delete,
icon: widget.code.isTrashed
? Icons.delete_forever
: Icons.delete,
@@ -403,54 +407,64 @@ class _CodeWidgetState extends State<CodeWidget> {
);
}
Widget _getTopRow() {
bool isCompactMode = widget.isCompactMode;
return Padding(
padding: const EdgeInsets.only(left: 16, right: 16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
safeDecode(widget.code.issuer).trim(),
style: isCompactMode
? Theme.of(context).textTheme.bodyMedium
: Theme.of(context).textTheme.titleLarge,
),
if (!isCompactMode) const SizedBox(height: 2),
Text(
safeDecode(widget.code.account).trim(),
style: Theme.of(context).textTheme.bodySmall?.copyWith(
fontSize: isCompactMode ? 12 : 12,
color: Colors.grey,
),
),
],
Widget _getTopRow({required bool isSelected}) {
final colorScheme = getEnteColorScheme(context);
bool isCompactMode = widget.isCompactMode;
return Padding(
padding: const EdgeInsets.only(left: 16, right: 16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (isSelected)
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Icon(
Icons.check_circle,
color: colorScheme.primary400,
size: isCompactMode ? 20 : 24,
),
),
const SizedBox(width: 8),
Row(
mainAxisAlignment: MainAxisAlignment.end,
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(widget.code.hasSynced != null && widget.code.hasSynced!) ||
!hasConfiguredAccount
? const SizedBox.shrink()
: const Icon(
Icons.sync_disabled,
size: 20,
color: Colors.amber,
Text(
safeDecode(widget.code.issuer).trim(),
style: isCompactMode
? Theme.of(context).textTheme.bodyMedium
: Theme.of(context).textTheme.titleLarge,
),
if (!isCompactMode) const SizedBox(height: 2),
Text(
safeDecode(widget.code.account).trim(),
style: Theme.of(context).textTheme.bodySmall?.copyWith(
fontSize: isCompactMode ? 12 : 12,
color: Colors.grey,
),
const SizedBox(width: 12),
_shouldShowLargeIcon ? const SizedBox.shrink() : _getIcon(),
),
],
),
],
),
);
}
),
const SizedBox(width: 8),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
(widget.code.hasSynced != null && widget.code.hasSynced!) ||
!hasConfiguredAccount
? const SizedBox.shrink()
: const Icon(
Icons.sync_disabled,
size: 20,
color: Colors.amber,
),
const SizedBox(width: 12),
_shouldShowLargeIcon ? const SizedBox.shrink() : _getIcon(),
],
),
],
),
);
}
Widget _getIcon() {
final String iconData;
@@ -478,7 +492,7 @@ class _CodeWidgetState extends State<CodeWidget> {
_getCurrentOTP(),
confirmationMessage: context.l10n.copiedToClipboard,
);
_udateCodeMetadata().ignore();
_updateCodeMetadata().ignore();
}
void _copyNextToClipboard() {
@@ -486,10 +500,10 @@ class _CodeWidgetState extends State<CodeWidget> {
_getNextTotp(),
confirmationMessage: context.l10n.copiedNextToClipboard,
);
_udateCodeMetadata().ignore();
_updateCodeMetadata().ignore();
}
Future<void> _udateCodeMetadata() async {
Future<void> _updateCodeMetadata() async {
if (widget.sortKey == null) return;
Future.delayed(const Duration(milliseconds: 100), () {
if (mounted) {
@@ -502,7 +516,7 @@ class _CodeWidgetState extends State<CodeWidget> {
lastUsedAt: DateTime.now().microsecondsSinceEpoch,
),
);
unawaited(CodeStore.instance.addCode(code));
unawaited(CodeStore.instance.updateCode(widget.code, code));
}
}
});
@@ -568,7 +582,7 @@ class _CodeWidgetState extends State<CodeWidget> {
),
);
if (code != null) {
await CodeStore.instance.addCode(code);
await CodeStore.instance.updateCode(widget.code, code);
}
}
@@ -615,7 +629,7 @@ class _CodeWidgetState extends State<CodeWidget> {
display: display.copyWith(pinned: !currentlyPinned),
);
unawaited(
CodeStore.instance.addCode(code).then(
CodeStore.instance.updateCode(widget.code,code).then(
(value) => showToast(
context,
!currentlyPinned
@@ -694,7 +708,7 @@ class _CodeWidgetState extends State<CodeWidget> {
final Code code = widget.code.copyWith(
display: display.copyWith(trashed: true),
);
await CodeStore.instance.addCode(code);
await CodeStore.instance.updateCode(widget.code, code);
} catch (e) {
logger.severe('Failed to trash code: ${e.toString()}');
showGenericErrorDialog(context: context, error: e).ignore();
@@ -718,7 +732,7 @@ class _CodeWidgetState extends State<CodeWidget> {
final Code code = widget.code.copyWith(
display: display.copyWith(trashed: false),
);
await CodeStore.instance.addCode(code);
await CodeStore.instance.updateCode(widget.code, code);
} catch (e) {
logger.severe('Failed to restore code: ${e.toString()}');
if (mounted) {

View File

@@ -1,9 +1,9 @@
import 'package:ente_auth/ente_theme_data.dart';
import 'package:ente_auth/models/typedefs.dart';
import 'package:ente_auth/ente_theme_data.dart';
import 'package:ente_auth/ui/components/buttons/button_widget.dart';
import 'package:ente_auth/ui/components/dialog_widget.dart';
import 'package:ente_auth/ui/components/models/button_result.dart';
import 'package:ente_auth/ui/components/models/button_type.dart';
import 'package:ente_base/typedefs.dart';
import 'package:flutter/material.dart';
enum DialogUserChoice { firstChoice, secondChoice }

View File

@@ -1,5 +1,4 @@
import 'package:ente_auth/models/execution_states.dart';
import 'package:ente_auth/models/typedefs.dart';
import 'package:ente_auth/models/execution_states.dart';
import 'package:ente_auth/theme/colors.dart';
import 'package:ente_auth/theme/ente_theme.dart';
import 'package:ente_auth/theme/text_style.dart';
@@ -9,6 +8,7 @@ import 'package:ente_auth/ui/components/models/button_type.dart';
import 'package:ente_auth/ui/components/models/custom_button_style.dart';
import 'package:ente_auth/utils/debouncer.dart';
import "package:ente_auth/utils/dialog_util.dart";
import 'package:ente_base/typedefs.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';

View File

@@ -1,7 +1,6 @@
import 'dart:math';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/models/typedefs.dart';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/theme/colors.dart';
import 'package:ente_auth/theme/effects.dart';
import 'package:ente_auth/theme/ente_theme.dart';
@@ -11,6 +10,7 @@ import 'package:ente_auth/ui/components/models/button_result.dart';
import 'package:ente_auth/ui/components/models/button_type.dart';
import 'package:ente_auth/ui/components/separators.dart';
import 'package:ente_auth/ui/components/text_input_widget.dart';
import 'package:ente_base/typedefs.dart';
import 'package:flutter/material.dart';
///Will return null if dismissed by tapping outside

View File

@@ -1,8 +1,8 @@
import 'package:ente_auth/models/execution_states.dart';
import 'package:ente_auth/models/typedefs.dart';
import 'package:ente_auth/models/execution_states.dart';
import 'package:ente_auth/theme/ente_theme.dart';
import 'package:ente_auth/ui/components/menu_item_child_widgets.dart';
import 'package:ente_auth/utils/debouncer.dart';
import 'package:ente_base/typedefs.dart';
import 'package:expandable/expandable.dart';
import 'package:flutter/material.dart';

View File

@@ -1,9 +1,9 @@
import 'package:ente_auth/models/execution_states.dart';
import 'package:ente_auth/models/typedefs.dart';
import 'package:ente_auth/models/execution_states.dart';
import 'package:ente_auth/theme/ente_theme.dart';
import 'package:ente_auth/ui/common/loading_widget.dart';
import 'package:ente_auth/ui/components/separators.dart';
import 'package:ente_auth/utils/debouncer.dart';
import 'package:ente_base/typedefs.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:logging/logging.dart';

View File

@@ -1,9 +1,9 @@
import 'package:ente_auth/ente_theme_data.dart';
import 'package:ente_auth/models/execution_states.dart';
import 'package:ente_auth/models/typedefs.dart';
import 'package:ente_auth/theme/colors.dart';
import 'package:ente_auth/models/execution_states.dart';
import 'package:ente_auth/theme/colors.dart';
import 'package:ente_auth/ui/common/loading_widget.dart';
import 'package:ente_auth/utils/debouncer.dart';
import 'package:ente_base/typedefs.dart';
import 'package:flutter/material.dart';
typedef OnChangedCallBack = void Function(bool);

View File

@@ -0,0 +1,243 @@
import 'dart:async';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/models/code.dart';
import 'package:ente_auth/store/code_display_store.dart';
import 'package:ente_auth/store/code_store.dart';
import 'package:ente_auth/theme/ente_theme.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/ui/utils/icon_utils.dart';
import 'package:flutter/material.dart';
class AddTagSheet extends StatefulWidget {
final List<Code> selectedCodes;
const AddTagSheet({
super.key,
required this.selectedCodes,
});
@override
State<AddTagSheet> createState() => _AddTagSheetState();
}
class _AddTagSheetState extends State<AddTagSheet> {
List<String> _allTags = [];
final Set<String> _selectedTagsInSheet = {};
bool _isLoading = true;
@override
void initState() {
super.initState();
_loadInitialState();
}
Future<void> _loadInitialState() async {
final allTagsFromServer = await CodeDisplayStore.instance.getAllTags();
final initialTagsForSelection = <String>{};
for (final code in widget.selectedCodes) {
initialTagsForSelection.addAll(code.display.tags);
}
if (mounted) {
setState(() {
_allTags = allTagsFromServer;
_selectedTagsInSheet.addAll(initialTagsForSelection);
_isLoading = false;
});
}
}
Future<void> _onDonePressed() async {
final List<Future> updateFutures = [];
for (final code in widget.selectedCodes) {
final updatedCode = code.copyWith(
display: code.display.copyWith(tags: _selectedTagsInSheet.toList()),
);
updateFutures.add(CodeStore.instance.updateCode(code, updatedCode));
}
await Future.wait(updateFutures);
if (mounted) {
Navigator.of(context).pop();
}
}
Future<void> _showCreateTagDialog() async {
final textController = TextEditingController();
final newTag = await showDialog<String>(
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: Text(context.l10n.createNewTag),
content: TextField(
controller: textController,
autofocus: true,
onChanged: (_) => setState(() {}),
),
actions: [
Row(
children: [
Expanded(
child: ButtonWidget(
buttonType: ButtonType.secondary,
labelText: context.l10n.cancel,
onTap: () async => Navigator.of(context).pop(),
),
),
const SizedBox(width: 8),
Expanded(
child: ButtonWidget(
buttonType: ButtonType.primary,
labelText: context.l10n.create,
isDisabled: textController.text.trim().isEmpty,
onTap: () async => Navigator.of(context).pop(textController.text.trim()),
),
),
],
),
],
);
},
);
},
);
if (newTag != null && newTag.isNotEmpty) {
setState(() {
if (!_allTags.contains(newTag)) {
_allTags.add(newTag);
_allTags.sort();
}
_selectedTagsInSheet.add(newTag);
});
}
}
@override
Widget build(BuildContext context) {
final colorScheme = getEnteColorScheme(context);
final textTheme = getEnteTextTheme(context);
final bottomPadding = MediaQuery.of(context).padding.bottom;
return Padding(
padding: EdgeInsets.fromLTRB(16, 16, 16, 16 + bottomPadding),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${widget.selectedCodes.length} ${context.l10n.selected}',
style: textTheme.large,
),
IconButton(
icon: const Icon(Icons.close),
onPressed: () => Navigator.of(context).pop(),
),
],
),
const SizedBox(height: 16),
SizedBox(
height: 80,
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: widget.selectedCodes.length,
separatorBuilder: (_, __) => const SizedBox(width: 16),
itemBuilder: (context, index) {
final code = widget.selectedCodes[index];
final iconData =
code.display.isCustomIcon ? code.display.iconID : code.issuer;
return SizedBox(
width: 60,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconUtils.instance.getIcon(context, iconData.trim(), width: 40),
const SizedBox(height: 8),
Text(
code.issuer,
overflow: TextOverflow.ellipsis,
style: textTheme.mini.copyWith(color: colorScheme.textMuted,),
),
],
),
);
},
),
),
const SizedBox(height: 24),
Text(context.l10n.tags, style: textTheme.body),
const SizedBox(height: 12),
Flexible(
child: SingleChildScrollView(
child: _isLoading
? const Center(child: CircularProgressIndicator())
: Wrap(
spacing: 8.0,
runSpacing: 4.0,
children: [
..._allTags.map((tag) {
final isSelected = _selectedTagsInSheet.contains(tag);
return ChoiceChip(
label: Text(tag),
selected: isSelected,
onSelected: (selected) {
setState(() {
if (selected) {
_selectedTagsInSheet.add(tag);
} else {
_selectedTagsInSheet.remove(tag);
}
});
},
selectedColor: colorScheme.primary400,
backgroundColor: colorScheme.fillFaint,
labelStyle: TextStyle(
color: isSelected ? Colors.white : colorScheme.textBase,
),
avatar: isSelected ? const Icon(Icons.check, color: Colors.white, size: 16) : null,
side: BorderSide.none,
shape: const StadiumBorder(),
);
}),
ActionChip(
avatar: const Icon(Icons.add, size: 18),
label: Text(context.l10n.addNew),
onPressed: _showCreateTagDialog,
side: BorderSide.none,
shape: const StadiumBorder(),
backgroundColor: colorScheme.fillFaint,
),
],
),
),
),
const SizedBox(height: 24),
SizedBox(
width: double.infinity,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: colorScheme.fillBase,
foregroundColor: colorScheme.backgroundBase,
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
onPressed: _onDonePressed,
child: Text(context.l10n.done),
),
),
],
),
);
}
}

View File

@@ -1,10 +1,10 @@
import 'dart:ui';
import 'package:ente_auth/core/event_bus.dart';
import 'package:ente_auth/events/codes_updated_event.dart';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/services/preference_service.dart';
import 'package:ente_auth/utils/platform_util.dart';
import 'package:ente_events/event_bus.dart';
import 'package:flutter/material.dart';
class CoachMarkWidget extends StatelessWidget {

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@ import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/models/code.dart';
import 'package:ente_auth/utils/toast_util.dart';
import 'package:flutter/material.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart';
class ScannerPage extends StatefulWidget {
const ScannerPage({super.key});

View File

@@ -1,20 +1,21 @@
import 'package:ente_accounts/pages/change_email_dialog.dart';
import 'package:ente_accounts/pages/delete_account_page.dart';
import 'package:ente_accounts/pages/password_entry_page.dart';
import 'package:ente_accounts/pages/recovery_key_page.dart';
import 'package:ente_accounts/services/user_service.dart';
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';
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';
import 'package:ente_auth/ui/home_page.dart';
import 'package:ente_auth/ui/settings/common_settings.dart';
import 'package:ente_auth/utils/dialog_util.dart';
import 'package:ente_auth/utils/navigation_util.dart';
import 'package:ente_auth/utils/platform_util.dart';
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
import 'package:ente_lock_screen/local_authentication_service.dart';
import 'package:flutter/material.dart';
class AccountSectionWidget extends StatelessWidget {
@@ -81,8 +82,10 @@ class AccountSectionWidget extends StatelessWidget {
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {
return const PasswordEntryPage(
mode: PasswordEntryMode.update,
return PasswordEntryPage(
Configuration.instance,
PasswordEntryMode.update,
const HomePage(),
);
},
),
@@ -121,6 +124,7 @@ class AccountSectionWidget extends StatelessWidget {
routeToPage(
context,
RecoveryKeyPage(
Configuration.instance,
recoveryKey,
l10n.ok,
showAppBar: true,
@@ -151,8 +155,9 @@ class AccountSectionWidget extends StatelessWidget {
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
onTap: () async {
final config = Configuration.instance;
// ignore: unawaited_futures
routeToPage(context, const DeleteAccountPage());
routeToPage(context, DeleteAccountPage(config));
},
),
sectionOptionSpacing,

View File

@@ -10,6 +10,7 @@ import 'package:ente_auth/ui/settings/common_settings.dart';
import 'package:ente_auth/ui/settings/data/duplicate_code_page.dart';
import 'package:ente_auth/ui/settings/data/export_widget.dart';
import 'package:ente_auth/ui/settings/data/import_page.dart';
import 'package:ente_auth/ui/settings/data/local_backup_settings_page.dart'; //for local backup
import 'package:ente_auth/utils/dialog_util.dart';
import 'package:ente_auth/utils/navigation_util.dart';
import 'package:flutter/material.dart';
@@ -29,6 +30,10 @@ class DataSectionWidget extends StatelessWidget {
);
}
Future<void> _handleLocalBackupClick(BuildContext context) async {
await routeToPage(context, const LocalBackupSettingsPage());
}
Column _getSectionOptions(BuildContext context) {
final l10n = context.l10n;
List<Widget> children = [];
@@ -86,10 +91,21 @@ class DataSectionWidget extends StatelessWidget {
);
},
),
MenuItemWidget(
captionedTextWidget: CaptionedTextWidget(
title: l10n.localBackupSidebarTitle,
),
pressedColor: getEnteColorScheme(context).fillFaint,
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
onTap: () async {
await _handleLocalBackupClick(context);
},
),
sectionOptionSpacing,
]);
return Column(
children: children,
);
}
}
}

View File

@@ -1,11 +1,11 @@
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/models/code.dart';
import 'package:ente_auth/services/deduplication_service.dart';
import 'package:ente_auth/services/local_authentication_service.dart';
import 'package:ente_auth/store/code_store.dart';
import 'package:ente_auth/theme/ente_theme.dart';
import 'package:ente_auth/ui/code_widget.dart';
import 'package:ente_auth/utils/dialog_util.dart';
import 'package:ente_lock_screen/local_authentication_service.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:logging/logging.dart';

View File

@@ -2,8 +2,7 @@ import 'dart:convert';
import 'dart:io';
import 'package:ente_auth/core/configuration.dart';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/models/export/ente.dart';
import 'package:ente_auth/services/local_authentication_service.dart';
import 'package:ente_auth/models/export/ente.dart';
import 'package:ente_auth/store/code_store.dart';
import 'package:ente_auth/ui/components/buttons/button_widget.dart';
import 'package:ente_auth/ui/components/dialog_widget.dart';
@@ -14,6 +13,7 @@ import 'package:ente_auth/utils/platform_util.dart';
import 'package:ente_auth/utils/share_utils.dart';
import 'package:ente_auth/utils/toast_util.dart';
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
import 'package:ente_lock_screen/local_authentication_service.dart';
import 'package:file_saver/file_saver.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';

View File

@@ -5,7 +5,6 @@ import 'dart:io';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/models/code.dart';
import 'package:ente_auth/models/export/ente.dart';
import 'package:ente_auth/services/authenticator_service.dart';
import 'package:ente_auth/store/code_store.dart';
import 'package:ente_auth/ui/components/buttons/button_widget.dart';
import 'package:ente_auth/ui/components/dialog_widget.dart';
@@ -46,7 +45,7 @@ Future<void> showEncryptedImportInstruction(BuildContext context) async {
if (result?.action != null && result!.action != ButtonAction.cancel) {
if (result.action == ButtonAction.first) {
await _pickEnteJsonFile(context);
} else {}
}
}
}
@@ -58,6 +57,9 @@ Future<void> _decryptExportData(
final l10n = context.l10n;
bool isPasswordIncorrect = false;
int? importedCodeCount;
bool importHasRun = false;
await showTextInputDialog(
context,
title: l10n.passwordForDecryptingExport,
@@ -67,6 +69,11 @@ Future<void> _decryptExportData(
alwaysShowSuccessState: false,
showOnlyLoadingState: true,
onSubmit: (String password) async {
if (importHasRun) {
return;
}
importHasRun = true;
if (password.isEmpty) {
showToast(context, l10n.passwordEmptyError);
Future.delayed(const Duration(seconds: 0), () {
@@ -78,6 +85,7 @@ Future<void> _decryptExportData(
final progressDialog = createProgressDialog(context, l10n.pleaseWait);
try {
await progressDialog.show();
final derivedKey = await CryptoUtil.deriveKey(
utf8.encode(password),
CryptoUtil.base642bin(enteAuthExport.kdfParams.salt),
@@ -85,7 +93,6 @@ Future<void> _decryptExportData(
enteAuthExport.kdfParams.opsLimit,
);
Uint8List? decryptedContent;
// Encrypt the key with this derived key
try {
decryptedContent = await CryptoUtil.decryptData(
CryptoUtil.base642bin(enteAuthExport.encryptedData),
@@ -99,27 +106,62 @@ Future<void> _decryptExportData(
}
if (isPasswordIncorrect) {
await progressDialog.hide();
Future.delayed(const Duration(seconds: 0), () {
_decryptExportData(context, enteAuthExport, password: password);
});
return;
}
String content = utf8.decode(decryptedContent!);
List<String> splitCodes = content.split("\n");
final parsedCodes = [];
for (final code in splitCodes) {
final List<Code> parsedCodes = [];
for (final line in splitCodes) {
if (line.trim().isEmpty) continue;
try {
parsedCodes.add(Code.fromOTPAuthUrl(code));
String otpUrl = jsonDecode(line);
parsedCodes.add(Code.fromOTPAuthUrl(otpUrl));
} catch (e) {
Logger('EncryptedText').severe("Could not parse code", e);
}
}
for (final code in parsedCodes) {
await CodeStore.instance.addCode(code, shouldSync: false);
final List<Code> codesInApp = await CodeStore.instance.getAllCodes();
final Map<String, Code> appCodesBySecret = { for (var code in codesInApp) code.secret: code };
final List<Code> codesToImportAsNew = [];
final List<Code> codesToUpdate = [];
final Set<String> processedSecrets = {};
for (final codeFromFile in parsedCodes) {
if (processedSecrets.contains(codeFromFile.secret)) {
continue;
}
processedSecrets.add(codeFromFile.secret);
if (appCodesBySecret.containsKey(codeFromFile.secret)) {
final originalCodeInApp = appCodesBySecret[codeFromFile.secret]!;
final updatedCode = codeFromFile.copyWith();
updatedCode.generatedID = originalCodeInApp.generatedID;
codesToUpdate.add(updatedCode);
} else {
codesToImportAsNew.add(codeFromFile);
}
}
unawaited(AuthenticatorService.instance.onlineSync());
importedCodeCount = parsedCodes.length;
if (codesToUpdate.isNotEmpty) {
for (final codeToUpdate in codesToUpdate) {
final originalCode = appCodesBySecret[codeToUpdate.secret]!;
await CodeStore.instance.updateCode(originalCode, codeToUpdate, shouldSync: false);
}
}
if (codesToImportAsNew.isNotEmpty) {
for (final newCode in codesToImportAsNew) {
await CodeStore.instance.addCode(newCode, shouldSync: false);
}
}
importedCodeCount = codesToImportAsNew.length + codesToUpdate.length;
await progressDialog.hide();
} catch (e, s) {
await progressDialog.hide();
@@ -153,4 +195,4 @@ Future<void> _pickEnteJsonFile(BuildContext context) async {
context.l10n.importFailureDescNew,
);
}
}
}

View File

@@ -4,6 +4,7 @@ import 'package:ente_auth/ui/settings/data/import/encrypted_ente_import.dart';
import 'package:ente_auth/ui/settings/data/import/google_auth_import.dart';
import 'package:ente_auth/ui/settings/data/import/lastpass_import.dart';
import 'package:ente_auth/ui/settings/data/import/plain_text_import.dart';
import 'package:ente_auth/ui/settings/data/import/proton_import.dart';
import 'package:ente_auth/ui/settings/data/import/raivo_plain_text_import.dart';
import 'package:ente_auth/ui/settings/data/import/two_fas_import.dart';
import 'package:ente_auth/ui/settings/data/import_page.dart';
@@ -43,6 +44,9 @@ class ImportService {
case ImportType.lastpass:
await showLastpassImportInstruction(context);
break;
case ImportType.proton:
await showProtonImportInstruction(context);
break;
}
}
}

View File

@@ -0,0 +1,171 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:ente_auth/l10n/l10n.dart';
import 'package:ente_auth/models/code.dart';
import 'package:ente_auth/services/authenticator_service.dart';
import 'package:ente_auth/store/code_store.dart';
import 'package:ente_auth/ui/common/progress_dialog.dart';
import 'package:ente_auth/ui/components/buttons/button_widget.dart';
import 'package:ente_auth/ui/components/dialog_widget.dart';
import 'package:ente_auth/ui/components/models/button_type.dart';
import 'package:ente_auth/ui/settings/data/import/import_success.dart';
import 'package:ente_auth/utils/dialog_util.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
Future<void> showProtonImportInstruction(BuildContext context) async {
final l10n = context.l10n;
final result = await showDialogWidget(
context: context,
title: l10n.importFromApp("Proton Authenticator"),
body: l10n.importProtonAuthGuide,
buttons: [
ButtonWidget(
buttonType: ButtonType.primary,
labelText: l10n.importSelectJsonFile,
isInAlert: true,
buttonSize: ButtonSize.large,
buttonAction: ButtonAction.first,
),
ButtonWidget(
buttonType: ButtonType.secondary,
labelText: context.l10n.cancel,
buttonSize: ButtonSize.large,
isInAlert: true,
buttonAction: ButtonAction.second,
),
],
);
if (result?.action != null && result!.action != ButtonAction.cancel) {
if (result.action == ButtonAction.first) {
await _pickProtonJsonFile(context);
}
}
}
Future<void> _pickProtonJsonFile(BuildContext context) async {
final l10n = context.l10n;
FilePickerResult? result = await FilePicker.platform
.pickFiles(dialogTitle: l10n.importSelectJsonFile);
if (result == null) {
return;
}
final ProgressDialog progressDialog =
createProgressDialog(context, l10n.pleaseWait);
await progressDialog.show();
try {
String path = result.files.single.path!;
int? count = await _processProtonExportFile(context, path, progressDialog);
await progressDialog.hide();
if (count != null) {
await importSuccessDialog(context, count);
}
} catch (e, s) {
Logger('ProtonImport')
.severe('exception while processing proton import', e, s);
await progressDialog.hide();
await showErrorDialog(
context,
context.l10n.sorry,
"${context.l10n.importFailureDescNew}\n Error: ${e.toString()}",
);
}
}
Future<int?> _processProtonExportFile(
BuildContext context,
String path,
final ProgressDialog dialog,
) async {
File file = File(path);
final jsonString = await file.readAsString();
final decodedJson = jsonDecode(jsonString);
// Validate that this is a Proton export
if (decodedJson['version'] == null || decodedJson['entries'] == null) {
await dialog.hide();
await showErrorDialog(
context,
'Invalid Proton export',
'The selected file is not a valid Proton Authenticator export.',
);
return null;
}
final parsedCodes = <Code>[];
final entries = decodedJson['entries'] as List;
for (var entry in entries) {
try {
final content = entry['content'];
if (content == null) {
continue; // Skip entries without content
}
final entryType = content['entry_type'] as String?;
if (entryType != 'Totp' && entryType != 'Steam') {
// log warning
Logger('ProtonImport').warning('Unsupported entry type: $entryType');
continue; // Skip non-TOTP and non-Steam entries
}
Code code;
if (entryType == 'Steam') {
// Handle Steam entries with steam:// format
final steamUri = content['uri'] as String?;
if (steamUri == null || !steamUri.startsWith('steam://')) {
continue; // Skip invalid Steam URIs
}
final secret = steamUri.split('steam://')[1];
final name = content['name'] as String? ?? '';
code = Code.fromAccountAndSecret(
Type.steam,
'', // Steam doesn't typically have separate account
name, // Use name as issuer
secret,
null,
Code.steamDigits,
);
} else {
// Handle TOTP entries with otpauth:// format
final otpUri = content['uri'] as String?;
if (otpUri == null || !otpUri.startsWith('otpauth://')) {
continue; // Skip invalid OTP URIs
}
// Create code from OTP auth URL
code = Code.fromOTPAuthUrl(otpUri);
}
// Add note if present
final note = entry['note'] as String?;
if (note != null && note.isNotEmpty) {
code = code.copyWith(
display: code.display.copyWith(note: note),
);
}
parsedCodes.add(code);
} catch (e, s) {
Logger('ProtonImport').warning('Failed to parse entry', e, s);
// Continue processing other entries
}
}
// Add all parsed codes to the store
for (final code in parsedCodes) {
await CodeStore.instance.addCode(code, shouldSync: false);
}
// Trigger sync
unawaited(AuthenticatorService.instance.onlineSync());
return parsedCodes.length;
}

View File

@@ -17,6 +17,7 @@ enum ImportType {
twoFas,
bitwarden,
lastpass,
proton,
}
class ImportCodePage extends StatelessWidget {
@@ -29,6 +30,7 @@ class ImportCodePage extends StatelessWidget {
ImportType.aegis,
ImportType.bitwarden,
ImportType.googleAuthenticator,
ImportType.proton,
ImportType.ravio,
ImportType.lastpass,
];
@@ -51,6 +53,8 @@ class ImportCodePage extends StatelessWidget {
return 'Bitwarden';
case ImportType.lastpass:
return 'LastPass Authenticator';
case ImportType.proton:
return 'Proton Authenticator';
}
}

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