diff --git a/CHANGELOG.md b/CHANGELOG.md index c1ead36..40335a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,55 @@ Notable changes to Latch. +## 0.16.0-beta.1 + +### Encryption Management +- **Encryption management screen** for batch encrypt/decrypt of vault files +- `encryptFileInPlace` and `decryptFileInPlace` with progress callbacks +- Decrypt and re-encrypt operations added to progress sheet +- **Search filter and select-all scoping** in the re-encrypt file picker +- Fixed magic-byte constants to match little-endian read order + +### Explorer Redesign +- View modes renamed from sidebar/navigation to **list/grid** +- **List view mode** added to explorer file grid +- Removed sidebar view and resizer widget — simplified navigation + +### Animated Latch Logo +- **Animated Latch logo widget** with staggered entrance animation +- Animated logo entrance on auth method selection screen +- Reduced animation duration and stagger span for snappier feel + +### Performance Optimizations +- **Cancel token support** — cooperative cancellation for decrypt pipeline, file opening, and isolate operations +- Decrypted image cache switched from bytes to **File** to reduce memory pressure +- PDF viewer switched from bytes to **file path** to avoid ANR on large documents +- Encryption migrated to isolate pool; key derivation made async + +### Update System +- **Skip version** option in update dialog +- Deduplicated update emission to prevent repeated prompts + +### UI Polish +- Removed FAB elevation across all screens +- Removed default elevation and shadows from buttons +- Category filter bottom bar in gallery vault +- Theme-aware accent and success colors +- Context-based theming for AuthMethodCard +- Removed overlay transparency from container decoration + +### Shared Components +- **FileInfoSheet** — reusable bottom sheet for file metadata and actions +- Extracted from multiple duplicated implementations into a single shared widget + +### Cleanup +- Removed unused re-encryption feature +- Trimmed changelog; condensed README to core details +- Updated repository URLs and contributor info +- Added Google Play badge and transparency section to README + +--- + ## 0.15.0-beta.1 ### Password Vault & Autofill diff --git a/lib/screens/gallery_vault_screen.dart b/lib/screens/gallery_vault_screen.dart index 26ce9cd..6a57326 100644 --- a/lib/screens/gallery_vault_screen.dart +++ b/lib/screens/gallery_vault_screen.dart @@ -139,7 +139,7 @@ class _GalleryVaultScreenState extends ConsumerState { if (!await service.shouldShow()) return; final version = await service.currentVersion(); - final sections = service.highlightsFor(version); + final sections = await service.highlightsFor(version); if (sections.isEmpty || !mounted) return; await WhatsNewBottomSheet.show( diff --git a/lib/screens/vault_settings_screen.dart b/lib/screens/vault_settings_screen.dart index 565229d..f7d7561 100644 --- a/lib/screens/vault_settings_screen.dart +++ b/lib/screens/vault_settings_screen.dart @@ -798,7 +798,7 @@ class _VaultSettingsScreenState extends ConsumerState { WhatsNewService.instance.startRemoteRefresh(); final service = WhatsNewService.instance; final version = await service.currentVersion(); - final sections = service.highlightsFor(version); + final sections = await service.highlightsFor(version); if (!mounted) return; diff --git a/lib/services/whats_new_service.dart b/lib/services/whats_new_service.dart index 50d982d..6a292a8 100644 --- a/lib/services/whats_new_service.dart +++ b/lib/services/whats_new_service.dart @@ -3,7 +3,9 @@ import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:package_info_plus/package_info_plus.dart'; +import 'package:pub_semver/pub_semver.dart'; import 'package:shared_preferences/shared_preferences.dart'; /// A single highlight item shown in the "What's New" bottom sheet. @@ -33,9 +35,11 @@ class WhatsNewSection { /// Tracks app version transitions and serves the highlights shown to the /// user the first time they open a freshly-updated build. /// -/// Highlights are fetched from the GitHub repo on launch and cached -/// locally. If the network is unavailable the cached copy is used; if no -/// cache exists the built-in fallback is served instead. +/// Highlights are sourced from the bundled `whats_new.json` (mirrored from +/// GitHub) and refreshed from the remote copy on launch. When the network is +/// unavailable the bundled file is used. Version lookups ignore pre-release +/// suffixes, so `0.16.0-beta.1` matches a `0.16.0` entry; when several +/// pre-releases share a core version, the latest wins. class WhatsNewService { WhatsNewService._(); static final WhatsNewService instance = WhatsNewService._(); @@ -49,210 +53,9 @@ class WhatsNewService { static const _fetchTimeout = Duration(seconds: 8); - /// Built-in highlights shipped with the app. Used as the ultimate - /// fallback when there is no cached data and the network is unavailable. - /// - /// The key MUST match `PackageInfo.version` exactly (the `+buildNumber` - /// suffix is ignored). - static const Map> _builtIn = { - '0.15.0-beta.1': [ - WhatsNewSection( - title: 'Password Vault & Autofill', - items: [ - WhatsNewItem( - icon: Icons.lock_outlined, - title: 'Password vault', - description: - 'Store, edit, and organise credentials inside the encrypted vault with a built-in strong password generator.', - ), - WhatsNewItem( - icon: Icons.auto_awesome_outlined, - title: 'Android Autofill', - description: - 'Seamlessly fill credentials in other apps and browsers — Latch now integrates with the Android Autofill framework.', - ), - WhatsNewItem( - icon: Icons.info_outlined, - title: 'Clipboard auto-clear', - description: - 'Copied passwords are automatically cleared from the clipboard after use.', - ), - ], - ), - WhatsNewSection( - title: 'Crypto Overhaul', - items: [ - WhatsNewItem( - icon: Icons.shield_outlined, - title: 'Argon2id + key wrapping', - description: - 'AEAD key-wrapping protects the vault master key. Argon2id joins PBKDF2 for memory-hard key derivation.', - ), - WhatsNewItem( - icon: Icons.key_outlined, - title: '600,000 PBKDF2 iterations', - description: - 'Default iterations raised 6× — from 100k to 600k — for stronger resistance against brute-force attacks.', - ), - WhatsNewItem( - icon: Icons.new_releases_outlined, - title: 'Auto re-wrap on PIN change', - description: - 'The encryption key is automatically re-wrapped when you change your password or PIN.', - ), - ], - ), - WhatsNewSection( - title: 'Updates & Stability', - items: [ - WhatsNewItem( - icon: Icons.system_update_outlined, - title: 'GitHub update support', - description: - 'Update checks now detect your install source — Play Store, sideload, or GitHub — and route you accordingly.', - ), - WhatsNewItem( - icon: Icons.bug_report_outlined, - title: 'Comprehensive test suite', - description: - 'New unit tests cover every crypto path: AES-256-GCM, CTR, PBKDF2, Argon2id, and key wrapping.', - ), - WhatsNewItem( - icon: Icons.build_outlined, - title: 'CI workflow active', - description: - 'Automated builds and tests now run on every push to the versionF branch.', - ), - ], - ), - ], - '0.14.4-beta.4': [ - WhatsNewSection( - title: 'Notes', - items: [ - WhatsNewItem( - icon: Icons.note_outlined, - title: 'Encrypted notes', - description: - 'Create, edit, and organise secure notes inside the vault with folders, search, and multi-select.', - ), - WhatsNewItem( - icon: Icons.folder_outlined, - title: 'Note folders', - description: - 'Hierarchical folder organisation with full CRUD operations for your notes.', - ), - ], - ), - WhatsNewSection( - title: 'Audio Recording', - items: [ - WhatsNewItem( - icon: Icons.mic_outlined, - title: 'Voice recording', - description: - 'Record audio directly into the vault with real-time amplitude visualisation and preview.', - ), - ], - ), - WhatsNewSection( - title: 'UI & Design', - items: [ - WhatsNewItem( - icon: Icons.palette_outlined, - title: 'Adaptive logo', - description: - 'Logo automatically switches between light and dark variants to match your theme.', - ), - WhatsNewItem( - icon: Icons.build_outlined, - title: 'Resizable sidebar', - description: - 'Drag to resize the vault explorer sidebar to your preferred width.', - ), - ], - ), - WhatsNewSection( - title: 'Under the Hood', - items: [ - WhatsNewItem( - icon: Icons.new_releases_outlined, - title: 'Centralised file service', - description: - 'All vault file opening now routed through a single service for better reliability.', - ), - WhatsNewItem( - icon: Icons.auto_awesome_outlined, - title: 'Always up-to-date', - description: - 'What\'s New and Changelog now fetch live from GitHub — no APK update needed for release notes.', - ), - ], - ), - ], - '0.14.2-beta.3': [ - WhatsNewSection( - title: 'Encryption & Crypto', - items: [ - WhatsNewItem( - icon: Icons.shield_outlined, - title: 'AES-256-GCM v2', - description: - 'Authenticated encryption with an isolate-based crypto worker pool — heavy work runs off the UI thread.', - ), - WhatsNewItem( - icon: Icons.key_outlined, - title: 'Dynamic KDF iterations', - description: - 'Password and PIN hashing now uses per-credential PBKDF2 iteration counts that rotate automatically when vault settings change.', - ), - WhatsNewItem( - icon: Icons.warning_amber_outlined, - title: 'Re-encryption safeguards', - description: - 'Detailed risk explanations are surfaced before any re-encryption begins.', - ), - ], - ), - WhatsNewSection( - title: 'Selection & Gestures', - items: [ - WhatsNewItem( - icon: Icons.touch_app_outlined, - title: 'Hold-to-action', - description: - 'Long-press a file to open an action sheet instead of immediately entering selection mode.', - ), - WhatsNewItem( - icon: Icons.checklist_outlined, - title: 'Multi-select action sheet', - description: - 'Batch operations are now organised into a clean quick-action grid.', - ), - ], - ), - WhatsNewSection( - title: 'Updates & Reliability', - items: [ - WhatsNewItem( - icon: Icons.system_update_outlined, - title: 'In-app updates', - description: - 'Latch now checks the Play Store on launch and offers to update without leaving the app.', - ), - WhatsNewItem( - icon: Icons.bug_report_outlined, - title: 'Sturdier exports', - description: - 'Parallel file export with improved progress tracking, plus fixes for vault entry parsing and empty index saves.', - ), - ], - ), - ], - }; - String? _currentVersion; Map>? _fetchedHighlights; + Map>? _assetHighlights; bool _fetchInitiated = false; /// Maps icon names used in the remote JSON to Flutter [IconData]. @@ -292,6 +95,8 @@ class WhatsNewService { return Icons.note_outlined; case 'mic_outlined': return Icons.mic_outlined; + case 'filter_list': + return Icons.filter_list; case 'info_outlined': return Icons.info_outlined; default: @@ -308,20 +113,69 @@ class WhatsNewService { } /// Returns the highlight sections for [version], preferring the - /// remotely-fetched set when available, then checking the built-in - /// fallback. - List highlightsFor(String version) { - if (_fetchedHighlights != null) { - return _fetchedHighlights![version] ?? const []; + /// remotely-fetched set, then the bundled `whats_new.json` asset. + /// + /// Pre-release suffixes are ignored, so `0.16.0-beta.1` resolves to a + /// `0.16.0` entry. When several pre-releases share a core version the + /// latest one is returned. + Future> highlightsFor(String version) async { + final map = _fetchedHighlights ?? await _assetMap(); + return _lookup(map, version); + } + + Future>> _assetMap() async { + if (_assetHighlights != null) return _assetHighlights!; + try { + _assetHighlights = + _parseJson(await rootBundle.loadString('whats_new.json')); + } catch (_) { + _assetHighlights = {}; + } + return _assetHighlights!; + } + + List _lookup( + Map> map, + String version, + ) { + final exact = map[version]; + if (exact != null) return exact; + + final core = _coreVersion(version); + String? bestKey; + Version? bestVer; + for (final key in map.keys) { + if (_coreVersion(key) != core) continue; + final parsed = _tryParse(key); + if (parsed == null) { + bestKey ??= key; + continue; + } + if (bestVer == null || parsed > bestVer) { + bestVer = parsed; + bestKey = key; + } + } + return bestKey == null ? const [] : map[bestKey]!; + } + + /// Strips build and pre-release suffixes, leaving `major.minor.patch`. + static String _coreVersion(String v) => + v.split('+').first.split('-').first; + + static Version? _tryParse(String v) { + try { + return Version.parse(v); + } catch (_) { + return null; } - return _builtIn[version] ?? const []; } /// Kicks off a non-blocking fetch of the remote highlights JSON. /// /// Call once early in the app lifecycle. The result is ingested on a /// best-effort basis — if the network call fails the service silently - /// keeps using the built-in fallback. + /// keeps using the bundled asset. void startRemoteRefresh() { if (_fetchInitiated) return; _fetchInitiated = true; @@ -417,7 +271,7 @@ class WhatsNewService { } if (lastSeen == current) return false; - return highlightsFor(current).isNotEmpty; + return (await highlightsFor(current)).isNotEmpty; } /// Persist the current version as "seen" so the sheet does not reappear diff --git a/whats_new.json b/whats_new.json index a40b6eb..a14f859 100644 --- a/whats_new.json +++ b/whats_new.json @@ -1,4 +1,56 @@ { + "0.16.0-beta.1": [ + { + "title": "Encryption Management", + "items": [ + { + "icon": "shield_outlined", + "title": "Batch encrypt/decrypt", + "description": "New encryption management screen lets you encrypt or remove encryption from multiple files at once with progress tracking." + }, + { + "icon": "lock_outlined", + "title": "Per-file encryption control", + "description": "Add or strip encryption from individual files in-place with full progress feedback." + } + ] + }, + { + "title": "Explorer & Navigation", + "items": [ + { + "icon": "build_outlined", + "title": "List & grid views", + "description": "Toggle between list and grid layouts in the vault explorer — cleaner and simpler than the old sidebar." + }, + { + "icon": "filter_list", + "title": "Category filter bar", + "description": "Quickly filter your gallery by file type with the new bottom filter bar." + } + ] + }, + { + "title": "Performance", + "items": [ + { + "icon": "speed", + "title": "Cancelable operations", + "description": "File opening and decryption can now be cancelled mid-operation — no more waiting for slow files." + }, + { + "icon": "bug_report_outlined", + "title": "Large PDF fix", + "description": "PDF viewer now streams from disk instead of loading into memory, fixing ANR crashes on large documents." + }, + { + "icon": "auto_awesome_outlined", + "title": "Animated logo", + "description": "A new animated Latch logo greets you on the auth screen with a staggered entrance animation." + } + ] + } + ], "0.15.0-beta.1": [ { "title": "Password Vault & Autofill",