Skip to content

Commit

Permalink
fix: 5693 - able to run tasks without minimum duration wait (#5694)
Browse files Browse the repository at this point in the history
* fix: 5693 - able to run tasks without minimum duration wait

Impacted files:
* `background_task_manager.dart`: made it possible to force an immediate run if possible
* `offline_tasks_page.dart`: force an immediate run if possible when clicking on the refresh button

* Added a call to BackgroundTaskManager.run related to prices

* Minor fix

* Wtf tests

* Ugly fix for mocking hive
  • Loading branch information
monsieurtanuki authored Oct 20, 2024
1 parent 29f433c commit a8cfcd0
Show file tree
Hide file tree
Showing 9 changed files with 68 additions and 18 deletions.
54 changes: 37 additions & 17 deletions packages/smooth_app/lib/background/background_task_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class BackgroundTaskManager {
);
await DaoStringList(localDatabase).add(DaoStringList.keyTasks, taskId);
await task.preExecute(localDatabase);
run();
run(forceNowIfPossible: true);
}

/// Finishes a task cleanly.
Expand Down Expand Up @@ -103,18 +103,24 @@ class BackgroundTaskManager {
static const int _minimumDurationBetweenRuns = 5 * 1000;

/// Returns the "now" timestamp if we can run now, or `null`.
int? _canStartNow() {
final DaoInt daoInt = DaoInt(localDatabase);
///
/// With [forceNowIfPossible] we can be more aggressive and force the decision
/// of running now or at least just after the current running block.
int? _canStartNow(final bool forceNowIfPossible) {
final int now = LocalDatabase.nowInMillis();
final int? latestRunStart = daoInt.get(_lastStartTimestampKey);
final int? latestRunStop = daoInt.get(_lastStopTimestampKey);
final int? latestRunStart = localDatabase.daoIntGet(_lastStartTimestampKey);
final int? latestRunStop = localDatabase.daoIntGet(_lastStopTimestampKey);
if (_running) {
// if pretending to be running but started a very very long time ago
if (latestRunStart != null &&
latestRunStart + _aLongEnoughTimeInMilliseconds < now) {
// we assume we can run now.
return now;
}
// let's try again at the end of the current run.
if (forceNowIfPossible) {
_forceRunAgain = true;
}
return null;
}
// if the last run stopped correctly or was started a long time ago.
Expand All @@ -123,7 +129,10 @@ class BackgroundTaskManager {
// if the last run stopped not enough time ago.
if (latestRunStop != null &&
latestRunStop + _minimumDurationBetweenRuns >= now) {
return null;
// let's apply that minimum duration if there's no rush
if (!forceNowIfPossible) {
return null;
}
}
return now;
}
Expand All @@ -132,20 +141,27 @@ class BackgroundTaskManager {

/// Signals we've just finished working and that we're ready for a new run.
Future<void> _justFinished() async {
await DaoInt(localDatabase).put(_lastStartTimestampKey, null);
await DaoInt(localDatabase).put(
await localDatabase.daoIntPut(_lastStartTimestampKey, null);
await localDatabase.daoIntPut(
_lastStopTimestampKey,
LocalDatabase.nowInMillis(),
);
}

bool _running = false;

/// Flag to say: I know you're running, please try again, it's worth it.
bool _forceRunAgain = false;

/// Runs all the pending tasks, and then smoothly ends, without awaiting.
void run() {
// no await
_runAsync();
}
///
/// Can be called in 2 cases:
/// 1. we've just created a task and we really want it to be executed ASAP
/// `forceNowIfPossible = true`
/// 2. we're just checking casually if there are pending tasks
/// `forceNowIfPossible = false`
void run({final bool forceNowIfPossible = false}) =>
unawaited(_runAsync(forceNowIfPossible));

/// Runs all the pending tasks, and then smoothly ends.
///
Expand All @@ -154,19 +170,17 @@ class BackgroundTaskManager {
/// If a task fails and another task with the same stamp comes after,
/// we can remove the failed task from the list: it would have been
/// overwritten anyway.
Future<void> _runAsync() async {
final int? now = _canStartNow();
Future<void> _runAsync(final bool forceNowIfPossible) async {
final int? now = _canStartNow(forceNowIfPossible);
if (now == null) {
return;
}
_running = true;

///
/// Will also set the "latest start timestamp".
/// With this, we can detect a run that went wrong.
/// Like, still running 1 hour later.
final DaoInt daoInt = DaoInt(localDatabase);
await daoInt.put(_lastStartTimestampKey, now);
await localDatabase.daoIntPut(_lastStartTimestampKey, now);
bool runAgain = true;
while (runAgain) {
runAgain = false;
Expand Down Expand Up @@ -196,6 +210,12 @@ class BackgroundTaskManager {
}
}
await _justFinished();
if (!runAgain) {
if (_forceRunAgain) {
runAgain = true;
_forceRunAgain = false;
}
}
}
_running = false;
}
Expand Down
7 changes: 7 additions & 0 deletions packages/smooth_app/lib/database/local_database.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ class LocalDatabase extends ChangeNotifier {
List<String> getAllTaskIds() =>
DaoStringList(this).getAll(DaoStringList.keyTasks);

/// Ugly solution to be able to mock hive data.
int? daoIntGet(final String key) => DaoInt(this).get(key);

/// Ugly solution to be able to mock hive data.
Future<void> daoIntPut(final String key, final int? value) =>
DaoInt(this).put(key, value);

static Future<LocalDatabase> getLocalDatabase() async {
// sql from there
String? databasesRootPath;
Expand Down
4 changes: 3 additions & 1 deletion packages/smooth_app/lib/pages/offline_tasks_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ class _OfflineTaskState extends State<OfflineTaskPage> {
actions: <Widget>[
IconButton(
onPressed: () =>
BackgroundTaskManager.getInstance(localDatabase).run(),
BackgroundTaskManager.getInstance(localDatabase).run(
forceNowIfPossible: true,
),
icon: const Icon(Icons.refresh),
),
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:matomo_tracker/matomo_tracker.dart';
import 'package:provider/provider.dart';
import 'package:smooth_app/background/background_task_manager.dart';
import 'package:smooth_app/data_models/preferences/user_preferences.dart';
import 'package:smooth_app/data_models/product_preferences.dart';
import 'package:smooth_app/data_models/user_management_provider.dart';
import 'package:smooth_app/database/local_database.dart';
import 'package:smooth_app/generic_lib/design_constants.dart';
import 'package:smooth_app/generic_lib/widgets/smooth_back_button.dart';
import 'package:smooth_app/helpers/app_helper.dart';
Expand Down Expand Up @@ -44,6 +46,8 @@ enum PreferencePageType {
required final UserPreferences userPreferences,
required final BuildContext context,
}) {
final LocalDatabase localDatabase = context.read<LocalDatabase>();
BackgroundTaskManager.getInstance(localDatabase).run();
final AppLocalizations appLocalizations = AppLocalizations.of(context);
final ThemeProvider themeProvider = context.read<ThemeProvider>();
final ThemeData themeData = Theme.of(context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import 'package:smooth_app/themes/contrast_provider.dart';
import 'package:smooth_app/themes/theme_provider.dart';

import '../../tests_utils/goldens.dart';
import '../../tests_utils/local_database_mock.dart';
import '../../tests_utils/mocks.dart';

void main() {
Expand Down Expand Up @@ -72,6 +73,7 @@ void main() {
const UserPreferencesPage(
type: PreferencePageType.CONTRIBUTE,
),
localDatabase: MockLocalDatabase(),
),
);
await tester.pumpAndSettle();
Expand Down
9 changes: 9 additions & 0 deletions packages/smooth_app/test/tests_utils/local_database_mock.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@ import 'package:mockito/mockito.dart';
import 'package:smooth_app/database/local_database.dart';

class MockLocalDatabase extends Mock implements LocalDatabase {
final Map<String, int?> _daoInt = <String, int?>{};

@override
List<String> getAllTaskIds() => <String>[];

@override
int? daoIntGet(final String key) => _daoInt[key];

@override
Future<void> daoIntPut(final String key, final int? value) async =>
_daoInt[key] = value;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:smooth_app/themes/contrast_provider.dart';
import 'package:smooth_app/themes/theme_provider.dart';

import '../tests_utils/goldens.dart';
import '../tests_utils/local_database_mock.dart';
import '../tests_utils/mocks.dart';

void main() {
Expand Down Expand Up @@ -51,6 +52,7 @@ void main() {
textContrastProvider,
colorProvider,
const ForgotPasswordPage(),
localDatabase: MockLocalDatabase(),
),
);
await tester.pump();
Expand Down
2 changes: 2 additions & 0 deletions packages/smooth_app/test/users/login_page_layout_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:smooth_app/themes/contrast_provider.dart';
import 'package:smooth_app/themes/theme_provider.dart';

import '../tests_utils/goldens.dart';
import '../tests_utils/local_database_mock.dart';
import '../tests_utils/mocks.dart';

void main() {
Expand Down Expand Up @@ -51,6 +52,7 @@ void main() {
textContrastProvider,
colorProvider,
const LoginPage(),
localDatabase: MockLocalDatabase(),
),
);
await tester.pump();
Expand Down
2 changes: 2 additions & 0 deletions packages/smooth_app/test/users/signup_page_layout_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:smooth_app/themes/contrast_provider.dart';
import 'package:smooth_app/themes/theme_provider.dart';

import '../tests_utils/goldens.dart';
import '../tests_utils/local_database_mock.dart';
import '../tests_utils/mocks.dart';

void main() {
Expand Down Expand Up @@ -51,6 +52,7 @@ void main() {
textContrastProvider,
colorProvider,
const SignUpPage(),
localDatabase: MockLocalDatabase(),
),
);
await tester.pump();
Expand Down

0 comments on commit a8cfcd0

Please sign in to comment.