From 827626d1b6356f5abfafe06cb22473c4e87a7079 Mon Sep 17 00:00:00 2001 From: BeloMaximka Date: Tue, 18 Apr 2023 11:00:23 +0300 Subject: [PATCH 1/9] background hw refresh fix --- .../UserControls/Menus/Homeworks.xaml.cs | 572 +++++++++--------- 1 file changed, 288 insertions(+), 284 deletions(-) diff --git a/MystatDesktopWpf/UserControls/Menus/Homeworks.xaml.cs b/MystatDesktopWpf/UserControls/Menus/Homeworks.xaml.cs index 73813fe..7ef9f80 100644 --- a/MystatDesktopWpf/UserControls/Menus/Homeworks.xaml.cs +++ b/MystatDesktopWpf/UserControls/Menus/Homeworks.xaml.cs @@ -11,292 +11,296 @@ using System.IO; using System.IO.Compression; using System.Net.Http; +using System.Threading.Tasks; using System.Windows.Controls; namespace MystatDesktopWpf.UserControls.Menus { - /// - /// Interaction logic for Homeworks.xaml - /// - public partial class Homeworks : UserControl, IRefreshable - { - private HomeworksViewModel viewModel; - private static readonly HttpClient httpClient = new(); - private readonly UploadHomework uploadContent = new(); - private readonly DeleteHomework deleteContent = new(); - private readonly DonwloadHomeworkPreview downloadContent = new(); - - public Homeworks() - { - InitializeComponent(); - App.LanguageChanged += App_LanguageChanged; - } - - // Updating "All subjects" item in ComboBox - private void App_LanguageChanged(object? sender, EventArgs e) - { - SpecsComboBox.SelectionChanged -= SpecsComboBox_SelectionChanged; - var temp = SpecsComboBox.SelectedIndex; - SpecsComboBox.SelectedIndex = -1; - SpecsComboBox.Items.Refresh(); - SpecsComboBox.SelectedIndex = temp; - SpecsComboBox.SelectionChanged += SpecsComboBox_SelectionChanged; - } - - private void ScheduleAutoUpdate() - { - TaskService.CancelTask("auto-homework-refresh"); - TaskService.ScheduleTask("auto-homework-refresh", TimeSpan.FromHours(1), () => - { - ignoreErrorsOnce = true; - Refresh(); - ScheduleAutoUpdate(); - }); - } - - #region Homework interactions - private void OpenFileInExplorer(string filePath) - { - try - { - string argument = "/select, \"" + filePath + "\""; - Process.Start("explorer.exe", argument); - } - catch (Exception) - { - string folderOpenError = (string)FindResource("m_FolderOpenError"); - snackbar.MessageQueue?.Enqueue(folderOpenError); - } - } - - public async void DownloadHomework(string filePath) - { - try - { - if (homeworkDialog.IsOpen) return; - - var res = await httpClient.GetAsync(filePath); - string fileName = res.Content.Headers.ContentDisposition?.FileName.Trim('\"'); - string extension = fileName.Remove(0, fileName.LastIndexOf('.')); - - if (extension == ".txt") - { - downloadContent.Header = (string)FindResource("m_TxtPreview"); - downloadContent.TextBoxHint = (string)FindResource("m_TxtContent"); - - downloadContent.Text = await res.Content.ReadAsStringAsync(); - downloadContent.IsFileMissing = filePath == null; - bool? result = (bool?)await homeworkDialog.ShowDialog(downloadContent); - - if (!result.HasValue || result == false) - return; - } - - System.Windows.Forms.SaveFileDialog dialog = new() - { - FileName = fileName, - Filter = $"(*{extension})|*{extension}" - }; - - if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) - { - var buffer = await res.Content.ReadAsByteArrayAsync(); - File.WriteAllBytes(dialog.FileName, buffer); - - string homeworkDownloaded = (string)FindResource("m_HomeworkDownloaded"); - string openInExplorer = (string)FindResource("m_OpenInExplorer"); - snackbar.MessageQueue?.Enqueue(homeworkDownloaded, openInExplorer, OpenFileInExplorer, dialog.FileName); - homeworkDialog.IsOpen = false; - } - } - catch (Exception) - { - string homeworkDownloadError = (string)FindResource("m_HomeworkDownloadError"); - snackbar.MessageQueue?.Enqueue(homeworkDownloadError); - } - } - - public async void OpenDownloadUploadedDialog(Homework homework) - { - if (homeworkDialog.IsOpen) return; - - downloadContent.Header = (string)FindResource("m_UploadedWork"); - downloadContent.TextBoxHint = (string)FindResource("m_YourComment"); - - downloadContent.Text = homework.UploadedHomework.StudentAnswer; - downloadContent.IsFileMissing = homework.UploadedHomework.FilePath == null; - bool? result = (bool?)await homeworkDialog.ShowDialog(downloadContent); - - if (result.HasValue && result == true) - DownloadHomework(homework.UploadedHomework.FilePath); - } - public async void OpenUploadDialog(Homework homework, ICollection source, Button progress, Button upload, string[]? files = null) - { - if (homeworkDialog.IsOpen) return; - - uploadContent.ResetContent(); - uploadContent.Homework = homework; - uploadContent.HomeworkSource = source; - uploadContent.Files = files; - - if (homework.Status == HomeworkStatus.Checked) - { - uploadContent.Header = (string)FindResource("m_RedoRequest"); - uploadContent.SendButtonName = (string)FindResource("m_RedoSend"); - } - else - { - uploadContent.Header = (string)FindResource("m_UploadWork"); - uploadContent.SendButtonName = (string)FindResource("m_Send"); - } - bool? result = (bool?)await homeworkDialog.ShowDialog(uploadContent); - - if (result.HasValue && result == true) - { - // Делаем кнопку загрузки крутиться - ButtonProgressAssist.SetIsIndicatorVisible(progress, true); - upload.IsHitTestVisible = false; - try - { - UploadedHomeworkInfo info; - if (uploadContent.Files != null && uploadContent.Archive) - { - // Архивация - using MemoryStream stream = new(); - using (ZipArchive zip = new(stream, ZipArchiveMode.Create, true)) - { - foreach (var path in uploadContent.Files) - zip.CreateEntryFromAny(path); - } - HomeworkFile file = new(uploadContent.ArchiveName, stream.ToArray()); - - info = await MystatAPISingleton.Client.UploadHomeworkFile(uploadContent.Homework.Id, file, uploadContent.Comment); - } - else - info = await MystatAPISingleton.Client.UploadHomework(uploadContent.Homework.Id, uploadContent.Files?[0], uploadContent.Comment); - - uploadContent.Homework.UploadedHomework = info; - viewModel.MoveHomework(uploadContent.Homework, HomeworkStatus.Uploaded); - - string workUploaded = (string)FindResource("m_WorkUploaded"); - snackbar.MessageQueue?.Enqueue(workUploaded); - } - catch - { - string workUploadError = (string)FindResource("m_WorkUploadError"); - snackbar.MessageQueue?.Enqueue(workUploadError); - } - ButtonProgressAssist.SetIsIndicatorVisible(progress, false); - upload.IsHitTestVisible = true; - } - } - - public async void OpenDeleteDialog(Homework homework) - { - if (homeworkDialog.IsOpen) return; - - bool? result = (bool?)await homeworkDialog.ShowDialog(deleteContent); - - if (result.HasValue && result == true) - { - try - { - if (await MystatAPISingleton.Client.RemoveHomework(homework.UploadedHomework.Id) == false) - throw new HttpRequestException("Error deleting homework"); - - viewModel.DeleteHomework(homework); - homework.Status = DateTime.Parse(homework.OverdueTime) < DateTime.Now ? - HomeworkStatus.Overdue : HomeworkStatus.Active; - viewModel.AddHomework(homework); - - string workDeleted = (string)FindResource("m_WorkDeleted"); - snackbar.MessageQueue?.Enqueue(workDeleted); - } - catch (Exception) - { - string deleteWorkError = (string)FindResource("m_DeleteWorkError"); - snackbar.MessageQueue?.Enqueue(deleteWorkError); - } - } - } - #endregion - - private void ShowHomeworkSlide() - { - transitioner.SelectedIndex = 1; - OverdueList.UpdateNextPageButtonVisibility(); - DeletedList.UpdateNextPageButtonVisibility(); - ActiveList.UpdateNextPageButtonVisibility(); - UploadedList.UpdateNextPageButtonVisibility(); - CheckedList.UpdateNextPageButtonVisibility(); - } - - bool loading = false; - bool ignoreErrorsOnce = false; - public async void Refresh() - { - - if (loading) return; - loading = true; - transitioner.SelectedIndex = 0; - SpecsComboBox.SelectionChanged -= SpecsComboBox_SelectionChanged; - if (await viewModel.LoadSpecs() && await viewModel.LoadHomework()) - { - ShowHomeworkSlide(); - } - else if (!ignoreErrorsOnce) - { - transitioner.SelectedIndex = 2; // Switch to error slider - } - SpecsComboBox.SelectionChanged += SpecsComboBox_SelectionChanged; - loading = false; - ignoreErrorsOnce = false; - } - - // When view model is assigned outside this class - private async void Control_DataContextChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e) - { - if (e.NewValue is HomeworksViewModel vm) - { - viewModel = vm; - uploadContent.Host = homeworkDialog; - - OverdueList.Collection = viewModel.Homework[HomeworkStatus.Overdue]; - DeletedList.Collection = viewModel.Homework[HomeworkStatus.Deleted]; - ActiveList.Collection = viewModel.Homework[HomeworkStatus.Active]; - UploadedList.Collection = viewModel.Homework[HomeworkStatus.Uploaded]; - CheckedList.Collection = viewModel.Homework[HomeworkStatus.Checked]; - - if (viewModel.LoadedOnce == true) - { - SpecsComboBox.SelectionChanged -= SpecsComboBox_SelectionChanged; - await viewModel.LoadSpecs(); - SpecsComboBox.SelectionChanged += SpecsComboBox_SelectionChanged; - ShowHomeworkSlide(); - } - else - { - Refresh(); - } - - ScheduleAutoUpdate(); - } - } - - private async void SpecsComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - if (sender is ComboBox comboBox && comboBox.SelectedItem is Spec spec) - { - transitioner.SelectedIndex = 0; - if (await viewModel.LoadHomework(spec)) - { - ShowHomeworkSlide(); - } - else - { - transitioner.SelectedIndex = 2; // Switch to error slider - } - } - } - } + /// + /// Interaction logic for Homeworks.xaml + /// + public partial class Homeworks : UserControl, IRefreshable + { + private HomeworksViewModel viewModel; + private static readonly HttpClient httpClient = new(); + private readonly UploadHomework uploadContent = new(); + private readonly DeleteHomework deleteContent = new(); + private readonly DonwloadHomeworkPreview downloadContent = new(); + + public Homeworks() + { + InitializeComponent(); + App.LanguageChanged += App_LanguageChanged; + } + + // Updating "All subjects" item in ComboBox + private void App_LanguageChanged(object? sender, EventArgs e) + { + SpecsComboBox.SelectionChanged -= SpecsComboBox_SelectionChanged; + var temp = SpecsComboBox.SelectedIndex; + SpecsComboBox.SelectedIndex = -1; + SpecsComboBox.Items.Refresh(); + SpecsComboBox.SelectedIndex = temp; + SpecsComboBox.SelectionChanged += SpecsComboBox_SelectionChanged; + } + + private void ScheduleAutoUpdate() + { + TaskService.CancelTask("auto-homework-refresh"); + TaskService.ScheduleTask("auto-homework-refresh", TimeSpan.FromHours(1), () => + { + Refresh(true); + ScheduleAutoUpdate(); + }); + } + + #region Homework interactions + private void OpenFileInExplorer(string filePath) + { + try + { + string argument = "/select, \"" + filePath + "\""; + Process.Start("explorer.exe", argument); + } + catch (Exception) + { + string folderOpenError = (string)FindResource("m_FolderOpenError"); + snackbar.MessageQueue?.Enqueue(folderOpenError); + } + } + + public async void DownloadHomework(string filePath) + { + try + { + if (homeworkDialog.IsOpen) return; + + var res = await httpClient.GetAsync(filePath); + string fileName = res.Content.Headers.ContentDisposition?.FileName.Trim('\"'); + string extension = fileName.Remove(0, fileName.LastIndexOf('.')); + + if (extension == ".txt") + { + downloadContent.Header = (string)FindResource("m_TxtPreview"); + downloadContent.TextBoxHint = (string)FindResource("m_TxtContent"); + + downloadContent.Text = await res.Content.ReadAsStringAsync(); + downloadContent.IsFileMissing = filePath == null; + bool? result = (bool?)await homeworkDialog.ShowDialog(downloadContent); + + if (!result.HasValue || result == false) + return; + } + + System.Windows.Forms.SaveFileDialog dialog = new() + { + FileName = fileName, + Filter = $"(*{extension})|*{extension}" + }; + + if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + var buffer = await res.Content.ReadAsByteArrayAsync(); + File.WriteAllBytes(dialog.FileName, buffer); + + string homeworkDownloaded = (string)FindResource("m_HomeworkDownloaded"); + string openInExplorer = (string)FindResource("m_OpenInExplorer"); + snackbar.MessageQueue?.Enqueue(homeworkDownloaded, openInExplorer, OpenFileInExplorer, dialog.FileName); + homeworkDialog.IsOpen = false; + } + } + catch (Exception) + { + string homeworkDownloadError = (string)FindResource("m_HomeworkDownloadError"); + snackbar.MessageQueue?.Enqueue(homeworkDownloadError); + } + } + + public async void OpenDownloadUploadedDialog(Homework homework) + { + if (homeworkDialog.IsOpen) return; + + downloadContent.Header = (string)FindResource("m_UploadedWork"); + downloadContent.TextBoxHint = (string)FindResource("m_YourComment"); + + downloadContent.Text = homework.UploadedHomework.StudentAnswer; + downloadContent.IsFileMissing = homework.UploadedHomework.FilePath == null; + bool? result = (bool?)await homeworkDialog.ShowDialog(downloadContent); + + if (result.HasValue && result == true) + DownloadHomework(homework.UploadedHomework.FilePath); + } + public async void OpenUploadDialog(Homework homework, ICollection source, Button progress, Button upload, string[]? files = null) + { + if (homeworkDialog.IsOpen) return; + + uploadContent.ResetContent(); + uploadContent.Homework = homework; + uploadContent.HomeworkSource = source; + uploadContent.Files = files; + + if (homework.Status == HomeworkStatus.Checked) + { + uploadContent.Header = (string)FindResource("m_RedoRequest"); + uploadContent.SendButtonName = (string)FindResource("m_RedoSend"); + } + else + { + uploadContent.Header = (string)FindResource("m_UploadWork"); + uploadContent.SendButtonName = (string)FindResource("m_Send"); + } + bool? result = (bool?)await homeworkDialog.ShowDialog(uploadContent); + + if (result.HasValue && result == true) + { + // Делаем кнопку загрузки крутиться + ButtonProgressAssist.SetIsIndicatorVisible(progress, true); + upload.IsHitTestVisible = false; + try + { + UploadedHomeworkInfo info; + if (uploadContent.Files != null && uploadContent.Archive) + { + // Архивация + using MemoryStream stream = new(); + using (ZipArchive zip = new(stream, ZipArchiveMode.Create, true)) + { + foreach (var path in uploadContent.Files) + zip.CreateEntryFromAny(path); + } + HomeworkFile file = new(uploadContent.ArchiveName, stream.ToArray()); + + info = await MystatAPISingleton.Client.UploadHomeworkFile(uploadContent.Homework.Id, file, uploadContent.Comment); + } + else + info = await MystatAPISingleton.Client.UploadHomework(uploadContent.Homework.Id, uploadContent.Files?[0], uploadContent.Comment); + + uploadContent.Homework.UploadedHomework = info; + viewModel.MoveHomework(uploadContent.Homework, HomeworkStatus.Uploaded); + + string workUploaded = (string)FindResource("m_WorkUploaded"); + snackbar.MessageQueue?.Enqueue(workUploaded); + } + catch + { + string workUploadError = (string)FindResource("m_WorkUploadError"); + snackbar.MessageQueue?.Enqueue(workUploadError); + } + ButtonProgressAssist.SetIsIndicatorVisible(progress, false); + upload.IsHitTestVisible = true; + } + } + + public async void OpenDeleteDialog(Homework homework) + { + if (homeworkDialog.IsOpen) return; + + bool? result = (bool?)await homeworkDialog.ShowDialog(deleteContent); + + if (result.HasValue && result == true) + { + try + { + if (await MystatAPISingleton.Client.RemoveHomework(homework.UploadedHomework.Id) == false) + throw new HttpRequestException("Error deleting homework"); + + viewModel.DeleteHomework(homework); + homework.Status = DateTime.Parse(homework.OverdueTime) < DateTime.Now ? + HomeworkStatus.Overdue : HomeworkStatus.Active; + viewModel.AddHomework(homework); + + string workDeleted = (string)FindResource("m_WorkDeleted"); + snackbar.MessageQueue?.Enqueue(workDeleted); + } + catch (Exception) + { + string deleteWorkError = (string)FindResource("m_DeleteWorkError"); + snackbar.MessageQueue?.Enqueue(deleteWorkError); + } + } + } + #endregion + + private void ShowHomeworkSlide() + { + transitioner.SelectedIndex = 1; + OverdueList.UpdateNextPageButtonVisibility(); + DeletedList.UpdateNextPageButtonVisibility(); + ActiveList.UpdateNextPageButtonVisibility(); + UploadedList.UpdateNextPageButtonVisibility(); + CheckedList.UpdateNextPageButtonVisibility(); + } + + bool loading = false; + + public async void Refresh() => await Refresh(false); // Because of interface + public async Task Refresh(bool silent) + { + + if (loading) return; + loading = true; + if (!silent) + { + transitioner.SelectedIndex = 0; + } + + SpecsComboBox.SelectionChanged -= SpecsComboBox_SelectionChanged; + if (await viewModel.LoadSpecs() && await viewModel.LoadHomework()) + { + ShowHomeworkSlide(); + } + else if (!silent) + { + transitioner.SelectedIndex = 2; // Switch to error slider + } + SpecsComboBox.SelectionChanged += SpecsComboBox_SelectionChanged; + loading = false; + } + + // When view model is assigned outside this class + private async void Control_DataContextChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e) + { + if (e.NewValue is HomeworksViewModel vm) + { + viewModel = vm; + uploadContent.Host = homeworkDialog; + + OverdueList.Collection = viewModel.Homework[HomeworkStatus.Overdue]; + DeletedList.Collection = viewModel.Homework[HomeworkStatus.Deleted]; + ActiveList.Collection = viewModel.Homework[HomeworkStatus.Active]; + UploadedList.Collection = viewModel.Homework[HomeworkStatus.Uploaded]; + CheckedList.Collection = viewModel.Homework[HomeworkStatus.Checked]; + + if (viewModel.LoadedOnce == true) + { + SpecsComboBox.SelectionChanged -= SpecsComboBox_SelectionChanged; + await viewModel.LoadSpecs(); + SpecsComboBox.SelectionChanged += SpecsComboBox_SelectionChanged; + ShowHomeworkSlide(); + } + else + { + Refresh(); + } + + ScheduleAutoUpdate(); + } + } + + private async void SpecsComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (sender is ComboBox comboBox && comboBox.SelectedItem is Spec spec) + { + transitioner.SelectedIndex = 0; + if (await viewModel.LoadHomework(spec)) + { + ShowHomeworkSlide(); + } + else + { + transitioner.SelectedIndex = 2; // Switch to error slider + } + } + } + } } From 1a2d3a2b18ca6034b4a4e32802eb846821cca90f Mon Sep 17 00:00:00 2001 From: BeloMaximka Date: Tue, 18 Apr 2023 12:08:00 +0300 Subject: [PATCH 2/9] collapsible homework sections --- MystatDesktopWpf/Services/SettingsService.cs | 19 +- .../UserControls/HomeworkList.xaml | 374 ++++++++-------- .../UserControls/HomeworkList.xaml.cs | 423 ++++++++++-------- .../UserControls/Menus/Homeworks.xaml.cs | 17 +- 4 files changed, 445 insertions(+), 388 deletions(-) diff --git a/MystatDesktopWpf/Services/SettingsService.cs b/MystatDesktopWpf/Services/SettingsService.cs index 46ea0a0..6d11afa 100644 --- a/MystatDesktopWpf/Services/SettingsService.cs +++ b/MystatDesktopWpf/Services/SettingsService.cs @@ -16,7 +16,7 @@ internal static class SettingsService private static readonly string settingsFilePath; public static Settings Settings { get; private set; } - public static event Action OnSettingsChange; + public static event Action SettingsChanged; static SettingsService() { @@ -42,11 +42,11 @@ static SettingsService() }, TaskScheduler.Default); }; - OnSettingsChange += saveDebounced; - Settings.ScheduleNotification.OnPropertyChanged += OnSettingsChange; - Settings.Theme.OnPropertyChanged += OnSettingsChange; - Settings.Tray.OnPropertyChanged += OnSettingsChange; - Settings.TimezoneConvertion.OnPropertyChanged += OnSettingsChange; + SettingsChanged += saveDebounced; + Settings.ScheduleNotification.OnPropertyChanged += SettingsChanged; + Settings.Theme.OnPropertyChanged += SettingsChanged; + Settings.Tray.OnPropertyChanged += SettingsChanged; + Settings.TimezoneConvertion.OnPropertyChanged += SettingsChanged; } public static Settings? Load() @@ -120,6 +120,11 @@ public static bool SetPropertyValue(string property, object? value) return true; } + public static void OnSettingsChange() + { + SettingsChanged.Invoke(); + } + private static string EncpyptPassword(string password) { StringBuilder newPass = new(); @@ -169,5 +174,7 @@ то мы ставим анлийский*/ public TimezoneSubSettings TimezoneConvertion { get; set; } = new(); public bool AutoLessonEvaluationEnabled { get; set; } = false; public string Language { get; set; } + + public bool[] HomeworkSectionExpandedStates { get; set; } = { true, true, true, true, true }; } } diff --git a/MystatDesktopWpf/UserControls/HomeworkList.xaml b/MystatDesktopWpf/UserControls/HomeworkList.xaml index 7820f3c..7863b25 100644 --- a/MystatDesktopWpf/UserControls/HomeworkList.xaml +++ b/MystatDesktopWpf/UserControls/HomeworkList.xaml @@ -24,98 +24,102 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - + + + - - - - - - - - - - - - + + + + + + + + + + + - - - - + - - - - - - - + - - + - - - + + + - - + - - - + + + - - - - + + + + - - - - - - - - + + + + + + + + - + + - - + - - + + - - + + - - - + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + - - - - - + + + - + + + diff --git a/MystatDesktopWpf/UserControls/HomeworkList.xaml.cs b/MystatDesktopWpf/UserControls/HomeworkList.xaml.cs index e5aac57..5318a62 100644 --- a/MystatDesktopWpf/UserControls/HomeworkList.xaml.cs +++ b/MystatDesktopWpf/UserControls/HomeworkList.xaml.cs @@ -1,6 +1,7 @@ using MaterialDesignThemes.Wpf; using MystatAPI.Entity; using MystatDesktopWpf.Extensions; +using MystatDesktopWpf.Services; using MystatDesktopWpf.UserControls.Menus; using MystatDesktopWpf.ViewModels; using System; @@ -13,197 +14,233 @@ namespace MystatDesktopWpf.UserControls { - /// - /// Interaction logic for HomeworkList.xaml - /// - public partial class HomeworkList : UserControl - { - private const int defaultPageSize = 6; - - private FrameworkElement lastExtraInfoCirlce; - - // Чтобы можно было прикрутить Binding - public static readonly DependencyProperty CollectionProperty = - DependencyProperty.Register("Collection", typeof(HomeworkCollection), typeof(HomeworkList)); - public HomeworkCollection Collection - { - get => (HomeworkCollection)GetValue(CollectionProperty); - set => SetValue(CollectionProperty, value); - } - - public static readonly DependencyProperty HomeworkManagerProperty = - DependencyProperty.Register("HomeworkManager", typeof(Homeworks), typeof(HomeworkList)); - public Homeworks? HomeworkManager - { - get => (Homeworks)GetValue(HomeworkManagerProperty); - set => SetValue(HomeworkManagerProperty, value); - } - - public static readonly DependencyProperty HeaderProperty = - DependencyProperty.Register("Header", typeof(string), typeof(HomeworkList)); - public string Header - { - get => (string)GetValue(HeaderProperty); - set => SetValue(HeaderProperty, value); - } - - public HomeworkList() - { - InitializeComponent(); - PopupInfo.CustomPopupPlacementCallback += PopupExtraInfoPlacement; - } - - private void Card_Initialized(object sender, EventArgs e) - { - Card card = (Card)sender; - HomeworkStatus status = (HomeworkStatus)card.Tag; - if (status == HomeworkStatus.Uploaded || status == HomeworkStatus.Checked) - { - Button uploadButton = (Button)card.FindName("uploadButton"); - uploadButton.Click -= UploadButton_Click; - uploadButton.Click += DownloadUploadedButton_Click; - } - if (status == HomeworkStatus.Uploaded) - { - Button deleteButton = (Button)card.FindName("deleteButton"); - deleteButton.Click += DeleteButton_Click; - } - if (status == HomeworkStatus.Checked) - { - Button deleteButton = (Button)card.FindName("deleteButton"); - deleteButton.Click += UploadButton_Click; - } - } - - private void DeleteButton_Click(object sender, RoutedEventArgs e) - { - HomeworkManager?.OpenDeleteDialog((Homework)((Button)sender).Tag); - } - - private void DownloadButton_Click(object sender, RoutedEventArgs e) - { - Homework homework = (Homework)((Button)sender).Tag; - HomeworkManager?.DownloadHomework(homework.FilePath); - } - - private void UploadButton_Click(object sender, RoutedEventArgs e) - { - Button uploadButton = (Button)sender; - Grid grid = (Grid)uploadButton.Parent; - Button progressButton = (Button)grid.FindName("progressButton"); - HomeworkManager?.OpenUploadDialog((Homework)uploadButton.Tag, Collection.Items, progressButton, uploadButton); - } - - private void DownloadUploadedButton_Click(object sender, RoutedEventArgs e) - { - Button uploadButton = (Button)sender; - Homework homework = (Homework)uploadButton.Tag; - if (homework.UploadedHomework.StudentAnswer != null) - HomeworkManager?.OpenDownloadUploadedDialog(homework); - else - HomeworkManager?.DownloadHomework(homework.UploadedHomework.FilePath); - } - - private void Card_Drop(object sender, DragEventArgs e) - { - if (e.Data.GetDataPresent(DataFormats.FileDrop)) - { - Card card = (Card)sender; - Button uploadButton = (Button)card.FindName("uploadButton"); - Button progressButton = (Button)card.FindName("progressButton"); - var files = (string[])e.Data.GetData(DataFormats.FileDrop); - HomeworkManager?.OpenUploadDialog((Homework)uploadButton.Tag, Collection.Items, progressButton, uploadButton, files); - } - } - - // Sets the content of comment popup and shows it on screen - private void CommentButton_Click(object sender, RoutedEventArgs e) - { - Button button = (Button)sender; - popupComment.PlacementTarget = button; - - if (button.Tag is string homeworkTaskComment) - { - CommentParagraph.Inlines.SetInlinesWithHyperlinksFromText(homeworkTaskComment); - } - else if (button.Tag is HomeworkComment teacherComment) - { - CommentParagraph.Inlines.Clear(); - if (teacherComment.AttachmentPath == null) // No file attached, just show the text - { - CommentParagraph.Inlines.Add(new Run(teacherComment.Text)); - } - else // Add the ability to download attached file - { - var hyperlink = new Hyperlink(new Run(teacherComment.Text)) - { - // Adress doesn't matter. RequestNavigate won't fire without it - NavigateUri = new Uri(@"https://127.0.0.1/"), - }; - hyperlink.RequestNavigate += (sender, args) => HomeworkManager.DownloadHomework(teacherComment.AttachmentPath); - CommentParagraph.Inlines.Add(hyperlink); - } - } - CommentViewer.AdjustWidthToText(); - popupComment.IsOpen = true; - } - - private async void LoadPageButton_Click(object sender, RoutedEventArgs e) - { - Button button = (Button)sender; - - button.IsHitTestVisible = false; - ButtonProgressAssist.SetIsIndicatorVisible(progressPageButton, true); - await Collection.LoadNextPage(); - ButtonProgressAssist.SetIsIndicatorVisible(progressPageButton, false); - button.IsHitTestVisible = true; - - UpdateNextPageButtonVisibility(); - } - - public void UpdateNextPageButtonVisibility() - { - var count = Collection.Items.Count; - var maxCount = Collection.MaxCount; - if (count >= maxCount) - { - nextPageButton.Visibility = Visibility.Collapsed; - } - else if (count >= defaultPageSize) - { - nextPageButton.Visibility = Visibility.Visible; - } - } - - #region Extra info popup mouse handling - // MouseOver multibinding caused many problems so I ended up with manual handling - - private CustomPopupPlacement[] PopupExtraInfoPlacement(Size popupSize, Size targetSize, Point offset) - { - CustomPopupPlacement right = new(new Point(targetSize.Width / 2 + 3, -4), PopupPrimaryAxis.Horizontal); - CustomPopupPlacement left = new(new Point((popupSize.Width + targetSize.Width / 2) * -1 + 8, -4), PopupPrimaryAxis.Horizontal); - return new CustomPopupPlacement[] { right, left }; - } - private void Info_MouseEnter(object sender, MouseEventArgs e) - { - if (sender is FrameworkElement element && element.Tag is Homework homework) - { - lastExtraInfoCirlce = element; - ExtraInfoTextBox.DataContext = homework; - PopupInfo.PlacementTarget = element; - PopupInfo.IsOpen = true; - this.MouseMove += Control_MouseMove; - } - } - private void Control_MouseMove(object sender, MouseEventArgs e) - { - Point pt = e.GetPosition(lastExtraInfoCirlce); - if (!PopupInfoBorder.IsMouseOver && VisualTreeHelper.HitTest(lastExtraInfoCirlce, pt) == null) - { - PopupInfo.IsOpen = false; - this.MouseMove -= Control_MouseMove; - } - } - #endregion - } + /// + /// Interaction logic for HomeworkList.xaml + /// + public partial class HomeworkList : UserControl + { + private const int defaultPageSize = 6; + + private FrameworkElement lastExtraInfoCirlce; + + // Чтобы можно было прикрутить Binding + public static readonly DependencyProperty CollectionProperty = + DependencyProperty.Register("Collection", typeof(HomeworkCollection), typeof(HomeworkList)); + public HomeworkCollection Collection + { + get => (HomeworkCollection)GetValue(CollectionProperty); + set => SetValue(CollectionProperty, value); + } + + public static readonly DependencyProperty HomeworkManagerProperty = + DependencyProperty.Register("HomeworkManager", typeof(Homeworks), typeof(HomeworkList)); + public Homeworks? HomeworkManager + { + get => (Homeworks)GetValue(HomeworkManagerProperty); + set => SetValue(HomeworkManagerProperty, value); + } + + public static readonly DependencyProperty HeaderProperty = + DependencyProperty.Register("Header", typeof(string), typeof(HomeworkList)); + + private int? sectionNumber; + public int? SectionNumber + { + get => sectionNumber; + set + { + if (value.HasValue && value.Value >= 0) + { + sectionNumber = value.Value; + MainExpander.IsExpanded = SettingsService.Settings.HomeworkSectionExpandedStates[value.Value]; + } + } + } + public string Header + { + get => (string)GetValue(HeaderProperty); + set => SetValue(HeaderProperty, value); + } + + public HomeworkList() + { + InitializeComponent(); + PopupInfo.CustomPopupPlacementCallback += PopupExtraInfoPlacement; + } + + private void Card_Initialized(object sender, EventArgs e) + { + Card card = (Card)sender; + HomeworkStatus status = (HomeworkStatus)card.Tag; + if (status == HomeworkStatus.Uploaded || status == HomeworkStatus.Checked) + { + Button uploadButton = (Button)card.FindName("uploadButton"); + uploadButton.Click -= UploadButton_Click; + uploadButton.Click += DownloadUploadedButton_Click; + } + if (status == HomeworkStatus.Uploaded) + { + Button deleteButton = (Button)card.FindName("deleteButton"); + deleteButton.Click += DeleteButton_Click; + } + if (status == HomeworkStatus.Checked) + { + Button deleteButton = (Button)card.FindName("deleteButton"); + deleteButton.Click += UploadButton_Click; + } + } + + private void DeleteButton_Click(object sender, RoutedEventArgs e) + { + HomeworkManager?.OpenDeleteDialog((Homework)((Button)sender).Tag); + } + + private void DownloadButton_Click(object sender, RoutedEventArgs e) + { + Homework homework = (Homework)((Button)sender).Tag; + HomeworkManager?.DownloadHomework(homework.FilePath); + } + + private void UploadButton_Click(object sender, RoutedEventArgs e) + { + Button uploadButton = (Button)sender; + Grid grid = (Grid)uploadButton.Parent; + Button progressButton = (Button)grid.FindName("progressButton"); + HomeworkManager?.OpenUploadDialog((Homework)uploadButton.Tag, Collection.Items, progressButton, uploadButton); + } + + private void DownloadUploadedButton_Click(object sender, RoutedEventArgs e) + { + Button uploadButton = (Button)sender; + Homework homework = (Homework)uploadButton.Tag; + if (homework.UploadedHomework.StudentAnswer != null) + HomeworkManager?.OpenDownloadUploadedDialog(homework); + else + HomeworkManager?.DownloadHomework(homework.UploadedHomework.FilePath); + } + + private void Card_Drop(object sender, DragEventArgs e) + { + if (e.Data.GetDataPresent(DataFormats.FileDrop)) + { + Card card = (Card)sender; + Button uploadButton = (Button)card.FindName("uploadButton"); + Button progressButton = (Button)card.FindName("progressButton"); + var files = (string[])e.Data.GetData(DataFormats.FileDrop); + HomeworkManager?.OpenUploadDialog((Homework)uploadButton.Tag, Collection.Items, progressButton, uploadButton, files); + } + } + + // Sets the content of comment popup and shows it on screen + private void CommentButton_Click(object sender, RoutedEventArgs e) + { + Button button = (Button)sender; + popupComment.PlacementTarget = button; + + if (button.Tag is string homeworkTaskComment) + { + CommentParagraph.Inlines.SetInlinesWithHyperlinksFromText(homeworkTaskComment); + } + else if (button.Tag is HomeworkComment teacherComment) + { + CommentParagraph.Inlines.Clear(); + if (teacherComment.AttachmentPath == null) // No file attached, just show the text + { + CommentParagraph.Inlines.Add(new Run(teacherComment.Text)); + } + else // Add the ability to download attached file + { + var hyperlink = new Hyperlink(new Run(teacherComment.Text)) + { + // Adress doesn't matter. RequestNavigate won't fire without it + NavigateUri = new Uri(@"https://127.0.0.1/"), + }; + hyperlink.RequestNavigate += (sender, args) => HomeworkManager.DownloadHomework(teacherComment.AttachmentPath); + CommentParagraph.Inlines.Add(hyperlink); + } + } + CommentViewer.AdjustWidthToText(); + popupComment.IsOpen = true; + } + + private async void LoadPageButton_Click(object sender, RoutedEventArgs e) + { + Button button = (Button)sender; + + button.IsHitTestVisible = false; + ButtonProgressAssist.SetIsIndicatorVisible(progressPageButton, true); + await Collection.LoadNextPage(); + ButtonProgressAssist.SetIsIndicatorVisible(progressPageButton, false); + button.IsHitTestVisible = true; + + UpdateNextPageButtonVisibility(); + } + + public void UpdateNextPageButtonVisibility() + { + var count = Collection.Items.Count; + var maxCount = Collection.MaxCount; + if (count >= maxCount) + { + nextPageButton.Visibility = Visibility.Collapsed; + } + else if (count >= defaultPageSize) + { + nextPageButton.Visibility = Visibility.Visible; + } + } + + #region Extra info popup mouse handling + // MouseOver multibinding caused many problems so I ended up with manual handling + + private CustomPopupPlacement[] PopupExtraInfoPlacement(Size popupSize, Size targetSize, Point offset) + { + CustomPopupPlacement right = new(new Point(targetSize.Width / 2 + 3, -4), PopupPrimaryAxis.Horizontal); + CustomPopupPlacement left = new(new Point((popupSize.Width + targetSize.Width / 2) * -1 + 8, -4), PopupPrimaryAxis.Horizontal); + return new CustomPopupPlacement[] { right, left }; + } + private void Info_MouseEnter(object sender, MouseEventArgs e) + { + if (sender is FrameworkElement element && element.Tag is Homework homework) + { + lastExtraInfoCirlce = element; + ExtraInfoTextBox.DataContext = homework; + PopupInfo.PlacementTarget = element; + PopupInfo.IsOpen = true; + this.MouseMove += Control_MouseMove; + } + } + private void Control_MouseMove(object sender, MouseEventArgs e) + { + Point pt = e.GetPosition(lastExtraInfoCirlce); + if (!PopupInfoBorder.IsMouseOver && VisualTreeHelper.HitTest(lastExtraInfoCirlce, pt) == null) + { + PopupInfo.IsOpen = false; + this.MouseMove -= Control_MouseMove; + } + } + #endregion + + #region Expander + private void SetMainExpanderState(bool isExpanded) + { + MainExpander.IsExpanded = isExpanded; + if (sectionNumber.HasValue) + { + SettingsService.Settings.HomeworkSectionExpandedStates[sectionNumber.Value] = isExpanded; + SettingsService.OnSettingsChange(); + } + + } + private void MainExpander_Collapsed(object sender, RoutedEventArgs e) + { + SetMainExpanderState(false); + } + + private void MainExpander_Expanded(object sender, RoutedEventArgs e) + { + SetMainExpanderState(true); + } + #endregion + } } diff --git a/MystatDesktopWpf/UserControls/Menus/Homeworks.xaml.cs b/MystatDesktopWpf/UserControls/Menus/Homeworks.xaml.cs index 7ef9f80..6325b16 100644 --- a/MystatDesktopWpf/UserControls/Menus/Homeworks.xaml.cs +++ b/MystatDesktopWpf/UserControls/Menus/Homeworks.xaml.cs @@ -26,10 +26,18 @@ public partial class Homeworks : UserControl, IRefreshable private readonly UploadHomework uploadContent = new(); private readonly DeleteHomework deleteContent = new(); private readonly DonwloadHomeworkPreview downloadContent = new(); + private readonly HomeworkList[] homeworkSections; public Homeworks() { InitializeComponent(); + + homeworkSections = new HomeworkList[] { OverdueList, DeletedList, ActiveList, UploadedList, CheckedList }; + + for (int i = 0; i < homeworkSections.Length; i++) + { + homeworkSections[i].SectionNumber = i; + } App.LanguageChanged += App_LanguageChanged; } @@ -224,11 +232,10 @@ public async void OpenDeleteDialog(Homework homework) private void ShowHomeworkSlide() { transitioner.SelectedIndex = 1; - OverdueList.UpdateNextPageButtonVisibility(); - DeletedList.UpdateNextPageButtonVisibility(); - ActiveList.UpdateNextPageButtonVisibility(); - UploadedList.UpdateNextPageButtonVisibility(); - CheckedList.UpdateNextPageButtonVisibility(); + foreach (HomeworkList section in homeworkSections) + { + section.UpdateNextPageButtonVisibility(); + } } bool loading = false; From bab4f77882874fbc2b8bc0d07f1297bd3dcbf9f5 Mon Sep 17 00:00:00 2001 From: BeloMaximka Date: Tue, 18 Apr 2023 12:45:58 +0300 Subject: [PATCH 3/9] folder upload fix --- .../UserControls/DialogContent/UploadHomework.xaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MystatDesktopWpf/UserControls/DialogContent/UploadHomework.xaml.cs b/MystatDesktopWpf/UserControls/DialogContent/UploadHomework.xaml.cs index 0ae7630..319bbd5 100644 --- a/MystatDesktopWpf/UserControls/DialogContent/UploadHomework.xaml.cs +++ b/MystatDesktopWpf/UserControls/DialogContent/UploadHomework.xaml.cs @@ -79,7 +79,7 @@ private void UpdateFileInfo() fileLine.Visibility = Visibility.Visible; string extension = Path.GetExtension(files[0]); - if (files?.Length != 1 || extension == ".txt" || extension == ".csv") + if (files?.Length != 1 || extension == ".txt" || extension == ".csv" || Directory.Exists(files[0])) { Archive = true; fileTextBox.Visibility = Visibility.Visible; From a34ea668a2b5778fd0afc69b50fc6c7432eefd07 Mon Sep 17 00:00:00 2001 From: BeloMaximka Date: Tue, 18 Apr 2023 21:49:14 +0300 Subject: [PATCH 4/9] long login fix --- MystatDesktopWpf/Domain/MystatAPISingleton.cs | 7 +++++-- MystatDesktopWpf/UserControls/Menus/MainPage.xaml.cs | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/MystatDesktopWpf/Domain/MystatAPISingleton.cs b/MystatDesktopWpf/Domain/MystatAPISingleton.cs index 45a63b3..7807976 100644 --- a/MystatDesktopWpf/Domain/MystatAPISingleton.cs +++ b/MystatDesktopWpf/Domain/MystatAPISingleton.cs @@ -7,14 +7,17 @@ namespace MystatDesktopWpf.Domain internal static class MystatAPISingleton { static public MystatAPIClient Client { get; private set; } - static public ProfileInfo? Profile { get; private set; } + static public Task DeferredProfileInfo { get; private set; } static async public Task LoginAndGetProfileInfo() { var result = await Client.Login(); if (result is MystatAuthSuccess responseSuccess) - Profile = await Client.GetProfileInfo(); + { + DeferredProfileInfo = Client.GetProfileInfo(); + } return result; } + static MystatAPISingleton() { Client = new MystatAPIClient(); diff --git a/MystatDesktopWpf/UserControls/Menus/MainPage.xaml.cs b/MystatDesktopWpf/UserControls/Menus/MainPage.xaml.cs index 38237e0..42d46db 100644 --- a/MystatDesktopWpf/UserControls/Menus/MainPage.xaml.cs +++ b/MystatDesktopWpf/UserControls/Menus/MainPage.xaml.cs @@ -35,11 +35,11 @@ private void GroupRadioButton_Checked(object sender, RoutedEventArgs e) leadersList.ItemsSource = viewModel.GroupLeaders; } - private void studentName_Initialized(object sender, EventArgs e) + private async void studentName_Initialized(object sender, EventArgs e) { Run run = (Run)sender; Student student = (Student)run.DataContext; - if (student.Id == MystatAPISingleton.Profile.Id) + if (student.Id == (await MystatAPISingleton.DeferredProfileInfo).Id) run.FontWeight = FontWeights.Bold; else if (student.Id == null) { From 988a7454678438f5f180dad77477a10a3eca1876 Mon Sep 17 00:00:00 2001 From: BeloMaximka Date: Fri, 21 Apr 2023 22:24:16 +0300 Subject: [PATCH 5/9] caching bottlenecks --- MystatDesktopWpf/Domain/MystatAPISingleton.cs | 6 +- .../Services/MystatAPICachingService.cs | 83 +++++++++++++++++++ .../UserControls/Menus/MainPage.xaml.cs | 2 +- .../ViewModels/HeaderBarViewModel.cs | 2 +- .../ViewModels/MainPageViewModel.cs | 28 +++++-- 5 files changed, 108 insertions(+), 13 deletions(-) create mode 100644 MystatDesktopWpf/Services/MystatAPICachingService.cs diff --git a/MystatDesktopWpf/Domain/MystatAPISingleton.cs b/MystatDesktopWpf/Domain/MystatAPISingleton.cs index 7807976..1a1b8ef 100644 --- a/MystatDesktopWpf/Domain/MystatAPISingleton.cs +++ b/MystatDesktopWpf/Domain/MystatAPISingleton.cs @@ -1,5 +1,6 @@ using MystatAPI; using MystatAPI.Entity; +using MystatDesktopWpf.Services; using System.Threading.Tasks; namespace MystatDesktopWpf.Domain @@ -7,13 +8,14 @@ namespace MystatDesktopWpf.Domain internal static class MystatAPISingleton { static public MystatAPIClient Client { get; private set; } - static public Task DeferredProfileInfo { get; private set; } + static public ProfileInfo ProfileInfo { get; private set; } static async public Task LoginAndGetProfileInfo() { var result = await Client.Login(); if (result is MystatAuthSuccess responseSuccess) { - DeferredProfileInfo = Client.GetProfileInfo(); + ProfileInfo = await MystatAPICachingService.GetAndUpdateCachedProfileInfo(); + Client.GroudId = ProfileInfo.CurrentGroupId; } return result; } diff --git a/MystatDesktopWpf/Services/MystatAPICachingService.cs b/MystatDesktopWpf/Services/MystatAPICachingService.cs new file mode 100644 index 0000000..87241b2 --- /dev/null +++ b/MystatDesktopWpf/Services/MystatAPICachingService.cs @@ -0,0 +1,83 @@ +using MystatAPI; +using MystatAPI.Entity; +using MystatDesktopWpf.Domain; +using System; +using System.IO; +using System.Text.Json; +using System.Threading.Tasks; +using System.Windows; + +namespace MystatDesktopWpf.Services +{ + internal static class MystatAPICachingService + { + private static readonly MystatAPIClient api; + private static readonly string cacheRootPath; + static MystatAPICachingService() + { + api = MystatAPISingleton.Client; + try + { + Directory.CreateDirectory(Environment.ExpandEnvironmentVariables(@"%appdata%\Mystat")); + Directory.CreateDirectory(Environment.ExpandEnvironmentVariables(@"%appdata%\Mystat\cache")); + cacheRootPath = Directory.CreateDirectory(Environment.ExpandEnvironmentVariables(@"%appdata%\Mystat\cache\users")).FullName; + } + catch (Exception exception) + { + MessageBox.Show(exception.Message); + throw; + } + } + + public static async Task GetAndUpdateCachedProfileInfo() + { + string filePath = $"{cacheRootPath}\\profileInfo.json"; + ProfileInfo? profileInfo = null; + if (File.Exists(filePath)) + { + string jsonData = await File.ReadAllTextAsync(filePath); + profileInfo = JsonSerializer.Deserialize(jsonData); + } + profileInfo ??= await api.GetProfileInfo(); + UpdateCachedProfileInfo(filePath, profileInfo); + return profileInfo; + } + + public static async Task GetCachedActivities() + { + string filePath = $"{cacheRootPath}\\activities.json"; + MystatAPI.Entity.Activity[]? activities = null; + if (File.Exists(filePath)) + { + string jsonData = await File.ReadAllTextAsync(filePath); + activities = JsonSerializer.Deserialize(jsonData); + } + return activities; + } + + public static async void UpdateCachedActivities(MystatAPI.Entity.Activity[] activities) + { + try + { + await File.WriteAllTextAsync($"{cacheRootPath}\\activities.json", JsonSerializer.Serialize(activities)); + } + catch (Exception) + { + // TODO LOG + } + } + + private async static void UpdateCachedProfileInfo(string path, ProfileInfo? profileInfo = null) + { + try + { + profileInfo ??= await api.GetProfileInfo(); + await File.WriteAllTextAsync(path, JsonSerializer.Serialize(profileInfo)); + } + catch (Exception) + { + // TODO LOG + } + } + } +} diff --git a/MystatDesktopWpf/UserControls/Menus/MainPage.xaml.cs b/MystatDesktopWpf/UserControls/Menus/MainPage.xaml.cs index 42d46db..d63be25 100644 --- a/MystatDesktopWpf/UserControls/Menus/MainPage.xaml.cs +++ b/MystatDesktopWpf/UserControls/Menus/MainPage.xaml.cs @@ -39,7 +39,7 @@ private async void studentName_Initialized(object sender, EventArgs e) { Run run = (Run)sender; Student student = (Student)run.DataContext; - if (student.Id == (await MystatAPISingleton.DeferredProfileInfo).Id) + if (student.Id == MystatAPISingleton.ProfileInfo.Id) run.FontWeight = FontWeights.Bold; else if (student.Id == null) { diff --git a/MystatDesktopWpf/ViewModels/HeaderBarViewModel.cs b/MystatDesktopWpf/ViewModels/HeaderBarViewModel.cs index 88d8fca..4145447 100644 --- a/MystatDesktopWpf/ViewModels/HeaderBarViewModel.cs +++ b/MystatDesktopWpf/ViewModels/HeaderBarViewModel.cs @@ -51,7 +51,7 @@ public async void Refresh() { try { - ProfileInfo info = await MystatAPISingleton.Client.GetProfileInfo(); + ProfileInfo info = await MystatAPICachingService.GetAndUpdateCachedProfileInfo(); Name = info.FullName; Group = info.GroupName; Badges = info.AchievesCount; diff --git a/MystatDesktopWpf/ViewModels/MainPageViewModel.cs b/MystatDesktopWpf/ViewModels/MainPageViewModel.cs index 387c5b8..01b5e2a 100644 --- a/MystatDesktopWpf/ViewModels/MainPageViewModel.cs +++ b/MystatDesktopWpf/ViewModels/MainPageViewModel.cs @@ -1,6 +1,7 @@ using MaterialDesignThemes.Wpf; using MystatAPI.Entity; using MystatDesktopWpf.Domain; +using MystatDesktopWpf.Services; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -43,16 +44,15 @@ public async Task LoadActivities() { try { - var result = await MystatAPISingleton.Client.GetActivities(); - List optimizedActivies = new(); - foreach (var activity in result) + var result = await MystatAPICachingService.GetCachedActivities(); + if(result != null) { - optimizedActivies.Add(new OptimizedActiviy(activity)); - if (activity.Badge > 0) optimizedActivies.Add(new OptimizedActiviy(activity, true)); - } - - Activities = new(optimizedActivies); - break; + Activities = new(MakeOptimizedActivities(result)); + } + result = await MystatAPISingleton.Client.GetActivities(); + Activities = new(MakeOptimizedActivities(result)); + MystatAPICachingService.UpdateCachedActivities(result); + break; } catch (Exception e) { @@ -61,6 +61,16 @@ public async Task LoadActivities() } LoadingActivities = false; } + private List MakeOptimizedActivities(Activity[] activities) + { + List optimizedActivies = new(); + foreach (var activity in activities) + { + optimizedActivies.Add(new OptimizedActiviy(activity)); + if (activity.Badge > 0) optimizedActivies.Add(new OptimizedActiviy(activity, true)); + } + return optimizedActivies; + } public bool LoadingLeaders { get; private set; } public async Task LoadLeaders() From fe8894feaa02cf3f9d8698dcb22e7be7c16837d1 Mon Sep 17 00:00:00 2001 From: BeloMaximka Date: Fri, 21 Apr 2023 22:55:08 +0300 Subject: [PATCH 6/9] mystat api fix +user control reorganization --- .../{ => SettingsSections}/NotificationSettings.xaml | 0 .../{ => SettingsSections}/NotificationSettings.xaml.cs | 0 .../UserControls/{ => SettingsSections}/ThemeSettings.xaml | 0 .../UserControls/{ => SettingsSections}/ThemeSettings.xaml.cs | 0 .../UserControls/{ => SettingsSections}/ThemeSettingsBar.xaml | 0 .../{ => SettingsSections}/ThemeSettingsBar.xaml.cs | 0 .../UserControls/{ => SettingsSections}/TimezoneSettings.xaml | 0 .../{ => SettingsSections}/TimezoneSettings.xaml.cs | 0 .../UserControls/{ => SettingsSections}/TraySettings.xaml | 0 .../UserControls/{ => SettingsSections}/TraySettings.xaml.cs | 0 mystatapi-cs | 2 +- 11 files changed, 1 insertion(+), 1 deletion(-) rename MystatDesktopWpf/UserControls/{ => SettingsSections}/NotificationSettings.xaml (100%) rename MystatDesktopWpf/UserControls/{ => SettingsSections}/NotificationSettings.xaml.cs (100%) rename MystatDesktopWpf/UserControls/{ => SettingsSections}/ThemeSettings.xaml (100%) rename MystatDesktopWpf/UserControls/{ => SettingsSections}/ThemeSettings.xaml.cs (100%) rename MystatDesktopWpf/UserControls/{ => SettingsSections}/ThemeSettingsBar.xaml (100%) rename MystatDesktopWpf/UserControls/{ => SettingsSections}/ThemeSettingsBar.xaml.cs (100%) rename MystatDesktopWpf/UserControls/{ => SettingsSections}/TimezoneSettings.xaml (100%) rename MystatDesktopWpf/UserControls/{ => SettingsSections}/TimezoneSettings.xaml.cs (100%) rename MystatDesktopWpf/UserControls/{ => SettingsSections}/TraySettings.xaml (100%) rename MystatDesktopWpf/UserControls/{ => SettingsSections}/TraySettings.xaml.cs (100%) diff --git a/MystatDesktopWpf/UserControls/NotificationSettings.xaml b/MystatDesktopWpf/UserControls/SettingsSections/NotificationSettings.xaml similarity index 100% rename from MystatDesktopWpf/UserControls/NotificationSettings.xaml rename to MystatDesktopWpf/UserControls/SettingsSections/NotificationSettings.xaml diff --git a/MystatDesktopWpf/UserControls/NotificationSettings.xaml.cs b/MystatDesktopWpf/UserControls/SettingsSections/NotificationSettings.xaml.cs similarity index 100% rename from MystatDesktopWpf/UserControls/NotificationSettings.xaml.cs rename to MystatDesktopWpf/UserControls/SettingsSections/NotificationSettings.xaml.cs diff --git a/MystatDesktopWpf/UserControls/ThemeSettings.xaml b/MystatDesktopWpf/UserControls/SettingsSections/ThemeSettings.xaml similarity index 100% rename from MystatDesktopWpf/UserControls/ThemeSettings.xaml rename to MystatDesktopWpf/UserControls/SettingsSections/ThemeSettings.xaml diff --git a/MystatDesktopWpf/UserControls/ThemeSettings.xaml.cs b/MystatDesktopWpf/UserControls/SettingsSections/ThemeSettings.xaml.cs similarity index 100% rename from MystatDesktopWpf/UserControls/ThemeSettings.xaml.cs rename to MystatDesktopWpf/UserControls/SettingsSections/ThemeSettings.xaml.cs diff --git a/MystatDesktopWpf/UserControls/ThemeSettingsBar.xaml b/MystatDesktopWpf/UserControls/SettingsSections/ThemeSettingsBar.xaml similarity index 100% rename from MystatDesktopWpf/UserControls/ThemeSettingsBar.xaml rename to MystatDesktopWpf/UserControls/SettingsSections/ThemeSettingsBar.xaml diff --git a/MystatDesktopWpf/UserControls/ThemeSettingsBar.xaml.cs b/MystatDesktopWpf/UserControls/SettingsSections/ThemeSettingsBar.xaml.cs similarity index 100% rename from MystatDesktopWpf/UserControls/ThemeSettingsBar.xaml.cs rename to MystatDesktopWpf/UserControls/SettingsSections/ThemeSettingsBar.xaml.cs diff --git a/MystatDesktopWpf/UserControls/TimezoneSettings.xaml b/MystatDesktopWpf/UserControls/SettingsSections/TimezoneSettings.xaml similarity index 100% rename from MystatDesktopWpf/UserControls/TimezoneSettings.xaml rename to MystatDesktopWpf/UserControls/SettingsSections/TimezoneSettings.xaml diff --git a/MystatDesktopWpf/UserControls/TimezoneSettings.xaml.cs b/MystatDesktopWpf/UserControls/SettingsSections/TimezoneSettings.xaml.cs similarity index 100% rename from MystatDesktopWpf/UserControls/TimezoneSettings.xaml.cs rename to MystatDesktopWpf/UserControls/SettingsSections/TimezoneSettings.xaml.cs diff --git a/MystatDesktopWpf/UserControls/TraySettings.xaml b/MystatDesktopWpf/UserControls/SettingsSections/TraySettings.xaml similarity index 100% rename from MystatDesktopWpf/UserControls/TraySettings.xaml rename to MystatDesktopWpf/UserControls/SettingsSections/TraySettings.xaml diff --git a/MystatDesktopWpf/UserControls/TraySettings.xaml.cs b/MystatDesktopWpf/UserControls/SettingsSections/TraySettings.xaml.cs similarity index 100% rename from MystatDesktopWpf/UserControls/TraySettings.xaml.cs rename to MystatDesktopWpf/UserControls/SettingsSections/TraySettings.xaml.cs diff --git a/mystatapi-cs b/mystatapi-cs index f2a35a2..795d5c2 160000 --- a/mystatapi-cs +++ b/mystatapi-cs @@ -1 +1 @@ -Subproject commit f2a35a26714513d85331bb87ff789d504e9b67d0 +Subproject commit 795d5c2d972c28fa75827c1eb9440797cd4692ad From 16b54414e02b144fa28059bfcf807a5120648dc7 Mon Sep 17 00:00:00 2001 From: BeloMaximka Date: Sat, 22 Apr 2023 23:52:41 +0300 Subject: [PATCH 7/9] namespace reorganization, clear cache --- MystatDesktopWpf/Domain/MystatAPISingleton.cs | 3 +- .../Services/MystatAPICachingService.cs | 80 +++++++++++++++++-- .../UserControls/Menus/Settings.xaml | 3 +- .../UserControls/Menus/Settings.xaml.cs | 12 ++- .../SettingsSections/CacheSettings.xaml | 22 +++++ .../SettingsSections/CacheSettings.xaml.cs | 54 +++++++++++++ .../NotificationSettings.xaml | 2 +- .../NotificationSettings.xaml.cs | 2 +- .../SettingsSections/ThemeSettings.xaml | 4 +- .../SettingsSections/ThemeSettings.xaml.cs | 2 +- .../SettingsSections/ThemeSettingsBar.xaml | 2 +- .../SettingsSections/ThemeSettingsBar.xaml.cs | 2 +- .../SettingsSections/TimezoneSettings.xaml | 2 +- .../SettingsSections/TimezoneSettings.xaml.cs | 2 +- .../SettingsSections/TraySettings.xaml | 2 +- .../SettingsSections/TraySettings.xaml.cs | 2 +- 16 files changed, 175 insertions(+), 21 deletions(-) create mode 100644 MystatDesktopWpf/UserControls/SettingsSections/CacheSettings.xaml create mode 100644 MystatDesktopWpf/UserControls/SettingsSections/CacheSettings.xaml.cs diff --git a/MystatDesktopWpf/Domain/MystatAPISingleton.cs b/MystatDesktopWpf/Domain/MystatAPISingleton.cs index 1a1b8ef..bcc0ea8 100644 --- a/MystatDesktopWpf/Domain/MystatAPISingleton.cs +++ b/MystatDesktopWpf/Domain/MystatAPISingleton.cs @@ -14,7 +14,8 @@ static async public Task LoginAndGetProfileInfo() var result = await Client.Login(); if (result is MystatAuthSuccess responseSuccess) { - ProfileInfo = await MystatAPICachingService.GetAndUpdateCachedProfileInfo(); + MystatAPICachingService.Login = Client.LoginData.Username; + ProfileInfo = await MystatAPICachingService.GetAndUpdateCachedProfileInfo(); Client.GroudId = ProfileInfo.CurrentGroupId; } return result; diff --git a/MystatDesktopWpf/Services/MystatAPICachingService.cs b/MystatDesktopWpf/Services/MystatAPICachingService.cs index 87241b2..74898de 100644 --- a/MystatDesktopWpf/Services/MystatAPICachingService.cs +++ b/MystatDesktopWpf/Services/MystatAPICachingService.cs @@ -3,6 +3,7 @@ using MystatDesktopWpf.Domain; using System; using System.IO; +using System.Text; using System.Text.Json; using System.Threading.Tasks; using System.Windows; @@ -12,7 +13,28 @@ namespace MystatDesktopWpf.Services internal static class MystatAPICachingService { private static readonly MystatAPIClient api; - private static readonly string cacheRootPath; + private static string? userCachePath; + private static string rootCachePath; + + + private static string UserCachePath + { + get + { + if (userCachePath == null) throw new DirectoryNotFoundException("No user directory speficied"); + return userCachePath; + } + } + public static string Login + { + set + { + string encodedLogin = Convert.ToBase64String(Encoding.UTF8.GetBytes(value)); + + userCachePath = Directory.CreateDirectory( + Environment.ExpandEnvironmentVariables(@"%appdata%\Mystat\cache\users\" + encodedLogin)).FullName; + } + } static MystatAPICachingService() { api = MystatAPISingleton.Client; @@ -20,7 +42,7 @@ static MystatAPICachingService() { Directory.CreateDirectory(Environment.ExpandEnvironmentVariables(@"%appdata%\Mystat")); Directory.CreateDirectory(Environment.ExpandEnvironmentVariables(@"%appdata%\Mystat\cache")); - cacheRootPath = Directory.CreateDirectory(Environment.ExpandEnvironmentVariables(@"%appdata%\Mystat\cache\users")).FullName; + rootCachePath = Directory.CreateDirectory(Environment.ExpandEnvironmentVariables(@"%appdata%\Mystat\cache\users")).FullName; } catch (Exception exception) { @@ -31,7 +53,7 @@ static MystatAPICachingService() public static async Task GetAndUpdateCachedProfileInfo() { - string filePath = $"{cacheRootPath}\\profileInfo.json"; + string filePath = $"{UserCachePath}\\profileInfo.json"; ProfileInfo? profileInfo = null; if (File.Exists(filePath)) { @@ -39,13 +61,14 @@ public static async Task GetAndUpdateCachedProfileInfo() profileInfo = JsonSerializer.Deserialize(jsonData); } profileInfo ??= await api.GetProfileInfo(); + CreateUserCacheDir(); UpdateCachedProfileInfo(filePath, profileInfo); return profileInfo; } public static async Task GetCachedActivities() { - string filePath = $"{cacheRootPath}\\activities.json"; + string filePath = $"{UserCachePath}\\activities.json"; MystatAPI.Entity.Activity[]? activities = null; if (File.Exists(filePath)) { @@ -59,7 +82,8 @@ public static async void UpdateCachedActivities(MystatAPI.Entity.Activity[] acti { try { - await File.WriteAllTextAsync($"{cacheRootPath}\\activities.json", JsonSerializer.Serialize(activities)); + CreateUserCacheDir(); + await File.WriteAllTextAsync($"{UserCachePath}\\activities.json", JsonSerializer.Serialize(activities)); } catch (Exception) { @@ -67,6 +91,52 @@ public static async void UpdateCachedActivities(MystatAPI.Entity.Activity[] acti } } + public async static Task ClearCacheAsync() + { + return await Task.Run(() => + { + try + { + foreach (var directory in Directory.GetDirectories(rootCachePath)) + { + Directory.Delete(directory, true); + } + return true; + + } + catch (Exception) + { + return false; + } + }); + } + + public async static Task GetCacheSize() + { + return await Task.Run(() => + { + int size = 0; + try + { + + foreach (var dir in Directory.GetDirectories(rootCachePath)) + { + foreach (var file in Directory.GetFiles(dir)) + { + size += file.Length; + } + } + } + catch (Exception) {} + return size; + }); + } + + private static void CreateUserCacheDir() + { + Directory.CreateDirectory(userCachePath); + } + private async static void UpdateCachedProfileInfo(string path, ProfileInfo? profileInfo = null) { try diff --git a/MystatDesktopWpf/UserControls/Menus/Settings.xaml b/MystatDesktopWpf/UserControls/Menus/Settings.xaml index 8ede445..1eddb3e 100644 --- a/MystatDesktopWpf/UserControls/Menus/Settings.xaml +++ b/MystatDesktopWpf/UserControls/Menus/Settings.xaml @@ -5,7 +5,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:MystatDesktopWpf" xmlns:domain="clr-namespace:MystatDesktopWpf.Domain" - xmlns:controls="clr-namespace:MystatDesktopWpf.UserControls" + xmlns:controls="clr-namespace:MystatDesktopWpf.UserControls.SettingsSections" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:vm="clr-namespace:MystatDesktopWpf.ViewModels" xmlns:converters="clr-namespace:MystatDesktopWpf.Converters" @@ -19,6 +19,7 @@ + diff --git a/MystatDesktopWpf/UserControls/Menus/Settings.xaml.cs b/MystatDesktopWpf/UserControls/Menus/Settings.xaml.cs index aa4e8cd..8e30fa6 100644 --- a/MystatDesktopWpf/UserControls/Menus/Settings.xaml.cs +++ b/MystatDesktopWpf/UserControls/Menus/Settings.xaml.cs @@ -1,15 +1,21 @@ -using System.Windows.Controls; +using MystatDesktopWpf.Domain; +using System.Windows.Controls; namespace MystatDesktopWpf.UserControls { /// /// Interaction logic for Settings.xaml /// - public partial class Settings : UserControl + public partial class Settings : UserControl, IRefreshable { public Settings() { InitializeComponent(); } - } + + public void Refresh() + { + CacheSettings.UpdateCacheSize(); + } + } } diff --git a/MystatDesktopWpf/UserControls/SettingsSections/CacheSettings.xaml b/MystatDesktopWpf/UserControls/SettingsSections/CacheSettings.xaml new file mode 100644 index 0000000..0a4c87d --- /dev/null +++ b/MystatDesktopWpf/UserControls/SettingsSections/CacheSettings.xaml @@ -0,0 +1,22 @@ + + + + + + + + + +