Updates to add more ads and fix ad types.
This commit is contained in:
parent
ad850d2be6
commit
ad2d932842
17 changed files with 552 additions and 59 deletions
|
|
@ -54,6 +54,8 @@ class _ConfirmFuelEntryScreenState extends State<ConfirmFuelEntryScreen> {
|
|||
_totalController = TextEditingController(
|
||||
text: widget.parsed.totalCost?.toStringAsFixed(2) ?? '',
|
||||
);
|
||||
// So an ad is ready by the time _save() finishes without delaying it.
|
||||
context.read<AppState>().preloadFuelSaveAd();
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -92,6 +92,25 @@ class _DataSettingsScreenState extends State<DataSettingsScreen> {
|
|||
}
|
||||
}
|
||||
|
||||
/// The resting (non-syncing) Sync Now icon — badged with the same
|
||||
/// play-triangle [CloudBackupActionButton] uses, for anyone who isn't on
|
||||
/// an active ad-free purchase, since tapping this can trigger the
|
||||
/// rewarded ad gate (see [AppState.syncNow]/`AdService.showGateAd`). Not
|
||||
/// a live prediction of whether *this* tap specifically will show one —
|
||||
/// same general "this leads to an ad-supported feature" disclosure the
|
||||
/// icon-badge uses elsewhere, not an attempt to account for the open
|
||||
/// gate window or the free-first-sync case.
|
||||
Widget _syncNowIcon(BuildContext context, AppState appState) {
|
||||
const icon = Icon(Icons.sync);
|
||||
if (appState.adsCurrentlyDisabled) return icon;
|
||||
final colors = Theme.of(context).colorScheme;
|
||||
return Badge(
|
||||
backgroundColor: colors.tertiary,
|
||||
label: Icon(Icons.play_arrow, size: 8, color: colors.onTertiary),
|
||||
child: icon,
|
||||
);
|
||||
}
|
||||
|
||||
/// Shared warning-dialog shell for both purge actions — [message] is the
|
||||
/// caller's job to make specific and unambiguous, since this is the only
|
||||
/// thing standing between the user and an unrecoverable delete.
|
||||
|
|
@ -283,7 +302,7 @@ class _DataSettingsScreenState extends State<DataSettingsScreen> {
|
|||
height: 16,
|
||||
width: 16,
|
||||
child: CircularProgressIndicator(strokeWidth: 2))
|
||||
: const Icon(Icons.sync),
|
||||
: _syncNowIcon(context, appState),
|
||||
label: const Text('Sync Now'),
|
||||
),
|
||||
TextButton.icon(
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import 'package:provider/provider.dart';
|
|||
import '../models/vehicle.dart';
|
||||
import '../services/app_state.dart';
|
||||
import '../services/onboarding_keys.dart';
|
||||
import '../widgets/cloud_backup_action_button.dart';
|
||||
import 'add_edit_vehicle_screen.dart';
|
||||
import 'faq_screen.dart';
|
||||
import 'vehicle_detail_screen.dart';
|
||||
|
|
@ -31,6 +32,7 @@ class HomeScreen extends StatelessWidget {
|
|||
MaterialPageRoute(builder: (_) => const AddEditVehicleScreen()),
|
||||
),
|
||||
),
|
||||
const CloudBackupActionButton(),
|
||||
const FaqActionButton(),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import '../services/app_state.dart';
|
|||
import '../services/estimated_refund.dart';
|
||||
import '../services/onboarding_keys.dart';
|
||||
import '../theme/app_theme.dart';
|
||||
import '../widgets/cloud_backup_action_button.dart';
|
||||
import '../widgets/hero_banner.dart';
|
||||
import '../widgets/receipt_capture.dart';
|
||||
import 'add_edit_vehicle_screen.dart';
|
||||
|
|
@ -144,6 +145,7 @@ class _ReceiptsScreenState extends State<ReceiptsScreen> with AutomaticKeepAlive
|
|||
tooltip: 'Log Fuel Receipt',
|
||||
onPressed: () => _addReceipt(context),
|
||||
),
|
||||
const CloudBackupActionButton(),
|
||||
const FaqActionButton(),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
|
@ -12,6 +13,7 @@ import '../services/fuel_report.dart';
|
|||
import '../services/fuel_report_images.dart';
|
||||
import '../services/fuel_report_pdf.dart';
|
||||
import '../services/onboarding_keys.dart';
|
||||
import '../widgets/cloud_backup_action_button.dart';
|
||||
import 'faq_screen.dart';
|
||||
|
||||
/// Missouri's Motor Fuel Refund Claim form isn't something this app can
|
||||
|
|
@ -59,6 +61,7 @@ class _ReportScreenState extends State<ReportScreen> with AutomaticKeepAliveClie
|
|||
final (start, end) = defaultReportDateRange(DateTime.now());
|
||||
_startDate = start;
|
||||
_endDate = end;
|
||||
context.read<AppState>().preloadReportAd();
|
||||
}
|
||||
|
||||
Future<void> _pickDate({required bool isStart}) async {
|
||||
|
|
@ -118,6 +121,7 @@ class _ReportScreenState extends State<ReportScreen> with AutomaticKeepAliveClie
|
|||
try {
|
||||
final bytes = await _buildPdfBytes(report);
|
||||
await Printing.sharePdf(bytes: bytes, filename: fuelReportFileName(report));
|
||||
if (mounted) unawaited(context.read<AppState>().maybeShowReportAd());
|
||||
} finally {
|
||||
if (mounted) setState(() => _busy = false);
|
||||
}
|
||||
|
|
@ -128,6 +132,7 @@ class _ReportScreenState extends State<ReportScreen> with AutomaticKeepAliveClie
|
|||
try {
|
||||
final bytes = await _buildPdfBytes(report);
|
||||
await Printing.layoutPdf(onLayout: (_) async => bytes, name: fuelReportFileName(report));
|
||||
if (mounted) unawaited(context.read<AppState>().maybeShowReportAd());
|
||||
} finally {
|
||||
if (mounted) setState(() => _busy = false);
|
||||
}
|
||||
|
|
@ -194,7 +199,7 @@ class _ReportScreenState extends State<ReportScreen> with AutomaticKeepAliveClie
|
|||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Fuel Report'),
|
||||
actions: const [FaqActionButton()],
|
||||
actions: const [CloudBackupActionButton(), FaqActionButton()],
|
||||
),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import 'package:provider/provider.dart';
|
|||
|
||||
import '../services/app_state.dart';
|
||||
import '../services/onboarding_keys.dart';
|
||||
import '../widgets/cloud_backup_action_button.dart';
|
||||
import 'data_settings_screen.dart';
|
||||
import 'faq_screen.dart';
|
||||
import 'ui_settings_screen.dart';
|
||||
|
|
@ -22,7 +23,7 @@ class SettingsScreen extends StatelessWidget {
|
|||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Settings'),
|
||||
actions: const [FaqActionButton()],
|
||||
actions: const [CloudBackupActionButton(), FaqActionButton()],
|
||||
),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
|
|
|
|||
|
|
@ -61,7 +61,12 @@ class UserAgreementScreen extends StatelessWidget {
|
|||
'this device. If you connect a cloud storage account (Google Drive, '
|
||||
'Dropbox, OneDrive, or your own WebDAV server) in Settings, a copy is '
|
||||
'also kept there — in storage you control, not on any server we run. '
|
||||
"We don't operate a backend, and we don't have a copy of your data.",
|
||||
"We don't operate a backend, and we don't have a copy of your data.\n\n"
|
||||
"This is also how sharing works: if the folder you connect to is one "
|
||||
"you share with someone else, anyone else who connects the app to "
|
||||
"that same folder sees and can edit the same vehicles, fuel entries, "
|
||||
"and receipt photos you do. Data in a shared folder isn't private to "
|
||||
"just you.",
|
||||
),
|
||||
_Section(
|
||||
title: 'Privacy',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue