// Haveno App extends the features of Haveno, supporting mobile devices and more.
// Copyright (C) 2024 Kewbit (https://kewbit.org)
// Source Code: https://git.haveno.com/haveno/haveno-app.git
//
// Author: Kewbit
// Website: https://kewbit.org
// Contact Email: me@kewbit.org
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:haveno/grpc_models.dart';
import 'package:haveno/profobuf_models.dart';
import 'package:shared_preferences/shared_preferences.dart';
abstract class DeviceManagerAutoInitialization {
Future init();
}
abstract class PollingProvider with ChangeNotifier {
Timer? _timer;
bool _isPolling = false;
final Duration maxPollingInterval;
PollingProvider(this.maxPollingInterval);
// Method to be implemented by subclasses to define the polling action
Future pollAction();
// Method to start polling with a custom interval
void startPolling([Duration? interval]) {
if (_isPolling) return; // Prevent multiple polling tasks
_isPolling = true;
_timer = Timer.periodic(interval ?? maxPollingInterval, (Timer t) async {
await pollAction();
});
}
// Method to stop polling
void stopPolling() {
_timer?.cancel();
_isPolling = false;
}
@override
void dispose() {
stopPolling();
super.dispose();
}
}
class SyncTask {
final Future Function() taskFunction;
final Duration cooldown;
final List dependencies;
DateTime _lastRun;
SyncTask({
required this.taskFunction,
required this.cooldown,
this.dependencies = const [], // Default to no dependencies
}) : _lastRun = DateTime.fromMillisecondsSinceEpoch(0); // Initialize to a time far in the past
bool shouldRun() {
// Check if all dependencies have been run
bool dependenciesMet = dependencies.every((task) => task.hasRun);
// Check if cooldown period has passed and dependencies are met
return dependenciesMet && DateTime.now().difference(_lastRun) >= cooldown;
}
Future run() async {
if (shouldRun()) {
await taskFunction();
_lastRun = DateTime.now();
}
}
bool get hasRun => _lastRun.isAfter(DateTime.fromMillisecondsSinceEpoch(0));
}
class SyncManager {
final List _tasks = [];
final Duration checkInterval;
Timer? _timer;
SyncManager({required this.checkInterval});
void addTask(SyncTask task) {
_tasks.add(task);
}
void start() {
_timer = Timer.periodic(checkInterval, (timer) async {
// Iterate over tasks, making sure dependencies are resolved
for (var task in _tasks) {
await task.run();
}
});
}
void stop() {
_timer?.cancel();
}
}
mixin CooldownMixin {
final Map _cooldownDurations = {};
// Initialize cooldown durations
void setCooldownDurations(Map durations) {
_cooldownDurations.addAll(durations);
}
// Check if the cooldown is valid by comparing the current time with the stored last run time
Future isCooldownValid(String key) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
final lastRunTimestamp = prefs.getInt('cooldown_$key');
if (lastRunTimestamp == null) return false; // No previous run recorded, so it's invalid.
final lastRunTime = DateTime.fromMillisecondsSinceEpoch(lastRunTimestamp);
final duration = _cooldownDurations[key] ?? const Duration(minutes: 5);
return DateTime.now().isBefore(lastRunTime.add(duration)); // Valid if current time is before cooldown expires.
}
// Update the cooldown with the current time and store it in shared_preferences
Future updateCooldown(String key) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
final now = DateTime.now();
final duration = _cooldownDurations[key] ?? const Duration(minutes: 5); // Default to 5 minutes if not defined
final cooldownEndTime = now.add(duration);
await prefs.setInt('cooldown_$key', cooldownEndTime.millisecondsSinceEpoch); // Store the expiration time
}
// Optionally, you can add a method to clear cooldowns for testing or reset purposes
Future clearCooldown(String key) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.remove('cooldown_$key');
}
}
abstract class PlatformLifecycleWidget extends StatefulWidget {
final Widget child;
final Widget Function(BuildContext context, Widget child) builder;
const PlatformLifecycleWidget({
super.key,
required this.child,
required this.builder,
});
}
abstract class PlatformLifecycleState extends State {
Future initPlatform();
@override
void initState() {
super.initState();
initPlatform();
}
@override
Widget build(BuildContext context) {
return widget.builder(context, widget.child);
}
}
enum BackgroundEventType {
updateTorStatus,
updateDaemonStatus,
torStdOutLog,
torStdErrLog,
}
class BackgroundEvent {
final BackgroundEventType type;
final Map data;
BackgroundEvent({required this.type, required this.data});
}
class EventDispatcher {
final Map)>> _listeners = {};
void subscribe(BackgroundEventType eventType, Function(Map) callback) {
_listeners[eventType] ??= [];
_listeners[eventType]!.add(callback);
}
void unsubscribe(BackgroundEventType eventType, Function(Map) callback) {
_listeners[eventType]?.remove(callback);
}
void dispatch(BackgroundEvent event) {
if (_listeners[event.type] != null) {
for (var listener in _listeners[event.type]!) {
listener(event.data);
}
}
}
}
mixin StreamListenerProviderMixin on ChangeNotifier {
StreamSubscription? _subscription;
// A helper function to manage stream subscriptions
void listenToStream(Stream stream, VoidCallback onData) {
_subscription = stream.listen((_) {
onData(); // Perform action when data arrives (e.g., notifyListeners)
});
}
@override
void dispose() {
_subscription?.cancel();
super.dispose();
}
}
// Notification callbacks
typedef NewChatMessageCallback = void Function(ChatMessage chatMessage);
typedef TradeUpdateCallback = void Function(TradeInfo trade, bool isNewTrade);
// Background service callbacks
typedef UpdateTorStatusBackgroundCallback = void Function(String status, String detail);
typedef UpdateDaemonStatusBackgroundCallback = void Function(String status, String detail);
typedef TorStdOutLogBackgroundCallback = void Function(String details);
typedef TorStdErrLogBackgroundCallback = void Function(String details);