Skip to content

Commit

Permalink
Merge pull request #354 from flow-mn/sadespresso/polishes-0-12-20250216
Browse files Browse the repository at this point in the history
Sadespresso/polishes 0 12 20250216
  • Loading branch information
sadespresso authored Feb 23, 2025
2 parents 191fb94 + 1051dff commit 10bbb6f
Show file tree
Hide file tree
Showing 31 changed files with 489 additions and 184 deletions.
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# Changelog

## Beta 0.12.1

### New features

* Now you can setup daily reminders, so you won't forget to track your expenses

### Changes and improvements

* Now Flow shows warnings for ungranted notification/location permissions
* Flow now saves logs on-device. Logs contain information about certain actions being
executed, and information about failures and errors in the app, and could be used
for debugging.

You can find logs at the end of Backup > Backup history page

> At the moment, most sensitive information stored on the app is transaction UUID,
> which doesn't reveal any sensitive information without the data.
## Beta 0.12.0

### New features
Expand Down
2 changes: 1 addition & 1 deletion assets/l10n/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@
"preferences.transactionGeo.auto.enable": "Auto-attach",
"preferences.transactionGeo.auto.enabled": "Auto-attach enabled",
"preferences.transactionGeo.auto.description": "Automatically attach your current location to new transactions. Even if you have this turned off, you can still choose a location on a map to attach it.",
"preferences.transactionGeo.auto.permissionDenied": "Location permission was denied. You can enable it from your settings if you wish.",
"preferences.transactionGeo.auto.permissionDenied": "Location permission isn't granted",
"preferences.pendingTransactions": "Pending transactions",
"preferences.pendingTransactions.homeTimeframe": "Show on home",
"preferences.pendingTransactions.requireConfirmation": "Require confirmation",
Expand Down
2 changes: 1 addition & 1 deletion assets/l10n/fr_FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@
"preferences.transactionGeo.auto.enable": "Attacher automatiquement",
"preferences.transactionGeo.auto.enabled": "Attachement automatique activé",
"preferences.transactionGeo.auto.description": "Attache automatiquement votre position actuelle aux nouvelles transactions. Même si vous avez désactivé cette option, vous pouvez toujours choisir un lieu sur une carte pour l'attacher.",
"preferences.transactionGeo.auto.permissionDenied": "La permission de localisation a été refusée. Vous pouvez l'activer dans vos paramètres si vous le souhaitez.",
"preferences.transactionGeo.auto.permissionDenied": "L'autorisation de localisation n'est pas accordée",
"preferences.pendingTransactions": "Transactions en attente",
"preferences.pendingTransactions.homeTimeframe": "Afficher sur l'accueil",
"preferences.pendingTransactions.requireConfirmation": "Nécessite une confirmation",
Expand Down
2 changes: 1 addition & 1 deletion assets/l10n/mn_MN.json
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@
"preferences.transactionGeo.auto.enable": "Автоматаар хавсаргах",
"preferences.transactionGeo.auto.enabled": "Автоматаар хавсаргах идэвхжсэн",
"preferences.transactionGeo.auto.description": "Гүйлгээ хийх үед утасны байршлын мэдээллийг автоматаар хавсаргана. Энэ тохиргоог асаагаагүй үед газрын зураг ашиглан өөрөө байршил оруулах боломжтой.",
"preferences.transactionGeo.auto.permissionDenied": "Байршил авах эрх олгоогүй байна. Хүсвэл утасны тохиргооноос идэвхжүүлэх боломжтой.",
"preferences.transactionGeo.auto.permissionDenied": "Байршил авах эрх олгоогүй байна",
"preferences.pendingTransactions": "Хүлээгдэж буй гүйлгээнүүд",
"preferences.pendingTransactions.requireConfirmation": "Баталгаажуулалт шаардах",
"preferences.pendingTransactions.requireConfirmation.description": "Баталгаажуулаагүй гүйлгээнүүд орлого, зарлага, дансны үлдэгдэлд тооцогдохгүй",
Expand Down
2 changes: 1 addition & 1 deletion assets/l10n/tr_TR.json
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@
"preferences.transactionGeo.auto.enable": "Otomatik ekleme",
"preferences.transactionGeo.auto.enabled": "Otomatik ekleme etkin",
"preferences.transactionGeo.auto.description": "Mevcut konumunuzu yeni işlemlere otomatik olarak ekleyin. Bu özelliği kapatmış olsanız bile, eklemek için haritada bir konum seçebilirsiniz.",
"preferences.transactionGeo.auto.permissionDenied": "Konum izni reddedildi. Dilerseniz ayarlarınızdan etkinleştirebilirsiniz.",
"preferences.transactionGeo.auto.permissionDenied": "Konum izni verilmedi",
"preferences.pendingTransactions": "Bekleyen işlemler",
"preferences.pendingTransactions.homeTimeframe": "Ana sayfada göster",
"preferences.pendingTransactions.requireConfirmation": "Onay gerektir",
Expand Down
28 changes: 17 additions & 11 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,23 +48,29 @@ import "package:path_provider/path_provider.dart"
show getApplicationSupportDirectory;
import "package:window_manager/window_manager.dart";

RotatingFileAppender? mainLogAppender;

void main() async {
Logger.root.level = Level.ALL;
if (flowDebugMode) {
PrintAppender(formatter: ColorFormatter()).attachToLogger(Logger.root);
}
unawaited(
getApplicationSupportDirectory().then((value) {
final String logsDir = path.join(value.path, "logs");
Directory(logsDir).createSync(recursive: true);
RotatingFileAppender(
baseFilePath: path.join(
logsDir,
flowDebugMode ? "flow_debug.log" : "flow.log",
),
keepRotateCount: 5,
).attachToLogger(Logger.root);
}),
getApplicationSupportDirectory()
.then((value) {
final String logsDir = path.join(value.path, "logs");
Directory(logsDir).createSync(recursive: true);
mainLogAppender = RotatingFileAppender(
baseFilePath: path.join(
logsDir,
flowDebugMode ? "flow_debug.log" : "flow.log",
),
keepRotateCount: 5,
)..attachToLogger(Logger.root);
})
.catchError((error) {
startupLog.severe("Failed to initialize log file appender", error);
}),
);

WidgetsFlutterBinding.ensureInitialized();
Expand Down
2 changes: 2 additions & 0 deletions lib/routes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "package:flow/routes/accounts_page.dart";
import "package:flow/routes/categories_page.dart";
import "package:flow/routes/category/category_edit_page.dart";
import "package:flow/routes/category_page.dart";
import "package:flow/routes/debug/debug_logs_page.dart";
import "package:flow/routes/debug/debug_theme_page.dart";
import "package:flow/routes/error_page.dart";
import "package:flow/routes/export/export_history_page.dart";
Expand Down Expand Up @@ -370,5 +371,6 @@ final router = GoRouter(
path: "/_debug/theme",
builder: (context, state) => DebugThemePage(),
),
GoRoute(path: "/_debug/logs", builder: (context, state) => DebugLogsPage()),
],
);
2 changes: 1 addition & 1 deletion lib/routes/account/account_edit_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ class _AccountEditPageState extends State<AccountEditPage> {

final int txnCount = TransactionsService().countMany(filter);

final bool? confirmation = await context.showConfirmDialog(
final bool? confirmation = await context.showConfirmationSheet(
isDeletionConfirmation: true,
title: "general.delete.confirmName".t(context, _currentlyEditing!.name),
child: Text("account.delete.description".t(context, txnCount)),
Expand Down
2 changes: 1 addition & 1 deletion lib/routes/category/category_edit_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ class _CategoryEditPageState extends State<CategoryEditPage> {

final int txnCount = TransactionsService().countMany(filter);

final bool? confirmation = await context.showConfirmDialog(
final bool? confirmation = await context.showConfirmationSheet(
isDeletionConfirmation: true,
title: "general.delete.confirmName".t(context, _currentlyEditing.name),
child: Text("category.delete.description".t(context, txnCount)),
Expand Down
85 changes: 85 additions & 0 deletions lib/routes/debug/debug_logs_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import "dart:io";

import "package:flow/main.dart" show mainLogAppender;
import "package:flow/utils/extensions/custom_popups.dart";
import "package:flow/utils/extensions/num.dart";
import "package:flutter/material.dart";
import "package:material_symbols_icons/symbols.dart";
import "package:moment_dart/moment_dart.dart";
import "package:path/path.dart" as path;

class DebugLogsPage extends StatefulWidget {
const DebugLogsPage({super.key});

@override
State<DebugLogsPage> createState() => _DebugLogsPageState();
}

class _DebugLogsPageState extends State<DebugLogsPage> {
bool appenderAvailable = false;

List<File>? files;

@override
void initState() {
super.initState();
appenderAvailable = mainLogAppender != null;
files = mainLogAppender?.getAllLogFiles();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Debug logs")),
body: SingleChildScrollView(
child:
files?.isNotEmpty == true
? Column(
mainAxisSize: MainAxisSize.min,
children:
files!
.map(
(file) => ListTile(
title: Text(path.basename(file.path)),
subtitle: Text(
[
file
.lastModifiedSync()
.toLocal()
.toMoment()
.llll,
file.statSync().size.humanReadableBinarySize,
].join(" • "),
),
trailing: Builder(
builder: (context) {
return IconButton(
onPressed:
() => showShareSheet(
file.path,
context.findRenderObject(),
),
icon: Icon(Symbols.share_rounded),
);
},
),
),
)
.toList(),
)
: Center(child: Text("No log files found")),
),
);
}

Future<void> showShareSheet(String path, RenderObject? renderObject) async {
final RenderBox? renderBox =
renderObject is RenderBox ? renderObject : null;

await context.showShareSheet(
subject: "Share log files",
filePath: path,
renderBox: renderBox,
);
}
}
32 changes: 26 additions & 6 deletions lib/routes/export/export_history_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import "package:flow/objectbox.dart";
import "package:flow/objectbox/objectbox.g.dart";
import "package:flow/widgets/export/export_history/backup_entry_card.dart";
import "package:flow/widgets/export/export_history/no_backups.dart";
import "package:flow/widgets/general/frame.dart";
import "package:flow/widgets/general/spinner.dart";
import "package:flutter/material.dart";
import "package:flutter_slidable/flutter_slidable.dart";
import "package:go_router/go_router.dart";

class ExportHistoryPage extends StatefulWidget {
const ExportHistoryPage({super.key});
Expand Down Expand Up @@ -40,13 +42,31 @@ class _ExportHistoryPageState extends State<ExportHistoryPage> {
(0, true) => const NoBackups(),
(_, true) => SlidableAutoCloseBehavior(
child: ListView.separated(
itemBuilder:
(context, index) => BackupEntryCard(
entry: backupEntires[index],
dismissibleKey: ValueKey(backupEntires[index].id),
),
itemBuilder: (context, index) {
if (index == backupEntires.length) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 16.0),
Frame(
child: Align(
alignment: Alignment.topRight,
child: TextButton(
onPressed: () => context.push("/_debug/logs"),
child: Text("View debug logs"),
),
),
),
],
);
}
return BackupEntryCard(
entry: backupEntires[index],
dismissibleKey: ValueKey(backupEntires[index].id),
);
},
separatorBuilder: (context, index) => separator,
itemCount: backupEntires!.length,
itemCount: backupEntires!.length + 1,
),
),
(_, false) => const Spinner.center(),
Expand Down
1 change: 1 addition & 0 deletions lib/routes/export_options_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class _ExportOptionsPageState extends State<ExportOptionsPage> {
title: "sync.export.history".t(context),
subtitle: "sync.export.history.description".t(context),
),
const SizedBox(height: 16.0),
],
),
),
Expand Down
26 changes: 8 additions & 18 deletions lib/routes/export_page.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import "dart:io";

import "package:flow/entity/backup_entry.dart";
import "package:flow/l10n/extensions.dart";
import "package:flow/sync/export.dart";
import "package:flow/sync/export/mode.dart";
import "package:flow/utils/utils.dart";
import "package:flow/widgets/export/export_success.dart";
import "package:flow/widgets/general/spinner.dart";
import "package:flutter/material.dart";
import "package:moment_dart/moment_dart.dart";
import "package:share_plus/share_plus.dart";

class ExportPage extends StatefulWidget {
final ExportMode mode;
Expand Down Expand Up @@ -55,7 +53,7 @@ class _ExportPageState extends State<ExportPage> {

return ExportSuccess(
mode: widget.mode,
shareFn: () => showShareSheet(),
shareFn: showShareSheet,
filePath: filePath!,
);
}
Expand All @@ -80,25 +78,17 @@ class _ExportPageState extends State<ExportPage> {
}
}

Future<void> showShareSheet() async {
if (Platform.isLinux) {
// openUrl(Uri.parse("file://$filePath"));
Process.runSync("xdg-open", [File(filePath!).parent.path]);
return;
}

final box = context.findRenderObject() as RenderBox?;
Future<void> showShareSheet(RenderObject? renderObject) async {
final RenderBox? renderBox =
renderObject is RenderBox ? renderObject : null;

final origin =
box == null ? Rect.zero : box.localToGlobal(Offset.zero) & box.size;

await Share.shareXFiles(
[XFile(filePath!)],
sharePositionOrigin: origin,
await context.showShareSheet(
subject: "sync.export.save.shareTitle".t(context, {
"type": widget.mode.name,
"date": Moment.now().lll,
}),
filePath: filePath!,
renderBox: renderBox,
);
}
}
2 changes: 1 addition & 1 deletion lib/routes/import_wizard/v1.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class _ImportWizardV1PageState extends State<ImportWizardV1Page> {
}

void _start() async {
final bool? confirm = await context.showConfirmDialog(
final bool? confirm = await context.showConfirmationSheet(
title: "sync.import.eraseWarning".t(context),
isDeletionConfirmation: true,
mainActionLabelOverride: "general.confirm".t(context),
Expand Down
2 changes: 1 addition & 1 deletion lib/routes/import_wizard/v2.dart
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class _ImportWizardV2PageState extends State<ImportWizardV2Page> {
}

void _start() async {
final bool? confirm = await context.showConfirmDialog(
final bool? confirm = await context.showConfirmationSheet(
title: "sync.import.eraseWarning".t(context),
isDeletionConfirmation: true,
mainActionLabelOverride: "general.confirm".t(context),
Expand Down
Loading

0 comments on commit 10bbb6f

Please sign in to comment.