diff --git a/lib/consts.dart b/lib/consts.dart index 7eb1ae95f..fbaa72da5 100644 --- a/lib/consts.dart +++ b/lib/consts.dart @@ -70,6 +70,7 @@ final kIconRemoveLight = Icon( const kCodePreviewLinesLimit = 500; enum HistoryRetentionPeriod { + fiveSeconds("5 Seconds", Icons.access_time_rounded), oneWeek("1 Week", Icons.calendar_view_week_rounded), oneMonth("1 Month", Icons.calendar_view_month_rounded), threeMonths("3 Months", Icons.calendar_month_rounded), diff --git a/lib/main.dart b/lib/main.dart index 8b5fab32a..edee291ae 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -47,7 +47,7 @@ Future initApp( ); debugPrint("openBoxesStatus: $openBoxesStatus"); if (openBoxesStatus) { - await autoClearHistory(settingsModel: settingsModel); + await HistoryServiceImpl().autoClearHistory(settingsModel: settingsModel); } return openBoxesStatus; } catch (e) { diff --git a/lib/services/history_service.dart b/lib/services/history_service.dart index 487b91640..bdd21283c 100644 --- a/lib/services/history_service.dart +++ b/lib/services/history_service.dart @@ -1,42 +1,62 @@ +import 'dart:developer'; + +import 'dart:async'; import 'package:apidash/models/models.dart'; import 'package:apidash/utils/utils.dart'; + import 'hive_services.dart'; -Future autoClearHistory({SettingsModel? settingsModel}) async { - final historyRetentionPeriod = settingsModel?.historyRetentionPeriod; - DateTime? retentionDate = getRetentionDate(historyRetentionPeriod); +abstract class HistoryService { + Future autoClearHistory({required SettingsModel? settingsModel}); +} - if (retentionDate == null) { - return; - } else { - List? historyIds = hiveHandler.getHistoryIds(); - List toRemoveIds = []; +class HistoryServiceImpl implements HistoryService { + @override + Future autoClearHistory({required SettingsModel? settingsModel}) async { + try { + final historyRetentionPeriod = settingsModel?.historyRetentionPeriod; + DateTime? retentionDate = getRetentionDate(historyRetentionPeriod); + if (retentionDate == null) return; - if (historyIds == null || historyIds.isEmpty) { - return; - } + List? historyIds = hiveHandler.getHistoryIds(); + if (historyIds == null || historyIds.isEmpty) return; - for (var historyId in historyIds) { - var jsonModel = hiveHandler.getHistoryMeta(historyId); - if (jsonModel != null) { - var jsonMap = Map.from(jsonModel); - HistoryMetaModel historyMetaModelFromJson = - HistoryMetaModel.fromJson(jsonMap); - if (historyMetaModelFromJson.timeStamp.isBefore(retentionDate)) { - toRemoveIds.add(historyId); + List toRemoveIds = historyIds.where((historyId) { + var jsonModel = hiveHandler.getHistoryMeta(historyId); + if (jsonModel != null) { + var jsonMap = Map.from(jsonModel); + HistoryMetaModel historyMetaModelFromJson = + HistoryMetaModel.fromJson(jsonMap); + return historyMetaModelFromJson.timeStamp.isBefore(retentionDate); } + return false; + }).toList(); + + if (toRemoveIds.isEmpty) return; + + int batchSize = calculateOptimalBatchSize(toRemoveIds.length); + final List> batches = createBatches(toRemoveIds, batchSize); + + for (var batch in batches) { + await deleteRecordsInBatches(batch); } - } - if (toRemoveIds.isEmpty) { - return; + hiveHandler.setHistoryIds(historyIds..removeWhere(toRemoveIds.contains)); + } catch (e, st) { + log("Error clearing history records", + name: "autoClearHistory", error: e, stackTrace: st); } + } - for (var id in toRemoveIds) { - await hiveHandler.deleteHistoryRequest(id); - hiveHandler.deleteHistoryMeta(id); + static Future deleteRecordsInBatches(List batch) async { + try { + for (var id in batch) { + hiveHandler.deleteHistoryMeta(id); + hiveHandler.deleteHistoryRequest(id); + } + } catch (e, st) { + log("Error deleting records in batches", + name: "deleteRecordsInBatches", error: e, stackTrace: st); } - hiveHandler.setHistoryIds( - historyIds..removeWhere((id) => toRemoveIds.contains(id))); } } diff --git a/lib/utils/history_utils.dart b/lib/utils/history_utils.dart index d1c3dbbc3..d2851c1f4 100644 --- a/lib/utils/history_utils.dart +++ b/lib/utils/history_utils.dart @@ -1,9 +1,17 @@ import 'package:apidash/models/models.dart'; import 'package:apidash/consts.dart'; + import 'convert_utils.dart'; DateTime stripTime(DateTime dateTime) { - return DateTime(dateTime.year, dateTime.month, dateTime.day); + return DateTime( + dateTime.year, + dateTime.month, + dateTime.day, + dateTime.hour, + dateTime.minute, + dateTime.second, + ); } RequestModel getRequestModelFromHistoryModel(HistoryRequestModel model) { @@ -115,11 +123,30 @@ List getRequestGroup( return requestGroup; } +int calculateOptimalBatchSize(int totalRecords) { + if (totalRecords < 100) return 50; + if (totalRecords < 500) return 80; + if (totalRecords < 5000) return 100; + return 500; +} + +List> createBatches(List items, int batchSize) { + return List.generate( + (items.length / batchSize).ceil(), + (index) => items.sublist( + index * batchSize, + (index * batchSize + batchSize).clamp(0, items.length), + ), + ); +} + DateTime? getRetentionDate(HistoryRetentionPeriod? retentionPeriod) { DateTime now = DateTime.now(); DateTime today = stripTime(now); switch (retentionPeriod) { + case HistoryRetentionPeriod.fiveSeconds: + return today.subtract(const Duration(seconds: 5)); case HistoryRetentionPeriod.oneWeek: return today.subtract(const Duration(days: 7)); case HistoryRetentionPeriod.oneMonth: