feat: implement version check and update dialog; add browser cache clearing functionality
Build and Deploy / build (push) Successful in 1m40s

This commit is contained in:
masoodafar-web
2025-12-27 04:47:27 +03:30
parent 51129704a0
commit 9cd275a777
6 changed files with 510 additions and 46 deletions
@@ -1,8 +1,50 @@
using FrontOffice.BFF.Configuration.Protobuf.Protos.AppVersion;
using Blazored.LocalStorage;
using Microsoft.JSInterop;
namespace FrontOffice.Main.Utilities;
/// <summary>
/// نتیجه بررسی نسخه اپلیکیشن
/// </summary>
public class VersionCheckResult
{
/// <summary>
/// آیا نسخه جدیدی وجود دارد
/// </summary>
public bool HasNewVersion { get; set; }
/// <summary>
/// آیا آپدیت اجباری است (force update)
/// </summary>
public bool IsForceUpdate { get; set; }
/// <summary>
/// نسخه فعلی (local)
/// </summary>
public string? OldVersion { get; set; }
/// <summary>
/// نسخه جدید (server)
/// </summary>
public string? NewVersion { get; set; }
/// <summary>
/// یادداشت‌های انتشار
/// </summary>
public string? ReleaseNotes { get; set; }
/// <summary>
/// پیام آپدیت
/// </summary>
public string? UpdateMessage { get; set; }
/// <summary>
/// آیا کش باید پاک شود
/// </summary>
public bool RequiresCacheClear { get; set; }
}
/// <summary>
/// سرویس مدیریت نسخه اپلیکیشن و کش
/// وقتی نسخه جدید منتشر بشه، این سرویس متوجه میشه و کش رو پاک می‌کنه
@@ -11,31 +53,44 @@ public class AppVersionService
{
private readonly AppVersionContract.AppVersionContractClient _client;
private readonly ILocalStorageService _localStorage;
private readonly IJSRuntime _jsRuntime;
private readonly ILogger<AppVersionService> _logger;
private const string APP_NAME = "FrontOffice";
private const string APP_NAME = "KaraBazarApp";
private const string LOCAL_VERSION_KEY = "app_version";
private const string LAST_CHECK_KEY = "app_version_last_check";
private const string SKIPPED_VERSION_KEY = "app_version_skipped";
private const string LAST_DIALOG_SHOWN_KEY = "app_version_last_dialog_shown";
public AppVersionService(
AppVersionContract.AppVersionContractClient client,
ILocalStorageService localStorage,
IJSRuntime jsRuntime,
ILogger<AppVersionService> logger)
{
_client = client;
_localStorage = localStorage;
_jsRuntime = jsRuntime;
_logger = logger;
}
/// <summary>
/// بررسی نسخه اپلیکیشن و پاک کردن کش در صورت نیاز
/// بررسی نسخه اپلیکیشن و برگرداندن نتیجه
/// </summary>
public async Task CheckVersionAndClearCacheIfNeededAsync()
/// <param name="ignoreSkipped">اگر true باشه، نسخه‌های skip شده هم نشون داده میشن (برای آیکون منو)</param>
/// <param name="forDialog">اگر true باشه، چک میکنه که یک روز از آخرین نمایش گذشته (برای popup)</param>
public async Task<VersionCheckResult> CheckVersionAsync(bool ignoreSkipped = false, bool forDialog = false)
{
var result = new VersionCheckResult();
try
{
// دریافت نسخه فعلی از localStorage
var localVersion = await _localStorage.GetItemAsStringAsync(LOCAL_VERSION_KEY);
var skippedVersion = await _localStorage.GetItemAsStringAsync(SKIPPED_VERSION_KEY);
var lastDialogShown = await _localStorage.GetItemAsStringAsync(LAST_DIALOG_SHOWN_KEY);
result.OldVersion = localVersion;
// بررسی نسخه از سرور
var response = await _client.GetAppVersionAsync(new GetAppVersionRequest
@@ -47,32 +102,119 @@ public class AppVersionService
if (!response.Found)
{
_logger.LogWarning("App version not found on server for {AppName}", APP_NAME);
return;
return result;
}
var serverVersion = response.CurrentVersion;
result.NewVersion = serverVersion;
result.ReleaseNotes = response.ReleaseNotes;
result.UpdateMessage = response.UpdateMessage;
result.RequiresCacheClear = response.RequiresFullCacheClear;
result.IsForceUpdate = response.RequiresUpdate; // اگر نسخه کمتر از حداقل باشه
// اگر نسخه جدید باشه یا پاک کردن کش لازم باشه
if (localVersion != serverVersion || response.RequiresFullCacheClear)
// آیا نسخه جدید داریم؟
if (localVersion != serverVersion)
{
result.HasNewVersion = true;
// اگر این نسخه رو قبلاً skip کرده و force نیست
if (!result.IsForceUpdate && skippedVersion == serverVersion && !ignoreSkipped)
{
// برای popup: چک کن یک روز گذشته یا نه
if (forDialog)
{
var oneDayPassed = await HasOneDayPassedSinceLastDialogAsync(lastDialogShown);
if (!oneDayPassed)
{
result.HasNewVersion = false;
}
// اگه یک روز گذشته، HasNewVersion = true میمونه
}
else
{
result.HasNewVersion = false;
}
}
_logger.LogInformation(
"New version detected: {OldVersion} -> {NewVersion}, CacheClear: {CacheClear}",
localVersion ?? "null", serverVersion, response.RequiresFullCacheClear);
// پاک کردن کش
await ClearAllCacheAsync();
// ذخیره نسخه جدید
await _localStorage.SetItemAsStringAsync(LOCAL_VERSION_KEY, serverVersion);
await _localStorage.SetItemAsStringAsync(LAST_CHECK_KEY, DateTime.UtcNow.ToString("O"));
_logger.LogInformation("Cache cleared and version updated to {Version}", serverVersion);
"Version check: {OldVersion} -> {NewVersion}, Force: {Force}, Skipped: {Skipped}",
localVersion ?? "null", serverVersion, result.IsForceUpdate, skippedVersion);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error checking app version");
}
return result;
}
/// <summary>
/// چک کردن اینکه یک روز از آخرین نمایش دیالوگ گذشته یا نه
/// </summary>
private async Task<bool> HasOneDayPassedSinceLastDialogAsync(string? lastDialogShown)
{
if (string.IsNullOrEmpty(lastDialogShown))
return true; // اگه هیچوقت نشون نداده، نشون بده
if (DateTime.TryParse(lastDialogShown, out var lastShown))
{
return DateTime.UtcNow.Subtract(lastShown).TotalDays >= 1;
}
return true;
}
/// <summary>
/// ثبت زمان نمایش دیالوگ
/// </summary>
public async Task RecordDialogShownAsync()
{
await _localStorage.SetItemAsStringAsync(LAST_DIALOG_SHOWN_KEY, DateTime.UtcNow.ToString("O"));
}
/// <summary>
/// اعمال آپدیت - پاک کردن کش و ذخیره نسخه جدید
/// </summary>
public async Task ApplyUpdateAsync(string newVersion)
{
try
{
_logger.LogInformation("Applying update to version {Version}", newVersion);
// پاک کردن کش
await ClearAllCacheAsync();
// ذخیره نسخه جدید
await _localStorage.SetItemAsStringAsync(LOCAL_VERSION_KEY, newVersion);
await _localStorage.SetItemAsStringAsync(LAST_CHECK_KEY, DateTime.UtcNow.ToString("O"));
// پاک کردن نسخه skip شده
await _localStorage.RemoveItemAsync(SKIPPED_VERSION_KEY);
_logger.LogInformation("Update applied successfully to {Version}", newVersion);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error applying update");
throw;
}
}
/// <summary>
/// Skip کردن این نسخه (بعداً)
/// </summary>
public async Task SkipVersionAsync(string version)
{
try
{
await _localStorage.SetItemAsStringAsync(SKIPPED_VERSION_KEY, version);
_logger.LogInformation("Version {Version} skipped by user", version);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error skipping version");
}
}
/// <summary>
@@ -82,33 +224,14 @@ public class AppVersionService
{
try
{
// لیست کلیدهایی که باید حفظ بشن (مثل توکن و اطلاعات کاربر)
var keysToKeep = new HashSet<string>
{
"access_token",
"refresh_token",
"user_info",
"user_roles"
};
// پاک کردن کش مرورگر (Cache Storage و Service Worker)
await _jsRuntime.InvokeVoidAsync("clearBrowserCache");
// دریافت همه کلیدها
var allKeys = await _localStorage.KeysAsync();
// پاک کردن کلیدهایی که در لیست حفظ نیستن
foreach (var key in allKeys)
{
if (!keysToKeep.Contains(key) && key != LOCAL_VERSION_KEY)
{
await _localStorage.RemoveItemAsync(key);
}
}
_logger.LogInformation("Local storage cache cleared, {Count} keys removed",
allKeys.Count() - keysToKeep.Count);
_logger.LogInformation("Browser cache cleared successfully");
}
catch (Exception ex)
{
_logger.LogError(ex, "Error clearing cache");
_logger.LogError(ex, "Error clearing browser cache");
}
}