2024-05-04 15:35:32 +00:00
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
2024-08-16 12:21:23 +00:00
|
|
|
using System.IO;
|
|
|
|
using Avalonia;
|
2024-05-04 15:35:32 +00:00
|
|
|
using Avalonia.Controls;
|
2024-08-20 16:40:45 +00:00
|
|
|
using Avalonia.Interactivity;
|
2024-05-24 22:46:11 +00:00
|
|
|
using Avalonia.Markup.Xaml;
|
2024-08-20 16:40:45 +00:00
|
|
|
using Avalonia.Media;
|
2024-08-16 12:21:23 +00:00
|
|
|
using Avalonia.Platform;
|
2024-08-21 01:19:04 +00:00
|
|
|
using CRD.Downloader.Crunchyroll;
|
2024-08-16 12:21:23 +00:00
|
|
|
using CRD.Utils;
|
2024-08-20 16:40:45 +00:00
|
|
|
using CRD.Utils.Files;
|
2024-08-16 12:21:23 +00:00
|
|
|
using CRD.Utils.Structs;
|
2024-05-05 16:03:07 +00:00
|
|
|
using CRD.Utils.Updater;
|
2024-05-04 15:35:32 +00:00
|
|
|
using CRD.ViewModels;
|
2024-08-16 12:21:23 +00:00
|
|
|
using CRD.Views;
|
2024-05-04 15:35:32 +00:00
|
|
|
using CRD.Views.Utils;
|
|
|
|
using FluentAvalonia.Core;
|
|
|
|
using FluentAvalonia.UI.Controls;
|
|
|
|
using FluentAvalonia.UI.Windowing;
|
2024-08-16 12:21:23 +00:00
|
|
|
using Newtonsoft.Json;
|
2024-05-04 15:35:32 +00:00
|
|
|
using ReactiveUI;
|
2024-08-15 00:27:49 +00:00
|
|
|
using ContentDialogUpdateViewModel = CRD.ViewModels.Utils.ContentDialogUpdateViewModel;
|
2024-05-04 15:35:32 +00:00
|
|
|
|
|
|
|
namespace CRD.Views;
|
|
|
|
|
|
|
|
public partial class MainWindow : AppWindow{
|
|
|
|
private Stack<object> navigationStack = new Stack<object>();
|
|
|
|
|
2024-05-05 16:03:07 +00:00
|
|
|
|
2024-05-24 22:46:11 +00:00
|
|
|
#region Singelton
|
|
|
|
|
|
|
|
private static MainWindow? _instance;
|
|
|
|
private static readonly object Padlock = new();
|
|
|
|
|
|
|
|
public static MainWindow Instance{
|
|
|
|
get{
|
|
|
|
if (_instance == null){
|
|
|
|
lock (Padlock){
|
|
|
|
if (_instance == null){
|
|
|
|
_instance = new MainWindow();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return _instance;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
2024-05-05 16:03:07 +00:00
|
|
|
private object selectedNavVieItem;
|
2024-05-24 22:46:11 +00:00
|
|
|
|
2024-08-20 16:40:45 +00:00
|
|
|
private const int TitleBarHeightAdjustment = 31;
|
|
|
|
|
|
|
|
private PixelPoint _restorePosition;
|
|
|
|
private Size _restoreSize;
|
|
|
|
|
2024-05-04 15:35:32 +00:00
|
|
|
public MainWindow(){
|
2024-05-24 22:46:11 +00:00
|
|
|
AvaloniaXamlLoader.Load(this);
|
2024-05-04 15:35:32 +00:00
|
|
|
InitializeComponent();
|
2024-08-16 12:21:23 +00:00
|
|
|
|
2024-08-20 16:40:45 +00:00
|
|
|
ExtendClientAreaTitleBarHeightHint = TitleBarHeightAdjustment;
|
|
|
|
TitleBar.Height = TitleBarHeightAdjustment;
|
|
|
|
TitleBar.ExtendsContentIntoTitleBar = true;
|
|
|
|
TitleBar.TitleBarHitTestType = TitleBarHitTestType.Complex;
|
2024-08-21 01:19:04 +00:00
|
|
|
|
2024-08-16 12:21:23 +00:00
|
|
|
Opened += OnOpened;
|
|
|
|
Closing += OnClosing;
|
|
|
|
|
2024-08-20 16:40:45 +00:00
|
|
|
PropertyChanged += OnWindowStateChanged;
|
2024-05-04 15:35:32 +00:00
|
|
|
|
2024-08-20 16:40:45 +00:00
|
|
|
PositionChanged += OnPositionChanged;
|
|
|
|
SizeChanged += OnSizeChanged;
|
2024-05-04 15:35:32 +00:00
|
|
|
|
2024-08-21 01:19:04 +00:00
|
|
|
|
2024-05-04 15:35:32 +00:00
|
|
|
//select first element as default
|
|
|
|
var nv = this.FindControl<NavigationView>("NavView");
|
|
|
|
nv.SelectedItem = nv.MenuItems.ElementAt(0);
|
2024-05-05 16:03:07 +00:00
|
|
|
selectedNavVieItem = nv.SelectedItem;
|
2024-05-24 22:46:11 +00:00
|
|
|
|
2024-05-04 15:35:32 +00:00
|
|
|
MessageBus.Current.Listen<NavigationMessage>()
|
|
|
|
.Subscribe(message => {
|
|
|
|
if (message.Refresh){
|
|
|
|
navigationStack.Pop();
|
|
|
|
var viewModel = Activator.CreateInstance(message.ViewModelType);
|
2024-06-21 01:49:44 +00:00
|
|
|
if (viewModel is SeriesPageViewModel){
|
|
|
|
((SeriesPageViewModel)viewModel).SetStorageProvider(StorageProvider);
|
|
|
|
}
|
2024-08-16 12:21:23 +00:00
|
|
|
|
2024-05-04 15:35:32 +00:00
|
|
|
navigationStack.Push(viewModel);
|
|
|
|
nv.Content = viewModel;
|
|
|
|
} else if (!message.Back && message.ViewModelType != null){
|
|
|
|
var viewModel = Activator.CreateInstance(message.ViewModelType);
|
2024-06-21 01:49:44 +00:00
|
|
|
if (viewModel is SeriesPageViewModel){
|
|
|
|
((SeriesPageViewModel)viewModel).SetStorageProvider(StorageProvider);
|
|
|
|
}
|
2024-08-16 12:21:23 +00:00
|
|
|
|
2024-05-04 15:35:32 +00:00
|
|
|
navigationStack.Push(viewModel);
|
|
|
|
nv.Content = viewModel;
|
|
|
|
} else{
|
|
|
|
navigationStack.Pop();
|
|
|
|
var viewModel = navigationStack.Peek();
|
|
|
|
nv.Content = viewModel;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
MessageBus.Current.Listen<ToastMessage>()
|
|
|
|
.Subscribe(message => ShowToast(message.Message, message.Type, message.Seconds));
|
|
|
|
}
|
|
|
|
|
2024-05-24 22:46:11 +00:00
|
|
|
public async void ShowError(string message){
|
|
|
|
var dialog = new ContentDialog(){
|
|
|
|
Title = "Error",
|
|
|
|
Content = message,
|
|
|
|
CloseButtonText = "Close"
|
|
|
|
};
|
2024-05-05 16:03:07 +00:00
|
|
|
|
2024-05-24 22:46:11 +00:00
|
|
|
_ = await dialog.ShowAsync();
|
2024-05-04 15:35:32 +00:00
|
|
|
}
|
|
|
|
|
2024-05-24 22:46:11 +00:00
|
|
|
|
2024-05-04 15:35:32 +00:00
|
|
|
public void ShowToast(string message, ToastType type, int durationInSeconds = 5){
|
|
|
|
this.FindControl<ToastNotification>("Toast").Show(message, type, durationInSeconds);
|
|
|
|
}
|
|
|
|
|
2024-05-24 22:46:11 +00:00
|
|
|
|
2024-05-04 15:35:32 +00:00
|
|
|
private void NavView_SelectionChanged(object? sender, NavigationViewSelectionChangedEventArgs e){
|
|
|
|
if (sender is NavigationView navView){
|
|
|
|
var selectedItem = navView.SelectedItem as NavigationViewItem;
|
|
|
|
if (selectedItem != null){
|
|
|
|
switch (selectedItem.Tag){
|
|
|
|
case "DownloadQueue":
|
2024-05-05 16:03:07 +00:00
|
|
|
navView.Content = Activator.CreateInstance(typeof(DownloadsPageViewModel));
|
|
|
|
selectedNavVieItem = selectedItem;
|
2024-05-04 15:35:32 +00:00
|
|
|
break;
|
|
|
|
case "AddDownload":
|
2024-05-05 16:03:07 +00:00
|
|
|
navView.Content = Activator.CreateInstance(typeof(AddDownloadPageViewModel));
|
|
|
|
selectedNavVieItem = selectedItem;
|
2024-05-04 15:35:32 +00:00
|
|
|
break;
|
|
|
|
case "Calendar":
|
2024-05-05 16:03:07 +00:00
|
|
|
navView.Content = Activator.CreateInstance(typeof(CalendarPageViewModel));
|
|
|
|
selectedNavVieItem = selectedItem;
|
2024-05-04 15:35:32 +00:00
|
|
|
break;
|
|
|
|
case "History":
|
2024-05-05 16:03:07 +00:00
|
|
|
navView.Content = Activator.CreateInstance(typeof(HistoryPageViewModel));
|
2024-08-16 12:21:23 +00:00
|
|
|
if (navView.Content is HistoryPageViewModel){
|
2024-06-26 22:04:50 +00:00
|
|
|
((HistoryPageViewModel)navView.Content).SetStorageProvider(StorageProvider);
|
|
|
|
}
|
2024-08-16 12:21:23 +00:00
|
|
|
|
2024-05-04 15:35:32 +00:00
|
|
|
navigationStack.Clear();
|
2024-05-05 16:03:07 +00:00
|
|
|
navigationStack.Push(navView.Content);
|
|
|
|
selectedNavVieItem = selectedItem;
|
2024-05-04 15:35:32 +00:00
|
|
|
break;
|
|
|
|
case "Account":
|
2024-05-05 16:03:07 +00:00
|
|
|
navView.Content = Activator.CreateInstance(typeof(AccountPageViewModel));
|
|
|
|
selectedNavVieItem = selectedItem;
|
2024-05-04 15:35:32 +00:00
|
|
|
break;
|
|
|
|
case "Settings":
|
2024-06-21 01:49:44 +00:00
|
|
|
var viewModel = (SettingsPageViewModel)Activator.CreateInstance(typeof(SettingsPageViewModel));
|
|
|
|
viewModel.SetStorageProvider(StorageProvider);
|
|
|
|
navView.Content = viewModel;
|
2024-05-05 16:03:07 +00:00
|
|
|
selectedNavVieItem = selectedItem;
|
|
|
|
break;
|
|
|
|
case "UpdateAvailable":
|
|
|
|
Updater.Instance.DownloadAndUpdateAsync();
|
2024-05-24 22:46:11 +00:00
|
|
|
ShowUpdateDialog();
|
2024-05-04 15:35:32 +00:00
|
|
|
break;
|
|
|
|
default:
|
2024-05-05 16:03:07 +00:00
|
|
|
// (sender as NavigationView).Content = Activator.CreateInstance(typeof(DownloadsPageViewModel));
|
2024-05-04 15:35:32 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-05-05 16:03:07 +00:00
|
|
|
|
2024-05-24 22:46:11 +00:00
|
|
|
public async void ShowUpdateDialog(){
|
|
|
|
var dialog = new ContentDialog(){
|
|
|
|
Title = "Updating",
|
|
|
|
// CloseButtonText = "Close"
|
|
|
|
};
|
2024-05-05 16:03:07 +00:00
|
|
|
|
2024-05-24 22:46:11 +00:00
|
|
|
var viewModel = new ContentDialogUpdateViewModel(dialog);
|
|
|
|
dialog.Content = new ContentDialogUpdateView(){
|
|
|
|
DataContext = viewModel
|
|
|
|
};
|
|
|
|
|
|
|
|
_ = await dialog.ShowAsync();
|
|
|
|
}
|
2024-08-16 12:21:23 +00:00
|
|
|
|
|
|
|
private void OnOpened(object sender, EventArgs e){
|
|
|
|
if (File.Exists(CfgManager.PathWindowSettings)){
|
|
|
|
var settings = JsonConvert.DeserializeObject<WindowSettings>(File.ReadAllText(CfgManager.PathWindowSettings));
|
|
|
|
if (settings != null){
|
|
|
|
var screens = Screens.All;
|
|
|
|
if (settings.ScreenIndex >= 0 && settings.ScreenIndex < screens.Count){
|
|
|
|
var screen = screens[settings.ScreenIndex];
|
|
|
|
|
2024-08-20 16:40:45 +00:00
|
|
|
// Restore the position first
|
|
|
|
Position = new PixelPoint(settings.PosX, settings.PosY + TitleBarHeightAdjustment);
|
2024-08-16 12:21:23 +00:00
|
|
|
|
2024-08-20 16:40:45 +00:00
|
|
|
// Restore the size
|
|
|
|
Width = settings.Width;
|
|
|
|
Height = settings.Height - TitleBarHeightAdjustment;
|
|
|
|
|
|
|
|
// Set restore size and position for non-maximized state
|
|
|
|
_restoreSize = new Size(settings.Width, settings.Height);
|
|
|
|
_restorePosition = new PixelPoint(settings.PosX, settings.PosY + TitleBarHeightAdjustment);
|
|
|
|
|
|
|
|
// Ensure the window is on the correct screen before maximizing
|
2024-08-21 01:19:04 +00:00
|
|
|
Position = new PixelPoint(settings.PosX, settings.PosY + TitleBarHeightAdjustment);
|
2024-08-20 16:40:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (settings.IsMaximized){
|
|
|
|
// Maximize the window after setting its position on the correct screen
|
|
|
|
WindowState = WindowState.Maximized;
|
2024-08-16 12:21:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void OnClosing(object sender, System.ComponentModel.CancelEventArgs e){
|
|
|
|
var screens = Screens.All;
|
|
|
|
int screenIndex = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < screens.Count; i++){
|
|
|
|
if (screens[i].Bounds.Contains(Position)){
|
|
|
|
screenIndex = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var settings = new WindowSettings{
|
2024-08-21 01:19:04 +00:00
|
|
|
Width = WindowState == WindowState.Maximized ? _restoreSize.Width : Width,
|
|
|
|
Height = WindowState == WindowState.Maximized ? _restoreSize.Height : Height,
|
2024-08-16 12:21:23 +00:00
|
|
|
ScreenIndex = screenIndex,
|
2024-08-21 01:19:04 +00:00
|
|
|
PosX = WindowState == WindowState.Maximized ? _restorePosition.X : Position.X,
|
|
|
|
PosY = WindowState == WindowState.Maximized ? _restorePosition.Y : Position.Y,
|
|
|
|
IsMaximized = WindowState == WindowState.Maximized
|
2024-08-16 12:21:23 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
File.WriteAllText(CfgManager.PathWindowSettings, JsonConvert.SerializeObject(settings, Formatting.Indented));
|
2024-08-21 01:19:04 +00:00
|
|
|
|
|
|
|
FileNameManager.DeleteEmptyFolders(!string.IsNullOrEmpty(CrunchyrollManager.Instance.CrunOptions.DownloadTempDirPath) ? CrunchyrollManager.Instance.CrunOptions.DownloadTempDirPath : CfgManager.PathTEMP_DIR);
|
2024-05-04 15:35:32 +00:00
|
|
|
}
|
2024-08-20 16:40:45 +00:00
|
|
|
|
|
|
|
private void OnWindowStateChanged(object sender, AvaloniaPropertyChangedEventArgs e){
|
|
|
|
if (e.Property == Window.WindowStateProperty){
|
|
|
|
if (WindowState == WindowState.Normal){
|
|
|
|
// When the window is restored to normal, use the stored restore size and position
|
|
|
|
Width = _restoreSize.Width;
|
|
|
|
Height = _restoreSize.Height;
|
|
|
|
Position = _restorePosition;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void OnPositionChanged(object sender, PixelPointEventArgs e){
|
|
|
|
if (WindowState == WindowState.Normal){
|
|
|
|
_restorePosition = e.Point;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void OnSizeChanged(object sender, SizeChangedEventArgs e){
|
|
|
|
if (WindowState == WindowState.Normal){
|
|
|
|
_restoreSize = e.NewSize;
|
|
|
|
}
|
|
|
|
}
|
2024-05-04 15:35:32 +00:00
|
|
|
}
|