didvan-app/lib/services/migration/app_migration.dart

97 lines
3.8 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ignore_for_file: deprecated_member_use
import 'package:didvan/services/auth/token_storage.dart';
import 'package:didvan/services/storage/storage.dart';
import 'package:flutter/foundation.dart';
import 'package:package_info_plus/package_info_plus.dart';
/// ساده‌ساز migrationهای داده‌ی محلی هنگام آپدیت اپ.
///
/// انگیزه: کاربرانی که از نسخه‌های قبل از سوییچ به API جدید آپدیت می‌کنند
/// توکن/cache قدیمی با schema سرور جدید نمی‌خواند و موقع splash گیر می‌افتد
/// (تا اینکه کاربر دستی Clear Data بکند). این کلاس همان clear data را خودش
/// انجام می‌دهد — فقط یک بار، روی اولین اجرا با نسخه‌ی جدید.
class AppMigration {
/// آخرین نسخه‌ای که schema داده‌ی محلی را شکست و باید کاربران را خروج کند.
/// هر کاربر با نسخه‌ی قبل از این، در آپدیت بعدی auto-logout می‌شود.
/// اگر در آینده schema باز هم شکست، این عدد را افزایش بده.
static const int _breakingDataVersion = 2;
/// کلید storage برای نگه‌داری نسخه‌ی schema که آخرین بار کاربر رویش بود.
static const String _storageKey = '_app_data_schema_version';
/// کلیدهایی که هنگام پاکسازی **حفظ** می‌شوند (UI preferences کاربر).
static const Set<String> _keysToKeep = {
'brightness',
'fontFamily',
'fontScale',
'hasSeenOnboarding',
_storageKey,
};
/// در اولین مرحله‌ی splash صدا زده می‌شود (قبل از هر API call).
/// در صورت لزوم data قدیمی را پاک می‌کند و سپس نسخه‌ی فعلی را ذخیره می‌کند.
static Future<void> runIfNeeded() async {
try {
final stored = await StorageService.getValue(key: _storageKey);
final int previousVersion = _parseInt(stored);
if (previousVersion >= _breakingDataVersion) {
return; // نیازی به migration نیست.
}
debugPrint(
'AppMigration: detected previous schema=$previousVersion '
'(< $_breakingDataVersion), running cleanup...',
);
await _clearLegacyData();
await StorageService.setValue(
key: _storageKey,
value: _breakingDataVersion.toString(),
);
debugPrint('AppMigration: done, marker set to $_breakingDataVersion');
} catch (e) {
// migration نباید app رو crash بده — اگه چیزی شکست، فقط log
debugPrint('AppMigration failed (non-fatal): $e');
}
}
/// پاکسازی auth + همه‌ی state صفحه‌ای؛ UI prefs حفظ می‌شوند.
static Future<void> _clearLegacyData() async {
// 1. توکن‌ها (legacy + keycloak)
try {
await TokenStorage.clear();
} catch (e) {
debugPrint('AppMigration: TokenStorage.clear failed: $e');
}
// 2. کلیدهای متفرقه‌ی legacy
const legacyKeys = [
'token',
'refresh_token',
'user_data',
'user_id',
'notification', // پایه‌ی notification ids
'image-cache',
];
for (final k in legacyKeys) {
try {
await StorageService.delete(key: k);
} catch (_) {}
}
}
static int _parseInt(dynamic v) {
if (v == null) return 0;
if (v is int) return v;
return int.tryParse(v.toString()) ?? 0;
}
/// برای debug — نسخه‌ی فعلی app
static Future<String> currentAppVersion() async {
try {
final info = await PackageInfo.fromPlatform();
return info.version;
} catch (_) {
return 'unknown';
}
}
}