Skip to content

Enhancements#679

Merged
sadespresso merged 12 commits into
betafrom
develop
Mar 6, 2026
Merged

Enhancements#679
sadespresso merged 12 commits into
betafrom
develop

Conversation

@sadespresso

Copy link
Copy Markdown
Collaborator

No description provided.

@sadespresso
sadespresso requested a review from Copilot March 6, 2026 05:59
@sadespresso
sadespresso merged commit b16c91a into beta Mar 6, 2026
4 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR bundles a set of “enhancements” across the Flutter app, iOS project config, and tooling—primarily updating dependencies, refreshing multi-select/filter UX, improving contact handling, and adjusting how pending transactions are surfaced on the Home tab.

Changes:

  • Update several Flutter/Dart dependencies (including flutter_contacts, openai_dart, camera, timezone) and bump app version.
  • Renovate multi-account/category selection sheets and update filter/export flows to use an Optional<List<T>> return type.
  • Update Home tab transaction streams to always include past pending transactions and tweak contact display/photo handling.

Reviewed changes

Copilot reviewed 41 out of 43 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
scripts/translate_missing.dart Migrates to openai_dart v1 client API usage for JSON translation.
pubspec.yaml Bumps app version and upgrades multiple dependencies.
pubspec.lock Lockfile updates to match dependency upgrades and SDK constraints.
macos/Flutter/GeneratedPluginRegistrant.swift Regenerated plugin registrant (adds flutter_contacts, removes path_provider_foundation).
lib/widgets/transaction_filter_head/select_multi_category_sheet.dart Adds select-all/clear UI and changes pop type to Optional<List<Category>>.
lib/widgets/transaction_filter_head/select_multi_account_sheet.dart Adds select-all/clear UI and changes pop type to Optional<List<Account>>.
lib/widgets/sheets/select_contact_sheet.dart Switches to flutter_contacts v2 API and improves avatar handling.
lib/widgets/home/home/info_card.dart Uses null-aware element syntax for optional widgets.
lib/widgets/grouped_transactions_list_view.dart Uses null-aware element syntax for optional header insertion.
lib/widgets/general/modal_sheet.dart Uses null-aware element syntax for optional title widget.
lib/widgets/default_transaction_filter_head.dart Updates account/category selection to consume Optional<List<T>> results.
lib/utils/extensions/flutter_contact.dart Adds resolvedName extension helper for contacts.
lib/services/transactions.dart Adds synchronous findManySync helper.
lib/routes/transaction_tag_page.dart Updates contact import + uses resolvedName.
lib/routes/transaction_page.dart Uses null-aware element syntax for optional extension list entries.
lib/routes/preferences/integrations/eny_preferences_page.dart Adds a try/catch around credit refresh flow.
lib/routes/home/home_tab.dart Refactors streams to merge current + pending transactions into a unified list.
lib/routes/export/export_pdf_page.dart Updates selection sheets to return Optional<List<T>> and adjusts selection logic.
lib/objectbox/actions.dart Prevents Siri-imported tx confirmation from updating transaction date; minor list literal cleanup.
lib/data/transaction_filter.dart Treats empty category whitelist as “uncategorized only” (categoryUuid IS NULL).
ios/Runner/RecordTransactionIntent.swift Makes Siri Shortcut fields optional (except Amount).
ios/Runner/Info.plist Adds Scene manifest entries for iOS scene configuration.
ios/Runner/AppDelegate.swift Switches plugin registration flow to implicit engine delegate callback.
ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved Adds SwiftPM resolved dependencies snapshot.
ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved Adds SwiftPM resolved dependencies snapshot (project workspace).
ios/Runner.xcodeproj/project.pbxproj Updates Xcode project settings for SwiftPM + Flutter generated plugin package reference.
ios/Podfile.lock Pod lockfile updated to reflect iOS dependency/packaging changes.
ios/Flutter/AppFrameworkInfo.plist Updates generated framework plist contents (min OS key removed).
assets/l10n/ar.json Adds general.select.clear translation.
assets/l10n/cs_CZ.json Adds general.select.clear translation.
assets/l10n/de_DE.json Adds general.select.clear translation.
assets/l10n/en.json Adds general.select.clear translation.
assets/l10n/es_ES.json Adds general.select.clear translation.
assets/l10n/fa_IR.json Adds general.select.clear translation.
assets/l10n/fr_FR.json Adds general.select.clear translation.
assets/l10n/it_IT.json Adds general.select.clear translation.
assets/l10n/mn_MN.json Adds general.select.clear translation.
assets/l10n/ru_RU.json Adds general.select.clear translation.
assets/l10n/tr_TR.json Adds general.select.clear translation.
assets/l10n/uk_UA.json Adds general.select.clear translation.
RELEASE_TEMPLATE.md Replaces hard-coded content with {{changelog}} and {{version}} placeholders.
README.md Adds Eny promo section and linked image.
CHANGELOG.md Adds “Next” section entries for pending tx visibility + sheet renovation + iOS shortcuts change.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +289 to 303
final StringMultiFilter? accountsFilterOverride = switch (accounts?.value) {
List<Account> accountsList when accountsList.isNotEmpty =>
StringMultiFilter.whitelist(
accountsList.map((account) => account.uuid).toList(),
),
_ => null,
};

if (accountsFilterOverride != null) {
setState(() {
filter = filter.copyWithOptional(
accounts: Optional(
.whitelist(accounts.map((account) => account.uuid).toList()),
),
accounts: Optional(accountsFilterOverride),
);
});
}

Copilot AI Mar 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

onSelectAccounts only updates the filter when the returned list is non-empty. This makes the sheet’s “clear selection” state (empty list) a no-op, so users can’t clear/reset the accounts filter after opening the sheet. Handle the empty-list case explicitly (e.g., set accounts: Optional(null) to remove the filter, or use a dedicated "keep nothing" filter if that’s the intended semantics).

Copilot uses AI. Check for mistakes.
Comment on lines +43 to 45
FlutterContacts.getAll(properties: {.photoFullRes, .photoThumbnail, .name})
.then((value) {
contacts = value;

Copilot AI Mar 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FlutterContacts.getAll is requested with only {.photoFullRes, .photoThumbnail, .name}, but later the sheet displays contact.phones in the subtitle. With the v2 API, unrequested properties may be missing/empty, so phone numbers may never show. Include .phones (and any other required fields) in the requested properties set.

Copilot uses AI. Check for mistakes.
try {
await EnyService().checkCredits();
} catch (e) {
//

Copilot AI Mar 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The catch block swallows all errors with just //, which makes failures silent and harder to diagnose (especially for network/auth issues). Consider at least logging the exception (and ideally surfacing a user-facing error, e.g. via SnackBar) so refresh failures aren’t silent.

Suggested change
//
// Log the error and notify the user instead of silently swallowing it.
print("Error refreshing Eny credits: $e");
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Failed to refresh Eny credits. Please try again."),
),
);
}

Copilot uses AI. Check for mistakes.
Comment on lines +76 to +89

// leading: showSearchBar
// ? Frame(
// child: TextField(
// onChanged: (value) => setState(() => _query = value),
// textInputAction: TextInputAction.done,
// decoration: InputDecoration(
// hintText: "general.search".t(context),
// prefixIcon: const Icon(Symbols.search_rounded),
// ),
// ),
// )
// : null,
leading: Column(

Copilot AI Mar 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There’s a large block of commented-out leading code left in the widget tree. Since the new leading: Column(...) implementation is in place, removing the commented code will keep the file easier to maintain and avoid confusion during future edits.

Copilot uses AI. Check for mistakes.
@Parameter(title: "Amount", description: "Expense amount. Sign doesn't matter.")
var amount: String

@Parameter(title: "Category", description: "Exact name, or UUID of the target account.")

Copilot AI Mar 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Category parameter description says “UUID of the target account”, which looks like a copy/paste error and is confusing in Shortcuts. Update the description to refer to the target category (and consider mentioning that the field is optional now).

Suggested change
@Parameter(title: "Category", description: "Exact name, or UUID of the target account.")
@Parameter(title: "Category", description: "Exact name, or UUID of the target category. Optional; leave empty if not categorized.")

Copilot uses AI. Check for mistakes.
import "package:flutter_contacts/flutter_contacts.dart";

extension ContactExtension on Contact {
String get resolvedName => displayName ?? name?.first ?? "<unnamed>";

Copilot AI Mar 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolvedName falls back to the hard-coded string "<unnamed>", which is user-visible and not localized. Consider returning an empty string (and handling it in the UI) or using a localized fallback via FlowLocalizations so the placeholder is translated and consistent with the rest of the app.

Copilot uses AI. Check for mistakes.

final List<Transaction> transactions = [
...?pendingTransactionsSnapshot.data,
...?currentTransactionsSnapshot.data,

Copilot AI Mar 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

transactions is built by concatenating pending + current query results, but normalizedCurrentFilter does not exclude isPending == true. That means pending transactions can appear in both snapshots and be duplicated in the merged list (affecting pending group counts/sums and potentially rendering duplicates). Consider either filtering pending out of normalizedCurrentFilter (e.g., set isPending: Optional(false) or add a predicate) or de-duplicating by transaction UUID when merging the lists.

Suggested change
...?currentTransactionsSnapshot.data,
...?currentTransactionsSnapshot.data
?.where((transaction) => transaction.isPending != true),

Copilot uses AI. Check for mistakes.
if (selected == null) return;
if (selected == null ||
selected.value == null ||
selected.value?.isEmpty == null) {

Copilot AI Mar 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The guard selected.value?.isEmpty == null doesn’t check for an empty selection (it’s only true when selected.value is null, which is already checked). If you intend to prevent applying an empty selection, this should check == true; otherwise empty selections will be treated as valid and the current logic may not behave as expected.

Suggested change
selected.value?.isEmpty == null) {
selected.value!.isEmpty) {

Copilot uses AI. Check for mistakes.
Comment on lines +15 to +18
final String openaiApiKey = Platform.environment["OPENAI_API_KEY"]!;
final OpenAIClient client = OpenAIClient(
config: OpenAIConfig(authProvider: ApiKeyProvider(openaiApiKey)),
);

Copilot AI Mar 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Platform.environment["OPENAI_API_KEY"]! will throw when the env var isn’t set, and the exception will be caught and logged as a generic translation error. Prefer handling a missing API key explicitly (with a clear message and early return) so failures are actionable and don’t rely on a forced unwrap.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants