Skip to content

Commit

Permalink
Ensure VNs and User Games are properly loaded after VNDB update and i…
Browse files Browse the repository at this point in the history
…mprove File not found filter in User Games.
  • Loading branch information
Zoltanar committed Apr 27, 2024
1 parent 757499b commit f6c42eb
Show file tree
Hide file tree
Showing 11 changed files with 147 additions and 118 deletions.
2 changes: 1 addition & 1 deletion Happy Reader/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Happy Reader")]
[assembly: AssemblyCopyright("Copyright © Zoltanar 2015-2023")]
[assembly: AssemblyCopyright("Copyright © Zoltanar 2015-2024")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

Expand Down
2 changes: 1 addition & 1 deletion Happy Reader/View/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ private async void Window_Loaded(object sender, RoutedEventArgs e)
await ViewModel.Initialize(watch, !noEntries, logVerbose);
//we call this here to create icons ahead of time.
BuildTrayPopup(sender, e);
UserGamesTabItem.GroupUserGames();
UserGamesTabItem.Initialise();
LoadSavedData();
}
finally
Expand Down
4 changes: 3 additions & 1 deletion Happy Reader/View/Tabs/SettingsTab.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,9 @@ private async void UpdateVndbData(object sender, RoutedEventArgs e)
var updateResult = await Happy_Apps_Core.DumpReader.Program.Execute(UpdateLoggingAction);
if (updateResult.Success)
{
await StaticMethods.MainWindow.ViewModel.DatabaseViewModel.Initialize();

await StaticMethods.MainWindow.ViewModel.InitialiseViewModels(false);
StaticMethods.MainWindow.UserGamesTabItem.Initialise();
}
var message = updateResult.Type.ToString();
if(!string.IsNullOrWhiteSpace(updateResult.ErrorMessage)) message += $" - {updateResult.ErrorMessage}";
Expand Down
2 changes: 1 addition & 1 deletion Happy Reader/View/Tabs/UserGamesTab.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@
<Button Grid.Column="0" Content="Expand Groups" Click="ToggleExpandGroups" Margin="0,2,2,2"/>
<ComboBox Margin="0,2,2,2" Grid.Column="1" SelectedIndex="0" ItemsSource="{Binding UserGameGroupings}" SelectedValuePath="Tag" SelectedValue="{Binding GroupBy}" SelectionChanged="GroupingChanged" Padding="5,0" />
<TextBox x:Name="SearchTextBox" TextChanged="SearchTextBox_OnTextChanged" Margin="2" Grid.Column="2" TextAlignment="Left" />
<CheckBox Margin="2" Grid.Column="3" Content="Show Not Found" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Checked="ToggleButton_OnChecked" Unchecked="ToggleButton_OnChecked" />
<CheckBox Margin="2" Grid.Column="3" Content="Show Not Found" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Checked="ShowNotFound_OnChecked" Unchecked="ShowNotFound_OnChecked" IsChecked="{Binding ShowNotFound}" />
<Button Margin="2" Padding="5,0" Content="Random" Grid.Column="4" Click="SelectRandom" ToolTip="Select random title from filtered view (result may be in collapsed group)"/>
<Button Margin="2" Padding="5,0" Content="Add New" Grid.Column="5" Click="AddNewUserGame"/>
<TextBlock Margin="2,2,0,2" Grid.Column="6" Text="{Binding UserGameItems.Count, StringFormat={}{0} Items.}" Background="Aquamarine" TextAlignment="Center" />
Expand Down
63 changes: 40 additions & 23 deletions Happy Reader/View/Tabs/UserGamesTab.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Threading;
Expand All @@ -22,13 +21,16 @@ namespace Happy_Reader.View.Tabs
{
public partial class UserGamesTab : UserControl
{
private readonly DispatcherTimer _scrollLabelTimer;
private static readonly Random Random = new();

private UserGamesViewModel ViewModel => (UserGamesViewModel)DataContext;
private readonly UIElement[] _normalContent;
private UserGamesViewModel ViewModel => (UserGamesViewModel)DataContext;
private readonly DispatcherTimer _scrollLabelTimer;
private readonly UIElement[] _normalContent;
private Func<object, bool> _searchFilter;
private Func<object, bool> _showNotFoundFilter = FilterFoundGames;

public UserGamesTab()

public UserGamesTab()
{
InitializeComponent();
_scrollLabelTimer = new DispatcherTimer(new TimeSpan(0, 0, 2), DispatcherPriority.ContextIdle, HideScrollLabel, Dispatcher);
Expand Down Expand Up @@ -82,10 +84,7 @@ private void AddGameFile(object sender, string filePath)
}
}

private void GroupingChanged(object sender, SelectionChangedEventArgs e)
{
GroupUserGames();
}
private void GroupingChanged(object sender, SelectionChangedEventArgs e) => GroupUserGames();

public void GroupUserGames()
{
Expand Down Expand Up @@ -238,7 +237,7 @@ private void ToggleUserGameGroups(string groupName, int count, bool expanded, bo
private void ShowLabelOnScrollbar(object sender, ScrollChangedEventArgs e)
{
if (ViewModel == null) return;
if (!(sender is ListBox list))
if (sender is not ListBox list)
{
ScrollLabel.Text = string.Empty;
ScrollBorder.Visibility = Visibility.Hidden;
Expand Down Expand Up @@ -301,7 +300,7 @@ private void HideScrollLabel(object sender, EventArgs eventArgs)

private T GetVisualItemOfType<T>(object originalSource, object parent) where T : DependencyObject
{
if (!(originalSource is DependencyObject depObj)) return null;
if (originalSource is not DependencyObject depObj) return null;
// go up the visual hierarchy until we find the list view item the click came from
// the click might have been on the grid or column headers so we need to cater for this
DependencyObject current = depObj;
Expand All @@ -313,19 +312,29 @@ private T GetVisualItemOfType<T>(object originalSource, object parent) where T :
return null;
}

private void ToggleExpandGroups(object sender, RoutedEventArgs e)
private void ToggleExpandGroups(object sender, RoutedEventArgs e)
{
ToggleUserGameGroups(null, 0, true, true);
}

private async void ToggleButton_OnChecked(object sender, RoutedEventArgs e)
private void ShowNotFound_OnChecked(object sender, RoutedEventArgs e)
{
var tb = (ToggleButton)sender;
var state = tb.IsChecked ?? false;
await ViewModel.LoadUserGames(state);
}
_showNotFoundFilter = ViewModel.ShowNotFound ? null : FilterFoundGames;
ApplyFilter();
}

private static bool FilterFoundGames(object obj) => ((UserGameTile)obj).UserGame.FileExists;

private bool FilterUserGames(object obj)
private void ApplyFilter()
{
var view = CollectionViewSource.GetDefaultView(ViewModel.UserGameItems);
if (_searchFilter != null && _showNotFoundFilter != null) view.Filter = obj=> _searchFilter(obj) && _showNotFoundFilter(obj);
else if (_searchFilter != null) view.Filter = obj => _searchFilter(obj);
else if (_showNotFoundFilter != null) view.Filter = obj => _showNotFoundFilter(obj);
else view.Filter = null;
}

private bool FilterUserGames(object obj)
{
if (SearchTextBox.Text.Length < 3) return true;
var searchTerm = SearchTextBox.Text.ToLowerInvariant();
Expand All @@ -339,10 +348,12 @@ private bool FilterUserGames(object obj)
}

private void SearchTextBox_OnTextChanged(object sender, TextChangedEventArgs e)
{
var view = CollectionViewSource.GetDefaultView(ViewModel.UserGameItems);
if (SearchTextBox.Text.Length < 3 && view.Filter != null) view.Filter = null;
else if (SearchTextBox.Text.Length >= 3) view.Filter = FilterUserGames;
{
var wasNull = _searchFilter == null;
if (SearchTextBox.Text.Length < 3) _searchFilter = null;
else if (SearchTextBox.Text.Length >= 3) _searchFilter = FilterUserGames;
if (wasNull && _searchFilter == null) return;
ApplyFilter();
}

private void SelectRandom(object sender, RoutedEventArgs e)
Expand Down Expand Up @@ -383,5 +394,11 @@ private void MergeGamesCallback(UserGame userGame, bool successful, MergeGame[]
StaticMethods.MainWindow.ViewModel.UserGamesViewModel.RemoveUserGame(mergeTarget.UserGame);
}
}
}

public void Initialise()
{
GroupUserGames();
ApplyFilter();
}
}
}
17 changes: 11 additions & 6 deletions Happy Reader/ViewModel/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -204,12 +204,7 @@ await Task.Run(() =>
StatusText = "Loading data from dump files...";
DumpFiles.Load();
});
await DatabaseViewModel.Initialize();
await ProducersViewModel.Initialize();
await CharactersViewModel.Initialize();
if (initialiseEntries) InitialiseEntries(logVerbose);
await UserGamesViewModel.Initialize();
InformationViewModel.Initialise(LocalDatabase, StaticMethods.Data);
await InitialiseViewModels(initialiseEntries);
LoadLogs();
SetLastPlayed();
OnPropertyChanged(nameof(TestViewModel));
Expand All @@ -220,6 +215,16 @@ await Task.Run(() =>
NotificationEvent(this, $"Took {watch.Elapsed.ToSeconds()}.", "Loading Complete", true);
}

public async Task InitialiseViewModels(bool initialiseEntries)
{
await DatabaseViewModel.Initialize();
await ProducersViewModel.Initialize();
await CharactersViewModel.Initialize();
if (initialiseEntries) InitialiseEntries(StaticHelpers.Logger.LogVerbose);
await UserGamesViewModel.Initialize();
InformationViewModel.Initialise(LocalDatabase, StaticMethods.Data);
}

private void InitialiseEntries(bool logVerbose)
{
StatusText = "Loading Translation Plugins...";
Expand Down
161 changes: 81 additions & 80 deletions Happy Reader/ViewModel/UserGamesViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,94 +14,95 @@

namespace Happy_Reader.ViewModel
{
public class UserGamesViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public class UserGamesViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;

public readonly MainWindowViewModel MainViewModel;
public readonly MainWindowViewModel MainViewModel;

public ObservableCollection<UserGameTile> UserGameItems { get; } = new();
public ComboBoxItem[] UserGameGroupings { get; } = StaticMethods.GetEnumValues(typeof(UserGameGrouping));
public UserGameGrouping GroupBy
{
get => StaticMethods.Settings.GuiSettings.UserGameGrouping;
set => StaticMethods.Settings.GuiSettings.UserGameGrouping = value;
}
public ObservableCollection<UserGameTile> UserGameItems { get; } = new();
public ComboBoxItem[] UserGameGroupings { get; } = StaticMethods.GetEnumValues(typeof(UserGameGrouping));
public UserGameGrouping GroupBy
{
get => StaticMethods.Settings.GuiSettings.UserGameGrouping;
set => StaticMethods.Settings.GuiSettings.UserGameGrouping = value;
}

public UserGamesViewModel(MainWindowViewModel mainViewModel)
{
MainViewModel = mainViewModel;
}
public bool ShowNotFound { get; set; }

public async Task Initialize()
{
MainViewModel.StatusText = "Loading User Games...";
await Task.Yield();
await LoadUserGames(false);
public UserGamesViewModel(MainWindowViewModel mainViewModel)
{
MainViewModel = mainViewModel;
}

}
public async Task Initialize()
{
MainViewModel.StatusText = "Loading User Games...";
await Task.Yield();
await LoadUserGames();

public async Task LoadUserGames(bool showFileNotFound)
{
UserGameItems.Clear();
IEnumerable<UserGame> orderedGames = null;
await Task.Run(() =>
{
foreach (var game in StaticMethods.Data.UserGames)
{
if (game.VNID != null)
{
game.VN = LocalDatabase.VisualNovels[game.VNID.Value];
//if game has vn and vn is not already marked as owned, this prevents overwriting CurrentlyOwned with PastOwned,
//if multiple user games have the same VN but the later one has been deleted.
if (game.VN != null && game.VN.IsOwned != OwnedStatus.CurrentlyOwned) game.VN.IsOwned = game.FileExists ? OwnedStatus.CurrentlyOwned : OwnedStatus.PastOwned;
}
}
orderedGames = StaticMethods.Data.UserGames.OrderBy(x => x.VNID ?? 0).ToList();
if (!showFileNotFound) orderedGames = orderedGames.Where(og => og.FileExists);
foreach (var entry in StaticMethods.Data.Entries)
{
entry.InitGameId();
}
});
foreach (var game in orderedGames) { UserGameItems.Add(new UserGameTile(game)); }
OnPropertyChanged(nameof(UserGameItems));
}
}

public UserGameTile AddGameFile(string file)
{
var userGame = StaticMethods.Data.UserGames.FirstOrDefault(x => x.FilePath == file);
if (userGame != null)
{
MainViewModel.StatusText = $"This file has already been added. ({userGame.DisplayName})";
return null;
}
var vn = StaticMethods.ResolveVNForFile(file);
userGame = new UserGame(file, vn) { Id = StaticMethods.Data.UserGames.HighestKey + 1 };
userGame.SaveIconImage();
StaticMethods.Data.UserGames.Add(userGame, true);
var entryGame = new EntryGame((int)userGame.Id, true, false);
if (!EntriesTabViewModel.EntryGames.Contains(entryGame)) EntriesTabViewModel.EntryGames.Add(entryGame);
MainViewModel.StatusText = vn == null ? "File was added without VN." : $"File was added as {userGame.DisplayName}.";
return new UserGameTile(userGame);
}
private async Task LoadUserGames()
{
UserGameItems.Clear();
IEnumerable<UserGame> orderedGames = null;
await Task.Run(() =>
{
foreach (var game in StaticMethods.Data.UserGames)
{
if (game.VNID != null)
{
game.VN = LocalDatabase.VisualNovels[game.VNID.Value];
//if game has vn and vn is not already marked as owned, this prevents overwriting CurrentlyOwned with PastOwned,
//if multiple user games have the same VN but the later one has been deleted.
if (game.VN != null && game.VN.IsOwned != OwnedStatus.CurrentlyOwned) game.VN.IsOwned = game.FileExists ? OwnedStatus.CurrentlyOwned : OwnedStatus.PastOwned;
}
}
orderedGames = StaticMethods.Data.UserGames.OrderBy(x => x.VNID ?? 0).ToList();
foreach (var entry in StaticMethods.Data.Entries)
{
entry.InitGameId();
}
});
foreach (var game in orderedGames) { UserGameItems.Add(new UserGameTile(game)); }
OnPropertyChanged(nameof(UserGameItems));
}

public void RemoveUserGame(UserGameTile item)
{
UserGameItems.Remove(item);
StaticMethods.Data.UserGames.Remove(item.UserGame, true);
var entryGame = new EntryGame((int)item.UserGame.Id, true, false);
if (EntriesTabViewModel.EntryGames.Contains(entryGame)) EntriesTabViewModel.EntryGames.Remove(entryGame);
MainViewModel.OnPropertyChanged(nameof(MainViewModel.TestViewModel));
}
public UserGameTile AddGameFile(string file)
{
var userGame = StaticMethods.Data.UserGames.FirstOrDefault(x => x.FilePath == file);
if (userGame != null)
{
MainViewModel.StatusText = $"This file has already been added. ({userGame.DisplayName})";
return null;
}
var vn = StaticMethods.ResolveVNForFile(file);
userGame = new UserGame(file, vn) { Id = StaticMethods.Data.UserGames.HighestKey + 1 };
userGame.SaveIconImage();
StaticMethods.Data.UserGames.Add(userGame, true);
var entryGame = new EntryGame((int)userGame.Id, true, false);
if (!EntriesTabViewModel.EntryGames.Contains(entryGame)) EntriesTabViewModel.EntryGames.Add(entryGame);
MainViewModel.StatusText = vn == null ? "File was added without VN." : $"File was added as {userGame.DisplayName}.";
return new UserGameTile(userGame);
}

public void RemoveUserGame(UserGame item)
{
var tile = UserGameItems.First(x => x.UserGame == item);
RemoveUserGame(tile);
}
public void RemoveUserGame(UserGameTile item)
{
UserGameItems.Remove(item);
StaticMethods.Data.UserGames.Remove(item.UserGame, true);
var entryGame = new EntryGame((int)item.UserGame.Id, true, false);
if (EntriesTabViewModel.EntryGames.Contains(entryGame)) EntriesTabViewModel.EntryGames.Remove(entryGame);
MainViewModel.OnPropertyChanged(nameof(MainViewModel.TestViewModel));
}

[NotifyPropertyChangedInvocator]
private void OnPropertyChanged([CallerMemberName] string propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void RemoveUserGame(UserGame item)
{
var tile = UserGameItems.First(x => x.UserGame == item);
RemoveUserGame(tile);
}

[NotifyPropertyChangedInvocator]
private void OnPropertyChanged([CallerMemberName] string propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Loading

0 comments on commit f6c42eb

Please sign in to comment.