Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Screenbox.Core/Enums/SettingsOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,15 @@ public enum ThemeOption
Light,
Dark
}

public enum LaunchPageOption
{
Home,
Songs,
Albums,
Artists,
VideoFolders,
AllVideos,
Network,
PlayQueue
}
Comment on lines +24 to +34
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having the launch page name as an enum is also problematic in the future. If we add new page types to the app, we have to add the page at the bottom of the enum. The order of this enum is fixed once it is released. We cannot change this order (only append to it) because doing so will break the user's settings.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is true, but we could also hardcode the enum values, then we could insert new ones in the middle. Though that would not be all that elegant either.

1 change: 1 addition & 0 deletions Screenbox.Core/Services/ISettingsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public interface ISettingsService
ThemeOption Theme { get; set; }
bool EnqueueAllFilesInFolder { get; set; }
bool RestorePlaybackPosition { get; set; }
LaunchPageOption LaunchPage { get; set; }
bool SearchRemovableStorage { get; set; }
int MaxVolume { get; set; }
string GlobalArguments { get; set; }
Expand Down
8 changes: 8 additions & 0 deletions Screenbox.Core/Services/SettingsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public sealed class SettingsService : ISettingsService
private const string GeneralShowRecent = "General/ShowRecent";
private const string GeneralEnqueueAllInFolder = "General/EnqueueAllInFolder";
private const string GeneralRestorePlaybackPosition = "General/RestorePlaybackPosition";
private const string GeneralLaunchPage = "General/LaunchPage";
private const string AdvancedModeKey = "Advanced/IsEnabled";
private const string AdvancedVideoUpscaleKey = "Advanced/VideoUpscale";
private const string AdvancedMultipleInstancesKey = "Advanced/MultipleInstances";
Expand Down Expand Up @@ -108,6 +109,12 @@ public bool RestorePlaybackPosition
set => SetValue(GeneralRestorePlaybackPosition, value);
}

public LaunchPageOption LaunchPage
{
get => (LaunchPageOption)GetValue<int>(GeneralLaunchPage);
set => SetValue(GeneralLaunchPage, (int)value);
}

public bool PlayerShowControls
{
get => GetValue<bool>(PlayerShowControlsKey);
Expand Down Expand Up @@ -174,6 +181,7 @@ public SettingsService()
SetDefault(LibrariesUseIndexerKey, true);
SetDefault(LibrariesSearchRemovableStorageKey, true);
SetDefault(GeneralShowRecent, true);
SetDefault(GeneralLaunchPage, (int)LaunchPageOption.Home);
SetDefault(PersistentRepeatModeKey, (int)MediaPlaybackAutoRepeatMode.None);
SetDefault(AdvancedModeKey, false);
SetDefault(AdvancedVideoUpscaleKey, (int)VideoUpscaleOption.Linear);
Expand Down
8 changes: 8 additions & 0 deletions Screenbox.Core/ViewModels/SettingsPageViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public sealed partial class SettingsPageViewModel : ObservableRecipient
[ObservableProperty] private int _theme;
[ObservableProperty] private bool _enqueueAllFilesInFolder;
[ObservableProperty] private bool _restorePlaybackPosition;
[ObservableProperty] private int _launchPage;
[ObservableProperty] private bool _searchRemovableStorage;
[ObservableProperty] private bool _advancedMode;
[ObservableProperty] private int _videoUpscaling;
Expand Down Expand Up @@ -104,6 +105,7 @@ public SettingsPageViewModel(ISettingsService settingsService, ILibraryService l
_theme = ((int)_settingsService.Theme + 2) % 3;
_enqueueAllFilesInFolder = _settingsService.EnqueueAllFilesInFolder;
_restorePlaybackPosition = _settingsService.RestorePlaybackPosition;
_launchPage = (int)_settingsService.LaunchPage;
_searchRemovableStorage = _settingsService.SearchRemovableStorage;
_advancedMode = _settingsService.AdvancedMode;
_useMultipleInstances = _settingsService.UseMultipleInstances;
Expand Down Expand Up @@ -211,6 +213,12 @@ partial void OnRestorePlaybackPositionChanged(bool value)
Messenger.Send(new SettingsChangedMessage(nameof(RestorePlaybackPosition), typeof(SettingsPageViewModel)));
}

partial void OnLaunchPageChanged(int value)
{
_settingsService.LaunchPage = (LaunchPageOption)value;
Messenger.Send(new SettingsChangedMessage(nameof(LaunchPage), typeof(SettingsPageViewModel)));
}

async partial void OnSearchRemovableStorageChanged(bool value)
{
_settingsService.SearchRemovableStorage = value;
Expand Down
36 changes: 36 additions & 0 deletions Screenbox/Helpers/EnumExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Screenbox.Core.Enums;
using Screenbox.Pages;
using System;

namespace Screenbox.Helpers;
public static class EnumExtensions
{
public static string GetNavPageFirstLevel(this LaunchPageOption launchPageOption)
{
return launchPageOption switch
{
LaunchPageOption.Home => "home",
LaunchPageOption.Songs => "music",
LaunchPageOption.Albums => "music",
LaunchPageOption.Artists => "music",
LaunchPageOption.VideoFolders => "videos",
LaunchPageOption.AllVideos => "videos",
LaunchPageOption.Network => "network",
LaunchPageOption.PlayQueue => "queue",
_ => throw new ArgumentOutOfRangeException(nameof(launchPageOption), launchPageOption, null),
Copy link
Preview

Copilot AI Aug 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ArgumentOutOfRangeException message parameter is null. Consider providing a descriptive error message to help with debugging.

Suggested change
_ => throw new ArgumentOutOfRangeException(nameof(launchPageOption), launchPageOption, null),
_ => throw new ArgumentOutOfRangeException(nameof(launchPageOption), launchPageOption, $"Invalid value for LaunchPageOption: {launchPageOption}"),

Copilot uses AI. Check for mistakes.

};
}

public static string GetNavPageSecondLevel(this LaunchPageOption launchPageOption)
Copy link
Preview

Copilot AI Aug 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The GetNavPageSecondLevel method doesn't handle all LaunchPageOption enum values. Home, Network, and PlayQueue options will throw ArgumentOutOfRangeException when called, but these methods are used in navigation logic where any enum value could be passed.

Copilot uses AI. Check for mistakes.

Copy link
Preview

Copilot AI Aug 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The GetNavPageSecondLevel method will throw ArgumentOutOfRangeException for Home, Network, and PlayQueue launch page options, but these are valid enum values that may be passed to this method. Consider handling these cases or documenting that this method should only be called for specific launch page types.

Copilot uses AI. Check for mistakes.

{
return launchPageOption switch
{
LaunchPageOption.Songs => "songs",
LaunchPageOption.Albums => "albums",
LaunchPageOption.Artists => "artists",
LaunchPageOption.VideoFolders => "folders",
LaunchPageOption.AllVideos => "all",
_ => throw new ArgumentOutOfRangeException(nameof(launchPageOption), launchPageOption, null),
Copy link
Preview

Copilot AI Aug 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ArgumentOutOfRangeException message parameter is null. Consider providing a descriptive error message to help with debugging.

Suggested change
_ => throw new ArgumentOutOfRangeException(nameof(launchPageOption), launchPageOption, null),
_ => throw new ArgumentOutOfRangeException(nameof(launchPageOption), launchPageOption, $"Unsupported LaunchPageOption value '{launchPageOption}' in GetNavPageSecondLevel."),

Copilot uses AI. Check for mistakes.

};
}
}
11 changes: 10 additions & 1 deletion Screenbox/Pages/MainPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
using System.Numerics;
using CommunityToolkit.Mvvm.DependencyInjection;
using Screenbox.Core;
using Screenbox.Core.Services;
using Screenbox.Core.ViewModels;
using Screenbox.Helpers;
using Sentry;
using Windows.ApplicationModel.Core;
using Windows.ApplicationModel.DataTransfer;
Expand Down Expand Up @@ -36,6 +38,8 @@ public sealed partial class MainPage : Page, IContentFrame

private readonly Dictionary<string, Type> _pages;

private ISettingsService Settings => Ioc.Default.GetRequiredService<ISettingsService>();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Getting the service directly from the control breaks the MVVM pattern. Ideally, we want only the ViewModels to have access to the services. Then the control will get the value from the ViewModel.


public MainPage()
{
InitializeComponent();
Expand Down Expand Up @@ -145,7 +149,12 @@ private void MainPage_Loaded(object sender, RoutedEventArgs e)
if (!ViewModel.PlayerVisible)
{
SetTitleBar();
NavView.SelectedItem = NavView.MenuItems[0];
var launchPage = Settings.LaunchPage;
var launchPageTag = launchPage.GetNavPageFirstLevel();
var navItem = NavView.MenuItems
.OfType<muxc.NavigationViewItem>()
.FirstOrDefault(item => item.Tag?.ToString() == launchPageTag);
NavView.SelectedItem = navItem ?? NavView.MenuItems[0];
_ = ViewModel.FetchLibraries();
}
}
Expand Down
26 changes: 22 additions & 4 deletions Screenbox/Pages/MusicPage.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
#nullable enable

using CommunityToolkit.Mvvm.DependencyInjection;
using Screenbox.Core;
using Screenbox.Core.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using CommunityToolkit.Mvvm.DependencyInjection;
using Screenbox.Core;
using Screenbox.Core.Services;
using Screenbox.Core.ViewModels;
using Screenbox.Helpers;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Animation;
using Windows.UI.Xaml.Navigation;
using NavigationView = Microsoft.UI.Xaml.Controls.NavigationView;
using NavigationViewSelectionChangedEventArgs = Microsoft.UI.Xaml.Controls.NavigationViewSelectionChangedEventArgs;
using muxc = Microsoft.UI.Xaml.Controls;

// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238

Expand All @@ -31,6 +34,8 @@ public sealed partial class MusicPage : Page, IContentFrame

private readonly Dictionary<string, Type> _pages;

private ISettingsService Settings => Ioc.Default.GetRequiredService<ISettingsService>();

public MusicPage()
{
this.InitializeComponent();
Expand All @@ -55,7 +60,20 @@ protected override void OnNavigatedTo(NavigationEventArgs e)
}
else
{
LibraryNavView.SelectedItem = LibraryNavView.MenuItems[0];
muxc.NavigationViewItem? libraryNavItem = null;
var launchPage = Settings.LaunchPage;
if (launchPage.GetNavPageFirstLevel() != "music")
{
libraryNavItem = (muxc.NavigationViewItem?)LibraryNavView.MenuItems[0];
}
else
{
var launchPageTag = launchPage.GetNavPageSecondLevel();
libraryNavItem = LibraryNavView.MenuItems
.OfType<muxc.NavigationViewItem>()
.FirstOrDefault(item => item.Tag?.ToString() == launchPageTag);
}
Copy link
Preview

Copilot AI Aug 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling GetNavPageSecondLevel() without checking if the launch page actually has a second level will cause an exception for Home, Network, and PlayQueue options.

Suggested change
}
// Only call GetNavPageSecondLevel if the first level is "music"
if (launchPage.GetNavPageFirstLevel() == "music")
{
var launchPageTag = launchPage.GetNavPageSecondLevel();
libraryNavItem = LibraryNavView.MenuItems
.OfType<muxc.NavigationViewItem>()
.FirstOrDefault(item => item.Tag?.ToString() == launchPageTag);
}
else
{
libraryNavItem = (muxc.NavigationViewItem?)LibraryNavView.MenuItems[0];
}

Copilot uses AI. Check for mistakes.

Copy link
Owner

@huynhsontung huynhsontung Sep 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling GetNavPageSecondLevel() without checking if the launch page actually has a second level will cause an exception for Home, Network, and PlayQueue options.

The Copilot comment is valid. The methods GetNavPageFirstLevel and GetNavPageSecondLevel have an implicit dependency, making the logic fragile. Despite the name, we want to avoid exceptions when calling methods out of order.

Comment on lines +70 to +75
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this flow, the second-level page is set each time the user navigates to the parent page. Is this expected? The settings text suggests that this behaviour only applies to app launches. However, in reality, it is set on every page navigation.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my testing, it actually only applies to app launches, not every page navigation.

LibraryNavView.SelectedItem = libraryNavItem ?? LibraryNavView.MenuItems[0];
}

ViewModel.UpdateSongs();
Expand Down
22 changes: 22 additions & 0 deletions Screenbox/Pages/SettingsPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,28 @@
<ToggleSwitch AutomationProperties.HelpText="{strings:Resources Key=SettingsRestorePlaybackPositionDescription}" IsOn="{x:Bind ViewModel.RestorePlaybackPosition, Mode=TwoWay}" />
</ctc:SettingsCard>

<ctc:SettingsCard
Margin="{StaticResource SettingsCardMargin}"
Description="{strings:Resources Key=SettingsLaunchPageDescription}"
Header="{strings:Resources Key=SettingsLaunchPageHeader}"
HeaderIcon="{ui:FontIcon Glyph=&#xE80F;}">
<ComboBox MinWidth="150" SelectedIndex="{x:Bind ViewModel.LaunchPage, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="x:String">
<TextBlock Text="{x:Bind Converter={StaticResource ResourceNameToResourceStringConverter}}" />
</DataTemplate>
</ComboBox.ItemTemplate>
<x:String>Home</x:String>
<x:String>Songs</x:String>
<x:String>Albums</x:String>
<x:String>Artists</x:String>
<x:String>VideoFolders</x:String>
<x:String>AllVideos</x:String>
<x:String>Network</x:String>
<x:String>PlayQueue</x:String>
</ComboBox>
</ctc:SettingsCard>

<!-- Player section -->
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="{strings:Resources Key=SettingsCategoryPlayer}" />

Expand Down
26 changes: 22 additions & 4 deletions Screenbox/Pages/VideosPage.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
#nullable enable

using CommunityToolkit.Mvvm.DependencyInjection;
using Screenbox.Core;
using Screenbox.Core.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using CommunityToolkit.Mvvm.DependencyInjection;
using Screenbox.Core;
using Screenbox.Core.Services;
using Screenbox.Core.ViewModels;
using Screenbox.Helpers;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Animation;
using Windows.UI.Xaml.Navigation;
using NavigationView = Microsoft.UI.Xaml.Controls.NavigationView;
using NavigationViewSelectionChangedEventArgs = Microsoft.UI.Xaml.Controls.NavigationViewSelectionChangedEventArgs;
using muxc = Microsoft.UI.Xaml.Controls;

// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238

Expand All @@ -33,6 +36,8 @@ public sealed partial class VideosPage : Page, IContentFrame

private readonly Dictionary<string, Type> _pages;

private ISettingsService Settings => Ioc.Default.GetRequiredService<ISettingsService>();

public VideosPage()
{
this.InitializeComponent();
Expand All @@ -56,7 +61,20 @@ protected override void OnNavigatedTo(NavigationEventArgs e)
}
else
{
LibraryNavView.SelectedItem = LibraryNavView.MenuItems[0];
muxc.NavigationViewItem? libraryNavItem = null;
var launchPage = Settings.LaunchPage;
if (launchPage.GetNavPageFirstLevel() != "videos")
{
libraryNavItem = (muxc.NavigationViewItem?)LibraryNavView.MenuItems[0];
}
else
{
var launchPageTag = launchPage.GetNavPageSecondLevel();
libraryNavItem = LibraryNavView.MenuItems
.OfType<muxc.NavigationViewItem>()
.FirstOrDefault(item => item.Tag?.ToString() == launchPageTag);
Copy link
Preview

Copilot AI Aug 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling GetNavPageSecondLevel() without checking if the launch page actually has a second level will cause an exception for Home, Network, and PlayQueue options.

Suggested change
.FirstOrDefault(item => item.Tag?.ToString() == launchPageTag);
// Only call GetNavPageSecondLevel if the launch page actually has a second level
string firstLevel = launchPage.GetNavPageFirstLevel();
if (firstLevel != "home" && firstLevel != "network" && firstLevel != "playqueue")
{
var launchPageTag = launchPage.GetNavPageSecondLevel();
libraryNavItem = LibraryNavView.MenuItems
.OfType<muxc.NavigationViewItem>()
.FirstOrDefault(item => item.Tag?.ToString() == launchPageTag);
}
else
{
libraryNavItem = (muxc.NavigationViewItem?)LibraryNavView.MenuItems[0];
}

Copilot uses AI. Check for mistakes.

}
LibraryNavView.SelectedItem = libraryNavItem ?? LibraryNavView.MenuItems[0];
}

ViewModel.UpdateVideos();
Expand Down
1 change: 1 addition & 0 deletions Screenbox/Screenbox.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@
<Compile Include="Converters\ThicknessFilterConverter.cs" />
<Compile Include="Converters\ToggleButtonCheckToRepeatModeConverter.cs" />
<Compile Include="Helpers\DeviceInfoHelper.cs" />
<Compile Include="Helpers\EnumExtensions.cs" />
<Compile Include="Helpers\GlobalizationHelper.cs" />
<Compile Include="Helpers\GlyphConvert.cs" />
<Compile Include="Helpers\TitleBarHelper.cs" />
Expand Down
6 changes: 6 additions & 0 deletions Screenbox/Strings/en-US/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -947,4 +947,10 @@
<data name="LanguageSystemDefault" xml:space="preserve">
<value>Use system language</value>
</data>
<data name="SettingsLaunchPageHeader" xml:space="preserve">
<value>Select what to show at launch</value>
</data>
<data name="SettingsLaunchPageDescription" xml:space="preserve">
<value>This page will be shown when the app is launched</value>
</data>
</root>
Loading