Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: 5693 - able to run tasks without minimum duration wait #5694

Merged
merged 9 commits into from
Oct 20, 2024
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
Loading