From 34aabf143cb034f5524f0ceab8f42a1015b7fc7f Mon Sep 17 00:00:00 2001 From: Drombeys Date: Sat, 27 Apr 2024 15:23:17 +0300 Subject: [PATCH] Implement caching of web resources and news --- .../Manager/InitializerManager.cs | 125 ++++++++++++------ .../SourceGenerationContext.cs | 2 + .../Storage/FileNameStorage.cs | 7 +- .../Storage/PathStorage.cs | 2 + 4 files changed, 94 insertions(+), 42 deletions(-) diff --git a/src/ImeSense.Launchers.Belarus.Core/Manager/InitializerManager.cs b/src/ImeSense.Launchers.Belarus.Core/Manager/InitializerManager.cs index 1ad38d6..4e547e3 100644 --- a/src/ImeSense.Launchers.Belarus.Core/Manager/InitializerManager.cs +++ b/src/ImeSense.Launchers.Belarus.Core/Manager/InitializerManager.cs @@ -1,4 +1,3 @@ -using System; using System.Diagnostics; using ImeSense.Launchers.Belarus.Core.Helpers; @@ -12,14 +11,14 @@ namespace ImeSense.Launchers.Belarus.Core.Manager; public class InitializerManager( - ILogger logger, + ILogger? logger, HttpClient httpClient, IGitStorageApiService gitStorageApiService, UserManager userManager, IApplicationLocaleManager localeManager, ILauncherStorage launcherStorage, IReleaseComparerService releaseComparerService, IUpdaterService updaterService) { - private readonly ILogger _logger = logger; + private readonly ILogger? _logger = logger; private readonly HttpClient _httpClient = httpClient; private readonly IGitStorageApiService _gitStorageApiService = gitStorageApiService; private readonly UserManager _userManager = userManager; @@ -34,17 +33,32 @@ public async Task InitializeAsync(ISplashScreenManager splashScreenManager) var stopwatch = new Stopwatch(); stopwatch.Start(); + if (_userManager is null) { + throw new NullReferenceException("User manager object is null"); + } + if (_userManager.UserSettings is null) { + throw new NullReferenceException("User settings object is null"); + } + if (_userManager.UserSettings.Locale is null) { + _logger?.LogError("User settings locale object is null. Default locale will be selected"); + _userManager.UserSettings.Locale = _launcherStorage.Locales[0]; + } + var locale = _userManager.UserSettings.Locale; + + if (!Directory.Exists(DirectoryStorage.LauncherCache)) { + Directory.CreateDirectory(DirectoryStorage.LauncherCache); + } + splashScreenManager.UpdateInformation(new InformationMessage( _localeManager.GetStringByKey("LocalizedStrings.Loading"), _localeManager.GetStringByKey("LocalizedStrings.AccessingRepository"))); _launcherStorage.IsCheckGitHubConnection = await CheckGitHubConnectionAsync(splashScreenManager.CancellationToken); - _logger.LogInformation("Check GitHub connection time: {Time}", stopwatch.ElapsedMilliseconds); + _logger?.LogInformation("Check GitHub connection time: {Time}", stopwatch.ElapsedMilliseconds); - var locale = _userManager?.UserSettings?.Locale; if (_launcherStorage.IsCheckGitHubConnection) { var isLauncherReleaseCurrent = await IsLauncherReleaseCurrentAsync(splashScreenManager.CancellationToken); - _logger.LogInformation("Check launcher update time: {Time}", stopwatch.ElapsedMilliseconds); + _logger?.LogInformation("Check launcher update time: {Time}", stopwatch.ElapsedMilliseconds); if (!isLauncherReleaseCurrent) { var pathLauncherUpdater = Path.Combine(DirectoryStorage.Base, FileNameStorage.SBLauncherUpdater); @@ -57,17 +71,30 @@ public async Task InitializeAsync(ISplashScreenManager splashScreenManager) } _launcherStorage.GitHubRelease = await _gitStorageApiService.GetLastReleaseAsync(cancellationToken: splashScreenManager.CancellationToken); - _logger.LogInformation("Check last release time: {Time}", stopwatch.ElapsedMilliseconds); + _logger?.LogInformation("Check last release time: {Time}", stopwatch.ElapsedMilliseconds); _launcherStorage.IsGameReleaseCurrent = await IsGameReleaseCurrentAsync(splashScreenManager.CancellationToken); _launcherStorage.IsUserAuthorized = File.Exists(PathStorage.LauncherSetting); - if (_launcherStorage.IsUserAuthorized) { - await Task.Factory.StartNew(() => LoadNewsAsync(locale, splashScreenManager.CancellationToken)); + if (_launcherStorage.IsGameReleaseCurrent) { + var contentNews = await LocaleLoadCacheAsync(PathStorage.NewsCache) ?? []; + var isContentNews = contentNews.Any(x => x.Locale is not null && x.Locale.Key.Equals(_userManager.UserSettings.Locale.Key)); + if (isContentNews) { + _launcherStorage.NewsContents = new(contentNews); + } else { + await LoadRemoteContent(splashScreenManager, locale); + } + + var contentRes = await LocaleLoadCacheAsync(PathStorage.WebResourcesCache); + if (contentRes is not null && contentRes.Count != 0) { + _launcherStorage.WebResources = new(contentRes); + } else { + await Task.Factory.StartNew(() => RemoteLoadWebResourcesAsync(cancellationToken: splashScreenManager.CancellationToken)); + } } else { - await Task.Factory.StartNew(() => LoadNewsAsync(cancellationToken: splashScreenManager.CancellationToken)); + await LoadRemoteContent(splashScreenManager, locale); + await Task.Factory.StartNew(() => RemoteLoadWebResourcesAsync(cancellationToken: splashScreenManager.CancellationToken)); } - await Task.Factory.StartNew(() => LoadWebResourcesAsync(cancellationToken: splashScreenManager.CancellationToken)); } else { if (_launcherStorage.IsUserAuthorized) { _launcherStorage.NewsContents = new(LoadErrorNews(locale) ?? []); @@ -79,10 +106,34 @@ public async Task InitializeAsync(ISplashScreenManager splashScreenManager) } stopwatch.Stop(); - _logger.LogInformation("Parsing time: {Time}", stopwatch.ElapsedMilliseconds); + _logger?.LogInformation("Parsing time: {Time}", stopwatch.ElapsedMilliseconds); } catch (Exception ex) { - _logger.LogError("{Message}", ex.Message); - _logger.LogError("{StackTrace}", ex.StackTrace); + _logger?.LogError("{Message}", ex.Message); + _logger?.LogError("{StackTrace}", ex.StackTrace); + throw; + } + } + + private async Task LoadRemoteContent(ISplashScreenManager splashScreenManager, Locale? locale) + { + if (_launcherStorage.IsUserAuthorized) { + await Task.Factory.StartNew(() => RemoteLoadNewsAsync(locale, splashScreenManager.CancellationToken), splashScreenManager.CancellationToken); + } else { + await Task.Factory.StartNew(() => RemoteLoadNewsAsync(cancellationToken: splashScreenManager.CancellationToken), splashScreenManager.CancellationToken); + } + } + + private async Task?> LocaleLoadCacheAsync(string cachePath, CancellationToken cancellationToken = default) + { + try { + if (!File.Exists(cachePath)) { + return null; + } + + return await FileDataHelper.LoadDataAsync>(cachePath, cancellationToken); + } catch (Exception ex) { + _logger?.LogError("{Message}", ex.Message); + _logger?.LogError("{StackTrace}", ex.StackTrace); throw; } } @@ -99,8 +150,8 @@ public async Task InitializeAsync(ISplashScreenManager splashScreenManager) _localeManager.GetStringByKey("LocalizedStrings.ErrorInternetDescription") )])); } catch (Exception ex) { - _logger.LogError("{Message}", ex.Message); - _logger.LogError("{StackTrace}", ex.StackTrace); + _logger?.LogError("{Message}", ex.Message); + _logger?.LogError("{StackTrace}", ex.StackTrace); } return allNews; @@ -117,22 +168,21 @@ private async Task CheckGitHubConnectionAsync(CancellationToken cancellati var response = await _httpClient.GetAsync("https://github.com", cancellationToken); if (response.IsSuccessStatusCode) { - _logger.LogInformation("Connection to github.com established"); + _logger?.LogInformation("Connection to github.com established"); return true; } else { - _logger.LogInformation("Failed to establish connection to github.com. Response code: {StatusCode}", response.StatusCode); + _logger?.LogInformation("Failed to establish connection to github.com. Response code: {StatusCode}", response.StatusCode); return false; } } catch (HttpRequestException ex) { - _logger.LogInformation("Failed to establish connection to github.com"); + _logger?.LogInformation("Failed to establish connection to github.com"); - _logger.LogError("{Message}", ex.Message); - _logger.LogError("{StackTrace}", ex.StackTrace); + _logger?.LogError("{Message}", ex.Message); + _logger?.LogError("{StackTrace}", ex.StackTrace); return false; } } - private async Task IsLauncherReleaseCurrentAsync(CancellationToken cancellationToken = default) { var tags = await _gitStorageApiService.GetTagsAsync(UriStorage.LauncherApiUri, cancellationToken); @@ -161,23 +211,19 @@ private async Task IsGameReleaseCurrentAsync(CancellationToken cancellatio { var gitStorageRelease = _launcherStorage.GitHubRelease; - if (!Directory.Exists(DirectoryStorage.LauncherCache)) { - Directory.CreateDirectory(DirectoryStorage.LauncherCache); - } - if (File.Exists(PathStorage.CurrentRelease)) { var releaseComparer = gitStorageRelease != null && await _releaseComparerService.IsComparerAsync(gitStorageRelease, cancellationToken); if (!releaseComparer) { await FileSystemHelper.WriteReleaseAsync(gitStorageRelease, PathStorage.CurrentRelease, cancellationToken); - _logger.LogInformation("The releases don't match. Update required!"); + _logger?.LogInformation("The releases don't match. Update required!"); return false; } else { - _logger.LogInformation("The releases are the same. No update required."); + _logger?.LogInformation("The releases are the same. No update required."); return true; } } else { await FileSystemHelper.WriteReleaseAsync(gitStorageRelease, PathStorage.CurrentRelease, cancellationToken); - _logger.LogInformation("The release configuration has not been previously saved"); + _logger?.LogInformation("The release configuration has not been previously saved"); return false; } } @@ -188,30 +234,30 @@ public void InitializeLocale() throw new Exception("Error loading user config!"); if (userSettings.Locale is null) { - _logger.LogError("Locale was not set"); + _logger?.LogError("Locale was not set"); userSettings.Locale = _launcherStorage.Locales[0]; } _localeManager.SetLocale(userSettings.Locale.Key); } - private async Task LoadWebResourcesAsync(CancellationToken cancellationToken = default) + private async Task RemoteLoadWebResourcesAsync(CancellationToken cancellationToken = default) { try { var contents = await _gitStorageApiService .DownloadJsonAsync>(FileNameStorage.WebResources, UriStorage.BelarusApiUri, cancellationToken); - if (contents != null) { + await FileSystemHelper.WriteReleaseAsync(contents, Path.Combine(DirectoryStorage.LauncherCache, FileNameStorage.WebResources), cancellationToken); _launcherStorage.WebResources = new(contents); } } catch (Exception ex) { - _logger.LogError("{Message}", ex.Message); - _logger.LogError("{StackTrace}", ex.StackTrace); + _logger?.LogError("{Message}", ex.Message); + _logger?.LogError("{StackTrace}", ex.StackTrace); throw; } } - private async Task LoadNewsAsync(Locale? locale = null, CancellationToken cancellationToken = default) + private async Task RemoteLoadNewsAsync(Locale? locale = null, CancellationToken cancellationToken = default) { // News in all languages var allNews = new List(); @@ -229,24 +275,25 @@ private async Task LoadNewsAsync(Locale? locale = null, CancellationToken cancel AddNews(locale, allNews, news); } } catch (Exception ex) { - _logger.LogError("{Message}", ex.Message); - _logger.LogError("{StackTrace}", ex.StackTrace); + _logger?.LogError("{Message}", ex.Message); + _logger?.LogError("{StackTrace}", ex.StackTrace); } + await FileSystemHelper.WriteReleaseAsync(allNews, Path.Combine(DirectoryStorage.LauncherCache, "News.json"), cancellationToken); _launcherStorage.NewsContents = new(allNews); } private void AddNews(Locale? locale, List allNews, IEnumerable? news) { if (locale is null) { - _logger.LogError("Failure to load locale!"); + _logger?.LogError("Failure to load locale!"); return; } if (news != null) { allNews.Add(new LangNewsContent(locale, news)); } else { - _logger.LogError("Failure to load news in {locale}", locale.Title); + _logger?.LogError("Failure to load news in {locale}", locale.Title); } } } diff --git a/src/ImeSense.Launchers.Belarus.Core/SourceGenerationContext.cs b/src/ImeSense.Launchers.Belarus.Core/SourceGenerationContext.cs index 8b39f0a..5798907 100644 --- a/src/ImeSense.Launchers.Belarus.Core/SourceGenerationContext.cs +++ b/src/ImeSense.Launchers.Belarus.Core/SourceGenerationContext.cs @@ -16,11 +16,13 @@ namespace ImeSense.Launchers.Belarus.Core; [JsonSerializable(typeof(NewsContent))] [JsonSerializable(typeof(IEnumerable))] [JsonSerializable(typeof(IEnumerable))] +[JsonSerializable(typeof(List))] [JsonSerializable(typeof(Tag))] [JsonSerializable(typeof(IEnumerable))] [JsonSerializable(typeof(UserSettings))] [JsonSerializable(typeof(WebResource))] [JsonSerializable(typeof(WebResource[]))] +[JsonSerializable(typeof(List))] [JsonSerializable(typeof(IEnumerable))] public partial class SourceGenerationContext : JsonSerializerContext { diff --git a/src/ImeSense.Launchers.Belarus.Core/Storage/FileNameStorage.cs b/src/ImeSense.Launchers.Belarus.Core/Storage/FileNameStorage.cs index 640bf52..2fb89c5 100644 --- a/src/ImeSense.Launchers.Belarus.Core/Storage/FileNameStorage.cs +++ b/src/ImeSense.Launchers.Belarus.Core/Storage/FileNameStorage.cs @@ -12,9 +12,10 @@ public static class FileNameStorage public static string GameSetting => "user.ltx"; public static string LauncherSetting => "sblauncher.json"; public static string CurrentRelease => "CurrentRelease.json"; - - public static string LegacyHash => "hash.json"; - public static string LegacyNews => "news.json"; + public static string NewsContent => "News.json"; public static string NewsContentRus => "news_content_rus.json"; public static string NewsContentEng => "news_content_eng.json"; + + public static string LegacyHash => "hash.json"; + public static string LegacyNews => "news.json"; } diff --git a/src/ImeSense.Launchers.Belarus.Core/Storage/PathStorage.cs b/src/ImeSense.Launchers.Belarus.Core/Storage/PathStorage.cs index ae4e6b7..dcbefc8 100644 --- a/src/ImeSense.Launchers.Belarus.Core/Storage/PathStorage.cs +++ b/src/ImeSense.Launchers.Belarus.Core/Storage/PathStorage.cs @@ -5,4 +5,6 @@ public static class PathStorage public static string LauncherSetting => Path.Combine(DirectoryStorage.AppData, FileNameStorage.LauncherSetting); public static string CurrentRelease => Path.Combine(DirectoryStorage.LauncherCache, FileNameStorage.CurrentRelease); public static string GameUser => Path.Combine(DirectoryStorage.AppData, FileNameStorage.GameSetting); + public static string NewsCache => Path.Combine(DirectoryStorage.LauncherCache, FileNameStorage.NewsContent); + public static string WebResourcesCache => Path.Combine(DirectoryStorage.LauncherCache, FileNameStorage.WebResources); }