fix:第一轮跑
This commit is contained in:
1 parent
e834ec7586
commit
33235f450b
56 files changed
+4357
-210
No files matched your search
@@ -0,0 +1,128 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io' show File;
|
||||
|
||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
||||
import '../../../common/storage/storage_service.dart';
|
||||
import '../data/ads_api_client.dart';
|
||||
|
||||
class SplashAdEntry {
|
||||
const SplashAdEntry(this.ad, this.dailyCap, this.assetPath, this.cachedAtMs);
|
||||
final AdCreative ad;
|
||||
final int dailyCap;
|
||||
final String? assetPath;
|
||||
final int cachedAtMs;
|
||||
bool get valid =>
|
||||
ad.isRenderable &&
|
||||
DateTime.now().millisecondsSinceEpoch <
|
||||
cachedAtMs + const Duration(hours: 24).inMilliseconds &&
|
||||
(ad.endAtMs <= 0 || ad.endAtMs > DateTime.now().millisecondsSinceEpoch);
|
||||
Map<String, dynamic> toJson() => {
|
||||
'ad': ad.toJson(),
|
||||
'dailyCap': dailyCap,
|
||||
'assetPath': assetPath,
|
||||
'cachedAtMs': cachedAtMs,
|
||||
};
|
||||
factory SplashAdEntry.fromJson(Map<String, dynamic> map) => SplashAdEntry(
|
||||
AdCreative.fromJson(Map<String, dynamic>.from(map['ad'] as Map)),
|
||||
(map['dailyCap'] as num?)?.toInt() ?? 0,
|
||||
map['assetPath'] as String?,
|
||||
(map['cachedAtMs'] as num?)?.toInt() ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
class SplashAdController {
|
||||
SplashAdController(this._api);
|
||||
final AdsApiClient _api;
|
||||
static const _entryKey = 'ads.splash.entry';
|
||||
static const _dailyKey = 'ads.splash.daily';
|
||||
Future<SplashAdEntry?> coldLaunchDecision() async {
|
||||
final raw = StorageService.to.preferences.getString(_entryKey);
|
||||
if (raw == null) return null;
|
||||
try {
|
||||
final entry = SplashAdEntry.fromJson(
|
||||
Map<String, dynamic>.from(jsonDecode(raw) as Map),
|
||||
);
|
||||
if (!entry.valid || !_underCap(entry) || !await _ready(entry)) {
|
||||
return null;
|
||||
}
|
||||
return entry;
|
||||
} catch (_) {
|
||||
await clearCache();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
bool _underCap(SplashAdEntry entry) {
|
||||
final value = StorageService.to.preferences.getString(_dailyKey);
|
||||
final today = DateTime.now().toIso8601String().substring(0, 10);
|
||||
if (value == null) return true;
|
||||
final map = Map<String, dynamic>.from(jsonDecode(value) as Map);
|
||||
return map['date'] != today ||
|
||||
entry.dailyCap <= 0 ||
|
||||
((map['count'] as num?)?.toInt() ?? 0) < entry.dailyCap;
|
||||
}
|
||||
|
||||
Future<bool> _ready(SplashAdEntry entry) async =>
|
||||
entry.assetPath != null && await File(entry.assetPath!).exists();
|
||||
Future<void> markShown(SplashAdEntry entry) async {
|
||||
final today = DateTime.now().toIso8601String().substring(0, 10);
|
||||
final raw = StorageService.to.preferences.getString(_dailyKey);
|
||||
var count = 0;
|
||||
if (raw != null) {
|
||||
final m = Map<String, dynamic>.from(jsonDecode(raw) as Map);
|
||||
if (m['date'] == today) count = (m['count'] as num?)?.toInt() ?? 0;
|
||||
}
|
||||
await StorageService.to.preferences.setString(
|
||||
_dailyKey,
|
||||
jsonEncode({'date': today, 'count': count + 1}),
|
||||
);
|
||||
await _report(entry.ad, 'ad_impression');
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
try {
|
||||
final result = await _api.getAdResult(splashAdPlacement);
|
||||
if (!result.enabled || result.ad == null) {
|
||||
await _clearEntry();
|
||||
return;
|
||||
}
|
||||
final file = await DefaultCacheManager().getSingleFile(
|
||||
result.ad!.imageUrl,
|
||||
);
|
||||
if (!await file.exists()) return;
|
||||
await StorageService.to.preferences.setString(
|
||||
_entryKey,
|
||||
jsonEncode(
|
||||
SplashAdEntry(
|
||||
result.ad!,
|
||||
result.dailyCap,
|
||||
file.path,
|
||||
DateTime.now().millisecondsSinceEpoch,
|
||||
).toJson(),
|
||||
),
|
||||
);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
Future<void> _report(AdCreative ad, String event, {String? clickId}) async {
|
||||
try {
|
||||
await _api.report(ad, event, clickId: clickId);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
Future<void> skip(SplashAdEntry entry) => _report(entry.ad, 'ad_skip');
|
||||
Future<void> clearCache() async {
|
||||
await _clearEntry();
|
||||
await StorageService.to.preferences.remove(_dailyKey);
|
||||
}
|
||||
|
||||
Future<void> _clearEntry() async {
|
||||
await StorageService.to.preferences.remove(_entryKey);
|
||||
}
|
||||
}
|
||||
|
||||
final splashAdControllerProvider = Provider(
|
||||
(ref) => SplashAdController(ref.watch(adsApiClientProvider)),
|
||||
);
|
||||
Reference in new issue
Block a user