diff --git a/Totoro.Core.Tests/Totoro.Core.Tests.csproj b/Totoro.Core.Tests/Totoro.Core.Tests.csproj index da3bd297..460b298d 100644 --- a/Totoro.Core.Tests/Totoro.Core.Tests.csproj +++ b/Totoro.Core.Tests/Totoro.Core.Tests.csproj @@ -10,16 +10,16 @@ - + - + - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/Totoro.Core/Contracts/IConnectivityService.cs b/Totoro.Core/Contracts/IConnectivityService.cs new file mode 100644 index 00000000..217255bb --- /dev/null +++ b/Totoro.Core/Contracts/IConnectivityService.cs @@ -0,0 +1,8 @@ +namespace Totoro.Core.Contracts; + +public interface IConnectivityService +{ + bool IsConnected { get; } + IObservable ConnectionLost { get; } + IObservable Connected { get; } +} diff --git a/Totoro.Core/Contracts/ISettings.cs b/Totoro.Core/Contracts/ISettings.cs index 5206cd3a..7da96fb7 100644 --- a/Totoro.Core/Contracts/ISettings.cs +++ b/Totoro.Core/Contracts/ISettings.cs @@ -1,5 +1,5 @@ -using System.ComponentModel; -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; +using System.ComponentModel; using Totoro.Core.ViewModels; namespace Totoro.Core.Contracts; @@ -36,6 +36,14 @@ public interface ISettings : INotifyPropertyChanged bool MediaDetectionEnabled { get; set; } bool OnlyDetectMediaInLibraryFolders { get; set; } ObservableCollection LibraryFolders { get; set; } + StartupOptions StartupOptions { get; set; } +} + +public class StartupOptions : ReactiveObject +{ + [Reactive] public bool MinimizeToTrayOnClose { get; set; } = false; + [Reactive] public bool StartMinimizedToTray { get; set; } = false; + [Reactive] public bool RunOnStartup { get; set; } = false; } public class DefaultUrls : ReactiveObject diff --git a/Totoro.Core/Services/AnimeServiceContext.cs b/Totoro.Core/Services/AnimeServiceContext.cs index 8d938976..c22c0b43 100644 --- a/Totoro.Core/Services/AnimeServiceContext.cs +++ b/Totoro.Core/Services/AnimeServiceContext.cs @@ -3,12 +3,15 @@ public class AnimeServiceContext : IAnimeServiceContext { private readonly ISettings _settings; + private readonly IConnectivityService _connectivityService; private readonly Dictionary _services; public AnimeServiceContext(ISettings settings, - IEnumerable services) + IEnumerable services, + IConnectivityService connectivityService) { _settings = settings; + _connectivityService = connectivityService; _services = services.Any() ? services.ToDictionary(x => x.Type, x => x) : new(); @@ -18,21 +21,41 @@ public AnimeServiceContext(ISettings settings, public IObservable> GetAiringAnime() { + if (!_connectivityService.IsConnected) + { + return Observable.Return(Enumerable.Empty()); + } + return _services[_settings.DefaultListService].GetAiringAnime(); } public IObservable> GetAnime(string name) { + if(!_connectivityService.IsConnected) + { + return Observable.Return(Enumerable.Empty()); + } + return _services[_settings.DefaultListService].GetAnime(name); } public IObservable GetInformation(long id) { + if (!_connectivityService.IsConnected) + { + return Observable.Return(new AnimeModel()); + } + return _services[_settings.DefaultListService].GetInformation(id); } public IObservable> GetSeasonalAnime() { + if (!_connectivityService.IsConnected) + { + return Observable.Return(Enumerable.Empty()); + } + return _services[_settings.DefaultListService].GetSeasonalAnime(); } } diff --git a/Totoro.Core/Services/Initalizer.cs b/Totoro.Core/Services/Initalizer.cs index 316670c4..48cec7f5 100644 --- a/Totoro.Core/Services/Initalizer.cs +++ b/Totoro.Core/Services/Initalizer.cs @@ -9,6 +9,7 @@ public class Initalizer : IInitializer private readonly IKnownFolders _knownFolders; private readonly ILocalSettingsService _localSettingsService; private readonly IRssDownloader _rssDownloader; + private readonly IConnectivityService _connectivityService; private readonly PluginOptionsStorage _pluginOptionsStorage; private readonly IUpdateService _updateService; private readonly IResumePlaybackService _playbackStateStorage; @@ -22,12 +23,14 @@ public Initalizer(IPluginManager pluginManager, ITorrentEngine torrentEngine, ILocalSettingsService localSettingsService, IRssDownloader rssDownloader, + IConnectivityService connectivityService, PluginOptionsStorage pluginOptionsStorage) { _pluginManager = pluginManager; _knownFolders = knownFolders; _localSettingsService = localSettingsService; _rssDownloader = rssDownloader; + _connectivityService = connectivityService; _pluginOptionsStorage = pluginOptionsStorage; _updateService = updateService; _playbackStateStorage = playbackStateStorage; @@ -39,8 +42,11 @@ public async Task Initialize() #if RELEASE await _pluginManager.Initialize(_knownFolders.Plugins); #endif - await _torrentEngine.TryRestoreState(); - await _rssDownloader.Initialize(); + if(_connectivityService.IsConnected) + { + await _torrentEngine.TryRestoreState(); + await _rssDownloader.Initialize(); + } _pluginOptionsStorage.Initialize(); RemoveObsoleteSettings(); } diff --git a/Totoro.Core/Services/TrackingServiceContext.cs b/Totoro.Core/Services/TrackingServiceContext.cs index 149ac3c5..724567a6 100644 --- a/Totoro.Core/Services/TrackingServiceContext.cs +++ b/Totoro.Core/Services/TrackingServiceContext.cs @@ -6,13 +6,15 @@ public class TrackingServiceContext : ITrackingServiceContext { private readonly Dictionary _trackers; private readonly ISettings _settings; + private readonly IConnectivityService _connectivityService; private readonly Subject _authenticatedSubject = new(); public TrackingServiceContext(ISettings settings, - IEnumerable trackers) + IEnumerable trackers, + IConnectivityService connectivityService) { _settings = settings; - + _connectivityService = connectivityService; _trackers = trackers.Any() ? trackers.ToDictionary(x => x.Type, x => x) : new(); @@ -23,11 +25,21 @@ public TrackingServiceContext(ISettings settings, public IObservable Authenticated => _authenticatedSubject; public IObservable> GetAnime() { + if (!_connectivityService.IsConnected) + { + return Observable.Return(Enumerable.Empty()); + } + return _trackers[_settings.DefaultListService].GetAnime(); } public IObservable> GetCurrentlyAiringTrackedAnime() { + if (!_connectivityService.IsConnected) + { + return Observable.Return(Enumerable.Empty()); + } + return _trackers[_settings.DefaultListService].GetCurrentlyAiringTrackedAnime(); } @@ -39,6 +51,11 @@ public void SetAccessToken(string token, ListServiceType type) public IObservable Update(long id, Tracking tracking) { + if (!_connectivityService.IsConnected) + { + return Observable.Return(tracking); + } + return _trackers[_settings.DefaultListService].Update(id, tracking); } } diff --git a/Totoro.Core/Services/WindowsUpdateService.cs b/Totoro.Core/Services/WindowsUpdateService.cs index 423b7664..e75a8798 100644 --- a/Totoro.Core/Services/WindowsUpdateService.cs +++ b/Totoro.Core/Services/WindowsUpdateService.cs @@ -19,14 +19,15 @@ public class WindowsUpdateService : ReactiveObject, IUpdateService, IEnableLogge public WindowsUpdateService(HttpClient httpClient, ISettings settings, - IKnownFolders knownFolders) + IKnownFolders knownFolders, + IConnectivityService connectivityService) { _httpClient = httpClient; _knownFolders = knownFolders; _onUpdate = Observable .Timer(TimeSpan.Zero, TimeSpan.FromHours(1)) - .Where(_ => settings.AutoUpdate) + .Where(_ => settings.AutoUpdate && connectivityService.IsConnected) .ObserveOn(RxApp.TaskpoolScheduler) .SelectMany(_ => TryGetStreamAsync()) .Where(x => !string.IsNullOrEmpty(x)) diff --git a/Totoro.Core/Settings.cs b/Totoro.Core/Settings.cs index 4c81d5a9..12cccac2 100644 --- a/Totoro.Core/Settings.cs +++ b/Totoro.Core/Settings.cs @@ -38,6 +38,7 @@ public static class Settings public static Key MediaDetectionEnabled { get; } = new("MediaDetectionEnabled", false); public static Key OnlyDetectMediaInLibraryFolders { get; } = new("OnlyDetectMediaInLibraryFolders", false); public static Key> LibraryFolders { get; } = new("LibraryFolders", new ObservableCollection()); + public static Key StartupOptions { get; } = new("StartupOptions", () => new StartupOptions()); public static IEnumerable GetObsoleteKeys() { diff --git a/Totoro.Core/Totoro.Core.csproj b/Totoro.Core/Totoro.Core.csproj index 4553d19e..d532bb38 100644 --- a/Totoro.Core/Totoro.Core.csproj +++ b/Totoro.Core/Totoro.Core.csproj @@ -26,24 +26,24 @@ - + - + - + - + - + - + diff --git a/Totoro.Core/ViewModels/DiscoverViewModel.cs b/Totoro.Core/ViewModels/DiscoverViewModel.cs index 4741e325..2846f216 100644 --- a/Totoro.Core/ViewModels/DiscoverViewModel.cs +++ b/Totoro.Core/ViewModels/DiscoverViewModel.cs @@ -7,6 +7,7 @@ namespace Totoro.Core.ViewModels; public class DiscoverViewModel : NavigatableViewModel { private readonly INavigationService _navigationService; + private readonly IConnectivityService _connectivityService; private readonly SourceCache _episodesCache = new(x => x.Url); private readonly SourceCache _animeSearchResultCache = new(x => x.Url); private readonly ReadOnlyObservableCollection _episodes; @@ -15,11 +16,12 @@ public class DiscoverViewModel : NavigatableViewModel public DiscoverViewModel(IPluginFactory providerFacotory, ISettings settings, - INavigationService navigationService) + INavigationService navigationService, + IConnectivityService connectivityService) { _provider = providerFacotory.CreatePlugin(settings.DefaultProviderType); _navigationService = navigationService; - + _connectivityService = connectivityService; _episodesCache .Connect() .RefCount() @@ -69,6 +71,12 @@ public DiscoverViewModel(IPluginFactory providerFacotory, .Throttle(TimeSpan.FromMilliseconds(200)) .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(_ => _animeSearchResultCache.Clear()); + + connectivityService + .Connected + .Where(_ => !_episodesCache.Items.Any()) + .SelectMany(_ => LoadPage(1)) + .Subscribe(); } [Reactive] public int SelectedIndex { get; set; } @@ -92,6 +100,11 @@ public DiscoverViewModel(IPluginFactory providerFacotory, public override Task OnNavigatedTo(IReadOnlyDictionary parameters) { + if(!_connectivityService.IsConnected) + { + return Task.CompletedTask; + } + LoadPage(1).Subscribe(_ => { }, RxApp.DefaultExceptionHandler.OnError); return Task.CompletedTask; diff --git a/Totoro.Core/ViewModels/SettingsModel.cs b/Totoro.Core/ViewModels/SettingsModel.cs index c7f5231a..4507db30 100644 --- a/Totoro.Core/ViewModels/SettingsModel.cs +++ b/Totoro.Core/ViewModels/SettingsModel.cs @@ -47,6 +47,7 @@ public SettingsModel(ILocalSettingsService localSettingsService, MediaDetectionEnabled = localSettingsService.ReadSetting(Settings.MediaDetectionEnabled); OnlyDetectMediaInLibraryFolders = localSettingsService.ReadSetting(Settings.OnlyDetectMediaInLibraryFolders); LibraryFolders = localSettingsService.ReadSetting(Settings.LibraryFolders); + StartupOptions = localSettingsService.ReadSetting(Settings.StartupOptions); if (UseDiscordRichPresense && !_dRpc.IsInitialized) { @@ -83,6 +84,7 @@ public void ObserveChanges() .Subscribe(_ => _localSettingsService.SaveSetting(Settings.LibraryFolders, LibraryFolders)); ObserveObject(TorrentSearchOptions, Settings.TorrentSearchOptions); + ObserveObject(StartupOptions, Settings.StartupOptions); } private void ObserveObject(T target, Key key) @@ -132,6 +134,7 @@ private void ObserveObject(T target, Key key) [Reactive] public bool MediaDetectionEnabled { get; set; } [Reactive] public bool OnlyDetectMediaInLibraryFolders { get; set; } public ObservableCollection LibraryFolders { get; set; } + [Reactive] public StartupOptions StartupOptions { get; set; } } diff --git a/Totoro.Core/ViewModels/UserListViewModel.cs b/Totoro.Core/ViewModels/UserListViewModel.cs index 33249dab..7960adaa 100644 --- a/Totoro.Core/ViewModels/UserListViewModel.cs +++ b/Totoro.Core/ViewModels/UserListViewModel.cs @@ -40,7 +40,8 @@ public class UserListViewModel : NavigatableViewModel, IHaveState public UserListViewModel(ITrackingServiceContext trackingService, IAnimeServiceContext animeService, - IViewService viewService) + IViewService viewService, + IConnectivityService connectivityService) { _trackingService = trackingService; _viewService = viewService; @@ -75,6 +76,11 @@ public UserListViewModel(ITrackingServiceContext trackingService, .SelectMany(animeService.GetAnime) .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(list => _searchCache.EditDiff(list, (first, second) => first.Id == second.Id)); + + connectivityService + .Connected + .Subscribe(_ => FetchAnime()) + .DisposeWith(Garbage); } [Reactive] public bool IsLoading { get; set; } @@ -98,9 +104,15 @@ public Task SetInitialState() return Task.CompletedTask; } + FetchAnime(); + + return Task.CompletedTask; + } + + private void FetchAnime() + { IsLoading = true; _genres.Clear(); - _animeCache.Clear(); _trackingService.GetAnime() .ObserveOn(RxApp.MainThreadScheduler) @@ -125,8 +137,6 @@ public Task SetInitialState() IsLoading = false; }, RxApp.DefaultExceptionHandler.OnError) .DisposeWith(Garbage); - - return Task.CompletedTask; } public void StoreState(IState state) diff --git a/Totoro.Plugins.Anime.AllAnime.Tests/Totoro.Plugins.Anime.AllAnime.Tests.csproj b/Totoro.Plugins.Anime.AllAnime.Tests/Totoro.Plugins.Anime.AllAnime.Tests.csproj index 3aec2878..70b3d543 100644 --- a/Totoro.Plugins.Anime.AllAnime.Tests/Totoro.Plugins.Anime.AllAnime.Tests.csproj +++ b/Totoro.Plugins.Anime.AllAnime.Tests/Totoro.Plugins.Anime.AllAnime.Tests.csproj @@ -11,13 +11,13 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/Totoro.Plugins.Anime.AnimePahe.Tests/Totoro.Plugins.Anime.AnimePahe.Tests.csproj b/Totoro.Plugins.Anime.AnimePahe.Tests/Totoro.Plugins.Anime.AnimePahe.Tests.csproj index 8fc562a9..c2b6b483 100644 --- a/Totoro.Plugins.Anime.AnimePahe.Tests/Totoro.Plugins.Anime.AnimePahe.Tests.csproj +++ b/Totoro.Plugins.Anime.AnimePahe.Tests/Totoro.Plugins.Anime.AnimePahe.Tests.csproj @@ -11,13 +11,13 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/Totoro.Plugins.Anime.GogoAnime.Tests/Totoro.Plugins.Anime.GogoAnime.Tests.csproj b/Totoro.Plugins.Anime.GogoAnime.Tests/Totoro.Plugins.Anime.GogoAnime.Tests.csproj index 3ecae019..3c42574f 100644 --- a/Totoro.Plugins.Anime.GogoAnime.Tests/Totoro.Plugins.Anime.GogoAnime.Tests.csproj +++ b/Totoro.Plugins.Anime.GogoAnime.Tests/Totoro.Plugins.Anime.GogoAnime.Tests.csproj @@ -11,13 +11,13 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/Totoro.Plugins.Anime.Marin.Tests/Totoro.Plugins.Anime.Marin.Tests.csproj b/Totoro.Plugins.Anime.Marin.Tests/Totoro.Plugins.Anime.Marin.Tests.csproj index 4a3c8485..8185a232 100644 --- a/Totoro.Plugins.Anime.Marin.Tests/Totoro.Plugins.Anime.Marin.Tests.csproj +++ b/Totoro.Plugins.Anime.Marin.Tests/Totoro.Plugins.Anime.Marin.Tests.csproj @@ -11,13 +11,13 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/Totoro.Plugins.Anime.YugenAnime.Tests/Totoro.Plugins.Anime.YugenAnime.Tests.csproj b/Totoro.Plugins.Anime.YugenAnime.Tests/Totoro.Plugins.Anime.YugenAnime.Tests.csproj index c88ddf88..5da1490c 100644 --- a/Totoro.Plugins.Anime.YugenAnime.Tests/Totoro.Plugins.Anime.YugenAnime.Tests.csproj +++ b/Totoro.Plugins.Anime.YugenAnime.Tests/Totoro.Plugins.Anime.YugenAnime.Tests.csproj @@ -11,13 +11,13 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/Totoro.Plugins.Anime.ZoroTests/Totoro.Plugins.Anime.Zoro.Tests.csproj b/Totoro.Plugins.Anime.ZoroTests/Totoro.Plugins.Anime.Zoro.Tests.csproj index 299f04c4..6d869519 100644 --- a/Totoro.Plugins.Anime.ZoroTests/Totoro.Plugins.Anime.Zoro.Tests.csproj +++ b/Totoro.Plugins.Anime.ZoroTests/Totoro.Plugins.Anime.Zoro.Tests.csproj @@ -11,13 +11,13 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/Totoro.Plugins.Torrents.AnimeTosho.Tests/Totoro.Plugins.Torrents.AnimeTosho.Tests.csproj b/Totoro.Plugins.Torrents.AnimeTosho.Tests/Totoro.Plugins.Torrents.AnimeTosho.Tests.csproj index 3331da93..43bcd5dc 100644 --- a/Totoro.Plugins.Torrents.AnimeTosho.Tests/Totoro.Plugins.Torrents.AnimeTosho.Tests.csproj +++ b/Totoro.Plugins.Torrents.AnimeTosho.Tests/Totoro.Plugins.Torrents.AnimeTosho.Tests.csproj @@ -11,13 +11,13 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/Totoro.Plugins.Torrents.Nya.Tests/Totoro.Plugins.Torrents.Nya.Tests.csproj b/Totoro.Plugins.Torrents.Nya.Tests/Totoro.Plugins.Torrents.Nya.Tests.csproj index 4df9237b..3ea474fc 100644 --- a/Totoro.Plugins.Torrents.Nya.Tests/Totoro.Plugins.Torrents.Nya.Tests.csproj +++ b/Totoro.Plugins.Torrents.Nya.Tests/Totoro.Plugins.Torrents.Nya.Tests.csproj @@ -11,13 +11,13 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/Totoro.WinUI/App.xaml.cs b/Totoro.WinUI/App.xaml.cs index 353ad13c..341baef0 100644 --- a/Totoro.WinUI/App.xaml.cs +++ b/Totoro.WinUI/App.xaml.cs @@ -37,8 +37,6 @@ public partial class App : Application, IEnableLogger }) .ConfigureServices((context, services) => { - MessageBus.Current.RegisterMessageSource(Observable.Timer(TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1)).Select(_ => new MinuteTick())); - services.AddPlatformServices() .AddTotoro() .AddPlugins() diff --git a/Totoro.WinUI/Helpers/ServiceCollectionExtensions.cs b/Totoro.WinUI/Helpers/ServiceCollectionExtensions.cs index 363b708f..1dde4fc4 100644 --- a/Totoro.WinUI/Helpers/ServiceCollectionExtensions.cs +++ b/Totoro.WinUI/Helpers/ServiceCollectionExtensions.cs @@ -83,6 +83,7 @@ public static IServiceCollection AddPlatformServices(this IServiceCollection ser services.AddSingleton(); services.AddSingleton(x => x.GetRequiredService()); services.AddSingleton(); + services.AddSingleton(); services.AddTransient(); services.AddTransient(); diff --git a/Totoro.WinUI/Services/ActivationService.cs b/Totoro.WinUI/Services/ActivationService.cs index b7aef305..dd37049b 100644 --- a/Totoro.WinUI/Services/ActivationService.cs +++ b/Totoro.WinUI/Services/ActivationService.cs @@ -17,6 +17,7 @@ public class ActivationService : IActivationService, IEnableLogger private readonly IThemeSelectorService _themeSelectorService; private readonly IInitializer _initializer; private readonly IPluginOptionsStorage _mediaPlayerPluginOptions; + private readonly ISettings _settings; private readonly string _prevWebviewFolder; private readonly string _tempPath = Path.GetTempPath(); @@ -26,13 +27,15 @@ public ActivationService(ActivationHandler defaultHand IEnumerable activationHandlers, IThemeSelectorService themeSelectorService, IInitializer initializer, - IPluginOptionsStorage mediaPlayerPluginOptions) + IPluginOptionsStorage mediaPlayerPluginOptions, + ISettings settings) { _defaultHandler = defaultHandler; _activationHandlers = activationHandlers; _themeSelectorService = themeSelectorService; _initializer = initializer; _mediaPlayerPluginOptions = mediaPlayerPluginOptions; + _settings = settings; _prevWebviewFolder = Environment.GetEnvironmentVariable("WEBVIEW2_USER_DATA_FOLDER"); Environment.SetEnvironmentVariable("WEBVIEW2_USER_DATA_FOLDER", _tempPath); @@ -47,8 +50,12 @@ public async Task ActivateAsync(object activationArgs) await HandleActivationAsync(activationArgs); // Activate the MainWindow. - App.MainWindow.Activate(); - App.MainWindow.Maximize(); + if(!_settings.StartupOptions.StartMinimizedToTray) + { + App.MainWindow.Activate(); + App.MainWindow.Maximize(); + } + App.MainWindow.AppWindow.Closing += AppWindow_Closing; // Execute tasks after activation. diff --git a/Totoro.WinUI/Services/ConnectivityService.cs b/Totoro.WinUI/Services/ConnectivityService.cs new file mode 100644 index 00000000..6e72bbe1 --- /dev/null +++ b/Totoro.WinUI/Services/ConnectivityService.cs @@ -0,0 +1,25 @@ +using CommunityToolkit.WinUI.Connectivity; + +namespace Totoro.WinUI.Services; + + +internal class ConnectivityService : ReactiveObject, IConnectivityService +{ + [Reactive] public bool IsConnected { get; set; } + + public IObservable ConnectionLost { get; } + public IObservable Connected { get; } + + public ConnectivityService() + { + IsConnected = NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable; + ConnectionLost = this.ObservableForProperty(x => x.IsConnected, x => x).Where(x => !x).Select(_ => Unit.Default); + Connected = this.ObservableForProperty(x => x.IsConnected, x => x).Where(x => x).Select(_ => Unit.Default); + NetworkHelper.Instance.NetworkChanged += ConnectivityChanged; + } + + private void ConnectivityChanged(object sender, EventArgs e) + { + IsConnected = NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable; + } +} diff --git a/Totoro.WinUI/Totoro.WinUI.csproj b/Totoro.WinUI/Totoro.WinUI.csproj index a9501545..44b26996 100644 --- a/Totoro.WinUI/Totoro.WinUI.csproj +++ b/Totoro.WinUI/Totoro.WinUI.csproj @@ -27,6 +27,7 @@ + @@ -34,15 +35,15 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + diff --git a/Totoro.WinUI/Views/SettingsSections/PreferencesSection.xaml b/Totoro.WinUI/Views/SettingsSections/PreferencesSection.xaml index 1a983c3a..ab847832 100644 --- a/Totoro.WinUI/Views/SettingsSections/PreferencesSection.xaml +++ b/Totoro.WinUI/Views/SettingsSections/PreferencesSection.xaml @@ -37,6 +37,42 @@ ItemsSource="{x:Bind ViewModel.AnimeActions}" SelectedItem="{x:Bind ViewModel.Settings.AnimeCardClickAction, Mode=TwoWay}" /> + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Totoro.WinUI/Views/SettingsSections/PreferencesSection.xaml.cs b/Totoro.WinUI/Views/SettingsSections/PreferencesSection.xaml.cs index 01132268..dcf4328d 100644 --- a/Totoro.WinUI/Views/SettingsSections/PreferencesSection.xaml.cs +++ b/Totoro.WinUI/Views/SettingsSections/PreferencesSection.xaml.cs @@ -1,6 +1,11 @@ // Copyright (c) Microsoft Corporation and Contributors. // Licensed under the MIT License. +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using System.Text; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Navigation; @@ -10,6 +15,8 @@ namespace Totoro.WinUI.Views.SettingsSections; public sealed partial class PreferencesSection : Page { + private CompositeDisposable _disposables = new(); + public SettingsViewModel ViewModel { get { return (SettingsViewModel)GetValue(ViewModelProperty); } @@ -28,5 +35,80 @@ public PreferencesSection() protected override void OnNavigatedTo(NavigationEventArgs e) { ViewModel = e.Parameter as SettingsViewModel; + var settings = App.GetService().StartupOptions; + + settings + .ObservableForProperty(x => x.MinimizeToTrayOnClose, x => x) + .Subscribe(value => App.HandleClosedEvents = value); + + settings + .ObservableForProperty(x => x.RunOnStartup, x => x) + .Subscribe(value => + { + if(value) + { + Task.Run(AddToStartup); + } + else + { + Task.Run(RemoveFromStartup); + } + }); + + } + + private static void AddToStartup() + { + IShellLink link = (IShellLink)new ShellLink(); + var location = Path.ChangeExtension(Assembly.GetExecutingAssembly().Location, "exe"); + link.SetPath(location); + link.SetWorkingDirectory(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)); + + // save it + IPersistFile file = (IPersistFile)link; + var startupApps = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"Microsoft\Windows\Start Menu\Programs\Startup"); + file.Save(Path.Combine(startupApps, "Totoro.lnk"), false); + } + + private static void RemoveFromStartup() + { + var startupApps = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"Microsoft\Windows\Start Menu\Programs\Startup"); + var shortcut = Path.Combine(startupApps, "Totoro.lnk"); + + if(File.Exists(shortcut)) + { + File.Delete(shortcut); + } } } + +[ComImport] +[Guid("00021401-0000-0000-C000-000000000046")] +internal class ShellLink +{ +} + +[ComImport] +[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +[Guid("000214F9-0000-0000-C000-000000000046")] +internal interface IShellLink +{ + void GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, out IntPtr pfd, int fFlags); + void GetIDList(out IntPtr ppidl); + void SetIDList(IntPtr pidl); + void GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName); + void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); + void GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath); + void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); + void GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath); + void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); + void GetHotkey(out short pwHotkey); + void SetHotkey(short wHotkey); + void GetShowCmd(out int piShowCmd); + void SetShowCmd(int iShowCmd); + void GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, int cchIconPath, out int piIcon); + void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); + void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved); + void Resolve(IntPtr hwnd, int fFlags); + void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); +} diff --git a/Totoro.WinUI/Views/ShellPage.xaml.cs b/Totoro.WinUI/Views/ShellPage.xaml.cs index 8304e2ae..f0b1b00a 100644 --- a/Totoro.WinUI/Views/ShellPage.xaml.cs +++ b/Totoro.WinUI/Views/ShellPage.xaml.cs @@ -38,6 +38,7 @@ public ShellPage() private void OnLoaded(object sender, RoutedEventArgs e) { + App.HandleClosedEvents = App.GetService().StartupOptions.MinimizeToTrayOnClose; App.MainWindow.Closed += (sender, args) => { if (App.HandleClosedEvents) @@ -53,7 +54,6 @@ private void OnLoaded(object sender, RoutedEventArgs e) App.MainWindow.ExtendsContentIntoTitleBar = true; App.MainWindow.SetTitleBar(AppTitleBar); - App.MainWindow.Activated += MainWindow_Activated; AppTitleBarText.Text = "AppDisplayName".GetLocalized(); TitleBarHelper.UpdateTitleBar(RequestedTheme); @@ -62,11 +62,6 @@ private void OnLoaded(object sender, RoutedEventArgs e) KeyboardAccelerators.Add(BuildKeyboardAccelerator(VirtualKey.GoBack)); } - private void MainWindow_Activated(object sender, WindowActivatedEventArgs args) - { - - } - private void NavigationViewControl_DisplayModeChanged(NavigationView sender, NavigationViewDisplayModeChangedEventArgs args) { AppTitleBar.Margin = new Thickness() @@ -120,11 +115,6 @@ public static void ShowHideWindow() return; } - if(!App.HandleClosedEvents) - { - return; - } - if (window.Visible) { window.Hide();