diff --git a/lib/kernel/kernel.dart b/lib/kernel/kernel.dart index e19f4ed8..9c8e3730 100644 --- a/lib/kernel/kernel.dart +++ b/lib/kernel/kernel.dart @@ -34,6 +34,7 @@ export 'src/notify.dart'; export 'src/os.dart'; export 'src/panels/arrangement.dart'; export 'src/project.dart'; +export 'src/scheduler.dart'; export 'src/secrets.dart'; export 'src/tray.dart'; export 'src/panels/drag_resize.dart'; diff --git a/lib/kernel/src/facade.dart b/lib/kernel/src/facade.dart index 8582a14a..00fc0c20 100644 --- a/lib/kernel/src/facade.dart +++ b/lib/kernel/src/facade.dart @@ -22,6 +22,7 @@ import 'package:clide/kernel/src/os.dart'; import 'package:clide/kernel/src/panels/arrangement.dart'; import 'package:clide/kernel/src/panels/registry.dart'; import 'package:clide/kernel/src/project.dart'; +import 'package:clide/kernel/src/scheduler.dart'; import 'package:clide/kernel/src/secrets.dart'; import 'package:clide/kernel/src/settings.dart'; import 'package:clide/kernel/src/theme/controller.dart'; @@ -61,6 +62,7 @@ class KernelServices { required this.extensions, required this.window, required this.toolCheck, + required this.scheduler, }); final Logger log; @@ -88,6 +90,7 @@ class KernelServices { final ExtensionManager extensions; final WindowControls window; final ToolCheck toolCheck; + final SchedulerService scheduler; static Future boot({ required Directory appDir, @@ -136,6 +139,8 @@ class KernelServices { final focus = FocusTracker(); final window = WindowControls(); final toolCheck = ToolCheck(); + final scheduler = SchedulerService(events); + scheduler.start(); final project = ProjectManager( log: log, events: events, @@ -203,6 +208,7 @@ class KernelServices { extensions: extensions, window: window, toolCheck: toolCheck, + scheduler: scheduler, ); } @@ -223,6 +229,7 @@ class KernelServices { focus.dispose(); project.dispose(); extensions.dispose(); + scheduler.dispose(); await log.dispose(); messages.dispose(); await events.dispose(); diff --git a/lib/kernel/src/scheduler.dart b/lib/kernel/src/scheduler.dart new file mode 100644 index 00000000..b3ec5702 --- /dev/null +++ b/lib/kernel/src/scheduler.dart @@ -0,0 +1,77 @@ +import 'dart:async'; +import 'dart:isolate'; + +import 'package:clide/kernel/src/events/bus.dart'; +import 'package:clide/kernel/src/events/types.dart'; +import 'package:flutter/foundation.dart'; + +enum SchedulerTier { + oneMinute(Duration(minutes: 1)), + tenMinutes(Duration(minutes: 10)), + fifteenMinutes(Duration(minutes: 15)), + oneHour(Duration(hours: 1)), + midnight(Duration(minutes: 1)); + + const SchedulerTier(this.interval); + final Duration interval; +} + +@immutable +class SchedulerTick extends ClideEvent { + const SchedulerTick({required this.tier}); + final SchedulerTier tier; + + @override + String get subsystem => 'scheduler'; + @override + String get kind => 'tick'; + @override + Map payload() => {'tier': tier.name}; +} + +class SchedulerService { + SchedulerService(this._events); + final DaemonBus _events; + + Isolate? _isolate; + ReceivePort? _port; + StreamSubscription? _sub; + + void start() { + if (_isolate != null) return; + _port = ReceivePort(); + _sub = _port!.listen((msg) { + if (msg is String) { + final tier = SchedulerTier.values.firstWhere((t) => t.name == msg); + _events.emit(SchedulerTick(tier: tier)); + } + }); + Isolate.spawn(_isolateEntry, _port!.sendPort).then((iso) => _isolate = iso); + } + + static void _isolateEntry(SendPort send) { + int lastDay = DateTime.now().day; + for (final tier in SchedulerTier.values) { + if (tier == SchedulerTier.midnight) { + Timer.periodic(const Duration(minutes: 1), (_) { + final day = DateTime.now().day; + if (day != lastDay) { + lastDay = day; + send.send(tier.name); + } + }); + continue; + } + Timer.periodic(tier.interval, (_) => send.send(tier.name)); + } + } + + void dispose() { + _sub?.cancel(); + _port?.close(); + _isolate?.kill(priority: Isolate.immediate); + _isolate = null; + _port = null; + _sub = null; + } +}