Quotation
This commit is contained in:
75
frontend/pweb/lib/providers/quotation/auto_refresh.dart
Normal file
75
frontend/pweb/lib/providers/quotation/auto_refresh.dart
Normal file
@@ -0,0 +1,75 @@
|
||||
import 'dart:async';
|
||||
|
||||
|
||||
class QuotationAutoRefreshController {
|
||||
bool _enabled = true;
|
||||
Timer? _timer;
|
||||
DateTime? _scheduledAt;
|
||||
DateTime? _triggeredAt;
|
||||
|
||||
void setEnabled(bool enabled) {
|
||||
if (_enabled == enabled) return;
|
||||
_enabled = enabled;
|
||||
}
|
||||
|
||||
void sync({
|
||||
required bool isLoading,
|
||||
required bool canRefresh,
|
||||
required DateTime? expiresAt,
|
||||
required Future<void> Function() onRefresh,
|
||||
}) {
|
||||
if (!_enabled || isLoading || !canRefresh) {
|
||||
_clearTimer();
|
||||
_scheduledAt = null;
|
||||
_triggeredAt = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (expiresAt == null) {
|
||||
_clearTimer();
|
||||
_scheduledAt = null;
|
||||
_triggeredAt = null;
|
||||
return;
|
||||
}
|
||||
|
||||
final delay = expiresAt.difference(DateTime.now().toUtc());
|
||||
if (delay <= Duration.zero) {
|
||||
if (_triggeredAt != null && _triggeredAt!.isAtSameMomentAs(expiresAt)) {
|
||||
return;
|
||||
}
|
||||
_triggeredAt = expiresAt;
|
||||
_clearTimer();
|
||||
onRefresh();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_scheduledAt != null &&
|
||||
_scheduledAt!.isAtSameMomentAs(expiresAt) &&
|
||||
_timer?.isActive == true) {
|
||||
return;
|
||||
}
|
||||
|
||||
_triggeredAt = null;
|
||||
_clearTimer();
|
||||
_scheduledAt = expiresAt;
|
||||
_timer = Timer(delay, () {
|
||||
onRefresh();
|
||||
});
|
||||
}
|
||||
|
||||
void reset() {
|
||||
_enabled = false;
|
||||
_scheduledAt = null;
|
||||
_triggeredAt = null;
|
||||
_clearTimer();
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_clearTimer();
|
||||
}
|
||||
|
||||
void _clearTimer() {
|
||||
_timer?.cancel();
|
||||
_timer = null;
|
||||
}
|
||||
}
|
||||
135
frontend/pweb/lib/providers/quotation/quotation.dart
Normal file
135
frontend/pweb/lib/providers/quotation/quotation.dart
Normal file
@@ -0,0 +1,135 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:pshared/models/auto_refresh_mode.dart';
|
||||
import 'package:pshared/models/payment/quote/status_type.dart';
|
||||
import 'package:pshared/provider/payment/quotation/quotation.dart';
|
||||
|
||||
import 'package:pweb/providers/quotation/auto_refresh.dart';
|
||||
|
||||
|
||||
class QuotationController extends ChangeNotifier {
|
||||
QuotationProvider? _quotation;
|
||||
AutoRefreshMode _autoRefreshMode = AutoRefreshMode.on;
|
||||
final QuotationAutoRefreshController _autoRefreshController =
|
||||
QuotationAutoRefreshController();
|
||||
Timer? _ticker;
|
||||
|
||||
void update(QuotationProvider quotation) {
|
||||
if (identical(_quotation, quotation)) return;
|
||||
_quotation?.removeListener(_handleQuotationChanged);
|
||||
_quotation = quotation;
|
||||
_quotation?.addListener(_handleQuotationChanged);
|
||||
_handleQuotationChanged();
|
||||
}
|
||||
|
||||
bool get isLoading => _quotation?.isLoading ?? false;
|
||||
Exception? get error => _quotation?.error;
|
||||
bool get canRefresh => _quotation?.canRefresh ?? false;
|
||||
bool get isReady => _quotation?.isReady ?? false;
|
||||
AutoRefreshMode get autoRefreshMode => _autoRefreshMode;
|
||||
|
||||
DateTime? get quoteExpiresAt => _quotation?.quoteExpiresAt;
|
||||
|
||||
Duration? get timeLeft {
|
||||
final expiresAt = quoteExpiresAt;
|
||||
if (expiresAt == null) return null;
|
||||
return expiresAt.difference(DateTime.now().toUtc());
|
||||
}
|
||||
|
||||
bool get isExpired {
|
||||
final remaining = timeLeft;
|
||||
if (remaining == null) return false;
|
||||
return remaining <= Duration.zero;
|
||||
}
|
||||
|
||||
QuoteStatusType get quoteStatus {
|
||||
if (isLoading) return QuoteStatusType.loading;
|
||||
if (error != null) return QuoteStatusType.error;
|
||||
if (_quotation?.quotation == null) return QuoteStatusType.missing;
|
||||
if (isExpired) return QuoteStatusType.expired;
|
||||
return QuoteStatusType.active;
|
||||
}
|
||||
|
||||
bool get hasLiveQuote => isReady && _quotation?.quotation != null && !isExpired;
|
||||
|
||||
void setAutoRefreshMode(AutoRefreshMode mode) {
|
||||
if (_autoRefreshMode == mode) return;
|
||||
_autoRefreshMode = mode;
|
||||
_syncAutoRefresh();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void refreshQuotation() {
|
||||
_quotation?.refreshQuotation();
|
||||
}
|
||||
|
||||
void _handleQuotationChanged() {
|
||||
_syncAutoRefresh();
|
||||
_syncTicker();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void _syncTicker() {
|
||||
final expiresAt = quoteExpiresAt;
|
||||
if (expiresAt == null) {
|
||||
_stopTicker();
|
||||
return;
|
||||
}
|
||||
|
||||
final remaining = expiresAt.difference(DateTime.now().toUtc());
|
||||
if (remaining <= Duration.zero) {
|
||||
_stopTicker();
|
||||
return;
|
||||
}
|
||||
|
||||
_ticker ??= Timer.periodic(const Duration(seconds: 1), (_) {
|
||||
final expiresAt = quoteExpiresAt;
|
||||
if (expiresAt == null) {
|
||||
_stopTicker();
|
||||
return;
|
||||
}
|
||||
final remaining = expiresAt.difference(DateTime.now().toUtc());
|
||||
if (remaining <= Duration.zero) {
|
||||
_stopTicker();
|
||||
}
|
||||
notifyListeners();
|
||||
});
|
||||
}
|
||||
|
||||
void _stopTicker() {
|
||||
_ticker?.cancel();
|
||||
_ticker = null;
|
||||
}
|
||||
|
||||
void _syncAutoRefresh() {
|
||||
final quotation = _quotation;
|
||||
if (quotation == null) {
|
||||
_autoRefreshController.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
final isAutoRefreshEnabled = _autoRefreshMode == AutoRefreshMode.on;
|
||||
_autoRefreshController.setEnabled(isAutoRefreshEnabled);
|
||||
final canAutoRefresh = isAutoRefreshEnabled && quotation.canRefresh;
|
||||
_autoRefreshController.sync(
|
||||
isLoading: quotation.isLoading,
|
||||
canRefresh: canAutoRefresh,
|
||||
expiresAt: quoteExpiresAt,
|
||||
onRefresh: _refreshQuotation,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _refreshQuotation() async {
|
||||
await _quotation?.refreshQuotation();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_quotation?.removeListener(_handleQuotationChanged);
|
||||
_autoRefreshController.dispose();
|
||||
_stopTicker();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user